lkml.org 
[lkml]   [2012]   [Apr]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[PATCH 08/17] hpsa: add abort error handler function
    From
    Date
    From: Stephen M. Cameron <scameron@beardog.cce.hp.com>

    Signed-off-by: Stephen M. Cameron <scameron@beardog.cce.hp.com>
    ---
    drivers/scsi/hpsa.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++
    drivers/scsi/hpsa.h | 21 ++++
    drivers/scsi/hpsa_cmd.h | 31 ++++++-
    3 files changed, 271 insertions(+), 2 deletions(-)

    diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
    index e45bfda..9882ef4 100644
    --- a/drivers/scsi/hpsa.c
    +++ b/drivers/scsi/hpsa.c
    @@ -159,6 +159,7 @@ static int hpsa_change_queue_depth(struct scsi_device *sdev,
    int qdepth, int reason);

    static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd);
    +static int hpsa_eh_abort_handler(struct scsi_cmnd *scsicmd);
    static int hpsa_slave_alloc(struct scsi_device *sdev);
    static void hpsa_slave_destroy(struct scsi_device *sdev);

    @@ -180,6 +181,7 @@ static int __devinit hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
    static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id);
    static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev,
    void __iomem *vaddr, int wait_for_ready);
    +static inline void finish_cmd(struct CommandList *c);
    #define BOARD_NOT_READY 0
    #define BOARD_READY 1

    @@ -506,6 +508,7 @@ static struct scsi_host_template hpsa_driver_template = {
    .change_queue_depth = hpsa_change_queue_depth,
    .this_id = -1,
    .use_clustering = ENABLE_CLUSTERING,
    + .eh_abort_handler = hpsa_eh_abort_handler,
    .eh_device_reset_handler = hpsa_eh_device_reset_handler,
    .ioctl = hpsa_ioctl,
    .slave_alloc = hpsa_slave_alloc,
    @@ -2344,6 +2347,191 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
    return FAILED;
    }

    +static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
    + struct CommandList *abort)
    +{
    + int rc = IO_OK;
    + struct CommandList *c;
    + struct ErrorInfo *ei;
    +
    + c = cmd_special_alloc(h);
    + if (c == NULL) { /* trouble... */
    + dev_warn(&h->pdev->dev, "cmd_special_alloc returned NULL!\n");
    + return -ENOMEM;
    + }
    +
    + fill_cmd(c, HPSA_ABORT_MSG, h, abort, 0, 0, scsi3addr, TYPE_MSG);
    + hpsa_scsi_do_simple_cmd_core(h, c);
    + dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: do_simple_cmd_core completed.\n",
    + __func__, abort->Header.Tag.upper, abort->Header.Tag.lower);
    + /* no unmap needed here because no data xfer. */
    +
    + ei = c->err_info;
    + switch (ei->CommandStatus) {
    + case CMD_SUCCESS:
    + break;
    + case CMD_UNABORTABLE: /* Very common, don't make noise. */
    + rc = -1;
    + break;
    + default:
    + dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: interpreting error.\n",
    + __func__, abort->Header.Tag.upper,
    + abort->Header.Tag.lower);
    + hpsa_scsi_interpret_error(c);
    + rc = -1;
    + break;
    + }
    + cmd_special_free(h, c);
    + dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: Finished.\n", __func__,
    + abort->Header.Tag.upper, abort->Header.Tag.lower);
    + return rc;
    +}
    +
    +/*
    + * hpsa_find_cmd_in_queue
    + *
    + * Used to determine whether a command (find) is still present
    + * in queue_head. Optionally excludes the last element of queue_head.
    + *
    + * This is used to avoid unnecessary aborts. Commands in h->reqQ have
    + * not yet been submitted, and so can be aborted by the driver without
    + * sending an abort to the hardware.
    + *
    + * Returns pointer to command if found in queue, NULL otherwise.
    + */
    +static struct CommandList *hpsa_find_cmd_in_queue(struct ctlr_info *h,
    + struct scsi_cmnd *find, struct list_head *queue_head)
    +{
    + unsigned long flags;
    + struct CommandList *c = NULL; /* ptr into cmpQ */
    +
    + if (!find)
    + return 0;
    + spin_lock_irqsave(&h->lock, flags);
    + list_for_each_entry(c, queue_head, list) {
    + if (c->scsi_cmd == NULL) /* e.g.: passthru ioctl */
    + continue;
    + if (c->scsi_cmd == find) {
    + spin_unlock_irqrestore(&h->lock, flags);
    + return c;
    + }
    + }
    + spin_unlock_irqrestore(&h->lock, flags);
    + return NULL;
    +}
    +
    +/* Send an abort for the specified command.
    + * If the device and controller support it,
    + * send a task abort request.
    + */
    +static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
    +{
    +
    + int i, rc;
    + struct ctlr_info *h;
    + struct hpsa_scsi_dev_t *dev;
    + struct CommandList *abort; /* pointer to command to be aborted */
    + struct CommandList *found;
    + struct scsi_cmnd *as; /* ptr to scsi cmd inside aborted command. */
    + char msg[256]; /* For debug messaging. */
    + int ml = 0;
    +
    + /* Find the controller of the command to be aborted */
    + h = sdev_to_hba(sc->device);
    + if (WARN(h == NULL,
    + "ABORT REQUEST FAILED, Controller lookup failed.\n"))
    + return FAILED;
    +
    + /* Check that controller supports some kind of task abort */
    + if (!(HPSATMF_PHYS_TASK_ABORT & h->TMFSupportFlags) &&
    + !(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags))
    + return FAILED;
    +
    + memset(msg, 0, sizeof(msg));
    + ml += sprintf(msg+ml, "ABORT REQUEST on C%d:B%d:T%d:L%d ",
    + h->scsi_host->host_no, sc->device->channel,
    + sc->device->id, sc->device->lun);
    +
    + /* Find the device of the command to be aborted */
    + dev = sc->device->hostdata;
    + if (!dev) {
    + dev_err(&h->pdev->dev, "%s FAILED, Device lookup failed.\n",
    + msg);
    + return FAILED;
    + }
    +
    + /* Get SCSI command to be aborted */
    + abort = (struct CommandList *) sc->host_scribble;
    + if (abort == NULL) {
    + dev_err(&h->pdev->dev, "%s FAILED, Command to abort is NULL.\n",
    + msg);
    + return FAILED;
    + }
    +
    + ml += sprintf(msg+ml, "Tag:0x%08x:%08x ",
    + abort->Header.Tag.upper, abort->Header.Tag.lower);
    + as = (struct scsi_cmnd *) abort->scsi_cmd;
    + if (as != NULL)
    + ml += sprintf(msg+ml, "Command:0x%x SN:0x%lx ",
    + as->cmnd[0], as->serial_number);
    + dev_dbg(&h->pdev->dev, "%s\n", msg);
    + dev_warn(&h->pdev->dev, "Abort request on C%d:B%d:T%d:L%d\n",
    + h->scsi_host->host_no, dev->bus, dev->target, dev->lun);
    +
    + /* Search reqQ to See if command is queued but not submitted,
    + * if so, complete the command with aborted status and remove
    + * it from the reqQ.
    + */
    + found = hpsa_find_cmd_in_queue(h, sc, &h->reqQ);
    + if (found) {
    + found->err_info->CommandStatus = CMD_ABORTED;
    + finish_cmd(found);
    + dev_info(&h->pdev->dev, "%s Request SUCCEEDED (driver queue).\n",
    + msg);
    + return SUCCESS;
    + }
    +
    + /* not in reqQ, if also not in cmpQ, must have already completed */
    + found = hpsa_find_cmd_in_queue(h, sc, &h->cmpQ);
    + if (!found) {
    + dev_dbg(&h->pdev->dev, "%s Request FAILED (not known to driver).\n",
    + msg);
    + return SUCCESS;
    + }
    +
    + /*
    + * Command is in flight, or possibly already completed
    + * by the firmware (but not to the scsi mid layer) but we can't
    + * distinguish which. Send the abort down.
    + */
    + rc = hpsa_send_abort(h, dev->scsi3addr, abort);
    + if (rc != 0) {
    + dev_dbg(&h->pdev->dev, "%s Request FAILED.\n", msg);
    + dev_warn(&h->pdev->dev, "FAILED abort on device C%d:B%d:T%d:L%d\n",
    + h->scsi_host->host_no,
    + dev->bus, dev->target, dev->lun);
    + return FAILED;
    + }
    + dev_info(&h->pdev->dev, "%s REQUEST SUCCEEDED.\n", msg);
    +
    + /* If the abort(s) above completed and actually aborted the
    + * command, then the command to be aborted should already be
    + * completed. If not, wait around a bit more to see if they
    + * manage to complete normally.
    + */
    +#define ABORT_COMPLETE_WAIT_SECS 30
    + for (i = 0; i < ABORT_COMPLETE_WAIT_SECS * 10; i++) {
    + found = hpsa_find_cmd_in_queue(h, sc, &h->cmpQ);
    + if (!found)
    + return SUCCESS;
    + msleep(100);
    + }
    + dev_warn(&h->pdev->dev, "%s FAILED. Aborted command has not completed after %d seconds.\n",
    + msg, ABORT_COMPLETE_WAIT_SECS);
    + return FAILED;
    +}
    +
    +
    /*
    * For operations that cannot sleep, a command block is allocated at init,
    * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
    @@ -2876,6 +3064,7 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
    int cmd_type)
    {
    int pci_dir = XFER_NONE;
    + struct CommandList *a; /* for commands to be aborted */

    c->cmd_type = CMD_IOCTL_PEND;
    c->Header.ReplyQueue = 0;
    @@ -2959,8 +3148,35 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
    c->Request.CDB[5] = 0x00;
    c->Request.CDB[6] = 0x00;
    c->Request.CDB[7] = 0x00;
    + break;
    + case HPSA_ABORT_MSG:
    + a = buff; /* point to command to be aborted */
    + dev_dbg(&h->pdev->dev, "Abort Tag:0x%08x:%08x using request Tag:0x%08x:%08x\n",
    + a->Header.Tag.upper, a->Header.Tag.lower,
    + c->Header.Tag.upper, c->Header.Tag.lower);
    + c->Request.CDBLen = 16;
    + c->Request.Type.Type = TYPE_MSG;
    + c->Request.Type.Attribute = ATTR_SIMPLE;
    + c->Request.Type.Direction = XFER_WRITE;
    + c->Request.Timeout = 0; /* Don't time out */
    + c->Request.CDB[0] = HPSA_TASK_MANAGEMENT;
    + c->Request.CDB[1] = HPSA_TMF_ABORT_TASK;
    + c->Request.CDB[2] = 0x00; /* reserved */
    + c->Request.CDB[3] = 0x00; /* reserved */
    + /* Tag to abort goes in CDB[4]-CDB[11] */
    + c->Request.CDB[4] = a->Header.Tag.lower & 0xFF;
    + c->Request.CDB[5] = (a->Header.Tag.lower >> 8) & 0xFF;
    + c->Request.CDB[6] = (a->Header.Tag.lower >> 16) & 0xFF;
    + c->Request.CDB[7] = (a->Header.Tag.lower >> 24) & 0xFF;
    + c->Request.CDB[8] = a->Header.Tag.upper & 0xFF;
    + c->Request.CDB[9] = (a->Header.Tag.upper >> 8) & 0xFF;
    + c->Request.CDB[10] = (a->Header.Tag.upper >> 16) & 0xFF;
    + c->Request.CDB[11] = (a->Header.Tag.upper >> 24) & 0xFF;
    + c->Request.CDB[12] = 0x00; /* reserved */
    + c->Request.CDB[13] = 0x00; /* reserved */
    + c->Request.CDB[14] = 0x00; /* reserved */
    + c->Request.CDB[15] = 0x00; /* reserved */
    break;
    -
    default:
    dev_warn(&h->pdev->dev, "unknown message type %d\n",
    cmd);
    @@ -3840,6 +4056,9 @@ static void __devinit hpsa_find_board_params(struct ctlr_info *h)
    h->maxsgentries = 31; /* default to traditional values */
    h->chainsize = 0;
    }
    +
    + /* Find out what task management functions are supported and cache */
    + h->TMFSupportFlags = readl(&(h->cfgtable->TMFSupportFlags));
    }

    static inline bool hpsa_CISS_signature_present(struct ctlr_info *h)
    diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
    index 48f7812..d8aa95c 100644
    --- a/drivers/scsi/hpsa.h
    +++ b/drivers/scsi/hpsa.h
    @@ -125,6 +125,27 @@ struct ctlr_info {
    u64 last_heartbeat_timestamp;
    u32 lockup_detected;
    struct list_head lockup_list;
    + u32 TMFSupportFlags; /* cache what task mgmt funcs are supported. */
    +#define HPSATMF_BITS_SUPPORTED (1 << 0)
    +#define HPSATMF_PHYS_LUN_RESET (1 << 1)
    +#define HPSATMF_PHYS_NEX_RESET (1 << 2)
    +#define HPSATMF_PHYS_TASK_ABORT (1 << 3)
    +#define HPSATMF_PHYS_TSET_ABORT (1 << 4)
    +#define HPSATMF_PHYS_CLEAR_ACA (1 << 5)
    +#define HPSATMF_PHYS_CLEAR_TSET (1 << 6)
    +#define HPSATMF_PHYS_QRY_TASK (1 << 7)
    +#define HPSATMF_PHYS_QRY_TSET (1 << 8)
    +#define HPSATMF_PHYS_QRY_ASYNC (1 << 9)
    +#define HPSATMF_MASK_SUPPORTED (1 << 16)
    +#define HPSATMF_LOG_LUN_RESET (1 << 17)
    +#define HPSATMF_LOG_NEX_RESET (1 << 18)
    +#define HPSATMF_LOG_TASK_ABORT (1 << 19)
    +#define HPSATMF_LOG_TSET_ABORT (1 << 20)
    +#define HPSATMF_LOG_CLEAR_ACA (1 << 21)
    +#define HPSATMF_LOG_CLEAR_TSET (1 << 22)
    +#define HPSATMF_LOG_QRY_TASK (1 << 23)
    +#define HPSATMF_LOG_QRY_TSET (1 << 24)
    +#define HPSATMF_LOG_QRY_ASYNC (1 << 25)
    };
    #define HPSA_ABORT_MSG 0
    #define HPSA_DEVICE_RESET_MSG 1
    diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
    index 8049815..14b56c9 100644
    --- a/drivers/scsi/hpsa_cmd.h
    +++ b/drivers/scsi/hpsa_cmd.h
    @@ -82,6 +82,29 @@
    #define TYPE_CMD 0x00
    #define TYPE_MSG 0x01

    +/* Message Types */
    +#define HPSA_TASK_MANAGEMENT 0x00
    +#define HPSA_RESET 0x01
    +#define HPSA_SCAN 0x02
    +#define HPSA_NOOP 0x03
    +
    +#define HPSA_CTLR_RESET_TYPE 0x00
    +#define HPSA_BUS_RESET_TYPE 0x01
    +#define HPSA_TARGET_RESET_TYPE 0x03
    +#define HPSA_LUN_RESET_TYPE 0x04
    +#define HPSA_NEXUS_RESET_TYPE 0x05
    +
    +/* Task Management Functions */
    +#define HPSA_TMF_ABORT_TASK 0x00
    +#define HPSA_TMF_ABORT_TASK_SET 0x01
    +#define HPSA_TMF_CLEAR_ACA 0x02
    +#define HPSA_TMF_CLEAR_TASK_SET 0x03
    +#define HPSA_TMF_QUERY_TASK 0x04
    +#define HPSA_TMF_QUERY_TASK_SET 0x05
    +#define HPSA_TMF_QUERY_ASYNCEVENT 0x06
    +
    +
    +
    /* config space register offsets */
    #define CFG_VENDORID 0x00
    #define CFG_DEVICEID 0x02
    @@ -337,11 +360,17 @@ struct CfgTable {
    u32 MaxPhysicalDevices;
    u32 MaxPhysicalDrivesPerLogicalUnit;
    u32 MaxPerformantModeCommands;
    - u8 reserved[0x78 - 0x58];
    + u32 MaxBlockFetch;
    + u32 PowerConservationSupport;
    + u32 PowerConservationEnable;
    + u32 TMFSupportFlags;
    + u8 TMFTagMask[8];
    + u8 reserved[0x78 - 0x70];
    u32 misc_fw_support; /* offset 0x78 */
    #define MISC_FW_DOORBELL_RESET (0x02)
    #define MISC_FW_DOORBELL_RESET2 (0x010)
    u8 driver_version[32];
    +
    };

    #define NUM_BLOCKFETCH_ENTRIES 8


    \
     
     \ /
      Last update: 2012-04-20 17:13    [W:4.557 / U:0.144 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site