Re: [RFC] [PATCH 1/1] blk request timeout handler patches

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Jens Axboe [jens.axboe@xxxxxxxxxx] wrote:
> On Tue, Oct 09 2007, James Bottomley wrote:
> > On Tue, 2007-10-09 at 14:15 +0200, Jens Axboe wrote:
> > > On Tue, Oct 09 2007, Matthew Wilcox wrote:
> > > > On Mon, Oct 08, 2007 at 10:36:10PM -0700, malahal@xxxxxxxxxx wrote:
> > > > > Thank you Randy, Jens for your suggestions. I folded the second patch as
> > > > > it is just a clean up. Here is the fixed one patch version.
> > > > 
> > > > I was thinking about this (in the context of shrinking scsi_cmnd --
> > > > obviously, things are not improved if we simply move the timer to request
> > > > instead of scsi_cmnd).  Why do we need a timer _per request_?  We don't
> > > > need one per network packet.  I appreciate we had one per scsi_cmnd and
> > > > this patch is just moving it upwards in the hierarchy, but perhaps we
> > > > can do better.
> > > > 
> > > > What if we have one timer per request queue instead?  It needs to expire
> > > > as soon as the earliest request timer would expire, then needs to be
> > > > reset to the next earliest one.  We might walk the request queue more
> > > > frequently, but we'd save 48 bytes in the struct request.
> > > 
> > > I agree, adding a full timer to each request is not nice. You jump over
> > > the actual implementation details of having just one timer in the queue
> > > though, it's pretty cheap to just say it can be done :-). You need to
> > > track each request anyways. If all drivers used the block layer tagging
> > > it would be easy since we are tracking each pending request in that
> > > case, but right now they don't. So pending requests may very well be
> > > outside of block layer knowledge.
> > 
> > Can't we handle this a bit like the Linux timer infrastructure?  Instead
> > of a timer per cmnd we have one per queue that's reset by commands
> > returning?  If we retained a linked list of commands in timer order and
> > expiry times, that's still going to save us an unsigned long and two
> > pointers over struct timer_list.
> 
> Here's an approach that uses a single timer. I purposely do not sort
> commands in expiry times, as that would introduce an O(N) operation far
> out weighing the IO scheduling cost, pretty silly for a timeout
> mechanism that supposedly should never trigger. Or we could waste more
> memory and fewer cycles (but still far more cycles than we need) by
> sorting in some sort of tree.
> 
> So I don't sort the list, instead I push the cost of locating expired
> request to the timeout handler. It should really only run, when a
> request times out. Timeout is then reset to next command timeout
> (rounded), if further commands exist. It also doesn't fiddle with the
> timer unless an incoming command has a lower timeout than the current
> one.
> 
> Totally untested...
> 
> diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c
> index ed39313..cb3210a 100644
> --- a/block/ll_rw_blk.c
> +++ b/block/ll_rw_blk.c
> @@ -42,6 +42,7 @@ static void drive_stat_acct(struct request *rq, int nr_sectors, int new_io);
>  static void init_request_from_bio(struct request *req, struct bio *bio);
>  static int __make_request(struct request_queue *q, struct bio *bio);
>  static struct io_context *current_io_context(gfp_t gfp_flags, int node);
> +static void blk_rq_timed_out_timer(unsigned long);
> 
>  /*
>   * For the allocated request tables
> @@ -177,6 +178,18 @@ void blk_queue_softirq_done(struct request_queue *q, softirq_done_fn *fn)
> 
>  EXPORT_SYMBOL(blk_queue_softirq_done);
> 
> +void blk_queue_rq_timeout(struct request_queue *q, unsigned int timeout)
> +{
> +	q->rq_timeout = timeout;
> +}
> +EXPORT_SYMBOL_GPL(blk_queue_rq_timeout);
> +
> +void blk_queue_rq_timed_out(struct request_queue *q, rq_timed_out_fn *fn)
> +{
> +	q->rq_timed_out_fn = fn;
> +}
> +EXPORT_SYMBOL_GPL(blk_queue_rq_timed_out);
> +
>  /**
>   * blk_queue_make_request - define an alternate make_request function for a device
>   * @q:  the request queue for the device to be affected
> @@ -239,7 +252,9 @@ static void rq_init(struct request_queue *q, struct request *rq)
>  {
>  	INIT_LIST_HEAD(&rq->queuelist);
>  	INIT_LIST_HEAD(&rq->donelist);
> +	INIT_LIST_HEAD(&rq->timeout_list);
> 
> +	rq->timeout = 0;
>  	rq->errors = 0;
>  	rq->bio = rq->biotail = NULL;
>  	INIT_HLIST_NODE(&rq->hash);
> @@ -1851,6 +1866,8 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
>  		return NULL;
> 
>  	init_timer(&q->unplug_timer);
> +	setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q);
> +	INIT_LIST_HEAD(&q->timeout_list);
> 
>  	snprintf(q->kobj.name, KOBJ_NAME_LEN, "%s", "queue");
>  	q->kobj.ktype = &queue_ktype;
> @@ -2271,6 +2288,7 @@ EXPORT_SYMBOL(blk_start_queueing);
>   */
>  void blk_requeue_request(struct request_queue *q, struct request *rq)
>  {
> +	blk_delete_timer(rq);
>  	blk_add_trace_rq(q, rq, BLK_TA_REQUEUE);
> 
>  	if (blk_rq_tagged(rq))
> @@ -3600,24 +3618,132 @@ static struct notifier_block __devinitdata blk_cpu_notifier = {
>  };
> 
>  /**
> - * blk_complete_request - end I/O on a request
> - * @req:      the request being processed
> + * blk_delete_timer - Delete/cancel timer for a given function.
> + * @req:	request that we are canceling timer for
>   *
> - * Description:
> - *     Ends all I/O on a request. It does not handle partial completions,
> - *     unless the driver actually implements this in its completion callback
> - *     through requeueing. Theh actual completion happens out-of-order,
> - *     through a softirq handler. The user must have registered a completion
> - *     callback through blk_queue_softirq_done().
> - **/
> + * Return value:
> + *     1 if we were able to detach the timer.  0 if we blew it, and the
> + *     timer function has already started to run.
> + */
> +int blk_delete_timer(struct request *req)
> +{
> +	if (!req->q->rq_timed_out_fn)
> +		return 1;
> 
> -void blk_complete_request(struct request *req)
> +	if (!list_empty(&req->timeout_list)) {

This could race with timeout handler. In other words, this routine may
return 1 while blk_rq_timed_out() is running on the request.

> +		list_del_init(&req->timeout_list);
> +		return 1;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(blk_delete_timer);
> +
> +static void blk_rq_timed_out(struct request *req)
> +{
> +	struct request_queue *q = req->q;
> +	enum blk_eh_timer_return ret;
> +
> +	ret = q->rq_timed_out_fn(req);
> +	switch (ret) {
> +	case BLK_EH_HANDLED:
> +		__blk_complete_request(req);
> +		break;
> +	case BLK_EH_RESET_TIMER:
> +		blk_add_timer(req);

You will need to delete the req from the timeout_list before calling
blk_add_timer again. You may need to remove the req from the list in
other cases too.

> +		break;
> +	case BLK_EH_NOT_HANDLED:
> +		/*
> +		 * LLD handles this for now but in the future
> +		 * we can send a request msg to abort the command
> +		 * and we can move more of the generic scsi eh code to
> +		 * the blk layer.
> +		 */
> +		break;
> +	default:
> +		printk(KERN_ERR "block: bad eh return: %d\n", ret);
> +		break;
> +	}
> +}
> +
> +static void blk_rq_timed_out_timer(unsigned long data)
> +{
> +	struct request_queue *q = (struct request_queue *) data;
> +	unsigned long flags, next = 0;
> +	struct request *rq, *tmp;
> +
> +	spin_lock_irqsave(q->queue_lock, flags);
> +
> +	list_for_each_entry_safe(rq, tmp, &q->timeout_list, timeout_list) {
> +		if (!next || time_before(next, rq->timeout))
> +			next = rq->timeout;
> +		if (time_after_eq(jiffies, rq->timeout))
> +			blk_rq_timed_out(rq);
> +	}
> +
> +	if (next)
> +		mod_timer(&q->timeout, round_jiffies(next));
> +
> +	spin_unlock_irqrestore(q->queue_lock, flags);
> +}
> +
> +/**
> + * blk_abort_req -- Request request recovery for the specified command
> + * @req:	pointer to the request of interest
> + *
> + * This function requests that the block layer start recovery for the
> + * request by deleting the timer and calling the q's timeout function.
> + * LLDDs who implement their own error recovery MAY ignore the timeout
> + * event if they generated blk_abort_req.
> + */
> +void blk_abort_req(struct request *req)
> +{
> +        if (!blk_delete_timer(req))
> +                return;
> +        blk_rq_timed_out(req);
> +}
> +EXPORT_SYMBOL_GPL(blk_abort_req);
> +
> +/**
> + * blk_add_timer - Start timeout timer for a single request
> + * @req:	request that is about to start running.
> + *
> + * Notes:
> + *    Each request has its own timer, and as it is added to the queue, we
> + *    set up the timer.  When the request completes, we cancel the timer.
> + */
> +void blk_add_timer(struct request *req)
> +{
> +	struct request_queue *q = req->q;
> +	unsigned long expiry;
> +
> +	BUG_ON(!list_empty(&req->timeout_list));
> +
> +	if (req->timeout)
> +		expiry = jiffies + req->timeout;
> +	else
> +		expiry = jiffies + q->rq_timeout;
> +
> +	req->timeout = expiry;
> +	list_add_tail(&req->timeout_list, &q->timeout_list);
> +
> +	/*
> +	 * This is for timeout purposes, round to next second
> +	 */
> +	expiry = round_jiffies(expiry);
> +
> +	if (!timer_pending(&q->timeout) || time_before(expiry, q->timeout.expires))
> +		mod_timer(&q->timeout, expiry);
> +}
> +EXPORT_SYMBOL_GPL(blk_add_timer);
> +
> +void __blk_complete_request(struct request *req)
>  {
>  	struct list_head *cpu_list;
>  	unsigned long flags;
> 
>  	BUG_ON(!req->q->softirq_done_fn);
> -		
> +
>  	local_irq_save(flags);
> 
>  	cpu_list = &__get_cpu_var(blk_cpu_done);
> @@ -3627,8 +3753,34 @@ void blk_complete_request(struct request *req)
>  	local_irq_restore(flags);
>  }
> 
> +/**
> + * blk_complete_request - end I/O on a request
> + * @req:	the request being processed
> + *
> + * Description:
> + *     Ends all I/O on a request. It does not handle partial completions,
> + *     unless the driver actually implements this in its completion callback
> + *     through requeueing. Theh actual completion happens out-of-order,
> + *     through a softirq handler. The user must have registered a completion
> + *     callback through blk_queue_softirq_done().
> + */
> +void blk_complete_request(struct request *req)
> +{
> +	/*
> +	 * We don't have to worry about this one timing out any more.
> +	 * If we are unable to remove the timer, then the command
> +	 * has already timed out.  In which case, we have no choice but to
> +	 * let the timeout function run, as we have no idea where in fact
> +	 * that function could really be.  It might be on another processor,
> +	 * etc, etc.
> +	 */
> +	if (!blk_delete_timer(req))
> +		return;
> +
> +	__blk_complete_request(req);
> +}
>  EXPORT_SYMBOL(blk_complete_request);
> -	
> +
>  /*
>   * queue lock must be held
>   */
> diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
> index ac6ceed..017a236 100644
> --- a/drivers/ata/libata-eh.c
> +++ b/drivers/ata/libata-eh.c
> @@ -33,6 +33,7 @@
>   */
> 
>  #include <linux/kernel.h>
> +#include <linux/blkdev.h>
>  #include <scsi/scsi.h>
>  #include <scsi/scsi_host.h>
>  #include <scsi/scsi_eh.h>
> @@ -244,29 +245,29 @@ static void ata_eh_clear_action(struct ata_device *dev,
>   *	RETURNS:
>   *	EH_HANDLED or EH_NOT_HANDLED
>   */
> -enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd)
> +enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd)
>  {
>  	struct Scsi_Host *host = cmd->device->host;
>  	struct ata_port *ap = ata_shost_to_port(host);
>  	unsigned long flags;
>  	struct ata_queued_cmd *qc;
> -	enum scsi_eh_timer_return ret;
> +	enum blk_eh_timer_return ret;
> 
>  	DPRINTK("ENTER\n");
> 
>  	if (ap->ops->error_handler) {
> -		ret = EH_NOT_HANDLED;
> +		ret = BLK_EH_NOT_HANDLED;
>  		goto out;
>  	}
> 
> -	ret = EH_HANDLED;
> +	ret = BLK_EH_HANDLED;
>  	spin_lock_irqsave(ap->lock, flags);
>  	qc = ata_qc_from_tag(ap, ap->active_tag);
>  	if (qc) {
>  		WARN_ON(qc->scsicmd != cmd);
>  		qc->flags |= ATA_QCFLAG_EH_SCHEDULED;
>  		qc->err_mask |= AC_ERR_TIMEOUT;
> -		ret = EH_NOT_HANDLED;
> +		ret = BLK_EH_NOT_HANDLED;
>  	}
>  	spin_unlock_irqrestore(ap->lock, flags);
> 
> @@ -692,7 +693,7 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc)
>  	 * Note that ATA_QCFLAG_FAILED is unconditionally set after
>  	 * this function completes.
>  	 */
> -	scsi_req_abort_cmd(qc->scsicmd);
> +	blk_abort_req(qc->scsicmd->request);
>  }
> 
>  /**
> diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
> index 564cd23..6021b9c 100644
> --- a/drivers/ata/libata.h
> +++ b/drivers/ata/libata.h
> @@ -148,7 +148,7 @@ extern void ata_scsi_dev_rescan(struct work_struct *work);
>  extern int ata_bus_probe(struct ata_port *ap);
> 
>  /* libata-eh.c */
> -extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
> +extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
>  extern void ata_scsi_error(struct Scsi_Host *host);
>  extern void ata_port_wait_eh(struct ata_port *ap);
>  extern void ata_eh_fastdrain_timerfn(unsigned long arg);
> diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
> index 6800e57..6fa65c8 100644
> --- a/drivers/scsi/aacraid/aachba.c
> +++ b/drivers/scsi/aacraid/aachba.c
> @@ -1125,7 +1125,7 @@ static struct aac_srb * aac_scsi_common(struct fib * fib, struct scsi_cmnd * cmd
>  	srbcmd->id       = cpu_to_le32(scmd_id(cmd));
>  	srbcmd->lun      = cpu_to_le32(cmd->device->lun);
>  	srbcmd->flags    = cpu_to_le32(flag);
> -	timeout = cmd->timeout_per_command/HZ;
> +	timeout = cmd->request->timeout/HZ;
>  	if (timeout == 0)
>  		timeout = 1;
>  	srbcmd->timeout  = cpu_to_le32(timeout);  // timeout in seconds
> diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
> index 79c0b6e..d139075 100644
> --- a/drivers/scsi/advansys.c
> +++ b/drivers/scsi/advansys.c
> @@ -7901,7 +7901,7 @@ static void asc_prt_scsi_cmnd(struct scsi_cmnd *s)
>  	printk(" serial_number 0x%x, retries %d, allowed %d\n",
>  	       (unsigned)s->serial_number, s->retries, s->allowed);
> 
> -	printk(" timeout_per_command %d\n", s->timeout_per_command);
> +	printk(" request timeout %d\n", s->request->timeout);
> 
>  	printk
>  	    (" scsi_done 0x%lx, done 0x%lx, host_scribble 0x%lx, result 0x%x\n",
> diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
> index 55e4d2d..c5fc436 100644
> --- a/drivers/scsi/gdth.c
> +++ b/drivers/scsi/gdth.c
> @@ -733,7 +733,6 @@ int __gdth_execute(struct scsi_device *sdev, gdth_cmd_str *gdtcmd, char *cmnd,
>      scp->device = sdev;
>      /* use request field to save the ptr. to completion struct. */
>      scp->request = (struct request *)&wait;
> -    scp->timeout_per_command = timeout*HZ;
>      scp->request_buffer = gdtcmd;
>      scp->cmd_len = 12;
>      memcpy(scp->cmnd, cmnd, 12);
> @@ -4948,7 +4947,7 @@ static int gdth_queuecommand(Scsi_Cmnd *scp,void (*done)(Scsi_Cmnd *))
>      if (scp->done == gdth_scsi_done)
>          priority = scp->SCp.this_residual;
>      else
> -        gdth_update_timeout(hanum, scp, scp->timeout_per_command * 6);
> +        gdth_update_timeout(hanum, scp, scp->request->timeout* 6);
> 
>      gdth_putq( hanum, scp, priority );
>      gdth_next( hanum );
> diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c
> index 32982eb..22a9013 100644
> --- a/drivers/scsi/gdth_proc.c
> +++ b/drivers/scsi/gdth_proc.c
> @@ -846,19 +846,19 @@ static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout)
>  {
>      int oldto;
> 
> -    oldto = scp->timeout_per_command;
> -    scp->timeout_per_command = timeout;
> +    oldto = scp->request->timeout;
> +    scp->request->timeout = timeout;
> 
>      if (timeout == 0) {
> -        del_timer(&scp->eh_timeout);
> -        scp->eh_timeout.data = (unsigned long) NULL;
> -        scp->eh_timeout.expires = 0;
> +        del_timer(&scp->request->timer);
> +        scp->request->timer.data = (unsigned long) NULL;
> +        scp->request->timer.expires = 0;
>      } else {
> -        if (scp->eh_timeout.data != (unsigned long) NULL) 
> -            del_timer(&scp->eh_timeout);
> -        scp->eh_timeout.data = (unsigned long) scp;
> -        scp->eh_timeout.expires = jiffies + timeout;
> -        add_timer(&scp->eh_timeout);
> +        if (scp->request->timer.data != (unsigned long) NULL) 
> +            del_timer(&scp->request->timer);
> +        scp->request->timer.data = (unsigned long) scp;
> +        scp->request->timer.expires = jiffies + timeout;
> +        add_timer(&scp->request->timer);
>      }
> 
>      return oldto;
> diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
> index 5ecc63d..04dbe3b 100644
> --- a/drivers/scsi/ibmvscsi/ibmvscsi.c
> +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
> @@ -726,7 +726,7 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd,
>  	init_event_struct(evt_struct,
>  			  handle_cmd_rsp,
>  			  VIOSRP_SRP_FORMAT,
> -			  cmnd->timeout_per_command/HZ);
> +			  cmnd->request->timeout/HZ);
> 
>  	evt_struct->cmnd = cmnd;
>  	evt_struct->cmnd_done = done;
> diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
> index 1cc01ac..2035923 100644
> --- a/drivers/scsi/ide-scsi.c
> +++ b/drivers/scsi/ide-scsi.c
> @@ -916,7 +916,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd,
>  	pc->request_transfer = pc->buffer_size = cmd->request_bufflen;
>  	pc->scsi_cmd = cmd;
>  	pc->done = done;
> -	pc->timeout = jiffies + cmd->timeout_per_command;
> +	pc->timeout = jiffies + cmd->request->timeout;
> 
>  	if (should_transform(drive, cmd))
>  		set_bit(PC_TRANSFORM, &pc->flags);
> diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
> index f142eaf..b351f14 100644
> --- a/drivers/scsi/ipr.c
> +++ b/drivers/scsi/ipr.c
> @@ -3654,7 +3654,8 @@ static int ipr_slave_configure(struct scsi_device *sdev)
>  			sdev->no_uld_attach = 1;
>  		}
>  		if (ipr_is_vset_device(res)) {
> -			sdev->timeout = IPR_VSET_RW_TIMEOUT;
> +			blk_queue_rq_timeout(sdev->request_queue,
> +					     IPR_VSET_RW_TIMEOUT);
>  			blk_queue_max_sectors(sdev->request_queue, IPR_VSET_MAX_SECTORS);
>  		}
>  		if (ipr_is_vset_device(res) || ipr_is_scsi_disk(res))
> diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
> index 492a51b..c81dd8f 100644
> --- a/drivers/scsi/ips.c
> +++ b/drivers/scsi/ips.c
> @@ -3860,7 +3860,7 @@ ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb)
>  		scb->cmd.dcdb.segment_4G = 0;
>  		scb->cmd.dcdb.enhanced_sg = 0;
> 
> -		TimeOut = scb->scsi_cmd->timeout_per_command;
> +		TimeOut = scb->scsi_cmd->request->timeout;
> 
>  		if (ha->subsys->param[4] & 0x00100000) {	/* If NEW Tape DCDB is Supported */
>  			if (!scb->sg_len) {
> diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
> index 5e573ef..f3ae2df 100644
> --- a/drivers/scsi/libsas/sas_ata.c
> +++ b/drivers/scsi/libsas/sas_ata.c
> @@ -408,7 +408,7 @@ void sas_ata_task_abort(struct sas_task *task)
> 
>  	/* Bounce SCSI-initiated commands to the SCSI EH */
>  	if (qc->scsicmd) {
> -		scsi_req_abort_cmd(qc->scsicmd);
> +		blk_abort_req(qc->scsicmd->request);
>  		scsi_schedule_eh(qc->scsicmd->device->host);
>  		return;
>  	}
> diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
> index 2b8213b..13ac4a3 100644
> --- a/drivers/scsi/libsas/sas_internal.h
> +++ b/drivers/scsi/libsas/sas_internal.h
> @@ -55,7 +55,7 @@ void sas_unregister_phys(struct sas_ha_struct *sas_ha);
>  int  sas_register_ports(struct sas_ha_struct *sas_ha);
>  void sas_unregister_ports(struct sas_ha_struct *sas_ha);
> 
> -enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *);
> +enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *);
> 
>  int  sas_init_queue(struct sas_ha_struct *sas_ha);
>  int  sas_init_events(struct sas_ha_struct *sas_ha);
> diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
> index 7663841..6d6867c 100644
> --- a/drivers/scsi/libsas/sas_scsi_host.c
> +++ b/drivers/scsi/libsas/sas_scsi_host.c
> @@ -654,43 +654,43 @@ out:
>  	return;
>  }
> 
> -enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
> +enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
>  {
>  	struct sas_task *task = TO_SAS_TASK(cmd);
>  	unsigned long flags;
> 
>  	if (!task) {
> -		cmd->timeout_per_command /= 2;
> +		cmd->request->timeout /= 2;
>  		SAS_DPRINTK("command 0x%p, task 0x%p, gone: %s\n",
> -			    cmd, task, (cmd->timeout_per_command ?
> -			    "EH_RESET_TIMER" : "EH_NOT_HANDLED"));
> -		if (!cmd->timeout_per_command)
> -			return EH_NOT_HANDLED;
> -		return EH_RESET_TIMER;
> +			    cmd, task, (cmd->request->timeout ?
> +			    "BLK_EH_RESET_TIMER" : "BLK_EH_NOT_HANDLED"));
> +		if (!cmd->request->timeout)
> +			return BLK_EH_NOT_HANDLED;
> +		return BLK_EH_RESET_TIMER;
>  	}
> 
>  	spin_lock_irqsave(&task->task_state_lock, flags);
>  	BUG_ON(task->task_state_flags & SAS_TASK_STATE_ABORTED);
>  	if (task->task_state_flags & SAS_TASK_STATE_DONE) {
>  		spin_unlock_irqrestore(&task->task_state_lock, flags);
> -		SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n",
> -			    cmd, task);
> -		return EH_HANDLED;
> +		SAS_DPRINTK("command 0x%p, task 0x%p, timed out: "
> +			    "BLK_EH_HANDLED\n", cmd, task);
> +		return BLK_EH_HANDLED;
>  	}
>  	if (!(task->task_state_flags & SAS_TASK_AT_INITIATOR)) {
>  		spin_unlock_irqrestore(&task->task_state_lock, flags);
>  		SAS_DPRINTK("command 0x%p, task 0x%p, not at initiator: "
> -			    "EH_RESET_TIMER\n",
> +			    "BLK_EH_RESET_TIMER\n",
>  			    cmd, task);
> -		return EH_RESET_TIMER;
> +		return BLK_EH_RESET_TIMER;
>  	}
>  	task->task_state_flags |= SAS_TASK_STATE_ABORTED;
>  	spin_unlock_irqrestore(&task->task_state_lock, flags);
> 
> -	SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_NOT_HANDLED\n",
> +	SAS_DPRINTK("command 0x%p, task 0x%p, timed out: BLK_EH_NOT_HANDLED\n",
>  		    cmd, task);
> 
> -	return EH_NOT_HANDLED;
> +	return BLK_EH_NOT_HANDLED;
>  }
> 
>  int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
> @@ -1020,7 +1020,7 @@ void sas_task_abort(struct sas_task *task)
>  		return;
>  	}
> 
> -	scsi_req_abort_cmd(sc);
> +	blk_abort_req(sc->request);
>  	scsi_schedule_eh(sc->device->host);
>  }
> 
> diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c
> index ebb948c..84c0eb4 100644
> --- a/drivers/scsi/megaraid/megaraid_sas.c
> +++ b/drivers/scsi/megaraid/megaraid_sas.c
> @@ -969,7 +969,7 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd)
>   * cmd has not been completed within the timeout period.
>   */
>  static enum
> -scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd)
> +blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd)
>  {
>  	struct megasas_cmd *cmd = (struct megasas_cmd *)scmd->SCp.ptr;
>  	struct megasas_instance *instance;
> @@ -977,7 +977,7 @@ scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd)
> 
>  	if (time_after(jiffies, scmd->jiffies_at_alloc +
>  				(MEGASAS_DEFAULT_CMD_TIMEOUT * 2) * HZ)) {
> -		return EH_NOT_HANDLED;
> +		return BLK_EH_NOT_HANDLED;
>  	}
> 
>  	instance = cmd->instance;
> @@ -991,7 +991,7 @@ scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd)
> 
>  		spin_unlock_irqrestore(instance->host->host_lock, flags);
>  	}
> -	return EH_RESET_TIMER;
> +	return BLK_EH_RESET_TIMER;
>  }
> 
>  /**
> diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
> index 030ba49..39c2d4b 100644
> --- a/drivers/scsi/ncr53c8xx.c
> +++ b/drivers/scsi/ncr53c8xx.c
> @@ -4170,8 +4170,8 @@ static int ncr_queue_command (struct ncb *np, struct scsi_cmnd *cmd)
>  	**
>  	**----------------------------------------------------
>  	*/
> -	if (np->settle_time && cmd->timeout_per_command >= HZ) {
> -		u_long tlimit = jiffies + cmd->timeout_per_command - HZ;
> +	if (np->settle_time && cmd->request->timeout >= HZ) {
> +		u_long tlimit = jiffies + cmd->request->timeout - HZ;
>  		if (time_after(np->settle_time, tlimit))
>  			np->settle_time = tlimit;
>  	}
> diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
> index 54d8bdf..f5d5b2a 100644
> --- a/drivers/scsi/qla1280.c
> +++ b/drivers/scsi/qla1280.c
> @@ -2862,7 +2862,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
>  	memset(((char *)pkt + 8), 0, (REQUEST_ENTRY_SIZE - 8));
> 
>  	/* Set ISP command timeout. */
> -	pkt->timeout = cpu_to_le16(cmd->timeout_per_command/HZ);
> +	pkt->timeout = cpu_to_le16(cmd->request->timeout/HZ);
> 
>  	/* Set device target ID and LUN */
>  	pkt->lun = SCSI_LUN_32(cmd);
> @@ -3161,7 +3161,7 @@ qla1280_32bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
>  	memset(((char *)pkt + 8), 0, (REQUEST_ENTRY_SIZE - 8));
> 
>  	/* Set ISP command timeout. */
> -	pkt->timeout = cpu_to_le16(cmd->timeout_per_command/HZ);
> +	pkt->timeout = cpu_to_le16(cmd->request->timeout/HZ);
> 
>  	/* Set device target ID and LUN */
>  	pkt->lun = SCSI_LUN_32(cmd);
> diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
> index b1d565c..005eb66 100644
> --- a/drivers/scsi/qla4xxx/ql4_os.c
> +++ b/drivers/scsi/qla4xxx/ql4_os.c
> @@ -1564,7 +1564,7 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
>  	DEBUG2(printk(KERN_INFO
>  		      "scsi%ld: DEVICE_RESET cmd=%p jiffies = 0x%lx, to=%x,"
>  		      "dpc_flags=%lx, status=%x allowed=%d\n", ha->host_no,
> -		      cmd, jiffies, cmd->timeout_per_command / HZ,
> +		      cmd, jiffies, cmd->request->timeout / HZ,
>  		      ha->dpc_flags, cmd->result, cmd->allowed));
> 
>  	/* FIXME: wait for hba to go online */
> diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
> index a5de1a8..e67eb6e 100644
> --- a/drivers/scsi/scsi.c
> +++ b/drivers/scsi/scsi.c
> @@ -203,7 +203,6 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask)
> 
>  		memset(cmd, 0, sizeof(*cmd));
>  		cmd->device = dev;
> -		init_timer(&cmd->eh_timeout);
>  		INIT_LIST_HEAD(&cmd->list);
>  		spin_lock_irqsave(&dev->list_lock, flags);
>  		list_add_tail(&cmd->list, &dev->cmd_list);
> @@ -472,14 +471,19 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
>  	unsigned long timeout;
>  	int rtn = 0;
> 
> +	/*
> +	 * We will use a queued command if possible, otherwise we will
> +	 * emulate the queuing and calling of completion function ourselves.
> +	 */
> +	atomic_inc(&cmd->device->iorequest_cnt);
> +
>  	/* check if the device is still usable */
>  	if (unlikely(cmd->device->sdev_state == SDEV_DEL)) {
>  		/* in SDEV_DEL we error all commands. DID_NO_CONNECT
>  		 * returns an immediate error upwards, and signals
>  		 * that the device is no longer present */
>  		cmd->result = DID_NO_CONNECT << 16;
> -		atomic_inc(&cmd->device->iorequest_cnt);
> -		__scsi_done(cmd);
> +		scsi_done(cmd);
>  		/* return 0 (because the command has been processed) */
>  		goto out;
>  	}
> @@ -492,7 +496,8 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
>  		 * future requests should not occur until the device 
>  		 * transitions out of the suspend state.
>  		 */
> -		scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
> +
> +		scsi_queue_retry(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
> 
>  		SCSI_LOG_MLQUEUE(3, printk("queuecommand : device blocked \n"));
> 
> @@ -534,21 +539,9 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
>  		host->resetting = 0;
>  	}
> 
> -	/* 
> -	 * AK: unlikely race here: for some reason the timer could
> -	 * expire before the serial number is set up below.
> -	 */
> -	scsi_add_timer(cmd, cmd->timeout_per_command, scsi_times_out);
> -
>  	scsi_log_send(cmd);
> 
>  	/*
> -	 * We will use a queued command if possible, otherwise we will
> -	 * emulate the queuing and calling of completion function ourselves.
> -	 */
> -	atomic_inc(&cmd->device->iorequest_cnt);
> -
> -	/*
>  	 * Before we queue this command, check if the command
>  	 * length exceeds what the host adapter can handle.
>  	 */
> @@ -562,6 +555,12 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
>  	}
> 
>  	spin_lock_irqsave(host->host_lock, flags);
> +	/* 
> +	 * AK: unlikely race here: for some reason the timer could
> +	 * expire before the serial number is set up below.
> +	 *
> +	 * TODO: kill serial or move to blk layer
> +	 */
>  	scsi_cmd_get_serial(host, cmd); 
> 
>  	if (unlikely(host->shost_state == SHOST_DEL)) {
> @@ -572,12 +571,8 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
>  	}
>  	spin_unlock_irqrestore(host->host_lock, flags);
>  	if (rtn) {
> -		if (scsi_delete_timer(cmd)) {
> -			atomic_inc(&cmd->device->iodone_cnt);
> -			scsi_queue_insert(cmd,
> -					  (rtn == SCSI_MLQUEUE_DEVICE_BUSY) ?
> -					  rtn : SCSI_MLQUEUE_HOST_BUSY);
> -		}
> +		scsi_queue_retry(cmd, (rtn == SCSI_MLQUEUE_DEVICE_BUSY) ?
> +						rtn : SCSI_MLQUEUE_HOST_BUSY);
>  		SCSI_LOG_MLQUEUE(3,
>  		    printk("queuecommand : request rejected\n"));
>  	}
> @@ -588,24 +583,6 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
>  }
> 
>  /**
> - * scsi_req_abort_cmd -- Request command recovery for the specified command
> - * cmd: pointer to the SCSI command of interest
> - *
> - * This function requests that SCSI Core start recovery for the
> - * command by deleting the timer and adding the command to the eh
> - * queue.  It can be called by either LLDDs or SCSI Core.  LLDDs who
> - * implement their own error recovery MAY ignore the timeout event if
> - * they generated scsi_req_abort_cmd.
> - */
> -void scsi_req_abort_cmd(struct scsi_cmnd *cmd)
> -{
> -	if (!scsi_delete_timer(cmd))
> -		return;
> -	scsi_times_out(cmd);
> -}
> -EXPORT_SYMBOL(scsi_req_abort_cmd);
> -
> -/**
>   * scsi_done - Enqueue the finished SCSI command into the done queue.
>   * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives
>   * ownership back to SCSI Core -- i.e. the LLDD has finished with it.
> @@ -620,42 +597,7 @@ EXPORT_SYMBOL(scsi_req_abort_cmd);
>   */
>  static void scsi_done(struct scsi_cmnd *cmd)
>  {
> -	/*
> -	 * We don't have to worry about this one timing out any more.
> -	 * If we are unable to remove the timer, then the command
> -	 * has already timed out.  In which case, we have no choice but to
> -	 * let the timeout function run, as we have no idea where in fact
> -	 * that function could really be.  It might be on another processor,
> -	 * etc, etc.
> -	 */
> -	if (!scsi_delete_timer(cmd))
> -		return;
> -	__scsi_done(cmd);
> -}
> -
> -/* Private entry to scsi_done() to complete a command when the timer
> - * isn't running --- used by scsi_times_out */
> -void __scsi_done(struct scsi_cmnd *cmd)
> -{
> -	struct request *rq = cmd->request;
> -
> -	/*
> -	 * Set the serial numbers back to zero
> -	 */
> -	cmd->serial_number = 0;
> -
> -	atomic_inc(&cmd->device->iodone_cnt);
> -	if (cmd->result)
> -		atomic_inc(&cmd->device->ioerr_cnt);
> -
> -	BUG_ON(!rq);
> -
> -	/*
> -	 * The uptodate/nbytes values don't matter, as we allow partial
> -	 * completes and thus will check this in the softirq callback
> -	 */
> -	rq->completion_data = cmd;
> -	blk_complete_request(rq);
> +	blk_complete_request(cmd->request);
>  }
> 
>  /*
> diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
> index 8a525ab..4475648 100644
> --- a/drivers/scsi/scsi_error.c
> +++ b/drivers/scsi/scsi_error.c
> @@ -112,69 +112,8 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
>  }
> 
>  /**
> - * scsi_add_timer - Start timeout timer for a single scsi command.
> - * @scmd:	scsi command that is about to start running.
> - * @timeout:	amount of time to allow this command to run.
> - * @complete:	timeout function to call if timer isn't canceled.
> - *
> - * Notes:
> - *    This should be turned into an inline function.  Each scsi command
> - *    has its own timer, and as it is added to the queue, we set up the
> - *    timer.  When the command completes, we cancel the timer.
> - **/
> -void scsi_add_timer(struct scsi_cmnd *scmd, int timeout,
> -		    void (*complete)(struct scsi_cmnd *))
> -{
> -
> -	/*
> -	 * If the clock was already running for this command, then
> -	 * first delete the timer.  The timer handling code gets rather
> -	 * confused if we don't do this.
> -	 */
> -	if (scmd->eh_timeout.function)
> -		del_timer(&scmd->eh_timeout);
> -
> -	scmd->eh_timeout.data = (unsigned long)scmd;
> -	scmd->eh_timeout.expires = jiffies + timeout;
> -	scmd->eh_timeout.function = (void (*)(unsigned long)) complete;
> -
> -	SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p, time:"
> -					  " %d, (%p)\n", __FUNCTION__,
> -					  scmd, timeout, complete));
> -
> -	add_timer(&scmd->eh_timeout);
> -}
> -
> -/**
> - * scsi_delete_timer - Delete/cancel timer for a given function.
> - * @scmd:	Cmd that we are canceling timer for
> - *
> - * Notes:
> - *     This should be turned into an inline function.
> - *
> - * Return value:
> - *     1 if we were able to detach the timer.  0 if we blew it, and the
> - *     timer function has already started to run.
> - **/
> -int scsi_delete_timer(struct scsi_cmnd *scmd)
> -{
> -	int rtn;
> -
> -	rtn = del_timer(&scmd->eh_timeout);
> -
> -	SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p,"
> -					 " rtn: %d\n", __FUNCTION__,
> -					 scmd, rtn));
> -
> -	scmd->eh_timeout.data = (unsigned long)NULL;
> -	scmd->eh_timeout.function = NULL;
> -
> -	return rtn;
> -}
> -
> -/**
>   * scsi_times_out - Timeout function for normal scsi commands.
> - * @scmd:	Cmd that is timing out.
> + * @req:	request that is timing out.
>   *
>   * Notes:
>   *     We do not need to lock this.  There is the potential for a race
> @@ -182,9 +121,11 @@ int scsi_delete_timer(struct scsi_cmnd *scmd)
>   *     normal completion function determines that the timer has already
>   *     fired, then it mustn't do anything.
>   **/
> -void scsi_times_out(struct scsi_cmnd *scmd)
> +enum blk_eh_timer_return scsi_times_out(struct request *req)
>  {
> -	enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *);
> +	struct scsi_cmnd *scmd = req->special;
> +	enum blk_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *);
> +	enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED;
> 
>  	scsi_log_completion(scmd, TIMEOUT_ERROR);
> 
> @@ -196,22 +137,20 @@ void scsi_times_out(struct scsi_cmnd *scmd)
>  		eh_timed_out = NULL;
> 
>  	if (eh_timed_out)
> -		switch (eh_timed_out(scmd)) {
> -		case EH_HANDLED:
> -			__scsi_done(scmd);
> -			return;
> -		case EH_RESET_TIMER:
> -			scsi_add_timer(scmd, scmd->timeout_per_command,
> -				       scsi_times_out);
> -			return;
> -		case EH_NOT_HANDLED:
> +		rtn = eh_timed_out(scmd);
> +		switch (rtn) {
> +		case BLK_EH_NOT_HANDLED:
>  			break;
> +		default:
> +			return rtn;
>  		}
> 
>  	if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
>  		scmd->result |= DID_TIME_OUT << 16;
> -		__scsi_done(scmd);
> +		return BLK_EH_HANDLED;
>  	}
> +
> +	return BLK_EH_NOT_HANDLED;
>  }
> 
>  /**
> @@ -1666,7 +1605,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
>  	int rtn;
> 
>  	scmd->request = &req;
> -	memset(&scmd->eh_timeout, 0, sizeof(scmd->eh_timeout));
> 
>  	memset(&scmd->cmnd, '\0', sizeof(scmd->cmnd));
>      
> @@ -1679,8 +1617,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
> 
>  	scmd->sc_data_direction		= DMA_BIDIRECTIONAL;
> 
> -	init_timer(&scmd->eh_timeout);
> -
>  	/*
>  	 * Sometimes the command can get back into the timer chain,
>  	 * so use the pid as an identifier.
> diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
> index a417a6f..d9c7581 100644
> --- a/drivers/scsi/scsi_lib.c
> +++ b/drivers/scsi/scsi_lib.c
> @@ -162,6 +162,29 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
>  }
> 
>  /**
> + * scsi_queue_retry - Try inserting a command in the midlevel queue.
> + *
> + * @cmd:	command that we are adding to queue.
> + * @reason:	why we are inserting command to queue.
> + *
> + * Notes:       This is very similar to scsi_queue_insert except that we
> + *              call this function when we don't know if the blk layer timer
> + *              is active or not. We could implement this either by calling
> + *              blk_delete_timer and inserting in the midlevel queue if we
> + *              successfully delete the timer OR setting appropriate result
> + *              field in the cmd and letting it go through the normal done
> + *              routines which will retry the command. For now, We call
> + *              blk_delete_timer!
> + */
> +void scsi_queue_retry(struct scsi_cmnd *cmd, int reason)
> +{
> +	if (blk_delete_timer(cmd->request)) {
> +		atomic_inc(&cmd->device->iodone_cnt);
> +		scsi_queue_insert(cmd, reason);
> +	}
> +}
> +
> +/**
>   * scsi_execute - insert request and wait for the result
>   * @sdev:	scsi device
>   * @cmd:	scsi command
> @@ -1115,7 +1138,6 @@ static int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req)
>  	
>  	cmd->transfersize = req->data_len;
>  	cmd->allowed = req->retries;
> -	cmd->timeout_per_command = req->timeout;
>  	cmd->done = scsi_blk_pc_done;
>  	return BLKPREP_OK;
>  }
> @@ -1354,17 +1376,26 @@ static void scsi_kill_request(struct request *req, struct request_queue *q)
>  	spin_unlock(shost->host_lock);
>  	spin_lock(sdev->request_queue->queue_lock);
> 
> -	__scsi_done(cmd);
> +	__blk_complete_request(req);
>  }
> 
>  static void scsi_softirq_done(struct request *rq)
>  {
> -	struct scsi_cmnd *cmd = rq->completion_data;
> -	unsigned long wait_for = (cmd->allowed + 1) * cmd->timeout_per_command;
> +	struct scsi_cmnd *cmd = rq->special;
> +	unsigned long wait_for = (cmd->allowed + 1) * rq->timeout;
>  	int disposition;
> 
>  	INIT_LIST_HEAD(&cmd->eh_entry);
> 
> +	/*
> +	 * Set the serial numbers back to zero
> +	 */
> +	cmd->serial_number = 0;
> +	
> +	atomic_inc(&cmd->device->iodone_cnt);
> +	if (cmd->result)
> +		atomic_inc(&cmd->device->ioerr_cnt);
> +
>  	disposition = scsi_decide_disposition(cmd);
>  	if (disposition != SUCCESS &&
>  	    time_before(cmd->jiffies_at_alloc + wait_for, jiffies)) {
> @@ -1581,6 +1612,7 @@ struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
> 
>  	blk_queue_prep_rq(q, scsi_prep_fn);
>  	blk_queue_softirq_done(q, scsi_softirq_done);
> +	blk_queue_rq_timed_out(q, scsi_times_out);
>  	return q;
>  }
> 
> diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
> index ee8efe8..4c29a95 100644
> --- a/drivers/scsi/scsi_priv.h
> +++ b/drivers/scsi/scsi_priv.h
> @@ -4,6 +4,7 @@
>  #include <linux/device.h>
> 
>  struct request_queue;
> +struct request;
>  struct scsi_cmnd;
>  struct scsi_device;
>  struct scsi_host_template;
> @@ -27,7 +28,6 @@ extern void scsi_exit_hosts(void);
>  extern int scsi_dispatch_cmd(struct scsi_cmnd *cmd);
>  extern int scsi_setup_command_freelist(struct Scsi_Host *shost);
>  extern void scsi_destroy_command_freelist(struct Scsi_Host *shost);
> -extern void __scsi_done(struct scsi_cmnd *cmd);
>  #ifdef CONFIG_SCSI_LOGGING
>  void scsi_log_send(struct scsi_cmnd *cmd);
>  void scsi_log_completion(struct scsi_cmnd *cmd, int disposition);
> @@ -49,10 +49,7 @@ extern int __init scsi_init_devinfo(void);
>  extern void scsi_exit_devinfo(void);
> 
>  /* scsi_error.c */
> -extern void scsi_add_timer(struct scsi_cmnd *, int,
> -		void (*)(struct scsi_cmnd *));
> -extern int scsi_delete_timer(struct scsi_cmnd *);
> -extern void scsi_times_out(struct scsi_cmnd *cmd);
> +extern enum blk_eh_timer_return scsi_times_out(struct request *req);
>  extern int scsi_error_handler(void *host);
>  extern int scsi_decide_disposition(struct scsi_cmnd *cmd);
>  extern void scsi_eh_wakeup(struct Scsi_Host *shost);
> @@ -67,6 +64,7 @@ int scsi_eh_get_sense(struct list_head *work_q,
>  extern int scsi_maybe_unblock_host(struct scsi_device *sdev);
>  extern void scsi_device_unbusy(struct scsi_device *sdev);
>  extern int scsi_queue_insert(struct scsi_cmnd *cmd, int reason);
> +extern void scsi_queue_retry(struct scsi_cmnd *cmd, int reason);
>  extern void scsi_next_command(struct scsi_cmnd *cmd);
>  extern void scsi_run_host_queues(struct Scsi_Host *shost);
>  extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev);
> diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
> index 34cdce6..59d1570 100644
> --- a/drivers/scsi/scsi_sysfs.c
> +++ b/drivers/scsi/scsi_sysfs.c
> @@ -443,12 +443,15 @@ sdev_rd_attr (vendor, "%.8s\n");
>  sdev_rd_attr (model, "%.16s\n");
>  sdev_rd_attr (rev, "%.4s\n");
> 
> +/*
> + * TODO: can we make these symlinks to the block layer ones?
> + */
>  static ssize_t
>  sdev_show_timeout (struct device *dev, struct device_attribute *attr, char *buf)
>  {
>  	struct scsi_device *sdev;
>  	sdev = to_scsi_device(dev);
> -	return snprintf (buf, 20, "%d\n", sdev->timeout / HZ);
> +	return snprintf (buf, 20, "%d\n", sdev->request_queue->rq_timeout / HZ);
>  }
> 
>  static ssize_t
> @@ -458,7 +461,7 @@ sdev_store_timeout (struct device *dev, struct device_attribute *attr, const cha
>  	int timeout;
>  	sdev = to_scsi_device(dev);
>  	sscanf (buf, "%d\n", &timeout);
> -	sdev->timeout = timeout * HZ;
> +	blk_queue_rq_timeout(sdev->request_queue, timeout * HZ);
>  	return count;
>  }
>  static DEVICE_ATTR(timeout, S_IRUGO | S_IWUSR, sdev_show_timeout, sdev_store_timeout);
> diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
> index 4705725..0aef522 100644
> --- a/drivers/scsi/scsi_transport_fc.c
> +++ b/drivers/scsi/scsi_transport_fc.c
> @@ -1920,15 +1920,15 @@ static int fc_vport_match(struct attribute_container *cont,
>   * Notes:
>   *	This routine assumes no locks are held on entry.
>   **/
> -static enum scsi_eh_timer_return
> +static enum blk_eh_timer_return
>  fc_timed_out(struct scsi_cmnd *scmd)
>  {
>  	struct fc_rport *rport = starget_to_rport(scsi_target(scmd->device));
> 
>  	if (rport->port_state == FC_PORTSTATE_BLOCKED)
> -		return EH_RESET_TIMER;
> +		return BLK_EH_RESET_TIMER;
> 
> -	return EH_NOT_HANDLED;
> +	return BLK_EH_NOT_HANDLED;
>  }
> 
>  /*
> diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
> index 2c6116f..7720698 100644
> --- a/drivers/scsi/sd.c
> +++ b/drivers/scsi/sd.c
> @@ -338,7 +338,6 @@ static int sd_init_command(struct scsi_cmnd * SCpnt)
>  	struct gendisk *disk = rq->rq_disk;
>  	sector_t block = rq->sector;
>  	unsigned int this_count = SCpnt->request_bufflen >> 9;
> -	unsigned int timeout = sdp->timeout;
> 
>  	SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt,
>  					"sd_init_command: block=%llu, "
> @@ -489,7 +488,6 @@ static int sd_init_command(struct scsi_cmnd * SCpnt)
>  	SCpnt->transfersize = sdp->sector_size;
>  	SCpnt->underflow = this_count << 9;
>  	SCpnt->allowed = SD_MAX_RETRIES;
> -	SCpnt->timeout_per_command = timeout;
> 
>  	/*
>  	 * This is the completion routine we use.  This is matched in terms
> @@ -1629,11 +1627,12 @@ static int sd_probe(struct device *dev)
>  	sdkp->index = index;
>  	sdkp->openers = 0;
> 
> -	if (!sdp->timeout) {
> +	if (!sdp->request_queue->rq_timeout) {
>  		if (sdp->type != TYPE_MOD)
> -			sdp->timeout = SD_TIMEOUT;
> +			blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT);
>  		else
> -			sdp->timeout = SD_MOD_TIMEOUT;
> +			blk_queue_rq_timeout(sdp->request_queue,
> +					     SD_MOD_TIMEOUT);
>  	}
> 
>  	class_device_initialize(&sdkp->cdev);
> diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
> index 902eb11..cb73d58 100644
> --- a/drivers/scsi/sr.c
> +++ b/drivers/scsi/sr.c
> @@ -298,7 +298,7 @@ static void rw_intr(struct scsi_cmnd * SCpnt)
> 
>  static int sr_init_command(struct scsi_cmnd * SCpnt)
>  {
> -	int block=0, this_count, s_size, timeout = SR_TIMEOUT;
> +	int block=0, this_count, s_size;
>  	struct scsi_cd *cd = scsi_cd(SCpnt->request->rq_disk);
> 
>  	SCSI_LOG_HLQUEUE(1, printk("Doing sr request, dev = %s, block = %d\n",
> @@ -407,7 +407,6 @@ static int sr_init_command(struct scsi_cmnd * SCpnt)
>  	SCpnt->transfersize = cd->device->sector_size;
>  	SCpnt->underflow = this_count << 9;
>  	SCpnt->allowed = MAX_RETRIES;
> -	SCpnt->timeout_per_command = timeout;
> 
>  	/*
>  	 * This is the completion routine we use.  This is matched in terms
> @@ -570,6 +569,8 @@ static int sr_probe(struct device *dev)
>  	disk->fops = &sr_bdops;
>  	disk->flags = GENHD_FL_CD;
> 
> +	blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT);
> +
>  	cd->device = sdev;
>  	cd->disk = disk;
>  	cd->driver = &sr_template;
> diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c
> index 3db2232..702f730 100644
> --- a/drivers/scsi/sym53c8xx_2/sym_glue.c
> +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c
> @@ -571,8 +571,8 @@ static int sym53c8xx_queue_command(struct scsi_cmnd *cmd,
>  	 *  Shorten our settle_time if needed for 
>  	 *  this command not to time out.
>  	 */
> -	if (np->s.settle_time_valid && cmd->timeout_per_command) {
> -		unsigned long tlimit = jiffies + cmd->timeout_per_command;
> +	if (np->s.settle_time_valid && cmd->request->timeout) {
> +		unsigned long tlimit = jiffies + cmd->request->timeout;
>  		tlimit -= SYM_CONF_TIMER_INTERVAL*2;
>  		if (time_after(np->s.settle_time, tlimit)) {
>  			np->s.settle_time = tlimit;
> diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> index b126c6f..bb59cdb 100644
> --- a/include/linux/blkdev.h
> +++ b/include/linux/blkdev.h
> @@ -309,7 +309,8 @@ struct request {
>  	void *data;
>  	void *sense;
> 
> -	unsigned int timeout;
> +	unsigned long timeout;
> +	struct list_head timeout_list;
>  	int retries;
> 
>  	/*
> @@ -348,6 +349,14 @@ typedef int (issue_flush_fn) (struct request_queue *, struct gendisk *, sector_t
>  typedef void (prepare_flush_fn) (struct request_queue *, struct request *);
>  typedef void (softirq_done_fn)(struct request *);
> 
> +enum blk_eh_timer_return {
> +	BLK_EH_NOT_HANDLED,
> +	BLK_EH_HANDLED,
> +	BLK_EH_RESET_TIMER,
> +};
> +
> +typedef enum blk_eh_timer_return (rq_timed_out_fn)(struct request *);
> +
>  enum blk_queue_state {
>  	Queue_down,
>  	Queue_up,
> @@ -385,6 +394,7 @@ struct request_queue
>  	issue_flush_fn		*issue_flush_fn;
>  	prepare_flush_fn	*prepare_flush_fn;
>  	softirq_done_fn		*softirq_done_fn;
> +	rq_timed_out_fn		*rq_timed_out_fn;
> 
>  	/*
>  	 * Dispatch queue sorting
> @@ -455,6 +465,10 @@ struct request_queue
>  	unsigned int		nr_sorted;
>  	unsigned int		in_flight;
> 
> +	unsigned int		rq_timeout;
> +	struct			timer_list timeout;
> +	struct list_head	timeout_list;
> +
>  	/*
>  	 * sg stuff
>  	 */
> @@ -733,6 +747,10 @@ extern int end_that_request_chunk(struct request *, int, int);
>  extern void end_that_request_last(struct request *, int);
>  extern void end_request(struct request *req, int uptodate);
>  extern void blk_complete_request(struct request *);
> +extern void __blk_complete_request(struct request *);
> +extern void blk_abort_req(struct request *);
> +extern int blk_delete_timer(struct request *);
> +extern void blk_add_timer(struct request *);
> 
>  /*
>   * end_that_request_first/chunk() takes an uptodate argument. we account
> @@ -744,6 +762,8 @@ extern void blk_complete_request(struct request *);
> 
>  static inline void blkdev_dequeue_request(struct request *req)
>  {
> +	if (req->q->rq_timed_out_fn)
> +		blk_add_timer(req);
>  	elv_dequeue_request(req->q, req);
>  }
> 
> @@ -767,6 +787,8 @@ extern void blk_queue_prep_rq(struct request_queue *, prep_rq_fn *pfn);
>  extern void blk_queue_merge_bvec(struct request_queue *, merge_bvec_fn *);
>  extern void blk_queue_dma_alignment(struct request_queue *, int);
>  extern void blk_queue_softirq_done(struct request_queue *, softirq_done_fn *);
> +extern void blk_queue_rq_timed_out(struct request_queue *, rq_timed_out_fn *);
> +extern void blk_queue_rq_timeout(struct request_queue *, unsigned int);
>  extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev);
>  extern int blk_queue_ordered(struct request_queue *, unsigned, prepare_flush_fn *);
>  extern void blk_queue_issue_flush_fn(struct request_queue *, issue_flush_fn *);
> diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
> index 53e1705..5841369 100644
> --- a/include/scsi/scsi_cmnd.h
> +++ b/include/scsi/scsi_cmnd.h
> @@ -56,7 +56,6 @@ struct scsi_cmnd {
> 
>  	int retries;
>  	int allowed;
> -	int timeout_per_command;
> 
>  	unsigned char cmd_len;
>  	enum dma_data_direction sc_data_direction;
> @@ -66,7 +65,6 @@ struct scsi_cmnd {
>  	unsigned char cmnd[MAX_COMMAND_SIZE];
>  	unsigned request_bufflen;	/* Actual request size */
> 
> -	struct timer_list eh_timeout;	/* Used to time out the command. */
>  	void *request_buffer;		/* Actual requested buffer */
> 
>  	/* These elements define the operation we ultimately want to perform */
> @@ -126,7 +124,6 @@ extern void __scsi_put_command(struct Scsi_Host *, struct scsi_cmnd *,
>  			       struct device *);
>  extern void scsi_io_completion(struct scsi_cmnd *, unsigned int);
>  extern void scsi_finish_command(struct scsi_cmnd *cmd);
> -extern void scsi_req_abort_cmd(struct scsi_cmnd *cmd);
> 
>  extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count,
>  				 size_t *offset, size_t *len);
> diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
> index 3b8a6a8..165ccaf 100644
> --- a/include/scsi/scsi_host.h
> +++ b/include/scsi/scsi_host.h
> @@ -36,13 +36,6 @@ struct blk_queue_tags;
>  #define DISABLE_CLUSTERING 0
>  #define ENABLE_CLUSTERING 1
> 
> -enum scsi_eh_timer_return {
> -	EH_NOT_HANDLED,
> -	EH_HANDLED,
> -	EH_RESET_TIMER,
> -};
> -
> -
>  struct scsi_host_template {
>  	struct module *module;
>  	const char *name;
> @@ -336,7 +329,7 @@ struct scsi_host_template {
>  	 *
>  	 * Status: OPTIONAL
>  	 */
> -	enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *);
> +	enum blk_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *);
> 
>  	/*
>  	 * Name of proc directory
> diff --git a/include/scsi/scsi_transport.h b/include/scsi/scsi_transport.h
> index 3c18baa..23e8d97 100644
> --- a/include/scsi/scsi_transport.h
> +++ b/include/scsi/scsi_transport.h
> @@ -21,6 +21,7 @@
>  #define SCSI_TRANSPORT_H
> 
>  #include <linux/transport_class.h>
> +#include <linux/blkdev.h>
>  #include <scsi/scsi_host.h>
>  #include <scsi/scsi_device.h>
> 
> @@ -64,7 +65,7 @@ struct scsi_transport_template {
>  	 *			begin counting again
>  	 * EH_NOT_HANDLED	Begin normal error recovery
>  	 */
> -	enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *);
> +	enum blk_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *);
>  };
> 
>  #define transport_class_to_shost(tc) \
> 
> -- 
> Jens Axboe
> 
> -
> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux