drbd: add missing rcu locks around recently introduced idr_for_each
authorLars Ellenberg <lars.ellenberg@linbit.com>
Thu, 17 Nov 2011 13:32:12 +0000 (14:32 +0100)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Thu, 8 Nov 2012 15:58:10 +0000 (16:58 +0100)
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>
drivers/block/drbd/drbd_receiver.c

index be8f469bc8f0a50d213f14a8d733392e19150803..9c888e5b648fa063df8587ed4394e33522fd2fbf 100644 (file)
@@ -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();
        }
 }