diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/osd/osd_initiator.c | 148 |
1 files changed, 143 insertions, 5 deletions
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index acbdcb670ac..f5b5735f76d 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 | ||