diff options
author | Stephen M. Cameron <scameron@beardog.cce.hp.com> | 2012-05-01 12:42:51 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-05-10 04:14:29 -0400 |
commit | 75167d2cc7654f57b90497fe90b1f0ae946c22a6 (patch) | |
tree | e882cc25860896d37f5ae4b982c1333fa7bc38ac /drivers/scsi/hpsa.c | |
parent | 5a3d16f51ef62bf17c9752c469db881dd12bce9b (diff) |
[SCSI] hpsa: add abort error handler function
Signed-off-by: Stephen M. Cameron <scameron@beardog.cce.hp.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/hpsa.c')
-rw-r--r-- | drivers/scsi/hpsa.c | 221 |
1 files changed, 220 insertions, 1 deletions
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 1ceea8a42ee7..a2c99245b82c 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, | |||
159 | int qdepth, int reason); | 159 | int qdepth, int reason); |
160 | 160 | ||
161 | static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd); | 161 | static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd); |
162 | static int hpsa_eh_abort_handler(struct scsi_cmnd *scsicmd); | ||
162 | static int hpsa_slave_alloc(struct scsi_device *sdev); | 163 | static int hpsa_slave_alloc(struct scsi_device *sdev); |
163 | static void hpsa_slave_destroy(struct scsi_device *sdev); | 164 | static void hpsa_slave_destroy(struct scsi_device *sdev); |
164 | 165 | ||
@@ -180,6 +181,7 @@ static int __devinit hpsa_pci_find_memory_BAR(struct pci_dev *pdev, | |||
180 | static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id); | 181 | static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id); |
181 | static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev, | 182 | static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev, |
182 | void __iomem *vaddr, int wait_for_ready); | 183 | void __iomem *vaddr, int wait_for_ready); |
184 | static inline void finish_cmd(struct CommandList *c); | ||
183 | #define BOARD_NOT_READY 0 | 185 | #define BOARD_NOT_READY 0 |
184 | #define BOARD_READY 1 | 186 | #define BOARD_READY 1 |
185 | 187 | ||
@@ -507,6 +509,7 @@ static struct scsi_host_template hpsa_driver_template = { | |||
507 | .change_queue_depth = hpsa_change_queue_depth, | 509 | .change_queue_depth = hpsa_change_queue_depth, |
508 | .this_id = -1, | 510 | .this_id = -1, |
509 | .use_clustering = ENABLE_CLUSTERING, | 511 | .use_clustering = ENABLE_CLUSTERING, |
512 | .eh_abort_handler = hpsa_eh_abort_handler, | ||
510 | .eh_device_reset_handler = hpsa_eh_device_reset_handler, | 513 | .eh_device_reset_handler = hpsa_eh_device_reset_handler, |
511 | .ioctl = hpsa_ioctl, | 514 | .ioctl = hpsa_ioctl, |
512 | .slave_alloc = hpsa_slave_alloc, | 515 | .slave_alloc = hpsa_slave_alloc, |
@@ -2352,6 +2355,191 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd) | |||
2352 | return FAILED; | 2355 | return FAILED; |
2353 | } | 2356 | } |
2354 | 2357 | ||
2358 | static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr, | ||
2359 | struct CommandList *abort) | ||
2360 | { | ||
2361 | int rc = IO_OK; | ||
2362 | struct CommandList *c; | ||
2363 | struct ErrorInfo *ei; | ||
2364 | |||
2365 | c = cmd_special_alloc(h); | ||
2366 | if (c == NULL) { /* trouble... */ | ||
2367 | dev_warn(&h->pdev->dev, "cmd_special_alloc returned NULL!\n"); | ||
2368 | return -ENOMEM; | ||
2369 | } | ||
2370 | |||
2371 | fill_cmd(c, HPSA_ABORT_MSG, h, abort, 0, 0, scsi3addr, TYPE_MSG); | ||
2372 | hpsa_scsi_do_simple_cmd_core(h, c); | ||
2373 | dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: do_simple_cmd_core completed.\n", | ||
2374 | __func__, abort->Header.Tag.upper, abort->Header.Tag.lower); | ||
2375 | /* no unmap needed here because no data xfer. */ | ||
2376 | |||
2377 | ei = c->err_info; | ||
2378 | switch (ei->CommandStatus) { | ||
2379 | case CMD_SUCCESS: | ||
2380 | break; | ||
2381 | case CMD_UNABORTABLE: /* Very common, don't make noise. */ | ||
2382 | rc = -1; | ||
2383 | break; | ||
2384 | default: | ||
2385 | dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: interpreting error.\n", | ||
2386 | __func__, abort->Header.Tag.upper, | ||
2387 | abort->Header.Tag.lower); | ||
2388 | hpsa_scsi_interpret_error(c); | ||
2389 | rc = -1; | ||
2390 | break; | ||
2391 | } | ||
2392 | cmd_special_free(h, c); | ||
2393 | dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: Finished.\n", __func__, | ||
2394 | abort->Header.Tag.upper, abort->Header.Tag.lower); | ||
2395 | return rc; | ||
2396 | } | ||
2397 | |||
2398 | /* | ||
2399 | * hpsa_find_cmd_in_queue | ||
2400 | * | ||
2401 | * Used to determine whether a command (find) is still present | ||
2402 | * in queue_head. Optionally excludes the last element of queue_head. | ||
2403 | * | ||
2404 | * This is used to avoid unnecessary aborts. Commands in h->reqQ have | ||
2405 | * not yet been submitted, and so can be aborted by the driver without | ||
2406 | * sending an abort to the hardware. | ||
2407 | * | ||
2408 | * Returns pointer to command if found in queue, NULL otherwise. | ||
2409 | */ | ||
2410 | static struct CommandList *hpsa_find_cmd_in_queue(struct ctlr_info *h, | ||
2411 | struct scsi_cmnd *find, struct list_head *queue_head) | ||
2412 | { | ||
2413 | unsigned long flags; | ||
2414 | struct CommandList *c = NULL; /* ptr into cmpQ */ | ||
2415 | |||
2416 | if (!find) | ||
2417 | return 0; | ||
2418 | spin_lock_irqsave(&h->lock, flags); | ||
2419 | list_for_each_entry(c, queue_head, list) { | ||
2420 | if (c->scsi_cmd == NULL) /* e.g.: passthru ioctl */ | ||
2421 | continue; | ||
2422 | if (c->scsi_cmd == find) { | ||
2423 | spin_unlock_irqrestore(&h->lock, flags); | ||
2424 | return c; | ||
2425 | } | ||
2426 | } | ||
2427 | spin_unlock_irqrestore(&h->lock, flags); | ||
2428 | return NULL; | ||
2429 | } | ||
2430 | |||
2431 | /* Send an abort for the specified command. | ||
2432 | * If the device and controller support it, | ||
2433 | * send a task abort request. | ||
2434 | */ | ||
2435 | static int hpsa_eh_abort_handler(struct scsi_cmnd *sc) | ||
2436 | { | ||
2437 | |||
2438 | int i, rc; | ||
2439 | struct ctlr_info *h; | ||
2440 | struct hpsa_scsi_dev_t *dev; | ||
2441 | struct CommandList *abort; /* pointer to command to be aborted */ | ||
2442 | struct CommandList *found; | ||
2443 | struct scsi_cmnd *as; /* ptr to scsi cmd inside aborted command. */ | ||
2444 | char msg[256]; /* For debug messaging. */ | ||
2445 | int ml = 0; | ||
2446 | |||
2447 | /* Find the controller of the command to be aborted */ | ||
2448 | h = sdev_to_hba(sc->device); | ||
2449 | if (WARN(h == NULL, | ||
2450 | "ABORT REQUEST FAILED, Controller lookup failed.\n")) | ||
2451 | return FAILED; | ||
2452 | |||
2453 | /* Check that controller supports some kind of task abort */ | ||
2454 | if (!(HPSATMF_PHYS_TASK_ABORT & h->TMFSupportFlags) && | ||
2455 | !(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags)) | ||
2456 | return FAILED; | ||
2457 | |||
2458 | memset(msg, 0, sizeof(msg)); | ||
2459 | ml += sprintf(msg+ml, "ABORT REQUEST on C%d:B%d:T%d:L%d ", | ||
2460 | h->scsi_host->host_no, sc->device->channel, | ||
2461 | sc->device->id, sc->device->lun); | ||
2462 | |||
2463 | /* Find the device of the command to be aborted */ | ||
2464 | dev = sc->device->hostdata; | ||
2465 | if (!dev) { | ||
2466 | dev_err(&h->pdev->dev, "%s FAILED, Device lookup failed.\n", | ||
2467 | msg); | ||
2468 | return FAILED; | ||
2469 | } | ||
2470 | |||
2471 | /* Get SCSI command to be aborted */ | ||
2472 | abort = (struct CommandList *) sc->host_scribble; | ||
2473 | if (abort == NULL) { | ||
2474 | dev_err(&h->pdev->dev, "%s FAILED, Command to abort is NULL.\n", | ||
2475 | msg); | ||
2476 | return FAILED; | ||
2477 | } | ||
2478 | |||
2479 | ml += sprintf(msg+ml, "Tag:0x%08x:%08x ", | ||
2480 | abort->Header.Tag.upper, abort->Header.Tag.lower); | ||
2481 | as = (struct scsi_cmnd *) abort->scsi_cmd; | ||
2482 | if (as != NULL) | ||
2483 | ml += sprintf(msg+ml, "Command:0x%x SN:0x%lx ", | ||
2484 | as->cmnd[0], as->serial_number); | ||
2485 | dev_dbg(&h->pdev->dev, "%s\n", msg); | ||
2486 | dev_warn(&h->pdev->dev, "Abort request on C%d:B%d:T%d:L%d\n", | ||
2487 | h->scsi_host->host_no, dev->bus, dev->target, dev->lun); | ||
2488 | |||
2489 | /* Search reqQ to See if command is queued but not submitted, | ||
2490 | * if so, complete the command with aborted status and remove | ||
2491 | * it from the reqQ. | ||
2492 | */ | ||
2493 | found = hpsa_find_cmd_in_queue(h, sc, &h->reqQ); | ||
2494 | if (found) { | ||
2495 | found->err_info->CommandStatus = CMD_ABORTED; | ||
2496 | finish_cmd(found); | ||
2497 | dev_info(&h->pdev->dev, "%s Request SUCCEEDED (driver queue).\n", | ||
2498 | msg); | ||
2499 | return SUCCESS; | ||
2500 | } | ||
2501 | |||
2502 | /* not in reqQ, if also not in cmpQ, must have already completed */ | ||
2503 | found = hpsa_find_cmd_in_queue(h, sc, &h->cmpQ); | ||
2504 | if (!found) { | ||
2505 | dev_dbg(&h->pdev->dev, "%s Request FAILED (not known to driver).\n", | ||
2506 | msg); | ||
2507 | return SUCCESS; | ||
2508 | } | ||
2509 | |||
2510 | /* | ||
2511 | * Command is in flight, or possibly already completed | ||
2512 | * by the firmware (but not to the scsi mid layer) but we can't | ||
2513 | * distinguish which. Send the abort down. | ||
2514 | */ | ||
2515 | rc = hpsa_send_abort(h, dev->scsi3addr, abort); | ||
2516 | if (rc != 0) { | ||
2517 | dev_dbg(&h->pdev->dev, "%s Request FAILED.\n", msg); | ||
2518 | dev_warn(&h->pdev->dev, "FAILED abort on device C%d:B%d:T%d:L%d\n", | ||
2519 | h->scsi_host->host_no, | ||
2520 | dev->bus, dev->target, dev->lun); | ||
2521 | return FAILED; | ||
2522 | } | ||
2523 | dev_info(&h->pdev->dev, "%s REQUEST SUCCEEDED.\n", msg); | ||
2524 | |||
2525 | /* If the abort(s) above completed and actually aborted the | ||
2526 | * command, then the command to be aborted should already be | ||
2527 | * completed. If not, wait around a bit more to see if they | ||
2528 | * manage to complete normally. | ||
2529 | */ | ||
2530 | #define ABORT_COMPLETE_WAIT_SECS 30 | ||
2531 | for (i = 0; i < ABORT_COMPLETE_WAIT_SECS * 10; i++) { | ||
2532 | found = hpsa_find_cmd_in_queue(h, sc, &h->cmpQ); | ||
2533 | if (!found) | ||
2534 | return SUCCESS; | ||
2535 | msleep(100); | ||
2536 | } | ||
2537 | dev_warn(&h->pdev->dev, "%s FAILED. Aborted command has not completed after %d seconds.\n", | ||
2538 | msg, ABORT_COMPLETE_WAIT_SECS); | ||
2539 | return FAILED; | ||
2540 | } | ||
2541 | |||
2542 | |||
2355 | /* | 2543 | /* |
2356 | * For operations that cannot sleep, a command block is allocated at init, | 2544 | * For operations that cannot sleep, a command block is allocated at init, |
2357 | * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track | 2545 | * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track |
@@ -2884,6 +3072,7 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, | |||
2884 | int cmd_type) | 3072 | int cmd_type) |
2885 | { | 3073 | { |
2886 | int pci_dir = XFER_NONE; | 3074 | int pci_dir = XFER_NONE; |
3075 | struct CommandList *a; /* for commands to be aborted */ | ||
2887 | 3076 | ||
2888 | c->cmd_type = CMD_IOCTL_PEND; | 3077 | c->cmd_type = CMD_IOCTL_PEND; |
2889 | c->Header.ReplyQueue = 0; | 3078 | c->Header.ReplyQueue = 0; |
@@ -2967,8 +3156,35 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, | |||
2967 | c->Request.CDB[5] = 0x00; | 3156 | c->Request.CDB[5] = 0x00; |
2968 | c->Request.CDB[6] = 0x00; | 3157 | c->Request.CDB[6] = 0x00; |
2969 | c->Request.CDB[7] = 0x00; | 3158 | c->Request.CDB[7] = 0x00; |
3159 | break; | ||
3160 | case HPSA_ABORT_MSG: | ||
3161 | a = buff; /* point to command to be aborted */ | ||
3162 | dev_dbg(&h->pdev->dev, "Abort Tag:0x%08x:%08x using request Tag:0x%08x:%08x\n", | ||
3163 | a->Header.Tag.upper, a->Header.Tag.lower, | ||
3164 | c->Header.Tag.upper, c->Header.Tag.lower); | ||
3165 | c->Request.CDBLen = 16; | ||
3166 | c->Request.Type.Type = TYPE_MSG; | ||
3167 | c->Request.Type.Attribute = ATTR_SIMPLE; | ||
3168 | c->Request.Type.Direction = XFER_WRITE; | ||
3169 | c->Request.Timeout = 0; /* Don't time out */ | ||
3170 | c->Request.CDB[0] = HPSA_TASK_MANAGEMENT; | ||
3171 | c->Request.CDB[1] = HPSA_TMF_ABORT_TASK; | ||
3172 | c->Request.CDB[2] = 0x00; /* reserved */ | ||
3173 | c->Request.CDB[3] = 0x00; /* reserved */ | ||
3174 | /* Tag to abort goes in CDB[4]-CDB[11] */ | ||
3175 | c->Request.CDB[4] = a->Header.Tag.lower & 0xFF; | ||
3176 | c->Request.CDB[5] = (a->Header.Tag.lower >> 8) & 0xFF; | ||
3177 | c->Request.CDB[6] = (a->Header.Tag.lower >> 16) & 0xFF; | ||
3178 | c->Request.CDB[7] = (a->Header.Tag.lower >> 24) & 0xFF; | ||
3179 | c->Request.CDB[8] = a->Header.Tag.upper & 0xFF; | ||
3180 | c->Request.CDB[9] = (a->Header.Tag.upper >> 8) & 0xFF; | ||
3181 | c->Request.CDB[10] = (a->Header.Tag.upper >> 16) & 0xFF; | ||
3182 | c->Request.CDB[11] = (a->Header.Tag.upper >> 24) & 0xFF; | ||
3183 | c->Request.CDB[12] = 0x00; /* reserved */ | ||
3184 | c->Request.CDB[13] = 0x00; /* reserved */ | ||
3185 | c->Request.CDB[14] = 0x00; /* reserved */ | ||
3186 | c->Request.CDB[15] = 0x00; /* reserved */ | ||
2970 | break; | 3187 | break; |
2971 | |||
2972 | default: | 3188 | default: |
2973 | dev_warn(&h->pdev->dev, "unknown message type %d\n", | 3189 | dev_warn(&h->pdev->dev, "unknown message type %d\n", |
2974 | cmd); | 3190 | cmd); |
@@ -3848,6 +4064,9 @@ static void __devinit hpsa_find_board_params(struct ctlr_info *h) | |||
3848 | h->maxsgentries = 31; /* default to traditional values */ | 4064 | h->maxsgentries = 31; /* default to traditional values */ |
3849 | h->chainsize = 0; | 4065 | h->chainsize = 0; |
3850 | } | 4066 | } |
4067 | |||
4068 | /* Find out what task management functions are supported and cache */ | ||
4069 | h->TMFSupportFlags = readl(&(h->cfgtable->TMFSupportFlags)); | ||
3851 | } | 4070 | } |
3852 | 4071 | ||
3853 | static inline bool hpsa_CISS_signature_present(struct ctlr_info *h) | 4072 | static inline bool hpsa_CISS_signature_present(struct ctlr_info *h) |