aboutsummaryrefslogtreecommitdiffstats
path: root/net/rds/ib_rdma.c
diff options
context:
space:
mode:
authorZach Brown <zach.brown@oracle.com>2010-05-18 18:48:51 -0400
committerAndy Grover <andy.grover@oracle.com>2010-09-08 21:15:17 -0400
commit3e0249f9c05cb77b66f7f09644ca9ca208d991a9 (patch)
tree2f8aaddb51590d36a54b8f7044224073107d77d9 /net/rds/ib_rdma.c
parent89bf9d4158b5a1b6bd00960eb2e47601ec8cc138 (diff)
RDS/IB: add refcount tracking to struct rds_ib_device
The RDS IB client .remove callback used to free the rds_ibdev for the given device unconditionally. This could race other users of the struct. This patch adds refcounting so that we only free the rds_ibdev once all of its users are done. Many rds_ibdev users are tied to connections. We give the connection a reference and change these users to reference the device in the connection instead of looking it up in the IB client data. The only user of the IB client data remaining is the first lookup of the device as connections are built up. Incrementing the reference count of a device found in the IB client data could race with final freeing so we use an RCU grace period to make sure that freeing won't happen until those lookups are done. MRs need the rds_ibdev to get at the pool that they're freed in to. They exist outside a connection and many MRs can reference different devices from one socket, so it was natural to have each MR hold a reference. MR refs can be dropped from interrupt handlers and final device teardown can block so we push it off to a work struct. Pool teardown had to be fixed to cancel its pending work instead of deadlocking waiting for all queued work, including itself, to finish. MRs get their reference from the global device list, which gets a reference. It is left unprotected by locks and remains racy. A simple global lock would be a significant bottleneck. More scalable (complicated) locking should be done carefully in a later patch. Signed-off-by: Zach Brown <zach.brown@oracle.com>
Diffstat (limited to 'net/rds/ib_rdma.c')
-rw-r--r--net/rds/ib_rdma.c14
1 files changed, 12 insertions, 2 deletions
diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c
index 4ba01b9ffd44..64b5ede037c8 100644
--- a/net/rds/ib_rdma.c
+++ b/net/rds/ib_rdma.c
@@ -87,6 +87,7 @@ static struct rds_ib_device *rds_ib_get_device(__be32 ipaddr)
87 rcu_read_lock(); 87 rcu_read_lock();
88 list_for_each_entry_rcu(i_ipaddr, &rds_ibdev->ipaddr_list, list) { 88 list_for_each_entry_rcu(i_ipaddr, &rds_ibdev->ipaddr_list, list) {
89 if (i_ipaddr->ipaddr == ipaddr) { 89 if (i_ipaddr->ipaddr == ipaddr) {
90 atomic_inc(&rds_ibdev->refcount);
90 rcu_read_unlock(); 91 rcu_read_unlock();
91 return rds_ibdev; 92 return rds_ibdev;
92 } 93 }
@@ -141,8 +142,10 @@ int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr)
141 struct rds_ib_device *rds_ibdev_old; 142 struct rds_ib_device *rds_ibdev_old;
142 143
143 rds_ibdev_old = rds_ib_get_device(ipaddr); 144 rds_ibdev_old = rds_ib_get_device(ipaddr);
144 if (rds_ibdev_old) 145 if (rds_ibdev_old) {
145 rds_ib_remove_ipaddr(rds_ibdev_old, ipaddr); 146 rds_ib_remove_ipaddr(rds_ibdev_old, ipaddr);
147 rds_ib_dev_put(rds_ibdev_old);
148 }
146 149
147 return rds_ib_add_ipaddr(rds_ibdev, ipaddr); 150 return rds_ib_add_ipaddr(rds_ibdev, ipaddr);
148} 151}
@@ -163,6 +166,7 @@ void rds_ib_add_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *con
163 spin_unlock_irq(&ib_nodev_conns_lock); 166 spin_unlock_irq(&ib_nodev_conns_lock);
164 167
165 ic->rds_ibdev = rds_ibdev; 168 ic->rds_ibdev = rds_ibdev;
169 atomic_inc(&rds_ibdev->refcount);
166} 170}
167 171
168void rds_ib_remove_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn) 172void rds_ib_remove_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn)
@@ -182,6 +186,7 @@ void rds_ib_remove_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *
182 spin_unlock(&ib_nodev_conns_lock); 186 spin_unlock(&ib_nodev_conns_lock);
183 187
184 ic->rds_ibdev = NULL; 188 ic->rds_ibdev = NULL;
189 rds_ib_dev_put(rds_ibdev);
185} 190}
186 191
187void __rds_ib_destroy_conns(struct list_head *list, spinlock_t *list_lock) 192void __rds_ib_destroy_conns(struct list_head *list, spinlock_t *list_lock)
@@ -240,7 +245,7 @@ void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev, struct rds_info_rdma_co
240 245
241void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *pool) 246void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *pool)
242{ 247{
243 flush_workqueue(rds_wq); 248 cancel_work_sync(&pool->flush_worker);
244 rds_ib_flush_mr_pool(pool, 1); 249 rds_ib_flush_mr_pool(pool, 1);
245 WARN_ON(atomic_read(&pool->item_count)); 250 WARN_ON(atomic_read(&pool->item_count));
246 WARN_ON(atomic_read(&pool->free_pinned)); 251 WARN_ON(atomic_read(&pool->free_pinned));
@@ -597,6 +602,8 @@ void rds_ib_free_mr(void *trans_private, int invalidate)
597 queue_work(rds_wq, &pool->flush_worker); 602 queue_work(rds_wq, &pool->flush_worker);
598 } 603 }
599 } 604 }
605
606 rds_ib_dev_put(rds_ibdev);
600} 607}
601 608
602void rds_ib_flush_mrs(void) 609void rds_ib_flush_mrs(void)
@@ -640,6 +647,7 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents,
640 printk(KERN_WARNING "RDS/IB: map_fmr failed (errno=%d)\n", ret); 647 printk(KERN_WARNING "RDS/IB: map_fmr failed (errno=%d)\n", ret);
641 648
642 ibmr->device = rds_ibdev; 649 ibmr->device = rds_ibdev;
650 rds_ibdev = NULL;
643 651
644 out: 652 out:
645 if (ret) { 653 if (ret) {
@@ -647,5 +655,7 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents,
647 rds_ib_free_mr(ibmr, 0); 655 rds_ib_free_mr(ibmr, 0);
648 ibmr = ERR_PTR(ret); 656 ibmr = ERR_PTR(ret);
649 } 657 }
658 if (rds_ibdev)
659 rds_ib_dev_put(rds_ibdev);
650 return ibmr; 660 return ibmr;
651} 661}