RDS: have sockets get transport module references
authorZach Brown <zach.brown@oracle.com>
Fri, 23 Jul 2010 17:32:31 +0000 (10:32 -0700)
committerAndy Grover <andy.grover@oracle.com>
Thu, 9 Sep 2010 01:16:47 +0000 (18:16 -0700)
Right now there's nothing to stop the various paths that use
rs->rs_transport from racing with rmmod and executing freed transport
code.  The simple fix is to have binding to a transport also hold a
reference to the transport's module, removing this class of races.

We already had an unused t_owner field which was set for the modular
transports and which wasn't set for the built-in loop transport.

Signed-off-by: Zach Brown <zach.brown@oracle.com>
net/rds/af_rds.c
net/rds/connection.c
net/rds/rds.h
net/rds/transport.c

index 57ef0ec4f03df341a8505a97e34f84499ec3c6a5..8e3886d353b512e59cfacd610fdf6064b733f0bf 100644 (file)
@@ -90,6 +90,8 @@ static int rds_release(struct socket *sock)
        rds_sock_count--;
        spin_unlock_irqrestore(&rds_sock_lock, flags);
 
+       rds_trans_put(rs->rs_transport);
+
        sock->sk = NULL;
        sock_put(sk);
 out:
index 75a1a37d64d34e5975bbce1c262454a87f2c483f..968b7a798398fce90eeb2d8b2d2b1073770a5950 100644 (file)
@@ -117,6 +117,7 @@ static struct rds_connection *__rds_conn_create(__be32 laddr, __be32 faddr,
 {
        struct rds_connection *conn, *parent = NULL;
        struct hlist_head *head = rds_conn_bucket(laddr, faddr);
+       struct rds_transport *loop_trans;
        unsigned long flags;
        int ret;
 
@@ -163,7 +164,9 @@ static struct rds_connection *__rds_conn_create(__be32 laddr, __be32 faddr,
         * can bind to the destination address then we'd rather the messages
         * flow through loopback rather than either transport.
         */
-       if (rds_trans_get_preferred(faddr)) {
+       loop_trans = rds_trans_get_preferred(faddr);
+       if (loop_trans) {
+               rds_trans_put(loop_trans);
                conn->c_loopback = 1;
                if (is_outgoing && trans->t_prefer_loopback) {
                        /* "outgoing" connection - and the transport
index 2ff7fc9f0539c4b5c6573befc26646f8d8494297..aab5e949fa93682dd0addb8b11b4348406262a42 100644 (file)
@@ -798,6 +798,7 @@ void rds_connect_complete(struct rds_connection *conn);
 int rds_trans_register(struct rds_transport *trans);
 void rds_trans_unregister(struct rds_transport *trans);
 struct rds_transport *rds_trans_get_preferred(__be32 addr);
+void rds_trans_put(struct rds_transport *trans);
 unsigned int rds_trans_stats_info_copy(struct rds_info_iterator *iter,
                                       unsigned int avail);
 int rds_trans_init(void);
index 7e106790135353c036d6fa0dcba3345cf072f3dd..7f2ac4fec3678b28715b95094c6346bcc49333e1 100644 (file)
@@ -71,19 +71,28 @@ void rds_trans_unregister(struct rds_transport *trans)
 }
 EXPORT_SYMBOL_GPL(rds_trans_unregister);
 
+void rds_trans_put(struct rds_transport *trans)
+{
+       if (trans && trans->t_owner)
+               module_put(trans->t_owner);
+}
+
 struct rds_transport *rds_trans_get_preferred(__be32 addr)
 {
        struct rds_transport *ret = NULL;
-       int i;
+       struct rds_transport *trans;
+       unsigned int i;
 
        if (IN_LOOPBACK(ntohl(addr)))
                return &rds_loop_transport;
 
        down_read(&rds_trans_sem);
-       for (i = 0; i < RDS_TRANS_COUNT; i++)
-       {
-               if (transports[i] && (transports[i]->laddr_check(addr) == 0)) {
-                       ret = transports[i];
+       for (i = 0; i < RDS_TRANS_COUNT; i++) {
+               trans = transports[i];
+
+               if (trans && (trans->laddr_check(addr) == 0) &&
+                   (!trans->t_owner || try_module_get(trans->t_owner))) {
+                       ret = trans;
                        break;
                }
        }