]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/block/drbd/drbd_main.c
drbd: call local-io-error handler early
[mirror_ubuntu-artful-kernel.git] / drivers / block / drbd / drbd_main.c
index c03e87f19e862fe77dc5e30c8a600b004d695079..29a2764250794af6339aad2225df4e871f198d97 100644 (file)
@@ -140,6 +140,7 @@ struct kmem_cache *drbd_al_ext_cache;       /* activity log extents */
 mempool_t *drbd_request_mempool;
 mempool_t *drbd_ee_mempool;
 mempool_t *drbd_md_io_page_pool;
+struct bio_set *drbd_md_io_bio_set;
 
 /* I do not use a standard mempool, because:
    1) I want to hand out the pre-allocated objects first.
@@ -160,6 +161,25 @@ static const struct block_device_operations drbd_ops = {
        .release = drbd_release,
 };
 
+static void bio_destructor_drbd(struct bio *bio)
+{
+       bio_free(bio, drbd_md_io_bio_set);
+}
+
+struct bio *bio_alloc_drbd(gfp_t gfp_mask)
+{
+       struct bio *bio;
+
+       if (!drbd_md_io_bio_set)
+               return bio_alloc(gfp_mask, 1);
+
+       bio = bio_alloc_bioset(gfp_mask, 1, drbd_md_io_bio_set);
+       if (!bio)
+               return NULL;
+       bio->bi_destructor = bio_destructor_drbd;
+       return bio;
+}
+
 #ifdef __CHECKER__
 /* When checking with sparse, and this is an inline function, sparse will
    give tons of false positives. When this is a real functions sparse works.
@@ -1494,6 +1514,13 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
 
        /* Do not change the order of the if above and the two below... */
        if (os.pdsk == D_DISKLESS && ns.pdsk > D_DISKLESS) {      /* attach on the peer */
+               /* we probably will start a resync soon.
+                * make sure those things are properly reset. */
+               mdev->rs_total = 0;
+               mdev->rs_failed = 0;
+               atomic_set(&mdev->rs_pending_cnt, 0);
+               drbd_rs_cancel_all(mdev);
+
                drbd_send_uuids(mdev);
                drbd_send_state(mdev, ns);
        }
@@ -1610,9 +1637,24 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
                        eh = mdev->ldev->dc.on_io_error;
                        was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags);
 
-                       /* Immediately allow completion of all application IO, that waits
-                          for completion from the local disk. */
-                       tl_abort_disk_io(mdev);
+                       if (was_io_error && eh == EP_CALL_HELPER)
+                               drbd_khelper(mdev, "local-io-error");
+
+                       /* Immediately allow completion of all application IO,
+                        * that waits for completion from the local disk,
+                        * if this was a force-detach due to disk_timeout
+                        * or administrator request (drbdsetup detach --force).
+                        * Do NOT abort otherwise.
+                        * Aborting local requests may cause serious problems,
+                        * if requests are completed to upper layers already,
+                        * and then later the already submitted local bio completes.
+                        * This can cause DMA into former bio pages that meanwhile
+                        * have been re-used for other things.
+                        * So aborting local requests may cause crashes,
+                        * or even worse, silent data corruption.
+                        */
+                       if (test_and_clear_bit(FORCE_DETACH, &mdev->flags))
+                               tl_abort_disk_io(mdev);
 
                        /* current state still has to be D_FAILED,
                         * there is only one way out: to D_DISKLESS,
@@ -1633,9 +1675,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
                        drbd_md_sync(mdev);
                }
                put_ldev(mdev);
-
-               if (was_io_error && eh == EP_CALL_HELPER)
-                       drbd_khelper(mdev, "local-io-error");
        }
 
         /* second half of local IO error, failure to attach,
@@ -1649,10 +1688,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
                                 "ASSERT FAILED: disk is %s while going diskless\n",
                                 drbd_disk_str(mdev->state.disk));
 
-                mdev->rs_total = 0;
-                mdev->rs_failed = 0;
-                atomic_set(&mdev->rs_pending_cnt, 0);
-
                if (ns.conn >= C_CONNECTED)
                        drbd_send_state(mdev, ns);
 
@@ -3263,6 +3298,8 @@ static void drbd_destroy_mempools(void)
 
        /* D_ASSERT(atomic_read(&drbd_pp_vacant)==0); */
 
+       if (drbd_md_io_bio_set)
+               bioset_free(drbd_md_io_bio_set);
        if (drbd_md_io_page_pool)
                mempool_destroy(drbd_md_io_page_pool);
        if (drbd_ee_mempool)
@@ -3278,6 +3315,7 @@ static void drbd_destroy_mempools(void)
        if (drbd_al_ext_cache)
                kmem_cache_destroy(drbd_al_ext_cache);
 
+       drbd_md_io_bio_set   = NULL;
        drbd_md_io_page_pool = NULL;
        drbd_ee_mempool      = NULL;
        drbd_request_mempool = NULL;
@@ -3303,6 +3341,7 @@ static int drbd_create_mempools(void)
        drbd_al_ext_cache    = NULL;
        drbd_pp_pool         = NULL;
        drbd_md_io_page_pool = NULL;
+       drbd_md_io_bio_set   = NULL;
 
        /* caches */
        drbd_request_cache = kmem_cache_create(
@@ -3326,6 +3365,12 @@ static int drbd_create_mempools(void)
                goto Enomem;
 
        /* mempools */
+#ifdef COMPAT_HAVE_BIOSET_CREATE
+       drbd_md_io_bio_set = bioset_create(DRBD_MIN_POOL_PAGES, 0);
+       if (drbd_md_io_bio_set == NULL)
+               goto Enomem;
+#endif
+
        drbd_md_io_page_pool = mempool_create_page_pool(DRBD_MIN_POOL_PAGES, 0);
        if (drbd_md_io_page_pool == NULL)
                goto Enomem;
@@ -3511,6 +3556,22 @@ static int drbd_congested(void *congested_data, int bdi_bits)
                goto out;
        }
 
+       if (test_bit(CALLBACK_PENDING, &mdev->flags)) {
+               r |= (1 << BDI_async_congested);
+               /* Without good local data, we would need to read from remote,
+                * and that would need the worker thread as well, which is
+                * currently blocked waiting for that usermode helper to
+                * finish.
+                */
+               if (!get_ldev_if_state(mdev, D_UP_TO_DATE))
+                       r |= (1 << BDI_sync_congested);
+               else
+                       put_ldev(mdev);
+               r &= bdi_bits;
+               reason = 'c';
+               goto out;
+       }
+
        if (get_ldev(mdev)) {
                q = bdev_get_queue(mdev->ldev->backing_bdev);
                r = bdi_congested(&q->backing_dev_info, bdi_bits);
@@ -3840,7 +3901,7 @@ void drbd_md_sync(struct drbd_conf *mdev)
        if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
                /* this was a try anyways ... */
                dev_err(DEV, "meta data update failed!\n");
-               drbd_chk_io_error(mdev, 1, true);
+               drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
        }
 
        /* Update mdev->ldev->md.la_size_sect,
@@ -4335,12 +4396,11 @@ const char *drbd_buildtag(void)
        static char buildtag[38] = "\0uilt-in";
 
        if (buildtag[0] == 0) {
-#ifdef CONFIG_MODULES
-               if (THIS_MODULE != NULL)
-                       sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion);
-               else
+#ifdef MODULE
+               sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion);
+#else
+               buildtag[0] = 'b';
 #endif
-                       buildtag[0] = 'b';
        }
 
        return buildtag;