Skip to content

Commit

Permalink
drbd: Fix the data-integrity-alg setting
Browse files Browse the repository at this point in the history
The last data-integrity-alg fix made data integrity checking work when the
algorithm was changed for an established connection, but the common case of
configuring the algorithm before connecting was still broken.  Fix that.

Signed-off-by: Andreas Gruenbacher <agruen@linbit.com>
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
  • Loading branch information
Andreas Gruenbacher authored and Philipp Reisner committed Nov 8, 2012
1 parent 71fc7ee commit 7d4c782
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 90 deletions.
6 changes: 2 additions & 4 deletions drivers/block/drbd/drbd_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1775,8 +1775,7 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)

sock = &mdev->tconn->data;
p = drbd_prepare_command(mdev, sock);
dgs = (mdev->tconn->agreed_pro_version >= 87 && mdev->tconn->integrity_tfm) ?
crypto_hash_digestsize(mdev->tconn->integrity_tfm) : 0;
dgs = mdev->tconn->integrity_tfm ? crypto_hash_digestsize(mdev->tconn->integrity_tfm) : 0;

if (!p)
return -EIO;
Expand Down Expand Up @@ -1849,8 +1848,7 @@ int drbd_send_block(struct drbd_conf *mdev, enum drbd_packet cmd,
sock = &mdev->tconn->data;
p = drbd_prepare_command(mdev, sock);

dgs = (mdev->tconn->agreed_pro_version >= 87 && mdev->tconn->integrity_tfm) ?
crypto_hash_digestsize(mdev->tconn->integrity_tfm) : 0;
dgs = mdev->tconn->integrity_tfm ? crypto_hash_digestsize(mdev->tconn->integrity_tfm) : 0;

if (!p)
return -EIO;
Expand Down
20 changes: 0 additions & 20 deletions drivers/block/drbd/drbd_nl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1824,8 +1824,6 @@ struct crypto {
struct crypto_hash *csums_tfm;
struct crypto_hash *cram_hmac_tfm;
struct crypto_hash *integrity_tfm;
void *int_dig_in;
void *int_dig_vv;
};

static int
Expand All @@ -1848,7 +1846,6 @@ alloc_crypto(struct crypto *crypto, struct net_conf *new_conf)
{
char hmac_name[CRYPTO_MAX_ALG_NAME];
enum drbd_ret_code rv;
int hash_size;

rv = alloc_hash(&crypto->csums_tfm, new_conf->csums_alg,
ERR_CSUMS_ALG);
Expand All @@ -1869,23 +1866,12 @@ alloc_crypto(struct crypto *crypto, struct net_conf *new_conf)
rv = alloc_hash(&crypto->cram_hmac_tfm, hmac_name,
ERR_AUTH_ALG);
}
if (crypto->integrity_tfm) {
hash_size = crypto_hash_digestsize(crypto->integrity_tfm);
crypto->int_dig_in = kmalloc(hash_size, GFP_KERNEL);
if (!crypto->int_dig_in)
return ERR_NOMEM;
crypto->int_dig_vv = kmalloc(hash_size, GFP_KERNEL);
if (!crypto->int_dig_vv)
return ERR_NOMEM;
}

return rv;
}

static void free_crypto(struct crypto *crypto)
{
kfree(crypto->int_dig_in);
kfree(crypto->int_dig_vv);
crypto_free_hash(crypto->cram_hmac_tfm);
crypto_free_hash(crypto->integrity_tfm);
crypto_free_hash(crypto->csums_tfm);
Expand Down Expand Up @@ -1974,10 +1960,6 @@ int drbd_adm_net_opts(struct sk_buff *skb, struct genl_info *info)
crypto.verify_tfm = NULL;
}

kfree(tconn->int_dig_in);
tconn->int_dig_in = crypto.int_dig_in;
kfree(tconn->int_dig_vv);
tconn->int_dig_vv = crypto.int_dig_vv;
crypto_free_hash(tconn->integrity_tfm);
tconn->integrity_tfm = crypto.integrity_tfm;
if (tconn->cstate >= C_WF_REPORT_PARAMS && tconn->agreed_pro_version >= 100)
Expand Down Expand Up @@ -2094,8 +2076,6 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info)
rcu_assign_pointer(tconn->net_conf, new_conf);

conn_free_crypto(tconn);
tconn->int_dig_in = crypto.int_dig_in;
tconn->int_dig_vv = crypto.int_dig_vv;
tconn->cram_hmac_tfm = crypto.cram_hmac_tfm;
tconn->integrity_tfm = crypto.integrity_tfm;
tconn->csums_tfm = crypto.csums_tfm;
Expand Down
130 changes: 64 additions & 66 deletions drivers/block/drbd/drbd_receiver.c
Original file line number Diff line number Diff line change
Expand Up @@ -3024,72 +3024,7 @@ static int receive_protocol(struct drbd_tconn *tconn, struct packet_info *pi)
integrity_alg[SHARED_SECRET_MAX - 1] = 0;
}

if (pi->cmd == P_PROTOCOL_UPDATE) {
if (integrity_alg[0]) {
int hash_size;

/*
* We can only change the peer data integrity algorithm
* here. Changing our own data integrity algorithm
* requires that we send a P_PROTOCOL_UPDATE packet at
* the same time; otherwise, the peer has no way to
* tell between which packets the algorithm should
* change.
*/

peer_integrity_tfm = crypto_alloc_hash(integrity_alg, 0, CRYPTO_ALG_ASYNC);
if (!peer_integrity_tfm) {
conn_err(tconn, "peer data-integrity-alg %s not supported\n",
integrity_alg);
goto disconnect;
}

hash_size = crypto_hash_digestsize(peer_integrity_tfm);
int_dig_in = kmalloc(hash_size, GFP_KERNEL);
int_dig_vv = kmalloc(hash_size, GFP_KERNEL);
if (!(int_dig_in && int_dig_vv)) {
conn_err(tconn, "Allocation of buffers for data integrity checking failed\n");
goto disconnect;
}
}

new_net_conf = kmalloc(sizeof(struct net_conf), GFP_KERNEL);
if (!new_net_conf) {
conn_err(tconn, "Allocation of new net_conf failed\n");
goto disconnect;
}

mutex_lock(&tconn->data.mutex);
mutex_lock(&tconn->conf_update);
old_net_conf = tconn->net_conf;
*new_net_conf = *old_net_conf;

new_net_conf->wire_protocol = p_proto;
new_net_conf->after_sb_0p = convert_after_sb(p_after_sb_0p);
new_net_conf->after_sb_1p = convert_after_sb(p_after_sb_1p);
new_net_conf->after_sb_2p = convert_after_sb(p_after_sb_2p);
new_net_conf->two_primaries = p_two_primaries;
strcpy(new_net_conf->integrity_alg, integrity_alg);
new_net_conf->integrity_alg_len = strlen(integrity_alg) + 1;

rcu_assign_pointer(tconn->net_conf, new_net_conf);
mutex_unlock(&tconn->conf_update);
mutex_unlock(&tconn->data.mutex);

crypto_free_hash(tconn->peer_integrity_tfm);
kfree(tconn->int_dig_in);
kfree(tconn->int_dig_vv);
tconn->peer_integrity_tfm = peer_integrity_tfm;
tconn->int_dig_in = int_dig_in;
tconn->int_dig_vv = int_dig_vv;

if (strcmp(old_net_conf->integrity_alg, integrity_alg))
conn_info(tconn, "peer data-integrity-alg: %s\n",
integrity_alg[0] ? integrity_alg : "(none)");

synchronize_rcu();
kfree(old_net_conf);
} else {
if (pi->cmd != P_PROTOCOL_UPDATE) {
clear_bit(CONN_DRY_RUN, &tconn->flags);

if (cf & CF_DRY_RUN)
Expand Down Expand Up @@ -3135,6 +3070,69 @@ static int receive_protocol(struct drbd_tconn *tconn, struct packet_info *pi)

rcu_read_unlock();
}

if (integrity_alg[0]) {
int hash_size;

/*
* We can only change the peer data integrity algorithm
* here. Changing our own data integrity algorithm
* requires that we send a P_PROTOCOL_UPDATE packet at
* the same time; otherwise, the peer has no way to
* tell between which packets the algorithm should
* change.
*/

peer_integrity_tfm = crypto_alloc_hash(integrity_alg, 0, CRYPTO_ALG_ASYNC);
if (!peer_integrity_tfm) {
conn_err(tconn, "peer data-integrity-alg %s not supported\n",
integrity_alg);
goto disconnect;
}

hash_size = crypto_hash_digestsize(peer_integrity_tfm);
int_dig_in = kmalloc(hash_size, GFP_KERNEL);
int_dig_vv = kmalloc(hash_size, GFP_KERNEL);
if (!(int_dig_in && int_dig_vv)) {
conn_err(tconn, "Allocation of buffers for data integrity checking failed\n");
goto disconnect;
}
}

new_net_conf = kmalloc(sizeof(struct net_conf), GFP_KERNEL);
if (!new_net_conf) {
conn_err(tconn, "Allocation of new net_conf failed\n");
goto disconnect;
}

mutex_lock(&tconn->data.mutex);
mutex_lock(&tconn->conf_update);
old_net_conf = tconn->net_conf;
*new_net_conf = *old_net_conf;

new_net_conf->wire_protocol = p_proto;
new_net_conf->after_sb_0p = convert_after_sb(p_after_sb_0p);
new_net_conf->after_sb_1p = convert_after_sb(p_after_sb_1p);
new_net_conf->after_sb_2p = convert_after_sb(p_after_sb_2p);
new_net_conf->two_primaries = p_two_primaries;

rcu_assign_pointer(tconn->net_conf, new_net_conf);
mutex_unlock(&tconn->conf_update);
mutex_unlock(&tconn->data.mutex);

crypto_free_hash(tconn->peer_integrity_tfm);
kfree(tconn->int_dig_in);
kfree(tconn->int_dig_vv);
tconn->peer_integrity_tfm = peer_integrity_tfm;
tconn->int_dig_in = int_dig_in;
tconn->int_dig_vv = int_dig_vv;

if (strcmp(old_net_conf->integrity_alg, integrity_alg))
conn_info(tconn, "peer data-integrity-alg: %s\n",
integrity_alg[0] ? integrity_alg : "(none)");

synchronize_rcu();
kfree(old_net_conf);
return 0;

disconnect_rcu_unlock:
Expand Down

0 comments on commit 7d4c782

Please sign in to comment.