]> Pileus Git - ~andy/linux/blobdiff - drivers/block/drbd/drbd_nl.c
Merge remote-tracking branch 'asoc/fix/wm0010' into asoc-linus
[~andy/linux] / drivers / block / drbd / drbd_nl.c
index 974ea47a656a8c06b7803089b9e2ad193d65fc34..9e3f441e7e8441e83d61273ed0f34f1c309c518b 100644 (file)
@@ -1141,15 +1141,32 @@ static bool should_set_defaults(struct genl_info *info)
        return 0 != (flags & DRBD_GENL_F_SET_DEFAULTS);
 }
 
-static void enforce_disk_conf_limits(struct disk_conf *dc)
+static unsigned int drbd_al_extents_max(struct drbd_backing_dev *bdev)
 {
-       if (dc->al_extents < DRBD_AL_EXTENTS_MIN)
-               dc->al_extents = DRBD_AL_EXTENTS_MIN;
-       if (dc->al_extents > DRBD_AL_EXTENTS_MAX)
-               dc->al_extents = DRBD_AL_EXTENTS_MAX;
+       /* This is limited by 16 bit "slot" numbers,
+        * and by available on-disk context storage.
+        *
+        * Also (u16)~0 is special (denotes a "free" extent).
+        *
+        * One transaction occupies one 4kB on-disk block,
+        * we have n such blocks in the on disk ring buffer,
+        * the "current" transaction may fail (n-1),
+        * and there is 919 slot numbers context information per transaction.
+        *
+        * 72 transaction blocks amounts to more than 2**16 context slots,
+        * so cap there first.
+        */
+       const unsigned int max_al_nr = DRBD_AL_EXTENTS_MAX;
+       const unsigned int sufficient_on_disk =
+               (max_al_nr + AL_CONTEXT_PER_TRANSACTION -1)
+               /AL_CONTEXT_PER_TRANSACTION;
+
+       unsigned int al_size_4k = bdev->md.al_size_4k;
 
-       if (dc->c_plan_ahead > DRBD_C_PLAN_AHEAD_MAX)
-               dc->c_plan_ahead = DRBD_C_PLAN_AHEAD_MAX;
+       if (al_size_4k > sufficient_on_disk)
+               return max_al_nr;
+
+       return (al_size_4k - 1) * AL_CONTEXT_PER_TRANSACTION;
 }
 
 int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
@@ -1196,7 +1213,13 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
        if (!expect(new_disk_conf->resync_rate >= 1))
                new_disk_conf->resync_rate = 1;
 
-       enforce_disk_conf_limits(new_disk_conf);
+       if (new_disk_conf->al_extents < DRBD_AL_EXTENTS_MIN)
+               new_disk_conf->al_extents = DRBD_AL_EXTENTS_MIN;
+       if (new_disk_conf->al_extents > drbd_al_extents_max(mdev->ldev))
+               new_disk_conf->al_extents = drbd_al_extents_max(mdev->ldev);
+
+       if (new_disk_conf->c_plan_ahead > DRBD_C_PLAN_AHEAD_MAX)
+               new_disk_conf->c_plan_ahead = DRBD_C_PLAN_AHEAD_MAX;
 
        fifo_size = (new_disk_conf->c_plan_ahead * 10 * SLEEP_TIME) / HZ;
        if (fifo_size != mdev->rs_plan_s->size) {
@@ -1344,7 +1367,8 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
                goto fail;
        }
 
-       enforce_disk_conf_limits(new_disk_conf);
+       if (new_disk_conf->c_plan_ahead > DRBD_C_PLAN_AHEAD_MAX)
+               new_disk_conf->c_plan_ahead = DRBD_C_PLAN_AHEAD_MAX;
 
        new_plan = fifo_alloc((new_disk_conf->c_plan_ahead * 10 * SLEEP_TIME) / HZ);
        if (!new_plan) {
@@ -1357,6 +1381,12 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
                goto fail;
        }
 
+       write_lock_irq(&global_state_lock);
+       retcode = drbd_resync_after_valid(mdev, new_disk_conf->resync_after);
+       write_unlock_irq(&global_state_lock);
+       if (retcode != NO_ERROR)
+               goto fail;
+
        rcu_read_lock();
        nc = rcu_dereference(mdev->tconn->net_conf);
        if (nc) {
@@ -1419,6 +1449,11 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
        if (retcode != NO_ERROR)
                goto fail;
 
+       if (new_disk_conf->al_extents < DRBD_AL_EXTENTS_MIN)
+               new_disk_conf->al_extents = DRBD_AL_EXTENTS_MIN;
+       if (new_disk_conf->al_extents > drbd_al_extents_max(nbc))
+               new_disk_conf->al_extents = drbd_al_extents_max(nbc);
+
        if (drbd_get_max_capacity(nbc) < new_disk_conf->disk_size) {
                dev_err(DEV, "max capacity %llu smaller than disk size %llu\n",
                        (unsigned long long) drbd_get_max_capacity(nbc),
@@ -2169,8 +2204,11 @@ static enum drbd_state_rv conn_try_disconnect(struct drbd_tconn *tconn, bool for
                return SS_SUCCESS;
        case SS_PRIMARY_NOP:
                /* Our state checking code wants to see the peer outdated. */
-               rv = conn_request_state(tconn, NS2(conn, C_DISCONNECTING,
-                                               pdsk, D_OUTDATED), CS_VERBOSE);
+               rv = conn_request_state(tconn, NS2(conn, C_DISCONNECTING, pdsk, D_OUTDATED), 0);
+
+               if (rv == SS_OUTDATE_WO_CONN) /* lost connection before graceful disconnect succeeded */
+                       rv = conn_request_state(tconn, NS(conn, C_DISCONNECTING), CS_VERBOSE);
+
                break;
        case SS_CW_FAILED_BY_PEER:
                /* The peer probably wants to see us outdated. */
@@ -2417,22 +2455,19 @@ int drbd_adm_invalidate(struct sk_buff *skb, struct genl_info *info)
        wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
        drbd_flush_workqueue(mdev);
 
-       retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T), CS_ORDERED);
-
-       if (retcode < SS_SUCCESS && retcode != SS_NEED_CONNECTION)
-               retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T));
-
-       while (retcode == SS_NEED_CONNECTION) {
-               spin_lock_irq(&mdev->tconn->req_lock);
-               if (mdev->state.conn < C_CONNECTED)
-                       retcode = _drbd_set_state(_NS(mdev, disk, D_INCONSISTENT), CS_VERBOSE, NULL);
-               spin_unlock_irq(&mdev->tconn->req_lock);
-
-               if (retcode != SS_NEED_CONNECTION)
-                       break;
-
+       /* If we happen to be C_STANDALONE R_SECONDARY, just change to
+        * D_INCONSISTENT, and set all bits in the bitmap.  Otherwise,
+        * try to start a resync handshake as sync target for full sync.
+        */
+       if (mdev->state.conn == C_STANDALONE && mdev->state.role == R_SECONDARY) {
+               retcode = drbd_request_state(mdev, NS(disk, D_INCONSISTENT));
+               if (retcode >= SS_SUCCESS) {
+                       if (drbd_bitmap_io(mdev, &drbd_bmio_set_n_write,
+                               "set_n_write from invalidate", BM_LOCKED_MASK))
+                               retcode = ERR_IO_MD_DISK;
+               }
+       } else
                retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T));
-       }
        drbd_resume_io(mdev);
 
 out:
@@ -2486,21 +2521,22 @@ int drbd_adm_invalidate_peer(struct sk_buff *skb, struct genl_info *info)
        wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
        drbd_flush_workqueue(mdev);
 
-       retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S), CS_ORDERED);
-       if (retcode < SS_SUCCESS) {
-               if (retcode == SS_NEED_CONNECTION && mdev->state.role == R_PRIMARY) {
-                       /* The peer will get a resync upon connect anyways.
-                        * Just make that into a full resync. */
-                       retcode = drbd_request_state(mdev, NS(pdsk, D_INCONSISTENT));
-                       if (retcode >= SS_SUCCESS) {
-                               if (drbd_bitmap_io(mdev, &drbd_bmio_set_susp_al,
-                                                  "set_n_write from invalidate_peer",
-                                                  BM_LOCKED_SET_ALLOWED))
-                                       retcode = ERR_IO_MD_DISK;
-                       }
-               } else
-                       retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S));
-       }
+       /* If we happen to be C_STANDALONE R_PRIMARY, just set all bits
+        * in the bitmap.  Otherwise, try to start a resync handshake
+        * as sync source for full sync.
+        */
+       if (mdev->state.conn == C_STANDALONE && mdev->state.role == R_PRIMARY) {
+               /* The peer will get a resync upon connect anyways. Just make that
+                  into a full resync. */
+               retcode = drbd_request_state(mdev, NS(pdsk, D_INCONSISTENT));
+               if (retcode >= SS_SUCCESS) {
+                       if (drbd_bitmap_io(mdev, &drbd_bmio_set_susp_al,
+                               "set_n_write from invalidate_peer",
+                               BM_LOCKED_SET_ALLOWED))
+                               retcode = ERR_IO_MD_DISK;
+               }
+       } else
+               retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S));
        drbd_resume_io(mdev);
 
 out:
@@ -3173,6 +3209,7 @@ static enum drbd_ret_code adm_delete_minor(struct drbd_conf *mdev)
                                    CS_VERBOSE + CS_WAIT_COMPLETE);
                idr_remove(&mdev->tconn->volumes, mdev->vnr);
                idr_remove(&minors, mdev_to_minor(mdev));
+               destroy_workqueue(mdev->submit.wq);
                del_gendisk(mdev->vdisk);
                synchronize_rcu();
                kref_put(&mdev->kref, &drbd_minor_destroy);