#define TCP_CA_MAX     128
 #define TCP_CA_BUF_MAX (TCP_CA_NAME_MAX*TCP_CA_MAX)
 
+#define TCP_CA_UNSPEC  0
+
 /* Algorithm can be set on socket without CAP_NET_ADMIN privileges */
 #define TCP_CONG_NON_RESTRICTED 0x1
 /* Requires ECN/ECT set on all packets */
 
 struct tcp_congestion_ops {
        struct list_head        list;
-       unsigned long flags;
+       u32 key;
+       u32 flags;
 
        /* initialize private data (optional) */
        void (*init)(struct sock *sk);
 void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked);
 extern struct tcp_congestion_ops tcp_reno;
 
+struct tcp_congestion_ops *tcp_ca_find_key(u32 key);
+u32 tcp_ca_get_key_by_name(const char *name);
+char *tcp_ca_get_name_by_key(u32 key, char *buffer);
+
 static inline bool tcp_ca_needs_ecn(const struct sock *sk)
 {
        const struct inet_connection_sock *icsk = inet_csk(sk);
 
 #include <linux/types.h>
 #include <linux/list.h>
 #include <linux/gfp.h>
+#include <linux/jhash.h>
 #include <net/tcp.h>
 
 static DEFINE_SPINLOCK(tcp_cong_list_lock);
        return NULL;
 }
 
+/* Must be called with rcu lock held */
+static const struct tcp_congestion_ops *__tcp_ca_find_autoload(const char *name)
+{
+       const struct tcp_congestion_ops *ca = tcp_ca_find(name);
+#ifdef CONFIG_MODULES
+       if (!ca && capable(CAP_NET_ADMIN)) {
+               rcu_read_unlock();
+               request_module("tcp_%s", name);
+               rcu_read_lock();
+               ca = tcp_ca_find(name);
+       }
+#endif
+       return ca;
+}
+
+/* Simple linear search, not much in here. */
+struct tcp_congestion_ops *tcp_ca_find_key(u32 key)
+{
+       struct tcp_congestion_ops *e;
+
+       list_for_each_entry_rcu(e, &tcp_cong_list, list) {
+               if (e->key == key)
+                       return e;
+       }
+
+       return NULL;
+}
+
 /*
  * Attach new congestion control algorithm to the list
  * of available options.
                return -EINVAL;
        }
 
+       ca->key = jhash(ca->name, sizeof(ca->name), strlen(ca->name));
+
        spin_lock(&tcp_cong_list_lock);
-       if (tcp_ca_find(ca->name)) {
-               pr_notice("%s already registered\n", ca->name);
+       if (ca->key == TCP_CA_UNSPEC || tcp_ca_find_key(ca->key)) {
+               pr_notice("%s already registered or non-unique key\n",
+                         ca->name);
                ret = -EEXIST;
        } else {
                list_add_tail_rcu(&ca->list, &tcp_cong_list);
        spin_lock(&tcp_cong_list_lock);
        list_del_rcu(&ca->list);
        spin_unlock(&tcp_cong_list_lock);
+
+       /* Wait for outstanding readers to complete before the
+        * module gets removed entirely.
+        *
+        * A try_module_get() should fail by now as our module is
+        * in "going" state since no refs are held anymore and
+        * module_exit() handler being called.
+        */
+       synchronize_rcu();
 }
 EXPORT_SYMBOL_GPL(tcp_unregister_congestion_control);
 
+u32 tcp_ca_get_key_by_name(const char *name)
+{
+       const struct tcp_congestion_ops *ca;
+       u32 key;
+
+       might_sleep();
+
+       rcu_read_lock();
+       ca = __tcp_ca_find_autoload(name);
+       key = ca ? ca->key : TCP_CA_UNSPEC;
+       rcu_read_unlock();
+
+       return key;
+}
+EXPORT_SYMBOL_GPL(tcp_ca_get_key_by_name);
+
+char *tcp_ca_get_name_by_key(u32 key, char *buffer)
+{
+       const struct tcp_congestion_ops *ca;
+       char *ret = NULL;
+
+       rcu_read_lock();
+       ca = tcp_ca_find_key(key);
+       if (ca)
+               ret = strncpy(buffer, ca->name,
+                             TCP_CA_NAME_MAX);
+       rcu_read_unlock();
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tcp_ca_get_name_by_key);
+
 /* Assign choice of congestion control. */
 void tcp_assign_congestion_control(struct sock *sk)
 {
 int tcp_set_congestion_control(struct sock *sk, const char *name)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
-       struct tcp_congestion_ops *ca;
+       const struct tcp_congestion_ops *ca;
        int err = 0;
 
-       rcu_read_lock();
-       ca = tcp_ca_find(name);
+       if (icsk->icsk_ca_dst_locked)
+               return -EPERM;
 
-       /* no change asking for existing value */
+       rcu_read_lock();
+       ca = __tcp_ca_find_autoload(name);
+       /* No change asking for existing value */
        if (ca == icsk->icsk_ca_ops)
                goto out;
-
-#ifdef CONFIG_MODULES
-       /* not found attempt to autoload module */
-       if (!ca && capable(CAP_NET_ADMIN)) {
-               rcu_read_unlock();
-               request_module("tcp_%s", name);
-               rcu_read_lock();
-               ca = tcp_ca_find(name);
-       }
-#endif
        if (!ca)
                err = -ENOENT;
        else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) ||