diff options
| -rw-r--r-- | drivers/scsi/osd/osd_initiator.c | 148 | ||||
| -rw-r--r-- | include/scsi/osd_initiator.h | 9 | ||||
| -rw-r--r-- | include/scsi/osd_protocol.h | 42 | ||||
| -rw-r--r-- | include/scsi/osd_types.h | 5 |
4 files changed, 198 insertions, 6 deletions
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index acbdcb670ac5..f5b5735f76d5 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c | |||
| @@ -464,6 +464,7 @@ void osd_end_request(struct osd_request *or) | |||
| 464 | _osd_free_seg(or, &or->get_attr); | 464 | _osd_free_seg(or, &or->get_attr); |
| 465 | _osd_free_seg(or, &or->enc_get_attr); | 465 | _osd_free_seg(or, &or->enc_get_attr); |
| 466 | _osd_free_seg(or, &or->set_attr); | 466 | _osd_free_seg(or, &or->set_attr); |
| 467 | _osd_free_seg(or, &or->cdb_cont); | ||
| 467 | 468 | ||
| 468 | _osd_request_free(or); | 469 | _osd_request_free(or); |
| 469 | } | 470 | } |
| @@ -548,6 +549,12 @@ static int _osd_realloc_seg(struct osd_request *or, | |||
| 548 | return 0; | 549 | return 0; |
| 549 | } | 550 | } |
| 550 | 551 | ||
| 552 | static int _alloc_cdb_cont(struct osd_request *or, unsigned total_bytes) | ||
| 553 | { | ||
| 554 | OSD_DEBUG("total_bytes=%d\n", total_bytes); | ||
| 555 | return _osd_realloc_seg(or, &or->cdb_cont, total_bytes); | ||
| 556 | } | ||
| 557 | |||
| 551 | static int _alloc_set_attr_list(struct osd_request *or, | 558 | static int _alloc_set_attr_list(struct osd_request *or, |
| 552 | const struct osd_attr *oa, unsigned nelem, unsigned add_bytes) | 559 | const struct osd_attr *oa, unsigned nelem, unsigned add_bytes) |
| 553 | { | 560 | { |
| @@ -886,6 +893,128 @@ int osd_req_read_kern(struct osd_request *or, | |||
| 886 | } | 893 | } |
| 887 | EXPORT_SYMBOL(osd_req_read_kern); | 894 | EXPORT_SYMBOL(osd_req_read_kern); |
| 888 | 895 | ||
| 896 | static int _add_sg_continuation_descriptor(struct osd_request *or, | ||
| 897 | const struct osd_sg_entry *sglist, unsigned numentries, u64 *len) | ||
| 898 | { | ||
| 899 | struct osd_sg_continuation_descriptor *oscd; | ||
| 900 | u32 oscd_size; | ||
| 901 | unsigned i; | ||
| 902 | int ret; | ||
| 903 | |||
| 904 | oscd_size = sizeof(*oscd) + numentries * sizeof(oscd->entries[0]); | ||
| 905 | |||
| 906 | if (!or->cdb_cont.total_bytes) { | ||
| 907 | /* First time, jump over the header, we will write to: | ||
| 908 | * cdb_cont.buff + cdb_cont.total_bytes | ||
| 909 | */ | ||
| 910 | or->cdb_cont.total_bytes = | ||
| 911 | sizeof(struct osd_continuation_segment_header); | ||
| 912 | } | ||
| 913 | |||
| 914 | ret = _alloc_cdb_cont(or, or->cdb_cont.total_bytes + oscd_size); | ||
| 915 | if (unlikely(ret)) | ||
| 916 | return ret; | ||
| 917 | |||
| 918 | oscd = or->cdb_cont.buff + or->cdb_cont.total_bytes; | ||
| 919 | oscd->hdr.type = cpu_to_be16(SCATTER_GATHER_LIST); | ||
| 920 | oscd->hdr.pad_length = 0; | ||
| 921 | oscd->hdr.length = cpu_to_be32(oscd_size - sizeof(*oscd)); | ||
| 922 | |||
| 923 | *len = 0; | ||
| 924 | /* copy the sg entries and convert to network byte order */ | ||
| 925 | for (i = 0; i < numentries; i++) { | ||
| 926 | oscd->entries[i].offset = cpu_to_be64(sglist[i].offset); | ||
| 927 | oscd->entries[i].len = cpu_to_be64(sglist[i].len); | ||
| 928 | *len += sglist[i].len; | ||
| 929 | } | ||
| 930 | |||
| 931 | or->cdb_cont.total_bytes += oscd_size; | ||
| 932 | OSD_DEBUG("total_bytes=%d oscd_size=%d numentries=%d\n", | ||
| 933 | or->cdb_cont.total_bytes, oscd_size, numentries); | ||
| 934 | return 0; | ||
| 935 | } | ||
| 936 | |||
| 937 | static int _osd_req_finalize_cdb_cont(struct osd_request *or, const u8 *cap_key) | ||
| 938 | { | ||
| 939 | struct request_queue *req_q = osd_request_queue(or->osd_dev); | ||
| 940 | struct bio *bio; | ||
| 941 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
| 942 | struct osd_continuation_segment_header *cont_seg_hdr; | ||
| 943 | |||
| 944 | if (!or->cdb_cont.total_bytes) | ||
| 945 | return 0; | ||
| 946 | |||
| 947 | cont_seg_hdr = or->cdb_cont.buff; | ||
| 948 | cont_seg_hdr->format = CDB_CONTINUATION_FORMAT_V2; | ||
| 949 | cont_seg_hdr->service_action = cdbh->varlen_cdb.service_action; | ||
| 950 | |||
| 951 | /* create a bio for continuation segment */ | ||
| 952 | bio = bio_map_kern(req_q, or->cdb_cont.buff, or->cdb_cont.total_bytes, | ||
| 953 | GFP_KERNEL); | ||
| 954 | if (unlikely(!bio)) | ||
| 955 | return -ENOMEM; | ||
| 956 | |||
| 957 | bio->bi_rw |= REQ_WRITE; | ||
| 958 | |||
| 959 | /* integrity check the continuation before the bio is linked | ||
| 960 | * with the other data segments since the continuation | ||
| 961 | * integrity is separate from the other data segments. | ||
| 962 | */ | ||
| 963 | osd_sec_sign_data(cont_seg_hdr->integrity_check, bio, cap_key); | ||
| 964 | |||
| 965 | cdbh->v2.cdb_continuation_length = cpu_to_be32(or->cdb_cont.total_bytes); | ||
| 966 | |||
| 967 | /* we can't use _req_append_segment, because we need to link in the | ||
| 968 | * continuation bio to the head of the bio list - the | ||
| 969 | * continuation segment (if it exists) is always the first segment in | ||
| 970 | * the out data buffer. | ||
| 971 | */ | ||
| 972 | bio->bi_next = or->out.bio; | ||
| 973 | or->out.bio = bio; | ||
| 974 | or->out.total_bytes += or->cdb_cont.total_bytes; | ||
| 975 | |||
| 976 | return 0; | ||
| 977 | } | ||
| 978 | |||
| 979 | /* osd_req_write_sg: Takes a @bio that points to the data out buffer and an | ||
| 980 | * @sglist that has the scatter gather entries. Scatter-gather enables a write | ||
| 981 | * of multiple none-contiguous areas of an object, in a single call. The extents | ||
| 982 | * may overlap and/or be in any order. The only constrain is that: | ||
| 983 | * total_bytes(sglist) >= total_bytes(bio) | ||
| 984 | */ | ||
| 985 | int osd_req_write_sg(struct osd_request *or, | ||
| 986 | const struct osd_obj_id *obj, struct bio *bio, | ||
| 987 | const struct osd_sg_entry *sglist, unsigned numentries) | ||
| 988 | { | ||
| 989 | u64 len; | ||
| 990 | int ret = _add_sg_continuation_descriptor(or, sglist, numentries, &len); | ||
| 991 | |||
| 992 | if (ret) | ||
| 993 | return ret; | ||
| 994 | osd_req_write(or, obj, 0, bio, len); | ||
| 995 | |||
| 996 | return 0; | ||
| 997 | } | ||
| 998 | EXPORT_SYMBOL(osd_req_write_sg); | ||
| 999 | |||
| 1000 | /* osd_req_read_sg: Read multiple extents of an object into @bio | ||
| 1001 | * See osd_req_write_sg | ||
| 1002 | */ | ||
| 1003 | int osd_req_read_sg(struct osd_request *or, | ||
| 1004 | const struct osd_obj_id *obj, struct bio *bio, | ||
| 1005 | const struct osd_sg_entry *sglist, unsigned numentries) | ||
| 1006 | { | ||
| 1007 | u64 len; | ||
| 1008 | int ret = _add_sg_continuation_descriptor(or, sglist, numentries, &len); | ||
| 1009 | |||
| 1010 | if (ret) | ||
| 1011 | return ret; | ||
| 1012 | osd_req_read(or, obj, 0, bio, len); | ||
| 1013 | |||
| 1014 | return 0; | ||
| 1015 | } | ||
| 1016 | EXPORT_SYMBOL(osd_req_read_sg); | ||
| 1017 | |||
| 889 | void osd_req_get_attributes(struct osd_request *or, | 1018 | void osd_req_get_attributes(struct osd_request *or, |
| 890 | const struct osd_obj_id *obj) | 1019 | const struct osd_obj_id *obj) |
| 891 | { | 1020 | { |
| @@ -1281,7 +1410,8 @@ static inline void osd_sec_parms_set_in_offset(bool is_v1, | |||
| 1281 | } | 1410 | } |
| 1282 | 1411 | ||
| 1283 | static int _osd_req_finalize_data_integrity(struct osd_request *or, | 1412 | static int _osd_req_finalize_data_integrity(struct osd_request *or, |
| 1284 | bool has_in, bool has_out, u64 out_data_bytes, const u8 *cap_key) | 1413 | bool has_in, bool has_out, struct bio *out_data_bio, u64 out_data_bytes, |
| 1414 | const u8 *cap_key) | ||
| 1285 | { | 1415 | { |
| 1286 | struct osd_security_parameters *sec_parms = _osd_req_sec_params(or); | 1416 | struct osd_security_parameters *sec_parms = _osd_req_sec_params(or); |
| 1287 | int ret; | 1417 | int ret; |
| @@ -1312,7 +1442,7 @@ static int _osd_req_finalize_data_integrity(struct osd_request *or, | |||
| 1312 | or->out.last_seg = NULL; | 1442 | or->out.last_seg = NULL; |
| 1313 | 1443 | ||
| 1314 | /* they are now all chained to request sign them all together */ | 1444 | /* they are now all chained to request sign them all together */ |
| 1315 | osd_sec_sign_data(&or->out_data_integ, or->out.req->bio, | 1445 | osd_sec_sign_data(&or->out_data_integ, out_data_bio, |
| 1316 | cap_key); | 1446 | cap_key); |
| 1317 | } | 1447 | } |
| 1318 | 1448 | ||
| @@ -1408,6 +1538,8 @@ int osd_finalize_request(struct osd_request *or, | |||
| 1408 | { | 1538 | { |
| 1409 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | 1539 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); |
| 1410 | bool has_in, has_out; | 1540 | bool has_in, has_out; |
| 1541 | /* Save for data_integrity without the cdb_continuation */ | ||
| 1542 | struct bio *out_data_bio = or->out.bio; | ||
| 1411 | u64 out_data_bytes = or->out.total_bytes; | 1543 | u64 out_data_bytes = or->out.total_bytes; |
| 1412 | int ret; | 1544 | int ret; |
| 1413 | 1545 | ||
| @@ -1423,9 +1555,14 @@ int osd_finalize_request(struct osd_request *or, | |||
| 1423 | osd_set_caps(&or->cdb, cap); | 1555 | osd_set_caps(&or->cdb, cap); |
| 1424 | 1556 | ||
| 1425 | has_in = or->in.bio || or->get_attr.total_bytes; | 1557 | has_in = or->in.bio || or->get_attr.total_bytes; |
| 1426 | has_out = or->out.bio || or->set_attr.total_bytes || | 1558 | has_out = or->out.bio || or->cdb_cont.total_bytes || |
| 1427 | or->enc_get_attr.total_bytes; | 1559 | or->set_attr.total_bytes || or->enc_get_attr.total_bytes; |
| 1428 | 1560 | ||
| 1561 | ret = _osd_req_finalize_cdb_cont(or, cap_key); | ||
| 1562 | if (ret) { | ||
| 1563 | OSD_DEBUG("_osd_req_finalize_cdb_cont failed\n"); | ||
| 1564 | return ret; | ||
| 1565 | } | ||
| 1429 | ret = _init_blk_request(or, has_in, has_out); | 1566 | ret = _init_blk_request(or, has_in, has_out); |
| 1430 | if (ret) { | 1567 | if (ret) { |
| 1431 | OSD_DEBUG("_init_blk_request failed\n"); | 1568 | OSD_DEBUG("_init_blk_request failed\n"); |
| @@ -1463,7 +1600,8 @@ int osd_finalize_request(struct osd_request *or, | |||
| 1463 | } | 1600 | } |
| 1464 | 1601 | ||
| 1465 | ret = _osd_req_finalize_data_integrity(or, has_in, has_out, | 1602 | ret = _osd_req_finalize_data_integrity(or, has_in, has_out, |
| 1466 | out_data_bytes, cap_key); | 1603 | out_data_bio, out_data_bytes, |
| 1604 | cap_key); | ||
| 1467 | if (ret) | 1605 | if (ret) |
| 1468 | return ret; | 1606 | return ret; |
| 1469 | 1607 | ||
diff --git a/include/scsi/osd_initiator.h b/include/scsi/osd_initiator.h index a8f370126632..169a5dcda096 100644 --- a/include/scsi/osd_initiator.h +++ b/include/scsi/osd_initiator.h | |||
| @@ -137,7 +137,7 @@ struct osd_request { | |||
| 137 | void *buff; | 137 | void *buff; |
| 138 | unsigned alloc_size; /* 0 here means: don't call kfree */ | 138 | unsigned alloc_size; /* 0 here means: don't call kfree */ |
| 139 | unsigned total_bytes; | 139 | unsigned total_bytes; |
| 140 | } set_attr, enc_get_attr, get_attr; | 140 | } cdb_cont, set_attr, enc_get_attr, get_attr; |
| 141 | 141 | ||
| 142 | struct _osd_io_info { | 142 | struct _osd_io_info { |
| 143 | struct bio *bio; | 143 | struct bio *bio; |
| @@ -448,6 +448,13 @@ void osd_req_read(struct osd_request *or, | |||
| 448 | int osd_req_read_kern(struct osd_request *or, | 448 | int osd_req_read_kern(struct osd_request *or, |
| 449 | const struct osd_obj_id *obj, u64 offset, void *buff, u64 len); | 449 | const struct osd_obj_id *obj, u64 offset, void *buff, u64 len); |
| 450 | 450 | ||
| 451 | /* Scatter/Gather write/read commands */ | ||
| 452 | int osd_req_write_sg(struct osd_request *or, | ||
| 453 | const struct osd_obj_id *obj, struct bio *bio, | ||
| 454 | const struct osd_sg_entry *sglist, unsigned numentries); | ||
| 455 | int osd_req_read_sg(struct osd_request *or, | ||
| 456 | const struct osd_obj_id *obj, struct bio *bio, | ||
| 457 | const struct osd_sg_entry *sglist, unsigned numentries); | ||
| 451 | /* | 458 | /* |
| 452 | * Root/Partition/Collection/Object Attributes commands | 459 | * Root/Partition/Collection/Object Attributes commands |
| 453 | */ | 460 | */ |
diff --git a/include/scsi/osd_protocol.h b/include/scsi/osd_protocol.h index 685661283540..a6026da25f3e 100644 --- a/include/scsi/osd_protocol.h +++ b/include/scsi/osd_protocol.h | |||
| @@ -631,4 +631,46 @@ static inline void osd_sec_set_caps(struct osd_capability_head *cap, | |||
| 631 | put_unaligned_le16(bit_mask, &cap->permissions_bit_mask); | 631 | put_unaligned_le16(bit_mask, &cap->permissions_bit_mask); |
| 632 | } | 632 | } |
| 633 | 633 | ||
| 634 | /* osd2r05a sec 5.3: CDB continuation segment formats */ | ||
| 635 | enum osd_continuation_segment_format { | ||
| 636 | CDB_CONTINUATION_FORMAT_V2 = 0x01, | ||
| 637 | }; | ||
| 638 | |||
| 639 | struct osd_continuation_segment_header { | ||
| 640 | u8 format; | ||
| 641 | u8 reserved1; | ||
| 642 | __be16 service_action; | ||
| 643 | __be32 reserved2; | ||
| 644 | u8 integrity_check[OSDv2_CRYPTO_KEYID_SIZE]; | ||
| 645 | } __packed; | ||
| 646 | |||
| 647 | /* osd2r05a sec 5.4.1: CDB continuation descriptors */ | ||
| 648 | enum osd_continuation_descriptor_type { | ||
| 649 | NO_MORE_DESCRIPTORS = 0x0000, | ||
| 650 | SCATTER_GATHER_LIST = 0x0001, | ||
| 651 | QUERY_LIST = 0x0002, | ||
| 652 | USER_OBJECT = 0x0003, | ||
| 653 | COPY_USER_OBJECT_SOURCE = 0x0101, | ||
| 654 | EXTENSION_CAPABILITIES = 0xFFEE | ||
| 655 | }; | ||
| 656 | |||
| 657 | struct osd_continuation_descriptor_header { | ||
| 658 | __be16 type; | ||
| 659 | u8 reserved; | ||
| 660 | u8 pad_length; | ||
| 661 | __be32 length; | ||
| 662 | } __packed; | ||
| 663 | |||
| 664 | |||
| 665 | /* osd2r05a sec 5.4.2: Scatter/gather list */ | ||
| 666 | struct osd_sg_list_entry { | ||
| 667 | __be64 offset; | ||
| 668 | __be64 len; | ||
| 669 | }; | ||
| 670 | |||
| 671 | struct osd_sg_continuation_descriptor { | ||
| 672 | struct osd_continuation_descriptor_header hdr; | ||
| 673 | struct osd_sg_list_entry entries[]; | ||
| 674 | }; | ||
| 675 | |||
| 634 | #endif /* ndef __OSD_PROTOCOL_H__ */ | 676 | #endif /* ndef __OSD_PROTOCOL_H__ */ |
diff --git a/include/scsi/osd_types.h b/include/scsi/osd_types.h index 3f5e88cc75c0..bd0be7ed4bcf 100644 --- a/include/scsi/osd_types.h +++ b/include/scsi/osd_types.h | |||
| @@ -37,4 +37,9 @@ struct osd_attr { | |||
| 37 | void *val_ptr; /* in network order */ | 37 | void *val_ptr; /* in network order */ |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | struct osd_sg_entry { | ||
| 41 | u64 offset; | ||
| 42 | u64 len; | ||
| 43 | }; | ||
| 44 | |||
| 40 | #endif /* ndef __OSD_TYPES_H__ */ | 45 | #endif /* ndef __OSD_TYPES_H__ */ |
