summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorPhilipp Reisner <philipp.reisner@linbit.com>2011-07-15 19:19:02 +0400
committerPhilipp Reisner <philipp.reisner@linbit.com>2012-11-08 19:57:58 +0400
commit1b7ab15b11716d075b3dca34cf41e8d7aba3cba2 (patch)
tree3aaa51da655ed15215d5ad1367cf4f0140bc4d89 /drivers
parent9b743da96c8640dbfc864cb5d79c51547c3fadb4 (diff)
downloadlinux-1b7ab15b11716d075b3dca34cf41e8d7aba3cba2.tar.xz
drbd: Fixed w_restart_disk_io() to handle non active AL-extents
Since we now apply the AL in user space onto the bitmap, the AL is not active for the requests we want to reply. For that a al_write_transaction() that might be called from worker context became necessary. Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/drbd/drbd_actlog.c70
-rw-r--r--drivers/block/drbd/drbd_worker.c4
2 files changed, 45 insertions, 29 deletions
diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c
index da8ffd54fc18..5731d6019518 100644
--- a/drivers/block/drbd/drbd_actlog.c
+++ b/drivers/block/drbd/drbd_actlog.c
@@ -112,7 +112,7 @@ struct drbd_atodb_wait {
};
-static int w_al_write_transaction(struct drbd_work *, int);
+static int al_write_transaction(struct drbd_conf *mdev);
void *drbd_md_get_buffer(struct drbd_conf *mdev)
{
@@ -272,18 +272,13 @@ void drbd_al_begin_io(struct drbd_conf *mdev, struct drbd_interval *i)
/* Double check: it may have been committed by someone else,
* while we have been waiting for the lock. */
if (mdev->act_log->pending_changes) {
- struct update_al_work al_work;
- init_completion(&al_work.event);
- al_work.w.cb = w_al_write_transaction;
- al_work.w.mdev = mdev;
- drbd_queue_work_front(&mdev->tconn->data.work, &al_work.w);
- wait_for_completion(&al_work.event);
-
+ int err;
+ err = al_write_transaction(mdev);
mdev->al_writ_cnt++;
spin_lock_irq(&mdev->al_lock);
/* FIXME
- if (al_work.err)
+ if (err)
we need an "lc_cancel" here;
*/
lc_committed(mdev->act_log);
@@ -348,23 +343,20 @@ static unsigned int rs_extent_to_bm_page(unsigned int rs_enr)
}
static int
-w_al_write_transaction(struct drbd_work *w, int unused)
+_al_write_transaction(struct drbd_conf *mdev)
{
- struct update_al_work *aw = container_of(w, struct update_al_work, w);
- struct drbd_conf *mdev = w->mdev;
struct al_transaction_on_disk *buffer;
struct lc_element *e;
sector_t sector;
int i, mx;
unsigned extent_nr;
unsigned crc = 0;
+ int err = 0;
if (!get_ldev(mdev)) {
dev_err(DEV, "disk is %s, cannot start al transaction\n",
drbd_disk_str(mdev->state.disk));
- aw->err = -EIO;
- complete(&((struct update_al_work *)w)->event);
- return 0;
+ return -EIO;
}
/* The bitmap write may have failed, causing a state change. */
@@ -372,19 +364,15 @@ w_al_write_transaction(struct drbd_work *w, int unused)
dev_err(DEV,
"disk is %s, cannot write al transaction\n",
drbd_disk_str(mdev->state.disk));
- aw->err = -EIO;
- complete(&((struct update_al_work *)w)->event);
put_ldev(mdev);
- return 0;
+ return -EIO;
}
buffer = drbd_md_get_buffer(mdev); /* protects md_io_buffer, al_tr_cycle, ... */
if (!buffer) {
dev_err(DEV, "disk failed while waiting for md_io buffer\n");
- aw->err = -EIO;
- complete(&((struct update_al_work *)w)->event);
put_ldev(mdev);
- return 1;
+ return -ENODEV;
}
memset(buffer, 0, sizeof(*buffer));
@@ -444,10 +432,10 @@ w_al_write_transaction(struct drbd_work *w, int unused)
buffer->crc32c = cpu_to_be32(crc);
if (drbd_bm_write_hinted(mdev))
- aw->err = -EIO;
+ err = -EIO;
/* drbd_chk_io_error done already */
else if (drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
- aw->err = -EIO;
+ err = -EIO;
drbd_chk_io_error(mdev, 1, true);
} else {
/* advance ringbuffer position and transaction counter */
@@ -456,10 +444,42 @@ w_al_write_transaction(struct drbd_work *w, int unused)
}
drbd_md_put_buffer(mdev);
- complete(&((struct update_al_work *)w)->event);
put_ldev(mdev);
- return 0;
+ return err;
+}
+
+
+static int w_al_write_transaction(struct drbd_work *w, int unused)
+{
+ struct update_al_work *aw = container_of(w, struct update_al_work, w);
+ struct drbd_conf *mdev = w->mdev;
+ int err;
+
+ err = _al_write_transaction(mdev);
+ aw->err = err;
+ complete(&aw->event);
+
+ return err != -EIO ? err : 0;
+}
+
+/* Calls from worker context (see w_restart_disk_io()) need to write the
+ transaction directly. Others came through generic_make_request(),
+ those need to delegate it to the worker. */
+static int al_write_transaction(struct drbd_conf *mdev)
+{
+ struct update_al_work al_work;
+
+ if (current == mdev->tconn->worker.task)
+ return _al_write_transaction(mdev);
+
+ init_completion(&al_work.event);
+ al_work.w.cb = w_al_write_transaction;
+ al_work.w.mdev = mdev;
+ drbd_queue_work_front(&mdev->tconn->data.work, &al_work.w);
+ wait_for_completion(&al_work.event);
+
+ return al_work.err;
}
static int _try_lc_del(struct drbd_conf *mdev, struct lc_element *al_ext)
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c
index 9ee9b9fab7a8..c57e47c0a1f5 100644
--- a/drivers/block/drbd/drbd_worker.c
+++ b/drivers/block/drbd/drbd_worker.c
@@ -1333,10 +1333,6 @@ int w_restart_disk_io(struct drbd_work *w, int cancel)
if (bio_data_dir(req->master_bio) == WRITE && req->rq_state & RQ_IN_ACT_LOG)
drbd_al_begin_io(mdev, &req->i);
- /* Calling drbd_al_begin_io() out of the worker might deadlocks
- theoretically. Practically it can not deadlock, since this is
- only used when unfreezing IOs. All the extents of the requests
- that made it into the TL are already active */
drbd_req_make_private_bio(req, req->master_bio);
req->private_bio->bi_bdev = mdev->ldev->backing_bdev;