Commit de0713a3 authored by Lars Ellenberg's avatar Lars Ellenberg Committed by Greg Kroah-Hartman

drbd: ignore "all zero" peer volume sizes in handshake

[ Upstream commit 94c43a13 ]

During handshake, if we are diskless ourselves, we used to accept any size
presented by the peer.

Which could be zero if that peer was just brought up and connected
to us without having a disk attached first, in which case both
peers would just "flip" their volume sizes.

Now, even a diskless node will ignore "zero" sizes
presented by a diskless peer.

Also a currently Diskless Primary will refuse to shrink during handshake:
it may be frozen, and waiting for a "suitable" local disk or peer to
re-appear (on-no-data-accessible suspend-io). If the peer is smaller
than what we used to be, it is not suitable.

The logic for a diskless node during handshake is now supposed to be:
believe the peer, if
 - I don't have a current size myself
 - we agree on the size anyways
 - I do have a current size, am Secondary, and he has the only disk
 - I do have a current size, am Primary, and he has the only disk,
   which is larger than my current size
Signed-off-by: default avatarLars Ellenberg <lars.ellenberg@linbit.com>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent e440f9fd
...@@ -3980,6 +3980,7 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info ...@@ -3980,6 +3980,7 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
struct o_qlim *o = (connection->agreed_features & DRBD_FF_WSAME) ? p->qlim : NULL; struct o_qlim *o = (connection->agreed_features & DRBD_FF_WSAME) ? p->qlim : NULL;
enum determine_dev_size dd = DS_UNCHANGED; enum determine_dev_size dd = DS_UNCHANGED;
sector_t p_size, p_usize, p_csize, my_usize; sector_t p_size, p_usize, p_csize, my_usize;
sector_t new_size, cur_size;
int ldsc = 0; /* local disk size changed */ int ldsc = 0; /* local disk size changed */
enum dds_flags ddsf; enum dds_flags ddsf;
...@@ -3987,6 +3988,7 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info ...@@ -3987,6 +3988,7 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
if (!peer_device) if (!peer_device)
return config_unknown_volume(connection, pi); return config_unknown_volume(connection, pi);
device = peer_device->device; device = peer_device->device;
cur_size = drbd_get_capacity(device->this_bdev);
p_size = be64_to_cpu(p->d_size); p_size = be64_to_cpu(p->d_size);
p_usize = be64_to_cpu(p->u_size); p_usize = be64_to_cpu(p->u_size);
...@@ -3997,7 +3999,6 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info ...@@ -3997,7 +3999,6 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
device->p_size = p_size; device->p_size = p_size;
if (get_ldev(device)) { if (get_ldev(device)) {
sector_t new_size, cur_size;
rcu_read_lock(); rcu_read_lock();
my_usize = rcu_dereference(device->ldev->disk_conf)->disk_size; my_usize = rcu_dereference(device->ldev->disk_conf)->disk_size;
rcu_read_unlock(); rcu_read_unlock();
...@@ -4015,7 +4016,6 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info ...@@ -4015,7 +4016,6 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
/* Never shrink a device with usable data during connect. /* Never shrink a device with usable data during connect.
But allow online shrinking if we are connected. */ But allow online shrinking if we are connected. */
new_size = drbd_new_dev_size(device, device->ldev, p_usize, 0); new_size = drbd_new_dev_size(device, device->ldev, p_usize, 0);
cur_size = drbd_get_capacity(device->this_bdev);
if (new_size < cur_size && if (new_size < cur_size &&
device->state.disk >= D_OUTDATED && device->state.disk >= D_OUTDATED &&
device->state.conn < C_CONNECTED) { device->state.conn < C_CONNECTED) {
...@@ -4080,9 +4080,36 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info ...@@ -4080,9 +4080,36 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
* *
* However, if he sends a zero current size, * However, if he sends a zero current size,
* take his (user-capped or) backing disk size anyways. * take his (user-capped or) backing disk size anyways.
*
* Unless of course he does not have a disk himself.
* In which case we ignore this completely.
*/ */
sector_t new_size = p_csize ?: p_usize ?: p_size;
drbd_reconsider_queue_parameters(device, NULL, o); drbd_reconsider_queue_parameters(device, NULL, o);
drbd_set_my_capacity(device, p_csize ?: p_usize ?: p_size); if (new_size == 0) {
/* Ignore, peer does not know nothing. */
} else if (new_size == cur_size) {
/* nothing to do */
} else if (cur_size != 0 && p_size == 0) {
drbd_warn(device, "Ignored diskless peer device size (peer:%llu != me:%llu sectors)!\n",
(unsigned long long)new_size, (unsigned long long)cur_size);
} else if (new_size < cur_size && device->state.role == R_PRIMARY) {
drbd_err(device, "The peer's device size is too small! (%llu < %llu sectors); demote me first!\n",
(unsigned long long)new_size, (unsigned long long)cur_size);
conn_request_state(peer_device->connection, NS(conn, C_DISCONNECTING), CS_HARD);
return -EIO;
} else {
/* I believe the peer, if
* - I don't have a current size myself
* - we agree on the size anyways
* - I do have a current size, am Secondary,
* and he has the only disk
* - I do have a current size, am Primary,
* and he has the only disk,
* which is larger than my current size
*/
drbd_set_my_capacity(device, new_size);
}
} }
if (get_ldev(device)) { if (get_ldev(device)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment