summaryrefslogtreecommitdiff
path: root/net/rds/iw_cm.c
diff options
context:
space:
mode:
authorAndy Grover <andy.grover@oracle.com>2009-04-01 12:20:19 +0400
committerDavid S. Miller <davem@davemloft.net>2009-04-02 11:52:22 +0400
commit745cbccac3fe8cead529a1b3358e1e86a1505bfa (patch)
tree6c865b19881dbdd64556cc1827f6966d8a646a38 /net/rds/iw_cm.c
parentf1cffcbfcc53b825da7d1d26244aabd8dccb24aa (diff)
downloadlinux-745cbccac3fe8cead529a1b3358e1e86a1505bfa.tar.xz
RDS: Rewrite connection cleanup, fixing oops on rmmod
This fixes a bug where a connection was unexpectedly not on *any* list while being destroyed. It also cleans up some code duplication and regularizes some function names. * Grab appropriate lock in conn_free() and explain in comment * Ensure via locking that a conn is never not on either a dev's list or the nodev list * Add rds_xx_remove_conn() to match rds_xx_add_conn() * Make rds_xx_add_conn() return void * Rename remove_{,nodev_}conns() to destroy_{,nodev_}conns() and unify their implementation in a helper function * Document lock ordering as nodev conn_lock before dev_conn_lock Reported-by: Yosef Etigin <yosefe@voltaire.com> Signed-off-by: Andy Grover <andy.grover@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/rds/iw_cm.c')
-rw-r--r--net/rds/iw_cm.c35
1 files changed, 19 insertions, 16 deletions
diff --git a/net/rds/iw_cm.c b/net/rds/iw_cm.c
index 57ecb3d4b8a5..0ffaa3e97ad6 100644
--- a/net/rds/iw_cm.c
+++ b/net/rds/iw_cm.c
@@ -86,9 +86,7 @@ void rds_iw_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
err = rds_iw_update_cm_id(rds_iwdev, ic->i_cm_id);
if (err)
printk(KERN_ERR "rds_iw_update_ipaddr failed (%d)\n", err);
- err = rds_iw_add_conn(rds_iwdev, conn);
- if (err)
- printk(KERN_ERR "rds_iw_add_conn failed (%d)\n", err);
+ rds_iw_add_conn(rds_iwdev, conn);
/* If the peer gave us the last packet it saw, process this as if
* we had received a regular ACK. */
@@ -637,19 +635,8 @@ void rds_iw_conn_shutdown(struct rds_connection *conn)
* Move connection back to the nodev list.
* Remove cm_id from the device cm_id list.
*/
- if (ic->rds_iwdev) {
-
- spin_lock_irq(&ic->rds_iwdev->spinlock);
- BUG_ON(list_empty(&ic->iw_node));
- list_del(&ic->iw_node);
- spin_unlock_irq(&ic->rds_iwdev->spinlock);
-
- spin_lock_irq(&iw_nodev_conns_lock);
- list_add_tail(&ic->iw_node, &iw_nodev_conns);
- spin_unlock_irq(&iw_nodev_conns_lock);
- rds_iw_remove_cm_id(ic->rds_iwdev, ic->i_cm_id);
- ic->rds_iwdev = NULL;
- }
+ if (ic->rds_iwdev)
+ rds_iw_remove_conn(ic->rds_iwdev, conn);
rdma_destroy_id(ic->i_cm_id);
@@ -726,11 +713,27 @@ int rds_iw_conn_alloc(struct rds_connection *conn, gfp_t gfp)
return 0;
}
+/*
+ * Free a connection. Connection must be shut down and not set for reconnect.
+ */
void rds_iw_conn_free(void *arg)
{
struct rds_iw_connection *ic = arg;
+ spinlock_t *lock_ptr;
+
rdsdebug("ic %p\n", ic);
+
+ /*
+ * Conn is either on a dev's list or on the nodev list.
+ * A race with shutdown() or connect() would cause problems
+ * (since rds_iwdev would change) but that should never happen.
+ */
+ lock_ptr = ic->rds_iwdev ? &ic->rds_iwdev->spinlock : &iw_nodev_conns_lock;
+
+ spin_lock_irq(lock_ptr);
list_del(&ic->iw_node);
+ spin_unlock_irq(lock_ptr);
+
kfree(ic);
}