Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

IB/ipath: Fix potential deadlock with multicast spinlocks

Lockdep found the following potential deadlock between mcast_lock and
n_mcast_grps_lock: mcast_lock is taken from both interrupt context and
process context, so spin_lock_irqsave() must be used to take it.
n_mcast_grps_lock is only taken from process context, so at first it
seems safe to take it with plain spin_lock(); however, it also nests
inside mcast_lock, and hence we could deadlock:

cpu A cpu B
ipath_mcast_add():
spin_lock_irq(&mcast_lock);

ipath_mcast_detach():
spin_lock(&n_mcast_grps_lock);

<enter interrupt>

ipath_mcast_find():
spin_lock_irqsave(&mcast_lock);

spin_lock(&n_mcast_grps_lock);

Fix this by using spin_lock_irq() to take n_mcast_grps_lock.

Signed-off-by: Roland Dreier <rolandd@cisco.com>

+7 -9
+7 -9
drivers/infiniband/hw/ipath/ipath_verbs_mcast.c
··· 165 165 { 166 166 struct rb_node **n = &mcast_tree.rb_node; 167 167 struct rb_node *pn = NULL; 168 - unsigned long flags; 169 168 int ret; 170 169 171 - spin_lock_irqsave(&mcast_lock, flags); 170 + spin_lock_irq(&mcast_lock); 172 171 173 172 while (*n) { 174 173 struct ipath_mcast *tmcast; ··· 227 228 ret = 0; 228 229 229 230 bail: 230 - spin_unlock_irqrestore(&mcast_lock, flags); 231 + spin_unlock_irq(&mcast_lock); 231 232 232 233 return ret; 233 234 } ··· 288 289 struct ipath_mcast *mcast = NULL; 289 290 struct ipath_mcast_qp *p, *tmp; 290 291 struct rb_node *n; 291 - unsigned long flags; 292 292 int last = 0; 293 293 int ret; 294 294 295 - spin_lock_irqsave(&mcast_lock, flags); 295 + spin_lock_irq(&mcast_lock); 296 296 297 297 /* Find the GID in the mcast table. */ 298 298 n = mcast_tree.rb_node; 299 299 while (1) { 300 300 if (n == NULL) { 301 - spin_unlock_irqrestore(&mcast_lock, flags); 301 + spin_unlock_irq(&mcast_lock); 302 302 ret = -EINVAL; 303 303 goto bail; 304 304 } ··· 332 334 break; 333 335 } 334 336 335 - spin_unlock_irqrestore(&mcast_lock, flags); 337 + spin_unlock_irq(&mcast_lock); 336 338 337 339 if (p) { 338 340 /* ··· 346 348 atomic_dec(&mcast->refcount); 347 349 wait_event(mcast->wait, !atomic_read(&mcast->refcount)); 348 350 ipath_mcast_free(mcast); 349 - spin_lock(&dev->n_mcast_grps_lock); 351 + spin_lock_irq(&dev->n_mcast_grps_lock); 350 352 dev->n_mcast_grps_allocated--; 351 - spin_unlock(&dev->n_mcast_grps_lock); 353 + spin_unlock_irq(&dev->n_mcast_grps_lock); 352 354 } 353 355 354 356 ret = 0;