]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/scsi/osd/osd_initiator.c
[SCSI] libosd: Error handling revamped
[net-next-2.6.git] / drivers / scsi / osd / osd_initiator.c
index 7a117c18114cd757de416045da2d453327742131..950202a70bcf5ea236924edaad5cf35734bfc4cf 100644 (file)
@@ -73,7 +73,8 @@ static const char *_osd_ver_desc(struct osd_request *or)
 
 #define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len)
 
-static int _osd_print_system_info(struct osd_dev *od, void *caps)
+static int _osd_get_print_system_info(struct osd_dev *od,
+       void *caps, struct osd_dev_info *odi)
 {
        struct osd_request *or;
        struct osd_attr get_attrs[] = {
@@ -137,8 +138,12 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
        OSD_INFO("PRODUCT_SERIAL_NUMBER  [%s]\n",
                (char *)pFirst);
 
-       pFirst = get_attrs[a].val_ptr;
-       OSD_INFO("OSD_NAME               [%s]\n", (char *)pFirst);
+       odi->osdname_len = get_attrs[a].len;
+       /* Avoid NULL for memcmp optimization 0-length is good enough */
+       odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL);
+       if (odi->osdname_len)
+               memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len);
+       OSD_INFO("OSD_NAME               [%s]\n", odi->osdname);
        a++;
 
        pFirst = get_attrs[a++].val_ptr;
@@ -171,6 +176,14 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
                                   sid_dump, sizeof(sid_dump), true);
                OSD_INFO("OSD_SYSTEM_ID(%d)\n"
                         "        [%s]\n", len, sid_dump);
+
+               if (unlikely(len > sizeof(odi->systemid))) {
+                       OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). "
+                               "device idetification might not work\n", len);
+                       len = sizeof(odi->systemid);
+               }
+               odi->systemid_len = len;
+               memcpy(odi->systemid, get_attrs[a].val_ptr, len);
                a++;
        }
 out:
@@ -178,16 +191,17 @@ out:
        return ret;
 }
 
-int osd_auto_detect_ver(struct osd_dev *od, void *caps)
+int osd_auto_detect_ver(struct osd_dev *od,
+       void *caps, struct osd_dev_info *odi)
 {
        int ret;
 
        /* Auto-detect the osd version */
-       ret = _osd_print_system_info(od, caps);
+       ret = _osd_get_print_system_info(od, caps, odi);
        if (ret) {
                osd_dev_set_ver(od, OSD_VER1);
                OSD_DEBUG("converting to OSD1\n");
-               ret = _osd_print_system_info(od, caps);
+               ret = _osd_get_print_system_info(od, caps, odi);
        }
 
        return ret;
@@ -461,7 +475,8 @@ EXPORT_SYMBOL(osd_end_request);
 
 int osd_execute_request(struct osd_request *or)
 {
-       return blk_execute_rq(or->request->q, NULL, or->request, 0);
+       return or->async_error =
+                       blk_execute_rq(or->request->q, NULL, or->request, 0);
 }
 EXPORT_SYMBOL(osd_execute_request);
 
@@ -471,8 +486,12 @@ static void osd_request_async_done(struct request *req, int error)
 
        or->async_error = error;
 
-       if (error)
-               OSD_DEBUG("osd_request_async_done error recieved %d\n", error);
+       if (unlikely(error)) {
+               OSD_DEBUG("osd_request_async_done error recieved %d "
+                         "errors 0x%x\n", error, req->errors);
+               if (!req->errors) /* don't miss out on this one */
+                       req->errors = error;
+       }
 
        if (or->async_done)
                or->async_done(or, or->async_private);
@@ -1153,6 +1172,7 @@ int osd_req_decode_get_attr_list(struct osd_request *or,
                                "c=%d r=%d n=%d\n",
                                cur_bytes, returned_bytes, n);
                        oa->val_ptr = NULL;
+                       cur_bytes = returned_bytes; /* break the caller loop */
                        break;
                }
 
@@ -1436,6 +1456,15 @@ int osd_finalize_request(struct osd_request *or,
 }
 EXPORT_SYMBOL(osd_finalize_request);
 
+static bool _is_osd_security_code(int code)
+{
+       return  (code == osd_security_audit_value_frozen) ||
+               (code == osd_security_working_key_frozen) ||
+               (code == osd_nonce_not_unique) ||
+               (code == osd_nonce_timestamp_out_of_range) ||
+               (code == osd_invalid_dataout_buffer_integrity_check_value);
+}
+
 #define OSD_SENSE_PRINT1(fmt, a...) \
        do { \
                if (__cur_sense_need_output) \
@@ -1458,9 +1487,16 @@ int osd_req_decode_sense_full(struct osd_request *or,
 #else
        bool __cur_sense_need_output = !silent;
 #endif
+       int ret;
 
-       if (!or->request->errors)
+       if (likely(!or->request->errors)) {
+               osi->out_resid = 0;
+               osi->in_resid = 0;
                return 0;
+       }
+
+       osi = osi ? : &local_osi;
+       memset(osi, 0, sizeof(*osi));
 
        ssdb = or->request->sense;
        sense_len = or->request->sense_len;
@@ -1468,17 +1504,15 @@ int osd_req_decode_sense_full(struct osd_request *or,
                OSD_ERR("Block-layer returned error(0x%x) but "
                        "sense_len(%u) || key(%d) is empty\n",
                        or->request->errors, sense_len, ssdb->sense_key);
-               return -EIO;
+               goto analyze;
        }
 
        if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) {
                OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n",
                        ssdb->response_code, sense_len);
-               return -EIO;
+               goto analyze;
        }
 
-       osi = osi ? : &local_osi;
-       memset(osi, 0, sizeof(*osi));
        osi->key = ssdb->sense_key;
        osi->additional_code = be16_to_cpu(ssdb->additional_sense_code);
        original_sense_len = ssdb->additional_sense_length + 8;
@@ -1488,9 +1522,10 @@ int osd_req_decode_sense_full(struct osd_request *or,
                __cur_sense_need_output = (osi->key > scsi_sk_recovered_error);
 #endif
        OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) "
-                       "additional_code=0x%x\n",
+                       "additional_code=0x%x async_error=%d errors=0x%x\n",
                        osi->key, original_sense_len, sense_len,
-                       osi->additional_code);
+                       osi->additional_code, or->async_error,
+                       or->request->errors);
 
        if (original_sense_len < sense_len)
                sense_len = original_sense_len;
@@ -1569,15 +1604,14 @@ int osd_req_decode_sense_full(struct osd_request *or,
                {
                        struct osd_sense_attributes_data_descriptor
                                *osadd = cur_descriptor;
-                       int len = min(cur_len, sense_len);
-                       int i = 0;
+                       unsigned len = min(cur_len, sense_len);
                        struct osd_sense_attr *pattr = osadd->sense_attrs;
 
-                       while (len < 0) {
+                       while (len >= sizeof(*pattr)) {
                                u32 attr_page = be32_to_cpu(pattr->attr_page);
                                u32 attr_id = be32_to_cpu(pattr->attr_id);
 
-                               if (i++ == 0) {
+                               if (!osi->attr.attr_page) {
                                        osi->attr.attr_page = attr_page;
                                        osi->attr.attr_id = attr_id;
                                }
@@ -1588,6 +1622,8 @@ int osd_req_decode_sense_full(struct osd_request *or,
                                        bad_attr_list++;
                                        max_attr--;
                                }
+
+                               len -= sizeof(*pattr);
                                OSD_SENSE_PRINT2(
                                        "osd_sense_attribute_identification"
                                        "attr_page=0x%x attr_id=0x%x\n",
@@ -1621,7 +1657,50 @@ int osd_req_decode_sense_full(struct osd_request *or,
                cur_descriptor += cur_len;
        }
 
-       return (osi->key > scsi_sk_recovered_error) ? -EIO : 0;
+analyze:
+       if (!osi->key) {
+               /* scsi sense is Empty, the request was never issued to target
+                * linux return code might tell us what happened.
+                */
+               if (or->async_error == -ENOMEM)
+                       osi->osd_err_pri = OSD_ERR_PRI_RESOURCE;
+               else
+                       osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE;
+               ret = or->async_error;
+       } else if (osi->key <= scsi_sk_recovered_error) {
+               osi->osd_err_pri = 0;
+               ret = 0;
+       } else if (osi->additional_code == scsi_invalid_field_in_cdb) {
+               if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) {
+                       osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES;
+                       ret = -EFAULT; /* caller should recover from this */
+               } else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) {
+                       osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND;
+                       ret = -ENOENT;
+               } else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) {
+                       osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS;
+                       ret = -EACCES;
+               } else {
+                       osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
+                       ret = -EINVAL;
+               }
+       } else if (osi->additional_code == osd_quota_error) {
+               osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE;
+               ret = -ENOSPC;
+       } else if (_is_osd_security_code(osi->additional_code)) {
+               osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
+               ret = -EINVAL;
+       } else {
+               osi->osd_err_pri = OSD_ERR_PRI_EIO;
+               ret = -EIO;
+       }
+
+       if (or->out.req)
+               osi->out_resid = or->out.req->resid_len ?: or->out.total_bytes;
+       if (or->in.req)
+               osi->in_resid = or->in.req->resid_len ?: or->in.total_bytes;
+
+       return ret;
 }
 EXPORT_SYMBOL(osd_req_decode_sense_full);