@@ -27,6 +27,7 @@
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
+#include <linux/list_sort.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -2186,6 +2187,75 @@ void scsi_eh_ready_devs(struct Scsi_Host *shost,
}
EXPORT_SYMBOL_GPL(scsi_eh_ready_devs);
+/*
+ * Returns true if .eh_prepare_resubmit should be called for the commands in
+ * @done_q.
+ */
+static bool scsi_needs_preparation(struct list_head *done_q)
+{
+ struct scsi_cmnd *scmd;
+
+ list_for_each_entry(scmd, done_q, eh_entry) {
+ struct scsi_driver *uld;
+ bool (*npr)(struct scsi_cmnd *);
+
+ if (!scmd->device)
+ continue;
+ uld = scsi_cmd_to_driver(scmd);
+ if (!uld)
+ continue;
+ npr = uld->eh_needs_prepare_resubmit;
+ if (npr && npr(scmd))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Comparison function that allows to sort SCSI commands by ULD driver.
+ */
+static int scsi_cmp_uld(void *priv, const struct list_head *_a,
+ const struct list_head *_b)
+{
+ struct scsi_cmnd *a = list_entry(_a, typeof(*a), eh_entry);
+ struct scsi_cmnd *b = list_entry(_b, typeof(*b), eh_entry);
+
+ /* See also the comment above the list_sort() definition. */
+ return scsi_cmd_to_driver(a) > scsi_cmd_to_driver(b);
+}
+
+void scsi_call_prepare_resubmit(struct list_head *done_q)
+{
+ struct scsi_cmnd *scmd, *next;
+
+ if (!scsi_needs_preparation(done_q))
+ return;
+
+ /* Sort pending SCSI commands by ULD. */
+ list_sort(NULL, done_q, scsi_cmp_uld);
+
+ /*
+ * Call .eh_prepare_resubmit for each range of commands with identical
+ * ULD driver pointer.
+ */
+ list_for_each_entry_safe(scmd, next, done_q, eh_entry) {
+ struct scsi_driver *uld =
+ scmd->device ? scsi_cmd_to_driver(scmd) : NULL;
+ struct list_head *prev, uld_cmd_list;
+
+ while (&next->eh_entry != done_q &&
+ scsi_cmd_to_driver(next) == uld)
+ next = list_next_entry(next, eh_entry);
+ if (!uld->eh_prepare_resubmit)
+ continue;
+ prev = scmd->eh_entry.prev;
+ list_cut_position(&uld_cmd_list, prev, next->eh_entry.prev);
+ uld->eh_prepare_resubmit(&uld_cmd_list);
+ list_splice(&uld_cmd_list, prev);
+ }
+}
+
/**
* scsi_eh_flush_done_q - finish processed commands or retry them.
* @done_q: list_head of processed commands.
@@ -2194,6 +2264,8 @@ void scsi_eh_flush_done_q(struct list_head *done_q)
{
struct scsi_cmnd *scmd, *next;
+ scsi_call_prepare_resubmit(done_q);
+
list_for_each_entry_safe(scmd, next, done_q, eh_entry) {
list_del_init(&scmd->eh_entry);
if (scsi_device_online(scmd->device) &&
@@ -101,6 +101,7 @@ int scsi_eh_get_sense(struct list_head *work_q,
struct list_head *done_q);
bool scsi_noretry_cmd(struct scsi_cmnd *scmd);
void scsi_eh_done(struct scsi_cmnd *scmd);
+void scsi_call_prepare_resubmit(struct list_head *done_q);
/* scsi_lib.c */
extern void scsi_device_unbusy(struct scsi_device *sdev, struct scsi_cmnd *cmd);
@@ -18,6 +18,8 @@ struct scsi_driver {
int (*done)(struct scsi_cmnd *);
int (*eh_action)(struct scsi_cmnd *, int);
void (*eh_reset)(struct scsi_cmnd *);
+ bool (*eh_needs_prepare_resubmit)(struct scsi_cmnd *cmd);
+ void (*eh_prepare_resubmit)(struct list_head *cmd_list);
};
#define to_scsi_driver(drv) \
container_of((drv), struct scsi_driver, gendrv)
Introduce the .eh_needs_prepare_resubmit and the .eh_prepare_resubmit function pointers in struct scsi_driver. Make the error handler call .eh_prepare_resubmit() before resubmitting commands if any of the .eh_needs_prepare_resubmit() invocations return true. A later patch will use this functionality to sort SCSI commands by LBA from inside the SCSI disk driver before these are resubmitted by the error handler. Cc: Martin K. Petersen <martin.petersen@oracle.com> Cc: Damien Le Moal <dlemoal@kernel.org> Cc: Christoph Hellwig <hch@lst.de> Cc: Ming Lei <ming.lei@redhat.com> Signed-off-by: Bart Van Assche <bvanassche@acm.org> --- drivers/scsi/scsi_error.c | 72 ++++++++++++++++++++++++++++++++++++++ drivers/scsi/scsi_priv.h | 1 + include/scsi/scsi_driver.h | 2 ++ 3 files changed, 75 insertions(+)