Merge tag 'firewire-updates' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee139...
[linux-2.6-microblaze.git] / drivers / block / drbd / drbd_worker.c
index 424dc7b..891c0ec 100644 (file)
@@ -89,7 +89,8 @@ void drbd_md_io_complete(struct bio *bio, int error)
        md_io->done = 1;
        wake_up(&mdev->misc_wait);
        bio_put(bio);
-       put_ldev(mdev);
+       if (mdev->ldev) /* special case: drbd_md_read() during drbd_adm_attach() */
+               put_ldev(mdev);
 }
 
 /* reads on behalf of the partner,
@@ -1410,7 +1411,7 @@ int w_restart_disk_io(struct drbd_work *w, int cancel)
        struct drbd_conf *mdev = w->mdev;
 
        if (bio_data_dir(req->master_bio) == WRITE && req->rq_state & RQ_IN_ACT_LOG)
-               drbd_al_begin_io(mdev, &req->i);
+               drbd_al_begin_io(mdev, &req->i, false);
 
        drbd_req_make_private_bio(req, req->master_bio);
        req->private_bio->bi_bdev = mdev->ldev->backing_bdev;
@@ -1425,7 +1426,7 @@ static int _drbd_may_sync_now(struct drbd_conf *mdev)
        int resync_after;
 
        while (1) {
-               if (!odev->ldev)
+               if (!odev->ldev || odev->state.disk == D_DISKLESS)
                        return 1;
                rcu_read_lock();
                resync_after = rcu_dereference(odev->ldev->disk_conf)->resync_after;
@@ -1433,7 +1434,7 @@ static int _drbd_may_sync_now(struct drbd_conf *mdev)
                if (resync_after == -1)
                        return 1;
                odev = minor_to_mdev(resync_after);
-               if (!expect(odev))
+               if (!odev)
                        return 1;
                if ((odev->state.conn >= C_SYNC_SOURCE &&
                     odev->state.conn <= C_PAUSED_SYNC_T) ||
@@ -1515,7 +1516,7 @@ enum drbd_ret_code drbd_resync_after_valid(struct drbd_conf *mdev, int o_minor)
 
        if (o_minor == -1)
                return NO_ERROR;
-       if (o_minor < -1 || minor_to_mdev(o_minor) == NULL)
+       if (o_minor < -1 || o_minor > MINORMASK)
                return ERR_RESYNC_AFTER;
 
        /* check for loops */
@@ -1524,6 +1525,15 @@ enum drbd_ret_code drbd_resync_after_valid(struct drbd_conf *mdev, int o_minor)
                if (odev == mdev)
                        return ERR_RESYNC_AFTER_CYCLE;
 
+               /* You are free to depend on diskless, non-existing,
+                * or not yet/no longer existing minors.
+                * We only reject dependency loops.
+                * We cannot follow the dependency chain beyond a detached or
+                * missing minor.
+                */
+               if (!odev || !odev->ldev || odev->state.disk == D_DISKLESS)
+                       return NO_ERROR;
+
                rcu_read_lock();
                resync_after = rcu_dereference(odev->ldev->disk_conf)->resync_after;
                rcu_read_unlock();
@@ -1652,7 +1662,9 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
        clear_bit(B_RS_H_DONE, &mdev->flags);
 
        write_lock_irq(&global_state_lock);
-       if (!get_ldev_if_state(mdev, D_NEGOTIATING)) {
+       /* Did some connection breakage or IO error race with us? */
+       if (mdev->state.conn < C_CONNECTED
+       || !get_ldev_if_state(mdev, D_NEGOTIATING)) {
                write_unlock_irq(&global_state_lock);
                mutex_unlock(mdev->state_mutex);
                return;