Browse Source

drbd: add missing rcu locks around recently introduced idr_for_each

Recent commit
 drbd: Move write_ordering from mdev to tconn
introduced a new idr_for_each loop over all volumes,
but did not take necessary rcu locks or krefs.

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Lars Ellenberg 13 years ago
parent
commit
615e087fbd
1 changed files with 20 additions and 12 deletions
  1. 20 12
      drivers/block/drbd/drbd_receiver.c

+ 20 - 12
drivers/block/drbd/drbd_receiver.c

@@ -1096,22 +1096,30 @@ static void drbd_flush(struct drbd_tconn *tconn)
 	int vnr;
 
 	if (tconn->write_ordering >= WO_bdev_flush) {
+		rcu_read_lock();
 		idr_for_each_entry(&tconn->volumes, mdev, vnr) {
-			if (get_ldev(mdev)) {
-				rv = blkdev_issue_flush(mdev->ldev->backing_bdev, GFP_KERNEL,
-							NULL);
-				put_ldev(mdev);
+			if (!get_ldev(mdev))
+				continue;
+			kref_get(&mdev->kref);
+			rcu_read_unlock();
 
-				if (rv) {
-					dev_info(DEV, "local disk flush failed with status %d\n", rv);
-					/* would rather check on EOPNOTSUPP, but that is not reliable.
-					 * don't try again for ANY return value != 0
-					 * if (rv == -EOPNOTSUPP) */
-					drbd_bump_write_ordering(tconn, WO_drain_io);
-					break;
-				}
+			rv = blkdev_issue_flush(mdev->ldev->backing_bdev,
+					GFP_NOIO, NULL);
+			if (rv) {
+				dev_info(DEV, "local disk flush failed with status %d\n", rv);
+				/* would rather check on EOPNOTSUPP, but that is not reliable.
+				 * don't try again for ANY return value != 0
+				 * if (rv == -EOPNOTSUPP) */
+				drbd_bump_write_ordering(tconn, WO_drain_io);
 			}
+			put_ldev(mdev);
+			kref_put(&mdev->kref, &drbd_minor_destroy);
+
+			rcu_read_lock();
+			if (rv)
+				break;
 		}
+		rcu_read_unlock();
 	}
 }