scsi_lib: Some love to scsi_lib
cover scsi_lib: Some love to scsi_lib
Submitted a much needed cleanup to scsi_lib's command completion path. The code after these patches should theoretically be behaviour equivalent to current code. Only cleaner smaller, and easier to understand. The motivation was to make things clearer to understand. It was all inspired by Alan Stern and more things added to that.
Theoretically, since I have only tested with the usual tests I have. Boot an x86_64 machine, git clone, make, xfstests ...
I did not run any Error injection tests with scsi_debug, since I don't have any set up. Though one should because the error handling might have changed accidentally, though not intended.
Please someone, Alan Jeff ... I have tried to go through every possible code path and prove that we do exactly as before. But I might have missed something. Please check my Math.
I'm sitting on these patches since 2.6.34-rc4, but never followed them through, and tested them. Sorry. They patched without any issues on top of current scsi-misc meaning nothing changed in this area since a long time.
The patches are:
[PATCH 1/3] scsi_lib: request_queue is only needed inside scsi_requeue_command [PATCH 2/3] scsi_lib: Remove that __scsi_release_buffers contraption
These two are pure and trivial
[PATCH 3/3] scsi_lib: Collapse scsi_end_request into only user
This one should be reviewed, but should not change any behaviour.
Thanks
Boaz
PATCH 1/3 scsi_lib: request_queue is only needed inside scsi_requeue_command
This is a pure cleanup. Just starting the engines for things to come.
--- drivers/scsi/scsi_lib.c | 11 +++++------ 1 files changed, 5 insertions(+), 6 deletions(-)
drivers/scsi/scsi_lib.c +5 -6
@@ -475,9 +475,10 @@ static void scsi_run_queue(struct request_queue *q)* sector. * Notes: Upon return, cmd is a stale pointer. */ -static void scsi_requeue_command (struct request_queue *q,struct scsi_cmnd *cmd) +static void scsi_requeue_command (struct scsi_cmnd *cmd) {struct request *req = cmd->request; +struct request_queue *q = req->q;unsigned long flags;spin_lock_irqsave (q->queue_lock, flags);
@@ -538,7 +539,6 @@ static void __scsi_release_buffers(struct scsi_cmnd *, int);static struct scsi_cmnd *scsi_end_request (struct scsi_cmnd *cmd,int error,int bytes,int requeue) { -struct request_queue *q = cmd->device->request_queue;struct request *req = cmd->request;/*
@@ -557,7 +557,7 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int error,* queue, and goose the queue again. */ scsi_release_buffers (cmd); -scsi_requeue_command (q, cmd); +scsi_requeue_command (cmd); cmd =NULL ; }return cmd;
@@ -706,7 +706,6 @@ EXPORT_SYMBOL(scsi_release_buffers);void scsi_io_completion (struct scsi_cmnd *cmd,unsigned int good_bytes) {int result = cmd->result; -struct request_queue *q = cmd->device->request_queue;struct request *req = cmd->request;int error =0 ;struct scsi_sense_hdr sshdr;
@@ -907,7 +906,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)scsi_print_command (cmd); }if (blk_end_request_err (req, error)) -scsi_requeue_command (q, cmd); +scsi_requeue_command (cmd);else scsi_next_command (cmd);break ;
@@ -916,7 +915,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)* A new command will be prepared and issued. */ scsi_release_buffers (cmd); -scsi_requeue_command (q, cmd); +scsi_requeue_command (cmd);break ;case ACTION_RETRY :/* Retry the same command immediately */
Signature · 1 line
1.7.2.3
PATCH 2/3 scsi_lib: Remove that __scsi_release_buffers contraption
In reply to Boaz HarroshRemove "do_bidi_check" by checking and nullifying cmd->request when blk_end_* indicates the request is no longer valid.
--- drivers/scsi/scsi_lib.c | 41 +++++++++++++++++------------------------ 1 files changed, 17 insertions(+), 24 deletions(-)
drivers/scsi/scsi_lib.c +17 -24
@@ -512,8 +512,6 @@ void scsi_run_host_queues(struct Scsi_Host *shost)scsi_run_queue (sdev->request_queue); } -static void __scsi_release_buffers (struct scsi_cmnd *,int ); -/* * Function: scsi_end_request() *
@@ -564,11 +562,12 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int error, } } + cmd->request =NULL ;/* * This will goose the queue request function at the end, so we don't * need to worry about launching another command. */ -__scsi_release_buffers (cmd,0 ); +scsi_release_buffers (cmd);scsi_next_command (cmd);return NULL ; }
@@ -624,26 +623,6 @@ static void scsi_free_sgtable(struct scsi_data_buffer *sdb)__sg_free_table (&sdb->table,SCSI_MAX_SG_SEGMENTS , scsi_sg_free); } -static void __scsi_release_buffers (struct scsi_cmnd *cmd,int do_bidi_check) -{ - -if (cmd->sdb.table.nents) -scsi_free_sgtable (&cmd->sdb); - -memset (&cmd->sdb,0 ,sizeof (cmd->sdb)); - -if (do_bidi_check &&scsi_bidi_cmnd (cmd)) { -struct scsi_data_buffer *bidi_sdb = - cmd->request->next_rq->special; -scsi_free_sgtable (bidi_sdb); -kmem_cache_free (scsi_sdb_cache, bidi_sdb); - cmd->request->next_rq->special =NULL ; - } - -if (scsi_prot_sg_count (cmd)) -scsi_free_sgtable (cmd->prot_sdb); -} -/* * Function: scsi_release_buffers() *
@@ -663,7 +642,21 @@ static void __scsi_release_buffers(struct scsi_cmnd *cmd, int do_bidi_check)*/ void scsi_release_buffers (struct scsi_cmnd *cmd) { -__scsi_release_buffers (cmd,1 ); +if (cmd->sdb.table.nents) +scsi_free_sgtable (&cmd->sdb); + +memset (&cmd->sdb,0 ,sizeof (cmd->sdb)); + +if (cmd->request &&scsi_bidi_cmnd (cmd)) { +struct scsi_data_buffer *bidi_sdb = + cmd->request->next_rq->special; +scsi_free_sgtable (bidi_sdb); +kmem_cache_free (scsi_sdb_cache, bidi_sdb); + cmd->request->next_rq->special =NULL ; + } + +if (scsi_prot_sg_count (cmd)) +scsi_free_sgtable (cmd->prot_sdb); }EXPORT_SYMBOL (scsi_release_buffers);
Signature · 1 line
1.7.2.3
PATCH 3/3 scsi_lib: Collapse scsi_end_request into only user
In reply to Boaz HarroshEmbedding scsi_end_request() into scsi_io_completion actually simplifies the code and makes it clearer what's going on.
There is absolutely no functional and/or side effects changes after this patch.
Patch was inspired by Alan Stern.
--- drivers/scsi/scsi_lib.c | 92 ++++++++++------------------------------------- 1 files changed, 19 insertions(+), 73 deletions(-)
drivers/scsi/scsi_lib.c +19 -73
@@ -512,66 +512,6 @@ void scsi_run_host_queues(struct Scsi_Host *shost)scsi_run_queue (sdev->request_queue); } -/* -* Function: scsi_end_request() -* -* Purpose: Post-processing of completed commands (usually invoked at end -* of upper level post-processing and scsi_io_completion). -* -* Arguments: cmd - command that is complete. -* error - 0 if I/O indicates success, < 0 for I/O error. -* bytes - number of bytes of completed I/O -* requeue - indicates whether we should requeue leftovers. -* -* Lock status: Assumed that lock is not held upon entry. -* -* Returns: cmd if requeue required, NULL otherwise. -* -* Notes: This is called for block device requests in order to -* mark some number of sectors as complete. -* -* We are guaranteeing that the request queue will be goosed -* at some point during this call. -* Notes: If cmd was requeued, upon return it will be a stale pointer. -*/ -static struct scsi_cmnd *scsi_end_request (struct scsi_cmnd *cmd,int error, -int bytes,int requeue) -{ -struct request *req = cmd->request; - -/* -* If there are blocks left over at the end, set up the command -* to queue the remainder of them. -*/ -if (blk_end_request (req, error, bytes)) { -/* kill remainder if no retrys */ -if (error &&scsi_noretry_cmd (cmd)) -blk_end_request_all (req, error); -else { -if (requeue) { -/* -* Bleah. Leftovers again. Stick the -* leftovers in the front of the -* queue, and goose the queue again. -*/ -scsi_release_buffers (cmd); -scsi_requeue_command (cmd); - cmd =NULL ; - } -return cmd; - } - } - - cmd->request =NULL ; -/* -* This will goose the queue request function at the end, so we don't -* need to worry about launching another command. -*/ -scsi_release_buffers (cmd); -scsi_next_command (cmd); -return NULL ; -} -static inline unsigned int scsi_sgtable_index (unsigned short nents) {unsigned int index;
@@ -704,7 +644,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)struct scsi_sense_hdr sshdr;int sense_valid =0 ;int sense_deferred =0 ; -enum {ACTION_FAIL ,ACTION_REPREP ,ACTION_RETRY , +enum {ACTION_NEXT_CMND ,ACTION_FAIL ,ACTION_REPREP ,ACTION_RETRY ,ACTION_DELAYED_RETRY } action;char *description =NULL ;
@@ -780,17 +720,19 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) error =0 ; } -/* -* A number of bytes were successfully read. If there -* are leftovers and there is some kind of error -* (result != 0), retry the rest. -*/ -if (scsi_end_request (cmd, error, good_bytes, result ==0 ) ==NULL ) -return ; - - error = -EIO ; - -if (host_byte (result) ==DID_RESET ) { +if (likely (0 ==blk_end_request (req, error, good_bytes))) { +/* All is done and good move to next command */ + cmd->request =NULL ; + action =ACTION_NEXT_CMND ; + }else if (result ==0 ) { +/* Wrote some bytes but request was split */ + action =ACTION_REPREP ; + }else if (error &&scsi_noretry_cmd (cmd)) { +/* kill remainder if no retries */ +blk_end_request_all (req, error); + cmd->request =NULL ; + action =ACTION_NEXT_CMND ; + }else if (host_byte (result) ==DID_RESET ) {/* Third party bus reset or reset for error recovery * reasons. Just retry the command and see what * happens.
@@ -886,6 +828,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) }switch (action) { +case ACTION_NEXT_CMND : +scsi_release_buffers (cmd); +scsi_next_command (cmd); +break ;case ACTION_FAIL :/* Give up and fail the remainder of the request */ scsi_release_buffers (cmd);
@@ -898,7 +844,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)scsi_print_sense ("" , cmd);scsi_print_command (cmd); } -if (blk_end_request_err (req, error)) +if (blk_end_request_err (req, error ? error : -EIO ))scsi_requeue_command (cmd);else scsi_next_command (cmd);
Signature · 1 line
1.7.2.3
PATCH 1/1 scsi_lib: BUG: Can't RETRY scsi_cmnd if some bytes were completed
In reply to Boaz HarroshRe-inspecting the code after the last cleanup actually exposed a BAD bug for me. See below. James this is based on the last patchset I sent.
Boaz
---
From: Boaz Harrosh <[email protected]>
Subject: [PATCH] scsi_lib: BUG: Can't RETRY scsi_cmnd if some bytes were completed
In scsi_io_completion() there are many cases where action is set to ACTION_RETRY or ACTION_DELAYED_RETRY. But we are not allowed to just RETRY a command if some bytes where already completed by blk_end_request(). This is a bad memory overrun of DMA writing/reading random memory. We must re-prepare the command in this case.
It is possible that all the cases that set ACTION_RETRY* have actually come with good_bytes=0 (.i.e resid = everything) But both the error and resid value come from LLDs and/or targets and should not be trusted with such a BAD crash. Better safe than sorry.
It is possible that this fix is actually not good enough and in the case of some of these RETRYs we need to not call blk_end_request() in the first place. But this calls for a structural reorganisation of scsi_io_completion(). James this is your turf please have a look.
--- drivers/scsi/scsi_lib.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
drivers/scsi/scsi_lib.c +6 -0
@@ -827,6 +827,12 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) action =ACTION_FAIL ; } +if (action >=ACTION_RETRY && good_bytes) +/* We cannot just retry if above blk_end_request advanced on +* some good_bytes, so ACTION_REPREP +*/ + action =ACTION_REPREP ; +switch (action) {case ACTION_NEXT_CMND :scsi_release_buffers (cmd);
Signature · 1 line
1.7.2.3