aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBoaz Harrosh <bharrosh@panasas.com>2010-10-19 08:22:21 -0400
committerJames Bottomley <James.Bottomley@suse.de>2010-10-26 11:42:34 -0400
commite96e72c45a1e78e9266dd70113b851395a440ef3 (patch)
tree92c60e5d7edc9d1129ba2e4ab38783d2727c7bc7 /drivers
parentc4df46c49d8677158c7fb070a08e0d386c80205f (diff)
[SCSI] libosd: Support for scatter gather write/read commands
This patch adds the Scatter-Gather (sg) API to libosd. Scatter-gather enables a write/read of multiple none-contiguous areas of an object, in a single call. The extents may overlap and/or be in any order. The Scatter-Gather list is sent to the target in what is called a "cdb continuation segment". This is yet another possible segment in the osd-out-buffer. It is unlike all other segments in that it sits before the actual "data" segment (which until now was always first), and that it is signed by itself and not part of the data buffer. This is because the cdb-continuation-segment is considered a spill-over of the CDB data, and is therefor signed under OSD_SEC_CAPKEY and higher. TODO: A new osd_finalize_request_ex version should be supplied so the @caps received on the network also contains a size parameter and can be spilled over into the "cdb continuation segment". Thanks to John Chandy <john.chandy@uconn.edu> for the original code, and investigations. And the implementation of SG support in the osd-target. Original-coded-by: John Chandy <john.chandy@uconn.edu> Signed-off-by: Boaz Harrosh <bharrosh@panasas.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/osd/osd_initiator.c148
1 files changed, 143 insertions, 5 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
552static 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
551static int _alloc_set_attr_list(struct osd_request *or, 558static 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}
887EXPORT_SYMBOL(osd_req_read_kern); 894EXPORT_SYMBOL(osd_req_read_kern);
888 895
896static 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
937static 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 */
985int 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}
998EXPORT_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 */
1003int 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}
1016EXPORT_SYMBOL(osd_req_read_sg);
1017
889void osd_req_get_attributes(struct osd_request *or, 1018void 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
1283static int _osd_req_finalize_data_integrity(struct osd_request *or, 1412static 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