drbd: detect modification of in-flight buffers
authorLars Ellenberg <lars.ellenberg@linbit.com>
Wed, 10 Nov 2010 09:36:52 +0000 (10:36 +0100)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Thu, 10 Mar 2011 10:19:08 +0000 (11:19 +0100)
With data-integrity digest enabled, double-check on the sending side
for modifications by upper layers of buffers under write back,
so we can tell it appart from corruption on the "wire".

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
drivers/block/drbd/drbd_main.c
drivers/block/drbd/drbd_receiver.c

index 6afb81f807bd47a86daf583623ee8a9a6865f470..451fc36a85cb353c8ba2bac6b81988d6c844c5b1 100644 (file)
@@ -2537,10 +2537,36 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
                ok = drbd_send(mdev, mdev->data.socket, dgb, dgs, 0);
        }
        if (ok) {
-               if (mdev->net_conf->wire_protocol == DRBD_PROT_A)
+               /* For protocol A, we have to memcpy the payload into
+                * socket buffers, as we may complete right away
+                * as soon as we handed it over to tcp, at which point the data
+                * pages may become invalid.
+                *
+                * For data-integrity enabled, we copy it as well, so we can be
+                * sure that even if the bio pages may still be modified, it
+                * won't change the data on the wire, thus if the digest checks
+                * out ok after sending on this side, but does not fit on the
+                * receiving side, we sure have detected corruption elsewhere.
+                */
+               if (mdev->net_conf->wire_protocol == DRBD_PROT_A || dgs)
                        ok = _drbd_send_bio(mdev, req->master_bio);
                else
                        ok = _drbd_send_zc_bio(mdev, req->master_bio);
+
+               /* double check digest, sometimes buffers have been modified in flight. */
+               if (dgs > 0 && dgs <= 64) {
+                       /* 64 byte, 512 bit, is the larges digest size
+                        * currently supported in kernel crypto. */
+                       unsigned char digest[64];
+                       drbd_csum_bio(mdev, mdev->integrity_w_tfm, req->master_bio, digest);
+                       if (memcmp(mdev->int_dig_out, digest, dgs)) {
+                               dev_warn(DEV,
+                                       "Digest mismatch, buffer modified by upper layers during write: %llus +%u\n",
+                                       (unsigned long long)req->sector, req->size);
+                       }
+               } /* else if (dgs > 64) {
+                    ... Be noisy about digest too large ...
+               } */
        }
 
        drbd_put_data_sock(mdev);
index d0e19a242af45db4e8d79b47ae84f0c656dc1605..ca213c6e5f9d4074edf0972e1e377f890579c005 100644 (file)
@@ -1281,7 +1281,8 @@ read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __
        if (dgs) {
                drbd_csum_ee(mdev, mdev->integrity_r_tfm, e, dig_vv);
                if (memcmp(dig_in, dig_vv, dgs)) {
-                       dev_err(DEV, "Digest integrity check FAILED.\n");
+                       dev_err(DEV, "Digest integrity check FAILED: %llus +%u\n",
+                               (unsigned long long)sector, data_size);
                        drbd_bcast_ee(mdev, "digest failed",
                                        dgs, dig_in, dig_vv, e);
                        drbd_free_ee(mdev, e);