diff options
author | Boaz Harrosh <bharrosh@panasas.com> | 2009-11-16 13:48:38 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-12-04 13:01:47 -0500 |
commit | aa9fffbe2c4db4557248c5c626a85bf3c7867044 (patch) | |
tree | e719a15e185a35f09e489a5a9f2a72d73c598dd1 | |
parent | eff21490c91f981126f0ead3c081dde4f425d387 (diff) |
[SCSI] libosd: Error handling revamped
Administer some love to the osd_req_decode_sense function
* Fix a bad bug with osd_req_decode_sense(). If there was no scsi
residual, .i.e the request never reached the target, then all the
osd_sense_info members where garbage.
* Add grossly missing in/out_resid to osd_sense_info and fill them in
properly.
* Define an osd_err_priority enum which divides the possible errors into
7 categories in ascending severity. Each category is also assigned a
Linux return code translation.
Analyze the different osd/scsi/block returned errors and set the
proper osd_err_priority and Linux return code accordingly.
* extra check a few situations so not to get stuck with inconsistent
error view. Example an empty residual with an error code, and other
places ...
Lots of libosd's osd_req_decode_sense clients had this logic in some
form or another. Consolidate all these into one place that should
actually know about osd returns. Thous translating it to a more
abstract error.
Signed-off-by: Boaz Harrosh <bharrosh@panasas.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
-rw-r--r-- | drivers/scsi/osd/osd_initiator.c | 85 | ||||
-rw-r--r-- | include/scsi/osd_initiator.h | 26 |
2 files changed, 99 insertions, 12 deletions
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index ba25b1e58a6c..950202a70bcf 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c | |||
@@ -475,7 +475,8 @@ EXPORT_SYMBOL(osd_end_request); | |||
475 | 475 | ||
476 | int osd_execute_request(struct osd_request *or) | 476 | int osd_execute_request(struct osd_request *or) |
477 | { | 477 | { |
478 | return blk_execute_rq(or->request->q, NULL, or->request, 0); | 478 | return or->async_error = |
479 | blk_execute_rq(or->request->q, NULL, or->request, 0); | ||
479 | } | 480 | } |
480 | EXPORT_SYMBOL(osd_execute_request); | 481 | EXPORT_SYMBOL(osd_execute_request); |
481 | 482 | ||
@@ -485,8 +486,12 @@ static void osd_request_async_done(struct request *req, int error) | |||
485 | 486 | ||
486 | or->async_error = error; | 487 | or->async_error = error; |
487 | 488 | ||
488 | if (error) | 489 | if (unlikely(error)) { |
489 | OSD_DEBUG("osd_request_async_done error recieved %d\n", error); | 490 | OSD_DEBUG("osd_request_async_done error recieved %d " |
491 | "errors 0x%x\n", error, req->errors); | ||
492 | if (!req->errors) /* don't miss out on this one */ | ||
493 | req->errors = error; | ||
494 | } | ||
490 | 495 | ||
491 | if (or->async_done) | 496 | if (or->async_done) |
492 | or->async_done(or, or->async_private); | 497 | or->async_done(or, or->async_private); |
@@ -1451,6 +1456,15 @@ int osd_finalize_request(struct osd_request *or, | |||
1451 | } | 1456 | } |
1452 | EXPORT_SYMBOL(osd_finalize_request); | 1457 | EXPORT_SYMBOL(osd_finalize_request); |
1453 | 1458 | ||
1459 | static bool _is_osd_security_code(int code) | ||
1460 | { | ||
1461 | return (code == osd_security_audit_value_frozen) || | ||
1462 | (code == osd_security_working_key_frozen) || | ||
1463 | (code == osd_nonce_not_unique) || | ||
1464 | (code == osd_nonce_timestamp_out_of_range) || | ||
1465 | (code == osd_invalid_dataout_buffer_integrity_check_value); | ||
1466 | } | ||
1467 | |||
1454 | #define OSD_SENSE_PRINT1(fmt, a...) \ | 1468 | #define OSD_SENSE_PRINT1(fmt, a...) \ |
1455 | do { \ | 1469 | do { \ |
1456 | if (__cur_sense_need_output) \ | 1470 | if (__cur_sense_need_output) \ |
@@ -1473,9 +1487,16 @@ int osd_req_decode_sense_full(struct osd_request *or, | |||
1473 | #else | 1487 | #else |
1474 | bool __cur_sense_need_output = !silent; | 1488 | bool __cur_sense_need_output = !silent; |
1475 | #endif | 1489 | #endif |
1490 | int ret; | ||
1476 | 1491 | ||
1477 | if (!or->request->errors) | 1492 | if (likely(!or->request->errors)) { |
1493 | osi->out_resid = 0; | ||
1494 | osi->in_resid = 0; | ||
1478 | return 0; | 1495 | return 0; |
1496 | } | ||
1497 | |||
1498 | osi = osi ? : &local_osi; | ||
1499 | memset(osi, 0, sizeof(*osi)); | ||
1479 | 1500 | ||
1480 | ssdb = or->request->sense; | 1501 | ssdb = or->request->sense; |
1481 | sense_len = or->request->sense_len; | 1502 | sense_len = or->request->sense_len; |
@@ -1483,17 +1504,15 @@ int osd_req_decode_sense_full(struct osd_request *or, | |||
1483 | OSD_ERR("Block-layer returned error(0x%x) but " | 1504 | OSD_ERR("Block-layer returned error(0x%x) but " |
1484 | "sense_len(%u) || key(%d) is empty\n", | 1505 | "sense_len(%u) || key(%d) is empty\n", |
1485 | or->request->errors, sense_len, ssdb->sense_key); | 1506 | or->request->errors, sense_len, ssdb->sense_key); |
1486 | return -EIO; | 1507 | goto analyze; |
1487 | } | 1508 | } |
1488 | 1509 | ||
1489 | if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) { | 1510 | if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) { |
1490 | OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n", | 1511 | OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n", |
1491 | ssdb->response_code, sense_len); | 1512 | ssdb->response_code, sense_len); |
1492 | return -EIO; | 1513 | goto analyze; |
1493 | } | 1514 | } |
1494 | 1515 | ||
1495 | osi = osi ? : &local_osi; | ||
1496 | memset(osi, 0, sizeof(*osi)); | ||
1497 | osi->key = ssdb->sense_key; | 1516 | osi->key = ssdb->sense_key; |
1498 | osi->additional_code = be16_to_cpu(ssdb->additional_sense_code); | 1517 | osi->additional_code = be16_to_cpu(ssdb->additional_sense_code); |
1499 | original_sense_len = ssdb->additional_sense_length + 8; | 1518 | original_sense_len = ssdb->additional_sense_length + 8; |
@@ -1503,9 +1522,10 @@ int osd_req_decode_sense_full(struct osd_request *or, | |||
1503 | __cur_sense_need_output = (osi->key > scsi_sk_recovered_error); | 1522 | __cur_sense_need_output = (osi->key > scsi_sk_recovered_error); |
1504 | #endif | 1523 | #endif |
1505 | OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) " | 1524 | OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) " |
1506 | "additional_code=0x%x\n", | 1525 | "additional_code=0x%x async_error=%d errors=0x%x\n", |
1507 | osi->key, original_sense_len, sense_len, | 1526 | osi->key, original_sense_len, sense_len, |
1508 | osi->additional_code); | 1527 | osi->additional_code, or->async_error, |
1528 | or->request->errors); | ||
1509 | 1529 | ||
1510 | if (original_sense_len < sense_len) | 1530 | if (original_sense_len < sense_len) |
1511 | sense_len = original_sense_len; | 1531 | sense_len = original_sense_len; |
@@ -1637,7 +1657,50 @@ int osd_req_decode_sense_full(struct osd_request *or, | |||
1637 | cur_descriptor += cur_len; | 1657 | cur_descriptor += cur_len; |
1638 | } | 1658 | } |
1639 | 1659 | ||
1640 | return (osi->key > scsi_sk_recovered_error) ? -EIO : 0; | 1660 | analyze: |
1661 | if (!osi->key) { | ||
1662 | /* scsi sense is Empty, the request was never issued to target | ||
1663 | * linux return code might tell us what happened. | ||
1664 | */ | ||
1665 | if (or->async_error == -ENOMEM) | ||
1666 | osi->osd_err_pri = OSD_ERR_PRI_RESOURCE; | ||
1667 | else | ||
1668 | osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE; | ||
1669 | ret = or->async_error; | ||
1670 | } else if (osi->key <= scsi_sk_recovered_error) { | ||
1671 | osi->osd_err_pri = 0; | ||
1672 | ret = 0; | ||
1673 | } else if (osi->additional_code == scsi_invalid_field_in_cdb) { | ||
1674 | if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) { | ||
1675 | osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES; | ||
1676 | ret = -EFAULT; /* caller should recover from this */ | ||
1677 | } else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) { | ||
1678 | osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND; | ||
1679 | ret = -ENOENT; | ||
1680 | } else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) { | ||
1681 | osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS; | ||
1682 | ret = -EACCES; | ||
1683 | } else { | ||
1684 | osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED; | ||
1685 | ret = -EINVAL; | ||
1686 | } | ||
1687 | } else if (osi->additional_code == osd_quota_error) { | ||
1688 | osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE; | ||
1689 | ret = -ENOSPC; | ||
1690 | } else if (_is_osd_security_code(osi->additional_code)) { | ||
1691 | osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED; | ||
1692 | ret = -EINVAL; | ||
1693 | } else { | ||
1694 | osi->osd_err_pri = OSD_ERR_PRI_EIO; | ||
1695 | ret = -EIO; | ||
1696 | } | ||
1697 | |||
1698 | if (or->out.req) | ||
1699 | osi->out_resid = or->out.req->resid_len ?: or->out.total_bytes; | ||
1700 | if (or->in.req) | ||
1701 | osi->in_resid = or->in.req->resid_len ?: or->in.total_bytes; | ||
1702 | |||
1703 | return ret; | ||
1641 | } | 1704 | } |
1642 | EXPORT_SYMBOL(osd_req_decode_sense_full); | 1705 | EXPORT_SYMBOL(osd_req_decode_sense_full); |
1643 | 1706 | ||
diff --git a/include/scsi/osd_initiator.h b/include/scsi/osd_initiator.h index 3ec346e15dda..39d6d1097153 100644 --- a/include/scsi/osd_initiator.h +++ b/include/scsi/osd_initiator.h | |||
@@ -267,7 +267,7 @@ int osd_execute_request_async(struct osd_request *or, | |||
267 | * @bad_attr_list - List of failing attributes (optional) | 267 | * @bad_attr_list - List of failing attributes (optional) |
268 | * @max_attr - Size of @bad_attr_list. | 268 | * @max_attr - Size of @bad_attr_list. |
269 | * | 269 | * |
270 | * After execution, sense + return code can be analyzed using this function. The | 270 | * After execution, osd_request results are analyzed using this function. The |
271 | * return code is the final disposition on the error. So it is possible that a | 271 | * return code is the final disposition on the error. So it is possible that a |
272 | * CHECK_CONDITION was returned from target but this will return NO_ERROR, for | 272 | * CHECK_CONDITION was returned from target but this will return NO_ERROR, for |
273 | * example on recovered errors. All parameters are optional if caller does | 273 | * example on recovered errors. All parameters are optional if caller does |
@@ -276,7 +276,31 @@ int osd_execute_request_async(struct osd_request *or, | |||
276 | * of the SCSI_OSD_DPRINT_SENSE Kconfig value. Set @silent if you know the | 276 | * of the SCSI_OSD_DPRINT_SENSE Kconfig value. Set @silent if you know the |
277 | * command would routinely fail, to not spam the dmsg file. | 277 | * command would routinely fail, to not spam the dmsg file. |
278 | */ | 278 | */ |
279 | |||
280 | /** | ||
281 | * osd_err_priority - osd categorized return codes in ascending severity. | ||
282 | * | ||
283 | * The categories are borrowed from the pnfs_osd_errno enum. | ||
284 | * See comments for translated Linux codes returned by osd_req_decode_sense. | ||
285 | */ | ||
286 | enum osd_err_priority { | ||
287 | OSD_ERR_PRI_NO_ERROR = 0, | ||
288 | /* Recoverable, caller should clear_highpage() all pages */ | ||
289 | OSD_ERR_PRI_CLEAR_PAGES = 1, /* -EFAULT */ | ||
290 | OSD_ERR_PRI_RESOURCE = 2, /* -ENOMEM */ | ||
291 | OSD_ERR_PRI_BAD_CRED = 3, /* -EINVAL */ | ||
292 | OSD_ERR_PRI_NO_ACCESS = 4, /* -EACCES */ | ||
293 | OSD_ERR_PRI_UNREACHABLE = 5, /* any other */ | ||
294 | OSD_ERR_PRI_NOT_FOUND = 6, /* -ENOENT */ | ||
295 | OSD_ERR_PRI_NO_SPACE = 7, /* -ENOSPC */ | ||
296 | OSD_ERR_PRI_EIO = 8, /* -EIO */ | ||
297 | }; | ||
298 | |||
279 | struct osd_sense_info { | 299 | struct osd_sense_info { |
300 | u64 out_resid; /* Zero on success otherwise out residual */ | ||
301 | u64 in_resid; /* Zero on success otherwise in residual */ | ||
302 | enum osd_err_priority osd_err_pri; | ||
303 | |||
280 | int key; /* one of enum scsi_sense_keys */ | 304 | int key; /* one of enum scsi_sense_keys */ |
281 | int additional_code ; /* enum osd_additional_sense_codes */ | 305 | int additional_code ; /* enum osd_additional_sense_codes */ |
282 | union { /* Sense specific information */ | 306 | union { /* Sense specific information */ |