diff options
Diffstat (limited to 'drivers/scsi/osd/osd_initiator.c')
-rw-r--r-- | drivers/scsi/osd/osd_initiator.c | 244 |
1 files changed, 229 insertions, 15 deletions
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index e88bbdde49c5..0433ea6f27c9 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c | |||
@@ -452,10 +452,6 @@ void osd_end_request(struct osd_request *or) | |||
452 | { | 452 | { |
453 | struct request *rq = or->request; | 453 | struct request *rq = or->request; |
454 | 454 | ||
455 | _osd_free_seg(or, &or->set_attr); | ||
456 | _osd_free_seg(or, &or->enc_get_attr); | ||
457 | _osd_free_seg(or, &or->get_attr); | ||
458 | |||
459 | if (rq) { | 455 | if (rq) { |
460 | if (rq->next_rq) { | 456 | if (rq->next_rq) { |
461 | _put_request(rq->next_rq); | 457 | _put_request(rq->next_rq); |
@@ -464,6 +460,12 @@ void osd_end_request(struct osd_request *or) | |||
464 | 460 | ||
465 | _put_request(rq); | 461 | _put_request(rq); |
466 | } | 462 | } |
463 | |||
464 | _osd_free_seg(or, &or->get_attr); | ||
465 | _osd_free_seg(or, &or->enc_get_attr); | ||
466 | _osd_free_seg(or, &or->set_attr); | ||
467 | _osd_free_seg(or, &or->cdb_cont); | ||
468 | |||
467 | _osd_request_free(or); | 469 | _osd_request_free(or); |
468 | } | 470 | } |
469 | EXPORT_SYMBOL(osd_end_request); | 471 | EXPORT_SYMBOL(osd_end_request); |
@@ -547,6 +549,12 @@ static int _osd_realloc_seg(struct osd_request *or, | |||
547 | return 0; | 549 | return 0; |
548 | } | 550 | } |
549 | 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 | |||
550 | static int _alloc_set_attr_list(struct osd_request *or, | 558 | static int _alloc_set_attr_list(struct osd_request *or, |
551 | const struct osd_attr *oa, unsigned nelem, unsigned add_bytes) | 559 | const struct osd_attr *oa, unsigned nelem, unsigned add_bytes) |
552 | { | 560 | { |
@@ -885,6 +893,199 @@ int osd_req_read_kern(struct osd_request *or, | |||
885 | } | 893 | } |
886 | EXPORT_SYMBOL(osd_req_read_kern); | 894 | EXPORT_SYMBOL(osd_req_read_kern); |
887 | 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 | |||
1018 | /* SG-list write/read Kern API | ||
1019 | * | ||
1020 | * osd_req_{write,read}_sg_kern takes an array of @buff pointers and an array | ||
1021 | * of sg_entries. @numentries indicates how many pointers and sg_entries there | ||
1022 | * are. By requiring an array of buff pointers. This allows a caller to do a | ||
1023 | * single write/read and scatter into multiple buffers. | ||
1024 | * NOTE: Each buffer + len should not cross a page boundary. | ||
1025 | */ | ||
1026 | static struct bio *_create_sg_bios(struct osd_request *or, | ||
1027 | void **buff, const struct osd_sg_entry *sglist, unsigned numentries) | ||
1028 | { | ||
1029 | struct request_queue *q = osd_request_queue(or->osd_dev); | ||
1030 | struct bio *bio; | ||
1031 | unsigned i; | ||
1032 | |||
1033 | bio = bio_kmalloc(GFP_KERNEL, numentries); | ||
1034 | if (unlikely(!bio)) { | ||
1035 | OSD_DEBUG("Faild to allocate BIO size=%u\n", numentries); | ||
1036 | return ERR_PTR(-ENOMEM); | ||
1037 | } | ||
1038 | |||
1039 | for (i = 0; i < numentries; i++) { | ||
1040 | unsigned offset = offset_in_page(buff[i]); | ||
1041 | struct page *page = virt_to_page(buff[i]); | ||
1042 | unsigned len = sglist[i].len; | ||
1043 | unsigned added_len; | ||
1044 | |||
1045 | BUG_ON(offset + len > PAGE_SIZE); | ||
1046 | added_len = bio_add_pc_page(q, bio, page, len, offset); | ||
1047 | if (unlikely(len != added_len)) { | ||
1048 | OSD_DEBUG("bio_add_pc_page len(%d) != added_len(%d)\n", | ||
1049 | len, added_len); | ||
1050 | bio_put(bio); | ||
1051 | return ERR_PTR(-ENOMEM); | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | return bio; | ||
1056 | } | ||
1057 | |||
1058 | int osd_req_write_sg_kern(struct osd_request *or, | ||
1059 | const struct osd_obj_id *obj, void **buff, | ||
1060 | const struct osd_sg_entry *sglist, unsigned numentries) | ||
1061 | { | ||
1062 | struct bio *bio = _create_sg_bios(or, buff, sglist, numentries); | ||
1063 | if (IS_ERR(bio)) | ||
1064 | return PTR_ERR(bio); | ||
1065 | |||
1066 | bio->bi_rw |= REQ_WRITE; | ||
1067 | osd_req_write_sg(or, obj, bio, sglist, numentries); | ||
1068 | |||
1069 | return 0; | ||
1070 | } | ||
1071 | EXPORT_SYMBOL(osd_req_write_sg_kern); | ||
1072 | |||
1073 | int osd_req_read_sg_kern(struct osd_request *or, | ||
1074 | const struct osd_obj_id *obj, void **buff, | ||
1075 | const struct osd_sg_entry *sglist, unsigned numentries) | ||
1076 | { | ||
1077 | struct bio *bio = _create_sg_bios(or, buff, sglist, numentries); | ||
1078 | if (IS_ERR(bio)) | ||
1079 | return PTR_ERR(bio); | ||
1080 | |||
1081 | osd_req_read_sg(or, obj, bio, sglist, numentries); | ||
1082 | |||
1083 | return 0; | ||
1084 | } | ||
1085 | EXPORT_SYMBOL(osd_req_read_sg_kern); | ||
1086 | |||
1087 | |||
1088 | |||
888 | void osd_req_get_attributes(struct osd_request *or, | 1089 | void osd_req_get_attributes(struct osd_request *or, |
889 | const struct osd_obj_id *obj) | 1090 | const struct osd_obj_id *obj) |
890 | { | 1091 | { |
@@ -1218,17 +1419,18 @@ int osd_req_add_get_attr_page(struct osd_request *or, | |||
1218 | or->get_attr.buff = attar_page; | 1419 | or->get_attr.buff = attar_page; |
1219 | or->get_attr.total_bytes = max_page_len; | 1420 | or->get_attr.total_bytes = max_page_len; |
1220 | 1421 | ||
1221 | or->set_attr.buff = set_one_attr->val_ptr; | ||
1222 | or->set_attr.total_bytes = set_one_attr->len; | ||
1223 | |||
1224 | cdbh->attrs_page.get_attr_page = cpu_to_be32(page_id); | 1422 | cdbh->attrs_page.get_attr_page = cpu_to_be32(page_id); |
1225 | cdbh->attrs_page.get_attr_alloc_length = cpu_to_be32(max_page_len); | 1423 | cdbh->attrs_page.get_attr_alloc_length = cpu_to_be32(max_page_len); |
1226 | /* ocdb->attrs_page.get_attr_offset; */ | 1424 | |
1425 | if (!set_one_attr || !set_one_attr->attr_page) | ||
1426 | return 0; /* The set is optional */ | ||
1427 | |||
1428 | or->set_attr.buff = set_one_attr->val_ptr; | ||
1429 | or->set_attr.total_bytes = set_one_attr->len; | ||
1227 | 1430 | ||
1228 | cdbh->attrs_page.set_attr_page = cpu_to_be32(set_one_attr->attr_page); | 1431 | cdbh->attrs_page.set_attr_page = cpu_to_be32(set_one_attr->attr_page); |
1229 | cdbh->attrs_page.set_attr_id = cpu_to_be32(set_one_attr->attr_id); | 1432 | cdbh->attrs_page.set_attr_id = cpu_to_be32(set_one_attr->attr_id); |
1230 | cdbh->attrs_page.set_attr_length = cpu_to_be32(set_one_attr->len); | 1433 | cdbh->attrs_page.set_attr_length = cpu_to_be32(set_one_attr->len); |
1231 | /* ocdb->attrs_page.set_attr_offset; */ | ||
1232 | return 0; | 1434 | return 0; |
1233 | } | 1435 | } |
1234 | EXPORT_SYMBOL(osd_req_add_get_attr_page); | 1436 | EXPORT_SYMBOL(osd_req_add_get_attr_page); |
@@ -1248,11 +1450,14 @@ static int _osd_req_finalize_attr_page(struct osd_request *or) | |||
1248 | if (ret) | 1450 | if (ret) |
1249 | return ret; | 1451 | return ret; |
1250 | 1452 | ||
1453 | if (or->set_attr.total_bytes == 0) | ||
1454 | return 0; | ||
1455 | |||
1251 | /* set one value */ | 1456 | /* set one value */ |
1252 | cdbh->attrs_page.set_attr_offset = | 1457 | cdbh->attrs_page.set_attr_offset = |
1253 | osd_req_encode_offset(or, or->out.total_bytes, &out_padding); | 1458 | osd_req_encode_offset(or, or->out.total_bytes, &out_padding); |
1254 | 1459 | ||
1255 | ret = _req_append_segment(or, out_padding, &or->enc_get_attr, NULL, | 1460 | ret = _req_append_segment(or, out_padding, &or->set_attr, NULL, |
1256 | &or->out); | 1461 | &or->out); |
1257 | return ret; | 1462 | return ret; |
1258 | } | 1463 | } |
@@ -1276,7 +1481,8 @@ static inline void osd_sec_parms_set_in_offset(bool is_v1, | |||
1276 | } | 1481 | } |
1277 | 1482 | ||
1278 | static int _osd_req_finalize_data_integrity(struct osd_request *or, | 1483 | static int _osd_req_finalize_data_integrity(struct osd_request *or, |
1279 | bool has_in, bool has_out, u64 out_data_bytes, const u8 *cap_key) | 1484 | bool has_in, bool has_out, struct bio *out_data_bio, u64 out_data_bytes, |
1485 | const u8 *cap_key) | ||
1280 | { | 1486 | { |
1281 | struct osd_security_parameters *sec_parms = _osd_req_sec_params(or); | 1487 | struct osd_security_parameters *sec_parms = _osd_req_sec_params(or); |
1282 | int ret; | 1488 | int ret; |
@@ -1307,7 +1513,7 @@ static int _osd_req_finalize_data_integrity(struct osd_request *or, | |||
1307 | or->out.last_seg = NULL; | 1513 | or->out.last_seg = NULL; |
1308 | 1514 | ||
1309 | /* they are now all chained to request sign them all together */ | 1515 | /* they are now all chained to request sign them all together */ |
1310 | osd_sec_sign_data(&or->out_data_integ, or->out.req->bio, | 1516 | osd_sec_sign_data(&or->out_data_integ, out_data_bio, |
1311 | cap_key); | 1517 | cap_key); |
1312 | } | 1518 | } |
1313 | 1519 | ||
@@ -1403,6 +1609,8 @@ int osd_finalize_request(struct osd_request *or, | |||
1403 | { | 1609 | { |
1404 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | 1610 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); |
1405 | bool has_in, has_out; | 1611 | bool has_in, has_out; |
1612 | /* Save for data_integrity without the cdb_continuation */ | ||
1613 | struct bio *out_data_bio = or->out.bio; | ||
1406 | u64 out_data_bytes = or->out.total_bytes; | 1614 | u64 out_data_bytes = or->out.total_bytes; |
1407 | int ret; | 1615 | int ret; |
1408 | 1616 | ||
@@ -1418,9 +1626,14 @@ int osd_finalize_request(struct osd_request *or, | |||
1418 | osd_set_caps(&or->cdb, cap); | 1626 | osd_set_caps(&or->cdb, cap); |
1419 | 1627 | ||
1420 | has_in = or->in.bio || or->get_attr.total_bytes; | 1628 | has_in = or->in.bio || or->get_attr.total_bytes; |
1421 | has_out = or->out.bio || or->set_attr.total_bytes || | 1629 | has_out = or->out.bio || or->cdb_cont.total_bytes || |
1422 | or->enc_get_attr.total_bytes; | 1630 | or->set_attr.total_bytes || or->enc_get_attr.total_bytes; |
1423 | 1631 | ||
1632 | ret = _osd_req_finalize_cdb_cont(or, cap_key); | ||
1633 | if (ret) { | ||
1634 | OSD_DEBUG("_osd_req_finalize_cdb_cont failed\n"); | ||
1635 | return ret; | ||
1636 | } | ||
1424 | ret = _init_blk_request(or, has_in, has_out); | 1637 | ret = _init_blk_request(or, has_in, has_out); |
1425 | if (ret) { | 1638 | if (ret) { |
1426 | OSD_DEBUG("_init_blk_request failed\n"); | 1639 | OSD_DEBUG("_init_blk_request failed\n"); |
@@ -1458,7 +1671,8 @@ int osd_finalize_request(struct osd_request *or, | |||
1458 | } | 1671 | } |
1459 | 1672 | ||
1460 | ret = _osd_req_finalize_data_integrity(or, has_in, has_out, | 1673 | ret = _osd_req_finalize_data_integrity(or, has_in, has_out, |
1461 | out_data_bytes, cap_key); | 1674 | out_data_bio, out_data_bytes, |
1675 | cap_key); | ||
1462 | if (ret) | 1676 | if (ret) |
1463 | return ret; | 1677 | return ret; |
1464 | 1678 | ||