]> 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 581f6800cc307b98459e7d67b4509bafe28a52c2..9e3f441e7e8441e83d61273ed0f34f1c309c518b 100644 (file)
@@ -721,30 +721,25 @@ static void drbd_md_set_sector_offsets(struct drbd_conf *mdev,
                                       struct drbd_backing_dev *bdev)
 {
        sector_t md_size_sect = 0;
-       unsigned int al_size_sect = MD_32kB_SECT;
-       int meta_dev_idx;
+       unsigned int al_size_sect = bdev->md.al_size_4k * 8;
 
-       rcu_read_lock();
-       meta_dev_idx = rcu_dereference(bdev->disk_conf)->meta_dev_idx;
+       bdev->md.md_offset = drbd_md_ss(bdev);
 
-       switch (meta_dev_idx) {
+       switch (bdev->md.meta_dev_idx) {
        default:
                /* v07 style fixed size indexed meta data */
                bdev->md.md_size_sect = MD_128MB_SECT;
-               bdev->md.md_offset = drbd_md_ss__(mdev, bdev);
                bdev->md.al_offset = MD_4kB_SECT;
                bdev->md.bm_offset = MD_4kB_SECT + al_size_sect;
                break;
        case DRBD_MD_INDEX_FLEX_EXT:
                /* just occupy the full device; unit: sectors */
                bdev->md.md_size_sect = drbd_get_capacity(bdev->md_bdev);
-               bdev->md.md_offset = 0;
                bdev->md.al_offset = MD_4kB_SECT;
                bdev->md.bm_offset = MD_4kB_SECT + al_size_sect;
                break;
        case DRBD_MD_INDEX_INTERNAL:
        case DRBD_MD_INDEX_FLEX_INT:
-               bdev->md.md_offset = drbd_md_ss__(mdev, bdev);
                /* al size is still fixed */
                bdev->md.al_offset = -al_size_sect;
                /* we need (slightly less than) ~ this much bitmap sectors: */
@@ -762,7 +757,6 @@ static void drbd_md_set_sector_offsets(struct drbd_conf *mdev,
                bdev->md.bm_offset   = -md_size_sect + MD_4kB_SECT;
                break;
        }
-       rcu_read_unlock();
 }
 
 /* input size is expected to be in KB */
@@ -825,7 +819,7 @@ void drbd_resume_io(struct drbd_conf *mdev)
 enum determine_dev_size drbd_determine_dev_size(struct drbd_conf *mdev, enum dds_flags flags) __must_hold(local)
 {
        sector_t prev_first_sect, prev_size; /* previous meta location */
-       sector_t la_size, u_size;
+       sector_t la_size_sect, u_size;
        sector_t size;
        char ppb[10];
 
@@ -848,7 +842,7 @@ enum determine_dev_size drbd_determine_dev_size(struct drbd_conf *mdev, enum dds
 
        prev_first_sect = drbd_md_first_sector(mdev->ldev);
        prev_size = mdev->ldev->md.md_size_sect;
-       la_size = mdev->ldev->md.la_size_sect;
+       la_size_sect = mdev->ldev->md.la_size_sect;
 
        /* TODO: should only be some assert here, not (re)init... */
        drbd_md_set_sector_offsets(mdev, mdev->ldev);
@@ -884,7 +878,7 @@ enum determine_dev_size drbd_determine_dev_size(struct drbd_conf *mdev, enum dds
        if (rv == dev_size_error)
                goto out;
 
-       la_size_changed = (la_size != mdev->ldev->md.la_size_sect);
+       la_size_changed = (la_size_sect != mdev->ldev->md.la_size_sect);
 
        md_moved = prev_first_sect != drbd_md_first_sector(mdev->ldev)
                || prev_size       != mdev->ldev->md.md_size_sect;
@@ -906,9 +900,9 @@ enum determine_dev_size drbd_determine_dev_size(struct drbd_conf *mdev, enum dds
                drbd_md_mark_dirty(mdev);
        }
 
-       if (size > la_size)
+       if (size > la_size_sect)
                rv = grew;
-       if (size < la_size)
+       if (size < la_size_sect)
                rv = shrunk;
 out:
        lc_unlock(mdev->act_log);
@@ -923,7 +917,7 @@ drbd_new_dev_size(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
                  sector_t u_size, int assume_peer_has_space)
 {
        sector_t p_size = mdev->p_size;   /* partner's disk size. */
-       sector_t la_size = bdev->md.la_size_sect; /* last agreed size. */
+       sector_t la_size_sect = bdev->md.la_size_sect; /* last agreed size. */
        sector_t m_size; /* my size */
        sector_t size = 0;
 
@@ -937,8 +931,8 @@ drbd_new_dev_size(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
        if (p_size && m_size) {
                size = min_t(sector_t, p_size, m_size);
        } else {
-               if (la_size) {
-                       size = la_size;
+               if (la_size_sect) {
+                       size = la_size_sect;
                        if (m_size && m_size < size)
                                size = m_size;
                        if (p_size && p_size < size)
@@ -1147,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;
 
-       if (dc->c_plan_ahead > DRBD_C_PLAN_AHEAD_MAX)
-               dc->c_plan_ahead = DRBD_C_PLAN_AHEAD_MAX;
+       unsigned int al_size_4k = bdev->md.al_size_4k;
+
+       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)
@@ -1202,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) {
@@ -1350,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) {
@@ -1363,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,8 +1443,16 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
                goto fail;
        }
 
-       /* RT - for drbd_get_max_capacity() DRBD_MD_INDEX_FLEX_INT */
-       drbd_md_set_sector_offsets(mdev, nbc);
+       /* Read our meta data super block early.
+        * This also sets other on-disk offsets. */
+       retcode = drbd_md_read(mdev, nbc);
+       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",
@@ -1487,8 +1519,6 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
        if (!get_ldev_if_state(mdev, D_ATTACHING))
                goto force_diskless;
 
-       drbd_md_set_sector_offsets(mdev, nbc);
-
        if (!mdev->bitmap) {
                if (drbd_bm_init(mdev)) {
                        retcode = ERR_NOMEM;
@@ -1496,10 +1526,6 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       retcode = drbd_md_read(mdev, nbc);
-       if (retcode != NO_ERROR)
-               goto force_diskless_dec;
-
        if (mdev->state.conn < C_CONNECTED &&
            mdev->state.role == R_PRIMARY &&
            (mdev->ed_uuid & ~((u64)1)) != (nbc->md.uuid[UI_CURRENT] & ~((u64)1))) {
@@ -2178,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. */
@@ -2426,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:
@@ -2495,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:
@@ -3182,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);