| From 6937040e44149f80b6a5482c9fce925356d13d20 Mon Sep 17 00:00:00 2001 |
| From: Roland Kammerer <roland.kammerer@linbit.com> |
| Date: Thu, 20 Dec 2018 17:23:28 +0100 |
| Subject: drbd: narrow rcu_read_lock in drbd_sync_handshake |
| |
| [ Upstream commit d29e89e34952a9ad02c77109c71a80043544296e ] |
| |
| So far there was the possibility that we called |
| genlmsg_new(GFP_NOIO)/mutex_lock() while holding an rcu_read_lock(). |
| |
| This included cases like: |
| |
| drbd_sync_handshake (acquire the RCU lock) |
| drbd_asb_recover_1p |
| drbd_khelper |
| drbd_bcast_event |
| genlmsg_new(GFP_NOIO) --> may sleep |
| |
| drbd_sync_handshake (acquire the RCU lock) |
| drbd_asb_recover_1p |
| drbd_khelper |
| notify_helper |
| genlmsg_new(GFP_NOIO) --> may sleep |
| |
| drbd_sync_handshake (acquire the RCU lock) |
| drbd_asb_recover_1p |
| drbd_khelper |
| notify_helper |
| mutex_lock --> may sleep |
| |
| While using GFP_ATOMIC whould have been possible in the first two cases, |
| the real fix is to narrow the rcu_read_lock. |
| |
| Reported-by: Jia-Ju Bai <baijiaju1990@163.com> |
| Reviewed-by: Lars Ellenberg <lars.ellenberg@linbit.com> |
| Signed-off-by: Roland Kammerer <roland.kammerer@linbit.com> |
| Signed-off-by: Jens Axboe <axboe@kernel.dk> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/block/drbd/drbd_receiver.c | 11 ++++++----- |
| 1 file changed, 6 insertions(+), 5 deletions(-) |
| |
| diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c |
| index 796eaf347dc0..143c5a666e25 100644 |
| --- a/drivers/block/drbd/drbd_receiver.c |
| +++ b/drivers/block/drbd/drbd_receiver.c |
| @@ -3361,7 +3361,7 @@ static enum drbd_conns drbd_sync_handshake(struct drbd_peer_device *peer_device, |
| enum drbd_conns rv = C_MASK; |
| enum drbd_disk_state mydisk; |
| struct net_conf *nc; |
| - int hg, rule_nr, rr_conflict, tentative; |
| + int hg, rule_nr, rr_conflict, tentative, always_asbp; |
| |
| mydisk = device->state.disk; |
| if (mydisk == D_NEGOTIATING) |
| @@ -3412,8 +3412,12 @@ static enum drbd_conns drbd_sync_handshake(struct drbd_peer_device *peer_device, |
| |
| rcu_read_lock(); |
| nc = rcu_dereference(peer_device->connection->net_conf); |
| + always_asbp = nc->always_asbp; |
| + rr_conflict = nc->rr_conflict; |
| + tentative = nc->tentative; |
| + rcu_read_unlock(); |
| |
| - if (hg == 100 || (hg == -100 && nc->always_asbp)) { |
| + if (hg == 100 || (hg == -100 && always_asbp)) { |
| int pcount = (device->state.role == R_PRIMARY) |
| + (peer_role == R_PRIMARY); |
| int forced = (hg == -100); |
| @@ -3452,9 +3456,6 @@ static enum drbd_conns drbd_sync_handshake(struct drbd_peer_device *peer_device, |
| "Sync from %s node\n", |
| (hg < 0) ? "peer" : "this"); |
| } |
| - rr_conflict = nc->rr_conflict; |
| - tentative = nc->tentative; |
| - rcu_read_unlock(); |
| |
| if (hg == -100) { |
| /* FIXME this log message is not correct if we end up here |
| -- |
| 2.19.1 |
| |