diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 267d9897ca8c4ae630e2652640d6ff6e5489298c..81030d8d654b26115e8d32a425cd9974e30737d1 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -990,6 +990,7 @@ struct drbd_conf { struct timer_list resync_timer; struct timer_list md_sync_timer; struct timer_list start_resync_timer; + struct timer_list request_timer; #ifdef DRBD_DEBUG_MD_SYNC struct { unsigned int line; diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 9043772de4005649bc6ef9e1c4215ff8373b5c79..dfc85f32d3177bb90bcc2b53f8d3921de82df544 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -3017,12 +3017,15 @@ void drbd_init_set_defaults(struct drbd_conf *mdev) init_timer(&mdev->resync_timer); init_timer(&mdev->md_sync_timer); init_timer(&mdev->start_resync_timer); + init_timer(&mdev->request_timer); mdev->resync_timer.function = resync_timer_fn; mdev->resync_timer.data = (unsigned long) mdev; mdev->md_sync_timer.function = md_sync_timer_fn; mdev->md_sync_timer.data = (unsigned long) mdev; mdev->start_resync_timer.function = start_resync_timer_fn; mdev->start_resync_timer.data = (unsigned long) mdev; + mdev->request_timer.function = request_timer_fn; + mdev->request_timer.data = (unsigned long) mdev; init_waitqueue_head(&mdev->misc_wait); init_waitqueue_head(&mdev->state_wait); diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 3d62ac7cdc4a1c35a8ee860b4ab1a5b4beff13ba..fe1564c7d8b6e835643939d18f3aba2bc9e6b60d 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -912,6 +912,7 @@ static int drbd_connect(struct drbd_conf *mdev) drbd_send_state(mdev); clear_bit(USE_DEGR_WFC_T, &mdev->flags); clear_bit(RESIZE_PENDING, &mdev->flags); + mod_timer(&mdev->request_timer, jiffies + HZ); /* just start it here. */ return 1; @@ -3822,6 +3823,8 @@ static void drbd_disconnect(struct drbd_conf *mdev) atomic_set(&mdev->rs_pending_cnt, 0); wake_up(&mdev->misc_wait); + del_timer(&mdev->request_timer); + /* make sure syncer is stopped and w_resume_next_sg queued */ del_timer_sync(&mdev->resync_timer); resync_timer_fn((unsigned long)mdev); diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 94fd5a2be559c12e1aa6b4abd86ced0c9eda8da7..c2cc28a55907bbeb8b98f455c5fa110bda8eb718 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -1194,3 +1194,42 @@ int drbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bvm, struct } return limit; } + +void request_timer_fn(unsigned long data) +{ + struct drbd_conf *mdev = (struct drbd_conf *) data; + struct drbd_request *req; /* oldest request */ + struct list_head *le; + unsigned long et = 0; /* effective timeout = ko_count * timeout */ + + if (get_net_conf(mdev)) { + et = mdev->net_conf->timeout*HZ/10 * mdev->net_conf->ko_count; + put_net_conf(mdev); + } + if (!et || mdev->state.conn < C_WF_REPORT_PARAMS) + return; /* Recurring timer stopped */ + + spin_lock_irq(&mdev->req_lock); + le = &mdev->oldest_tle->requests; + if (list_empty(le)) { + spin_unlock_irq(&mdev->req_lock); + mod_timer(&mdev->request_timer, jiffies + et); + return; + } + + le = le->prev; + req = list_entry(le, struct drbd_request, tl_requests); + if (time_is_before_eq_jiffies(req->start_time + et)) { + if (req->rq_state & RQ_NET_PENDING) { + dev_warn(DEV, "Remote failed to finish a request within ko-count * timeout\n"); + _drbd_set_state(_NS(mdev, conn, C_TIMEOUT), CS_VERBOSE, NULL); + } else { + dev_warn(DEV, "Local backing block device frozen?\n"); + mod_timer(&mdev->request_timer, jiffies + et); + } + } else { + mod_timer(&mdev->request_timer, req->start_time + et); + } + + spin_unlock_irq(&mdev->req_lock); +} diff --git a/drivers/block/drbd/drbd_req.h b/drivers/block/drbd/drbd_req.h index 32c1f2a312667e54b27cc023d94b254bb9d4d12d..32e2c3e6a8134220943873c671cb7ac6adf83a10 100644 --- a/drivers/block/drbd/drbd_req.h +++ b/drivers/block/drbd/drbd_req.h @@ -322,6 +322,7 @@ extern int __req_mod(struct drbd_request *req, enum drbd_req_event what, struct bio_and_error *m); extern void complete_master_bio(struct drbd_conf *mdev, struct bio_and_error *m); +extern void request_timer_fn(unsigned long data); /* use this if you don't want to deal with calling complete_master_bio() * outside the spinlock, e.g. when walking some list on cleanup. */