diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /drivers/scsi/osd | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'drivers/scsi/osd')
-rw-r--r-- | drivers/scsi/osd/osd_initiator.c | 181 | ||||
-rw-r--r-- | drivers/scsi/osd/osd_uld.c | 261 |
2 files changed, 314 insertions, 128 deletions
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index 7a117c18114c..ee4b6914667f 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c | |||
@@ -39,6 +39,8 @@ | |||
39 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 39 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | */ | 40 | */ |
41 | 41 | ||
42 | #include <linux/slab.h> | ||
43 | |||
42 | #include <scsi/osd_initiator.h> | 44 | #include <scsi/osd_initiator.h> |
43 | #include <scsi/osd_sec.h> | 45 | #include <scsi/osd_sec.h> |
44 | #include <scsi/osd_attributes.h> | 46 | #include <scsi/osd_attributes.h> |
@@ -73,7 +75,8 @@ static const char *_osd_ver_desc(struct osd_request *or) | |||
73 | 75 | ||
74 | #define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len) | 76 | #define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len) |
75 | 77 | ||
76 | static int _osd_print_system_info(struct osd_dev *od, void *caps) | 78 | static int _osd_get_print_system_info(struct osd_dev *od, |
79 | void *caps, struct osd_dev_info *odi) | ||
77 | { | 80 | { |
78 | struct osd_request *or; | 81 | struct osd_request *or; |
79 | struct osd_attr get_attrs[] = { | 82 | struct osd_attr get_attrs[] = { |
@@ -137,8 +140,12 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps) | |||
137 | OSD_INFO("PRODUCT_SERIAL_NUMBER [%s]\n", | 140 | OSD_INFO("PRODUCT_SERIAL_NUMBER [%s]\n", |
138 | (char *)pFirst); | 141 | (char *)pFirst); |
139 | 142 | ||
140 | pFirst = get_attrs[a].val_ptr; | 143 | odi->osdname_len = get_attrs[a].len; |
141 | OSD_INFO("OSD_NAME [%s]\n", (char *)pFirst); | 144 | /* Avoid NULL for memcmp optimization 0-length is good enough */ |
145 | odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL); | ||
146 | if (odi->osdname_len) | ||
147 | memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len); | ||
148 | OSD_INFO("OSD_NAME [%s]\n", odi->osdname); | ||
142 | a++; | 149 | a++; |
143 | 150 | ||
144 | pFirst = get_attrs[a++].val_ptr; | 151 | pFirst = get_attrs[a++].val_ptr; |
@@ -171,6 +178,14 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps) | |||
171 | sid_dump, sizeof(sid_dump), true); | 178 | sid_dump, sizeof(sid_dump), true); |
172 | OSD_INFO("OSD_SYSTEM_ID(%d)\n" | 179 | OSD_INFO("OSD_SYSTEM_ID(%d)\n" |
173 | " [%s]\n", len, sid_dump); | 180 | " [%s]\n", len, sid_dump); |
181 | |||
182 | if (unlikely(len > sizeof(odi->systemid))) { | ||
183 | OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). " | ||
184 | "device idetification might not work\n", len); | ||
185 | len = sizeof(odi->systemid); | ||
186 | } | ||
187 | odi->systemid_len = len; | ||
188 | memcpy(odi->systemid, get_attrs[a].val_ptr, len); | ||
174 | a++; | 189 | a++; |
175 | } | 190 | } |
176 | out: | 191 | out: |
@@ -178,16 +193,17 @@ out: | |||
178 | return ret; | 193 | return ret; |
179 | } | 194 | } |
180 | 195 | ||
181 | int osd_auto_detect_ver(struct osd_dev *od, void *caps) | 196 | int osd_auto_detect_ver(struct osd_dev *od, |
197 | void *caps, struct osd_dev_info *odi) | ||
182 | { | 198 | { |
183 | int ret; | 199 | int ret; |
184 | 200 | ||
185 | /* Auto-detect the osd version */ | 201 | /* Auto-detect the osd version */ |
186 | ret = _osd_print_system_info(od, caps); | 202 | ret = _osd_get_print_system_info(od, caps, odi); |
187 | if (ret) { | 203 | if (ret) { |
188 | osd_dev_set_ver(od, OSD_VER1); | 204 | osd_dev_set_ver(od, OSD_VER1); |
189 | OSD_DEBUG("converting to OSD1\n"); | 205 | OSD_DEBUG("converting to OSD1\n"); |
190 | ret = _osd_print_system_info(od, caps); | 206 | ret = _osd_get_print_system_info(od, caps, odi); |
191 | } | 207 | } |
192 | 208 | ||
193 | return ret; | 209 | return ret; |
@@ -418,30 +434,23 @@ static void _osd_free_seg(struct osd_request *or __unused, | |||
418 | seg->alloc_size = 0; | 434 | seg->alloc_size = 0; |
419 | } | 435 | } |
420 | 436 | ||
421 | static void _put_request(struct request *rq , bool is_async) | 437 | static void _put_request(struct request *rq) |
422 | { | 438 | { |
423 | if (is_async) { | 439 | /* |
424 | WARN_ON(rq->bio); | 440 | * If osd_finalize_request() was called but the request was not |
425 | __blk_put_request(rq->q, rq); | 441 | * executed through the block layer, then we must release BIOs. |
426 | } else { | 442 | * TODO: Keep error code in or->async_error. Need to audit all |
427 | /* | 443 | * code paths. |
428 | * If osd_finalize_request() was called but the request was not | 444 | */ |
429 | * executed through the block layer, then we must release BIOs. | 445 | if (unlikely(rq->bio)) |
430 | * TODO: Keep error code in or->async_error. Need to audit all | 446 | blk_end_request(rq, -ENOMEM, blk_rq_bytes(rq)); |
431 | * code paths. | 447 | else |
432 | */ | 448 | blk_put_request(rq); |
433 | if (unlikely(rq->bio)) | ||
434 | blk_end_request(rq, -ENOMEM, blk_rq_bytes(rq)); | ||
435 | else | ||
436 | blk_put_request(rq); | ||
437 | } | ||
438 | } | 449 | } |
439 | 450 | ||
440 | void osd_end_request(struct osd_request *or) | 451 | void osd_end_request(struct osd_request *or) |
441 | { | 452 | { |
442 | struct request *rq = or->request; | 453 | struct request *rq = or->request; |
443 | /* IMPORTANT: make sure this agrees with osd_execute_request_async */ | ||
444 | bool is_async = (or->request->end_io_data == or); | ||
445 | 454 | ||
446 | _osd_free_seg(or, &or->set_attr); | 455 | _osd_free_seg(or, &or->set_attr); |
447 | _osd_free_seg(or, &or->enc_get_attr); | 456 | _osd_free_seg(or, &or->enc_get_attr); |
@@ -449,19 +458,34 @@ void osd_end_request(struct osd_request *or) | |||
449 | 458 | ||
450 | if (rq) { | 459 | if (rq) { |
451 | if (rq->next_rq) { | 460 | if (rq->next_rq) { |
452 | _put_request(rq->next_rq, is_async); | 461 | _put_request(rq->next_rq); |
453 | rq->next_rq = NULL; | 462 | rq->next_rq = NULL; |
454 | } | 463 | } |
455 | 464 | ||
456 | _put_request(rq, is_async); | 465 | _put_request(rq); |
457 | } | 466 | } |
458 | _osd_request_free(or); | 467 | _osd_request_free(or); |
459 | } | 468 | } |
460 | EXPORT_SYMBOL(osd_end_request); | 469 | EXPORT_SYMBOL(osd_end_request); |
461 | 470 | ||
471 | static void _set_error_resid(struct osd_request *or, struct request *req, | ||
472 | int error) | ||
473 | { | ||
474 | or->async_error = error; | ||
475 | or->req_errors = req->errors ? : error; | ||
476 | or->sense_len = req->sense_len; | ||
477 | if (or->out.req) | ||
478 | or->out.residual = or->out.req->resid_len; | ||
479 | if (or->in.req) | ||
480 | or->in.residual = or->in.req->resid_len; | ||
481 | } | ||
482 | |||
462 | int osd_execute_request(struct osd_request *or) | 483 | int osd_execute_request(struct osd_request *or) |
463 | { | 484 | { |
464 | return blk_execute_rq(or->request->q, NULL, or->request, 0); | 485 | int error = blk_execute_rq(or->request->q, NULL, or->request, 0); |
486 | |||
487 | _set_error_resid(or, or->request, error); | ||
488 | return error; | ||
465 | } | 489 | } |
466 | EXPORT_SYMBOL(osd_execute_request); | 490 | EXPORT_SYMBOL(osd_execute_request); |
467 | 491 | ||
@@ -469,10 +493,16 @@ static void osd_request_async_done(struct request *req, int error) | |||
469 | { | 493 | { |
470 | struct osd_request *or = req->end_io_data; | 494 | struct osd_request *or = req->end_io_data; |
471 | 495 | ||
472 | or->async_error = error; | 496 | _set_error_resid(or, req, error); |
497 | if (req->next_rq) { | ||
498 | __blk_put_request(req->q, req->next_rq); | ||
499 | req->next_rq = NULL; | ||
500 | } | ||
473 | 501 | ||
474 | if (error) | 502 | __blk_put_request(req->q, req); |
475 | OSD_DEBUG("osd_request_async_done error recieved %d\n", error); | 503 | or->request = NULL; |
504 | or->in.req = NULL; | ||
505 | or->out.req = NULL; | ||
476 | 506 | ||
477 | if (or->async_done) | 507 | if (or->async_done) |
478 | or->async_done(or, or->async_private); | 508 | or->async_done(or, or->async_private); |
@@ -1153,6 +1183,7 @@ int osd_req_decode_get_attr_list(struct osd_request *or, | |||
1153 | "c=%d r=%d n=%d\n", | 1183 | "c=%d r=%d n=%d\n", |
1154 | cur_bytes, returned_bytes, n); | 1184 | cur_bytes, returned_bytes, n); |
1155 | oa->val_ptr = NULL; | 1185 | oa->val_ptr = NULL; |
1186 | cur_bytes = returned_bytes; /* break the caller loop */ | ||
1156 | break; | 1187 | break; |
1157 | } | 1188 | } |
1158 | 1189 | ||
@@ -1404,6 +1435,10 @@ int osd_finalize_request(struct osd_request *or, | |||
1404 | cdbh->command_specific_options |= or->attributes_mode; | 1435 | cdbh->command_specific_options |= or->attributes_mode; |
1405 | if (or->attributes_mode == OSD_CDB_GET_ATTR_PAGE_SET_ONE) { | 1436 | if (or->attributes_mode == OSD_CDB_GET_ATTR_PAGE_SET_ONE) { |
1406 | ret = _osd_req_finalize_attr_page(or); | 1437 | ret = _osd_req_finalize_attr_page(or); |
1438 | if (ret) { | ||
1439 | OSD_DEBUG("_osd_req_finalize_attr_page failed\n"); | ||
1440 | return ret; | ||
1441 | } | ||
1407 | } else { | 1442 | } else { |
1408 | /* TODO: I think that for the GET_ATTR command these 2 should | 1443 | /* TODO: I think that for the GET_ATTR command these 2 should |
1409 | * be reversed to keep them in execution order (for embeded | 1444 | * be reversed to keep them in execution order (for embeded |
@@ -1436,6 +1471,15 @@ int osd_finalize_request(struct osd_request *or, | |||
1436 | } | 1471 | } |
1437 | EXPORT_SYMBOL(osd_finalize_request); | 1472 | EXPORT_SYMBOL(osd_finalize_request); |
1438 | 1473 | ||
1474 | static bool _is_osd_security_code(int code) | ||
1475 | { | ||
1476 | return (code == osd_security_audit_value_frozen) || | ||
1477 | (code == osd_security_working_key_frozen) || | ||
1478 | (code == osd_nonce_not_unique) || | ||
1479 | (code == osd_nonce_timestamp_out_of_range) || | ||
1480 | (code == osd_invalid_dataout_buffer_integrity_check_value); | ||
1481 | } | ||
1482 | |||
1439 | #define OSD_SENSE_PRINT1(fmt, a...) \ | 1483 | #define OSD_SENSE_PRINT1(fmt, a...) \ |
1440 | do { \ | 1484 | do { \ |
1441 | if (__cur_sense_need_output) \ | 1485 | if (__cur_sense_need_output) \ |
@@ -1458,27 +1502,29 @@ int osd_req_decode_sense_full(struct osd_request *or, | |||
1458 | #else | 1502 | #else |
1459 | bool __cur_sense_need_output = !silent; | 1503 | bool __cur_sense_need_output = !silent; |
1460 | #endif | 1504 | #endif |
1505 | int ret; | ||
1461 | 1506 | ||
1462 | if (!or->request->errors) | 1507 | if (likely(!or->req_errors)) |
1463 | return 0; | 1508 | return 0; |
1464 | 1509 | ||
1465 | ssdb = or->request->sense; | 1510 | osi = osi ? : &local_osi; |
1466 | sense_len = or->request->sense_len; | 1511 | memset(osi, 0, sizeof(*osi)); |
1512 | |||
1513 | ssdb = (typeof(ssdb))or->sense; | ||
1514 | sense_len = or->sense_len; | ||
1467 | if ((sense_len < (int)sizeof(*ssdb) || !ssdb->sense_key)) { | 1515 | if ((sense_len < (int)sizeof(*ssdb) || !ssdb->sense_key)) { |
1468 | OSD_ERR("Block-layer returned error(0x%x) but " | 1516 | OSD_ERR("Block-layer returned error(0x%x) but " |
1469 | "sense_len(%u) || key(%d) is empty\n", | 1517 | "sense_len(%u) || key(%d) is empty\n", |
1470 | or->request->errors, sense_len, ssdb->sense_key); | 1518 | or->req_errors, sense_len, ssdb->sense_key); |
1471 | return -EIO; | 1519 | goto analyze; |
1472 | } | 1520 | } |
1473 | 1521 | ||
1474 | if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) { | 1522 | if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) { |
1475 | OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n", | 1523 | OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n", |
1476 | ssdb->response_code, sense_len); | 1524 | ssdb->response_code, sense_len); |
1477 | return -EIO; | 1525 | goto analyze; |
1478 | } | 1526 | } |
1479 | 1527 | ||
1480 | osi = osi ? : &local_osi; | ||
1481 | memset(osi, 0, sizeof(*osi)); | ||
1482 | osi->key = ssdb->sense_key; | 1528 | osi->key = ssdb->sense_key; |
1483 | osi->additional_code = be16_to_cpu(ssdb->additional_sense_code); | 1529 | osi->additional_code = be16_to_cpu(ssdb->additional_sense_code); |
1484 | original_sense_len = ssdb->additional_sense_length + 8; | 1530 | original_sense_len = ssdb->additional_sense_length + 8; |
@@ -1488,9 +1534,10 @@ int osd_req_decode_sense_full(struct osd_request *or, | |||
1488 | __cur_sense_need_output = (osi->key > scsi_sk_recovered_error); | 1534 | __cur_sense_need_output = (osi->key > scsi_sk_recovered_error); |
1489 | #endif | 1535 | #endif |
1490 | OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) " | 1536 | OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) " |
1491 | "additional_code=0x%x\n", | 1537 | "additional_code=0x%x async_error=%d errors=0x%x\n", |
1492 | osi->key, original_sense_len, sense_len, | 1538 | osi->key, original_sense_len, sense_len, |
1493 | osi->additional_code); | 1539 | osi->additional_code, or->async_error, |
1540 | or->req_errors); | ||
1494 | 1541 | ||
1495 | if (original_sense_len < sense_len) | 1542 | if (original_sense_len < sense_len) |
1496 | sense_len = original_sense_len; | 1543 | sense_len = original_sense_len; |
@@ -1569,15 +1616,14 @@ int osd_req_decode_sense_full(struct osd_request *or, | |||
1569 | { | 1616 | { |
1570 | struct osd_sense_attributes_data_descriptor | 1617 | struct osd_sense_attributes_data_descriptor |
1571 | *osadd = cur_descriptor; | 1618 | *osadd = cur_descriptor; |
1572 | int len = min(cur_len, sense_len); | 1619 | unsigned len = min(cur_len, sense_len); |
1573 | int i = 0; | ||
1574 | struct osd_sense_attr *pattr = osadd->sense_attrs; | 1620 | struct osd_sense_attr *pattr = osadd->sense_attrs; |
1575 | 1621 | ||
1576 | while (len < 0) { | 1622 | while (len >= sizeof(*pattr)) { |
1577 | u32 attr_page = be32_to_cpu(pattr->attr_page); | 1623 | u32 attr_page = be32_to_cpu(pattr->attr_page); |
1578 | u32 attr_id = be32_to_cpu(pattr->attr_id); | 1624 | u32 attr_id = be32_to_cpu(pattr->attr_id); |
1579 | 1625 | ||
1580 | if (i++ == 0) { | 1626 | if (!osi->attr.attr_page) { |
1581 | osi->attr.attr_page = attr_page; | 1627 | osi->attr.attr_page = attr_page; |
1582 | osi->attr.attr_id = attr_id; | 1628 | osi->attr.attr_id = attr_id; |
1583 | } | 1629 | } |
@@ -1588,6 +1634,8 @@ int osd_req_decode_sense_full(struct osd_request *or, | |||
1588 | bad_attr_list++; | 1634 | bad_attr_list++; |
1589 | max_attr--; | 1635 | max_attr--; |
1590 | } | 1636 | } |
1637 | |||
1638 | len -= sizeof(*pattr); | ||
1591 | OSD_SENSE_PRINT2( | 1639 | OSD_SENSE_PRINT2( |
1592 | "osd_sense_attribute_identification" | 1640 | "osd_sense_attribute_identification" |
1593 | "attr_page=0x%x attr_id=0x%x\n", | 1641 | "attr_page=0x%x attr_id=0x%x\n", |
@@ -1621,7 +1669,50 @@ int osd_req_decode_sense_full(struct osd_request *or, | |||
1621 | cur_descriptor += cur_len; | 1669 | cur_descriptor += cur_len; |
1622 | } | 1670 | } |
1623 | 1671 | ||
1624 | return (osi->key > scsi_sk_recovered_error) ? -EIO : 0; | 1672 | analyze: |
1673 | if (!osi->key) { | ||
1674 | /* scsi sense is Empty, the request was never issued to target | ||
1675 | * linux return code might tell us what happened. | ||
1676 | */ | ||
1677 | if (or->async_error == -ENOMEM) | ||
1678 | osi->osd_err_pri = OSD_ERR_PRI_RESOURCE; | ||
1679 | else | ||
1680 | osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE; | ||
1681 | ret = or->async_error; | ||
1682 | } else if (osi->key <= scsi_sk_recovered_error) { | ||
1683 | osi->osd_err_pri = 0; | ||
1684 | ret = 0; | ||
1685 | } else if (osi->additional_code == scsi_invalid_field_in_cdb) { | ||
1686 | if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) { | ||
1687 | osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES; | ||
1688 | ret = -EFAULT; /* caller should recover from this */ | ||
1689 | } else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) { | ||
1690 | osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND; | ||
1691 | ret = -ENOENT; | ||
1692 | } else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) { | ||
1693 | osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS; | ||
1694 | ret = -EACCES; | ||
1695 | } else { | ||
1696 | osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED; | ||
1697 | ret = -EINVAL; | ||
1698 | } | ||
1699 | } else if (osi->additional_code == osd_quota_error) { | ||
1700 | osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE; | ||
1701 | ret = -ENOSPC; | ||
1702 | } else if (_is_osd_security_code(osi->additional_code)) { | ||
1703 | osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED; | ||
1704 | ret = -EINVAL; | ||
1705 | } else { | ||
1706 | osi->osd_err_pri = OSD_ERR_PRI_EIO; | ||
1707 | ret = -EIO; | ||
1708 | } | ||
1709 | |||
1710 | if (!or->out.residual) | ||
1711 | or->out.residual = or->out.total_bytes; | ||
1712 | if (!or->in.residual) | ||
1713 | or->in.residual = or->in.total_bytes; | ||
1714 | |||
1715 | return ret; | ||
1625 | } | 1716 | } |
1626 | EXPORT_SYMBOL(osd_req_decode_sense_full); | 1717 | EXPORT_SYMBOL(osd_req_decode_sense_full); |
1627 | 1718 | ||
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c index 0bdef3390902..ffdd9fdb9995 100644 --- a/drivers/scsi/osd/osd_uld.c +++ b/drivers/scsi/osd/osd_uld.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #include <linux/idr.h> | 50 | #include <linux/idr.h> |
51 | #include <linux/major.h> | 51 | #include <linux/major.h> |
52 | #include <linux/file.h> | 52 | #include <linux/file.h> |
53 | #include <linux/slab.h> | ||
53 | 54 | ||
54 | #include <scsi/scsi.h> | 55 | #include <scsi/scsi.h> |
55 | #include <scsi/scsi_driver.h> | 56 | #include <scsi/scsi_driver.h> |
@@ -71,8 +72,7 @@ | |||
71 | #define SCSI_OSD_MAX_MINOR 64 | 72 | #define SCSI_OSD_MAX_MINOR 64 |
72 | 73 | ||
73 | static const char osd_name[] = "osd"; | 74 | static const char osd_name[] = "osd"; |
74 | static const char *osd_version_string = "open-osd 0.1.0"; | 75 | static const char *osd_version_string = "open-osd 0.2.0"; |
75 | const char osd_symlink[] = "scsi_osd"; | ||
76 | 76 | ||
77 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); | 77 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); |
78 | MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); | 78 | MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); |
@@ -82,15 +82,25 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD); | |||
82 | 82 | ||
83 | struct osd_uld_device { | 83 | struct osd_uld_device { |
84 | int minor; | 84 | int minor; |
85 | struct kref kref; | 85 | struct device class_dev; |
86 | struct cdev cdev; | 86 | struct cdev cdev; |
87 | struct osd_dev od; | 87 | struct osd_dev od; |
88 | struct osd_dev_info odi; | ||
88 | struct gendisk *disk; | 89 | struct gendisk *disk; |
89 | struct device *class_member; | ||
90 | }; | 90 | }; |
91 | 91 | ||
92 | static void __uld_get(struct osd_uld_device *oud); | 92 | struct osd_dev_handle { |
93 | static void __uld_put(struct osd_uld_device *oud); | 93 | struct osd_dev od; |
94 | struct file *file; | ||
95 | struct osd_uld_device *oud; | ||
96 | } ; | ||
97 | |||
98 | static DEFINE_IDA(osd_minor_ida); | ||
99 | |||
100 | static struct class osd_uld_class = { | ||
101 | .owner = THIS_MODULE, | ||
102 | .name = "scsi_osd", | ||
103 | }; | ||
94 | 104 | ||
95 | /* | 105 | /* |
96 | * Char Device operations | 106 | * Char Device operations |
@@ -101,7 +111,7 @@ static int osd_uld_open(struct inode *inode, struct file *file) | |||
101 | struct osd_uld_device *oud = container_of(inode->i_cdev, | 111 | struct osd_uld_device *oud = container_of(inode->i_cdev, |
102 | struct osd_uld_device, cdev); | 112 | struct osd_uld_device, cdev); |
103 | 113 | ||
104 | __uld_get(oud); | 114 | get_device(&oud->class_dev); |
105 | /* cache osd_uld_device on file handle */ | 115 | /* cache osd_uld_device on file handle */ |
106 | file->private_data = oud; | 116 | file->private_data = oud; |
107 | OSD_DEBUG("osd_uld_open %p\n", oud); | 117 | OSD_DEBUG("osd_uld_open %p\n", oud); |
@@ -114,7 +124,7 @@ static int osd_uld_release(struct inode *inode, struct file *file) | |||
114 | 124 | ||
115 | OSD_DEBUG("osd_uld_release %p\n", file->private_data); | 125 | OSD_DEBUG("osd_uld_release %p\n", file->private_data); |
116 | file->private_data = NULL; | 126 | file->private_data = NULL; |
117 | __uld_put(oud); | 127 | put_device(&oud->class_dev); |
118 | return 0; | 128 | return 0; |
119 | } | 129 | } |
120 | 130 | ||
@@ -177,7 +187,7 @@ static const struct file_operations osd_fops = { | |||
177 | struct osd_dev *osduld_path_lookup(const char *name) | 187 | struct osd_dev *osduld_path_lookup(const char *name) |
178 | { | 188 | { |
179 | struct osd_uld_device *oud; | 189 | struct osd_uld_device *oud; |
180 | struct osd_dev *od; | 190 | struct osd_dev_handle *odh; |
181 | struct file *file; | 191 | struct file *file; |
182 | int error; | 192 | int error; |
183 | 193 | ||
@@ -186,8 +196,8 @@ struct osd_dev *osduld_path_lookup(const char *name) | |||
186 | return ERR_PTR(-EINVAL); | 196 | return ERR_PTR(-EINVAL); |
187 | } | 197 | } |
188 | 198 | ||
189 | od = kzalloc(sizeof(*od), GFP_KERNEL); | 199 | odh = kzalloc(sizeof(*odh), GFP_KERNEL); |
190 | if (!od) | 200 | if (unlikely(!odh)) |
191 | return ERR_PTR(-ENOMEM); | 201 | return ERR_PTR(-ENOMEM); |
192 | 202 | ||
193 | file = filp_open(name, O_RDWR, 0); | 203 | file = filp_open(name, O_RDWR, 0); |
@@ -203,33 +213,134 @@ struct osd_dev *osduld_path_lookup(const char *name) | |||
203 | 213 | ||
204 | oud = file->private_data; | 214 | oud = file->private_data; |
205 | 215 | ||
206 | *od = oud->od; | 216 | odh->od = oud->od; |
207 | od->file = file; | 217 | odh->file = file; |
218 | odh->oud = oud; | ||
208 | 219 | ||
209 | return od; | 220 | return &odh->od; |
210 | 221 | ||
211 | close_file: | 222 | close_file: |
212 | fput(file); | 223 | fput(file); |
213 | free_od: | 224 | free_od: |
214 | kfree(od); | 225 | kfree(odh); |
215 | return ERR_PTR(error); | 226 | return ERR_PTR(error); |
216 | } | 227 | } |
217 | EXPORT_SYMBOL(osduld_path_lookup); | 228 | EXPORT_SYMBOL(osduld_path_lookup); |
218 | 229 | ||
219 | void osduld_put_device(struct osd_dev *od) | 230 | static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len, |
231 | const u8 *a2, unsigned a2_len) | ||
220 | { | 232 | { |
233 | if (!a2_len) /* User string is Empty means don't care */ | ||
234 | return true; | ||
235 | |||
236 | if (a1_len != a2_len) | ||
237 | return false; | ||
238 | |||
239 | return 0 == memcmp(a1, a2, a1_len); | ||
240 | } | ||
241 | |||
242 | struct find_oud_t { | ||
243 | const struct osd_dev_info *odi; | ||
244 | struct device *dev; | ||
245 | struct osd_uld_device *oud; | ||
246 | } ; | ||
247 | |||
248 | int _mach_odi(struct device *dev, void *find_data) | ||
249 | { | ||
250 | struct osd_uld_device *oud = container_of(dev, struct osd_uld_device, | ||
251 | class_dev); | ||
252 | struct find_oud_t *fot = find_data; | ||
253 | const struct osd_dev_info *odi = fot->odi; | ||
254 | |||
255 | if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len, | ||
256 | odi->systemid, odi->systemid_len) && | ||
257 | _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len, | ||
258 | odi->osdname, odi->osdname_len)) { | ||
259 | OSD_DEBUG("found device sysid_len=%d osdname=%d\n", | ||
260 | odi->systemid_len, odi->osdname_len); | ||
261 | fot->oud = oud; | ||
262 | return 1; | ||
263 | } else { | ||
264 | return 0; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | /* osduld_info_lookup - Loop through all devices, return the requested osd_dev. | ||
269 | * | ||
270 | * if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't | ||
271 | * care. .e.g if they're both zero /dev/osd0 is returned. | ||
272 | */ | ||
273 | struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi) | ||
274 | { | ||
275 | struct find_oud_t find = {.odi = odi}; | ||
276 | |||
277 | find.dev = class_find_device(&osd_uld_class, NULL, &find, _mach_odi); | ||
278 | if (likely(find.dev)) { | ||
279 | struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL); | ||
280 | |||
281 | if (unlikely(!odh)) { | ||
282 | put_device(find.dev); | ||
283 | return ERR_PTR(-ENOMEM); | ||
284 | } | ||
221 | 285 | ||
286 | odh->od = find.oud->od; | ||
287 | odh->oud = find.oud; | ||
288 | |||
289 | return &odh->od; | ||
290 | } | ||
291 | |||
292 | return ERR_PTR(-ENODEV); | ||
293 | } | ||
294 | EXPORT_SYMBOL(osduld_info_lookup); | ||
295 | |||
296 | void osduld_put_device(struct osd_dev *od) | ||
297 | { | ||
222 | if (od && !IS_ERR(od)) { | 298 | if (od && !IS_ERR(od)) { |
223 | struct osd_uld_device *oud = od->file->private_data; | 299 | struct osd_dev_handle *odh = |
300 | container_of(od, struct osd_dev_handle, od); | ||
301 | struct osd_uld_device *oud = odh->oud; | ||
224 | 302 | ||
225 | BUG_ON(od->scsi_device != oud->od.scsi_device); | 303 | BUG_ON(od->scsi_device != oud->od.scsi_device); |
226 | 304 | ||
227 | fput(od->file); | 305 | /* If scsi has released the device (logout), and exofs has last |
228 | kfree(od); | 306 | * reference on oud it will be freed by above osd_uld_release |
307 | * within fput below. But this will oops in cdev_release which | ||
308 | * is called after the fops->release. A get_/put_ pair makes | ||
309 | * sure we have a cdev for the duration of fput | ||
310 | */ | ||
311 | if (odh->file) { | ||
312 | get_device(&oud->class_dev); | ||
313 | fput(odh->file); | ||
314 | } | ||
315 | put_device(&oud->class_dev); | ||
316 | kfree(odh); | ||
229 | } | 317 | } |
230 | } | 318 | } |
231 | EXPORT_SYMBOL(osduld_put_device); | 319 | EXPORT_SYMBOL(osduld_put_device); |
232 | 320 | ||
321 | const struct osd_dev_info *osduld_device_info(struct osd_dev *od) | ||
322 | { | ||
323 | struct osd_dev_handle *odh = | ||
324 | container_of(od, struct osd_dev_handle, od); | ||
325 | return &odh->oud->odi; | ||
326 | } | ||
327 | EXPORT_SYMBOL(osduld_device_info); | ||
328 | |||
329 | bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi) | ||
330 | { | ||
331 | struct osd_dev_handle *odh = | ||
332 | container_of(od, struct osd_dev_handle, od); | ||
333 | struct osd_uld_device *oud = odh->oud; | ||
334 | |||
335 | return (oud->odi.systemid_len == odi->systemid_len) && | ||
336 | _the_same_or_null(oud->odi.systemid, oud->odi.systemid_len, | ||
337 | odi->systemid, odi->systemid_len) && | ||
338 | (oud->odi.osdname_len == odi->osdname_len) && | ||
339 | _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len, | ||
340 | odi->osdname, odi->osdname_len); | ||
341 | } | ||
342 | EXPORT_SYMBOL(osduld_device_same); | ||
343 | |||
233 | /* | 344 | /* |
234 | * Scsi Device operations | 345 | * Scsi Device operations |
235 | */ | 346 | */ |
@@ -250,14 +361,35 @@ static int __detect_osd(struct osd_uld_device *oud) | |||
250 | OSD_ERR("warning: scsi_test_unit_ready failed\n"); | 361 | OSD_ERR("warning: scsi_test_unit_ready failed\n"); |
251 | 362 | ||
252 | osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true); | 363 | osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true); |
253 | if (osd_auto_detect_ver(&oud->od, caps)) | 364 | if (osd_auto_detect_ver(&oud->od, caps, &oud->odi)) |
254 | return -ENODEV; | 365 | return -ENODEV; |
255 | 366 | ||
256 | return 0; | 367 | return 0; |
257 | } | 368 | } |
258 | 369 | ||
259 | static struct class *osd_sysfs_class; | 370 | static void __remove(struct device *dev) |
260 | static DEFINE_IDA(osd_minor_ida); | 371 | { |
372 | struct osd_uld_device *oud = container_of(dev, struct osd_uld_device, | ||
373 | class_dev); | ||
374 | struct scsi_device *scsi_device = oud->od.scsi_device; | ||
375 | |||
376 | kfree(oud->odi.osdname); | ||
377 | |||
378 | if (oud->cdev.owner) | ||
379 | cdev_del(&oud->cdev); | ||
380 | |||
381 | osd_dev_fini(&oud->od); | ||
382 | scsi_device_put(scsi_device); | ||
383 | |||
384 | OSD_INFO("osd_remove %s\n", | ||
385 | oud->disk ? oud->disk->disk_name : NULL); | ||
386 | |||
387 | if (oud->disk) | ||
388 | put_disk(oud->disk); | ||
389 | ida_remove(&osd_minor_ida, oud->minor); | ||
390 | |||
391 | kfree(oud); | ||
392 | } | ||
261 | 393 | ||
262 | static int osd_probe(struct device *dev) | 394 | static int osd_probe(struct device *dev) |
263 | { | 395 | { |
@@ -289,7 +421,6 @@ static int osd_probe(struct device *dev) | |||
289 | if (NULL == oud) | 421 | if (NULL == oud) |
290 | goto err_retract_minor; | 422 | goto err_retract_minor; |
291 | 423 | ||
292 | kref_init(&oud->kref); | ||
293 | dev_set_drvdata(dev, oud); | 424 | dev_set_drvdata(dev, oud); |
294 | oud->minor = minor; | 425 | oud->minor = minor; |
295 | 426 | ||
@@ -327,18 +458,25 @@ static int osd_probe(struct device *dev) | |||
327 | OSD_ERR("cdev_add failed\n"); | 458 | OSD_ERR("cdev_add failed\n"); |
328 | goto err_put_disk; | 459 | goto err_put_disk; |
329 | } | 460 | } |
330 | kobject_get(&oud->cdev.kobj); /* 2nd ref see osd_remove() */ | 461 | |
331 | 462 | /* class device member */ | |
332 | /* class_member */ | 463 | oud->class_dev.devt = oud->cdev.dev; |
333 | oud->class_member = device_create(osd_sysfs_class, dev, | 464 | oud->class_dev.class = &osd_uld_class; |
334 | MKDEV(SCSI_OSD_MAJOR, oud->minor), "%s", disk->disk_name); | 465 | oud->class_dev.parent = dev; |
335 | if (IS_ERR(oud->class_member)) { | 466 | oud->class_dev.release = __remove; |
336 | OSD_ERR("class_device_create failed\n"); | 467 | error = dev_set_name(&oud->class_dev, disk->disk_name); |
337 | error = PTR_ERR(oud->class_member); | 468 | if (error) { |
469 | OSD_ERR("dev_set_name failed => %d\n", error); | ||
470 | goto err_put_cdev; | ||
471 | } | ||
472 | |||
473 | error = device_register(&oud->class_dev); | ||
474 | if (error) { | ||
475 | OSD_ERR("device_register failed => %d\n", error); | ||
338 | goto err_put_cdev; | 476 | goto err_put_cdev; |
339 | } | 477 | } |
340 | 478 | ||
341 | dev_set_drvdata(oud->class_member, oud); | 479 | get_device(&oud->class_dev); |
342 | 480 | ||
343 | OSD_INFO("osd_probe %s\n", disk->disk_name); | 481 | OSD_INFO("osd_probe %s\n", disk->disk_name); |
344 | return 0; | 482 | return 0; |
@@ -367,54 +505,12 @@ static int osd_remove(struct device *dev) | |||
367 | scsi_device); | 505 | scsi_device); |
368 | } | 506 | } |
369 | 507 | ||
370 | if (oud->class_member) | 508 | device_unregister(&oud->class_dev); |
371 | device_destroy(osd_sysfs_class, | ||
372 | MKDEV(SCSI_OSD_MAJOR, oud->minor)); | ||
373 | 509 | ||
374 | /* We have 2 references to the cdev. One is released here | 510 | put_device(&oud->class_dev); |
375 | * and also takes down the /dev/osdX mapping. The second | ||
376 | * Will be released in __remove() after all users have released | ||
377 | * the osd_uld_device. | ||
378 | */ | ||
379 | if (oud->cdev.owner) | ||
380 | cdev_del(&oud->cdev); | ||
381 | |||
382 | __uld_put(oud); | ||
383 | return 0; | 511 | return 0; |
384 | } | 512 | } |
385 | 513 | ||
386 | static void __remove(struct kref *kref) | ||
387 | { | ||
388 | struct osd_uld_device *oud = container_of(kref, | ||
389 | struct osd_uld_device, kref); | ||
390 | struct scsi_device *scsi_device = oud->od.scsi_device; | ||
391 | |||
392 | /* now let delete the char_dev */ | ||
393 | kobject_put(&oud->cdev.kobj); | ||
394 | |||
395 | osd_dev_fini(&oud->od); | ||
396 | scsi_device_put(scsi_device); | ||
397 | |||
398 | OSD_INFO("osd_remove %s\n", | ||
399 | oud->disk ? oud->disk->disk_name : NULL); | ||
400 | |||
401 | if (oud->disk) | ||
402 | put_disk(oud->disk); | ||
403 | |||
404 | ida_remove(&osd_minor_ida, oud->minor); | ||
405 | kfree(oud); | ||
406 | } | ||
407 | |||
408 | static void __uld_get(struct osd_uld_device *oud) | ||
409 | { | ||
410 | kref_get(&oud->kref); | ||
411 | } | ||
412 | |||
413 | static void __uld_put(struct osd_uld_device *oud) | ||
414 | { | ||
415 | kref_put(&oud->kref, __remove); | ||
416 | } | ||
417 | |||
418 | /* | 514 | /* |
419 | * Global driver and scsi registration | 515 | * Global driver and scsi registration |
420 | */ | 516 | */ |
@@ -432,11 +528,10 @@ static int __init osd_uld_init(void) | |||
432 | { | 528 | { |
433 | int err; | 529 | int err; |
434 | 530 | ||
435 | osd_sysfs_class = class_create(THIS_MODULE, osd_symlink); | 531 | err = class_register(&osd_uld_class); |
436 | if (IS_ERR(osd_sysfs_class)) { | 532 | if (err) { |
437 | OSD_ERR("Unable to register sysfs class => %ld\n", | 533 | OSD_ERR("Unable to register sysfs class => %d\n", err); |
438 | PTR_ERR(osd_sysfs_class)); | 534 | return err; |
439 | return PTR_ERR(osd_sysfs_class); | ||
440 | } | 535 | } |
441 | 536 | ||
442 | err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), | 537 | err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), |
@@ -459,7 +554,7 @@ static int __init osd_uld_init(void) | |||
459 | err_out_chrdev: | 554 | err_out_chrdev: |
460 | unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); | 555 | unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); |
461 | err_out: | 556 | err_out: |
462 | class_destroy(osd_sysfs_class); | 557 | class_unregister(&osd_uld_class); |
463 | return err; | 558 | return err; |
464 | } | 559 | } |
465 | 560 | ||
@@ -467,7 +562,7 @@ static void __exit osd_uld_exit(void) | |||
467 | { | 562 | { |
468 | scsi_unregister_driver(&osd_driver.gendrv); | 563 | scsi_unregister_driver(&osd_driver.gendrv); |
469 | unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); | 564 | unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); |
470 | class_destroy(osd_sysfs_class); | 565 | class_unregister(&osd_uld_class); |
471 | OSD_INFO("UNLOADED %s\n", osd_version_string); | 566 | OSD_INFO("UNLOADED %s\n", osd_version_string); |
472 | } | 567 | } |
473 | 568 | ||