diff --git a/osd-target/cdb.c b/osd-target/cdb.c index 2a46e18..f29db9d 100644 --- a/osd-target/cdb.c +++ b/osd-target/cdb.c @@ -39,6 +39,7 @@ struct cdb_continuation_descriptor { uint16_t type; uint32_t length; union { + struct sg_list sglist; const void *desc_specific_hdr; }; }; @@ -793,8 +794,30 @@ static int cdb_read(struct command *cmd, uint32_t cdb_cont_len) uint64_t len = get_ntohll(&cmd->cdb[32]); uint64_t offset = get_ntohll(&cmd->cdb[40]); - ret = osd_read(cmd->osd, pid, oid, len, offset, cmd->indata, cmd->outdata, - &cmd->used_outlen, cdb_cont_len, cmd->sense, ddt); + struct sg_list sgl, *sglist; + const uint8_t *indata; + + /* osd2r04 6.27 - at most one sg list descriptor + and no other descriptors */ + if (cmd->cont.num_descriptors > 1 || + (cmd->cont.num_descriptors == 1 && + cmd->cont.descriptors[0].type != SCATTER_GATHER_LIST)) { + return sense_basic_build(cmd->sense, OSD_SSK_ILLEGAL_REQUEST, + OSD_ASC_INVALID_FIELD_IN_CDB, pid, oid); + } + + if (cmd->cont.num_descriptors == 1) { + sglist = &cmd->cont.descriptors[0].sglist; + indata = cmd->indata + cdb_cont_len; + ddt = DDT_SGL; + } else { + indata = cmd->indata; + sglist = NULL; + ddt = DDT_CONTIG; + } + + ret = osd_read(cmd->osd, pid, oid, len, offset, indata, cmd->outdata, + &cmd->used_outlen, sglist, cmd->sense, ddt); if (ret) { /* only tolerate recovered error, return for others */ if (!sense_test_type(cmd->sense, OSD_SSK_RECOVERED_ERROR, @@ -1269,6 +1292,111 @@ static int cdb_gen_cas(struct command *cmd, int osd_cmd, uint32_t cdb_cont_len) return ret; } +static int parse_cdb_continuation_segment(struct command *cmd, + uint32_t cdb_cont_len, + uint64_t pid, uint64_t oid) +{ + const uint8_t *cdb_cont = cmd->indata; + uint8_t cont_format = cdb_cont[0]; + uint16_t cont_action = get_ntohs(&cdb_cont[2]); + uint32_t used_bytes = 0; + + if (((cdb_cont_len % 8) != 0) || (cdb_cont_len < 48)) { + /* TODO: need to check if cdb_cont_len is greater than the value + * in the maximum CDB continuation length attribute in the root + * information attributes page (7.1.3.8) + */ + goto out_cdb_err; + } + + if (cont_action != cmd->action) { + goto out_cdb_err; + } + + /* continuation format 1 is the only format defined in OSDr204 */ + if (cont_format != 1) { + goto out_cdb_err; + } + + /* XXX need to check continuation integrity check value */ + + /* start parsing the descriptors */ + + /* skip over continuation header */ + cdb_cont += 40; + while (cdb_cont < cmd->indata + cdb_cont_len) { + struct cdb_continuation *cont = &cmd->cont; + struct cdb_continuation_descriptor *desc; + const struct cdb_continuation_descriptor_header *desc_hdr = + (typeof(desc_hdr))cdb_cont; + uint16_t type = get_ntohs(&desc_hdr->type); + uint32_t length = get_ntohl(&desc_hdr->length); + uint8_t pad_length = desc_hdr->pad_length & 0x7; + + uint32_t desc_len = length + pad_length; + + if (cont->num_descriptors % 8 == 0) { + size_t newsize = (cont->num_descriptors+8)* + sizeof(struct cdb_continuation_descriptor); + cont->descriptors = realloc(cont->descriptors, newsize); + } + desc = &cont->descriptors[cont->num_descriptors++]; + desc->type = type; + desc->length = length; + + if (desc_len%8 != 0) { + /* TODO: need to check if descriptor length is greater + * than the value of the supported CDB continuation + * descriptor type information attributes page (7.1.3.8) + */ + goto out_cdb_err; + } + + desc_len += sizeof(*desc_hdr); + if ((cdb_cont + desc_len) > (cmd->indata + cdb_cont_len)) { + /* XXX The spec doesnt say what to do if the + length of this descriptor goes past the end + of the continuation. For now, we'll just + return an error. */ + goto out_cdb_err; + } + + switch (type) { + + case NO_MORE_DESCRIPTORS: { + /* last descriptor */ + break; + } + + case SCATTER_GATHER_LIST: { + if (pad_length != 0) { + /* osd2r05 5.4.2 - pad length must be 0 */ + goto out_cdb_err; + } + + desc->sglist.num_entries = + length/sizeof(struct sg_list_entry); + /* entries are at the end of the header */ + desc->sglist.entries = + (const struct sg_list_entry *)(desc_hdr+1); + break; + } + + default: + goto out_cdb_err; + } + + /* move pointer to next descriptor */ + cdb_cont += desc_len; + } + + return OSD_OK; + + out_cdb_err: + return sense_basic_build(cmd->sense, OSD_SSK_ILLEGAL_REQUEST, + OSD_ASC_INVALID_FIELD_IN_CDB, pid, oid); +} + /* * Compare, and complain if iscsi did not deliver enough bytes to * satisfy the WRITE. It could deliver more for padding, so don't @@ -1299,18 +1427,15 @@ static void exec_service_action(struct command *cmd) // osd_debug("%s: start 0x%04x", __func__, cmd->action); if (cdb_cont_len != 0) { - if (((cdb_cont_len % 8) != 0) || (cdb_cont_len > 48)) { - /* need to check if cdb_cont_len is greater than the value in the - maximum CDB continuation length attribute in the root information - attributes page (7.1.3.8)*/ - ret = sense_basic_build(cmd->sense, OSD_SSK_ILLEGAL_REQUEST, - OSD_ASC_INVALID_FIELD_IN_CDB, pid, oid); - } + ret = parse_cdb_continuation_segment(cmd, cdb_cont_len, + pid, oid); + if (ret != OSD_OK) + goto out_exec; } switch (cmd->action) { case OSD_APPEND: { - uint8_t ddt = cdb[10]; + uint8_t ddt; uint64_t pid = get_ntohll(&cdb[16]); uint64_t oid = get_ntohll(&cdb[24]); uint64_t len = get_ntohll(&cdb[32]); @@ -1319,6 +1444,11 @@ static void exec_service_action(struct command *cmd) if (ret) break; + /* initial version from OSC supported scatter/gather + on APPEND. However, it is not supported in + official osd2 spec. */ + ddt = DDT_CONTIG; + ret = osd_append(osd, pid, oid, len, cmd->indata, cdb_cont_len, sense, ddt); if (ret) break; @@ -1351,17 +1481,41 @@ static void exec_service_action(struct command *cmd) break; } case OSD_CREATE_AND_WRITE: { - uint8_t ddt = cdb[10]; + uint8_t ddt; uint64_t pid = get_ntohll(&cdb[16]); uint64_t requested_oid = get_ntohll(&cdb[24]); uint64_t len = get_ntohll(&cdb[32]); uint64_t offset = get_ntohll(&cdb[40]); + struct sg_list sgl, *sglist; + const uint8_t *indata; ret = verify_enough_input_data(cmd, len); if (ret) break; + + /* osd2r04 6.6 - at most one sg list descriptor + and no other descriptors */ + if (cmd->cont.num_descriptors > 1 || + (cmd->cont.num_descriptors == 1 && + cmd->cont.descriptors[0].type != SCATTER_GATHER_LIST)) { + sense_basic_build(cmd->sense, OSD_SSK_ILLEGAL_REQUEST, + OSD_ASC_INVALID_FIELD_IN_CDB, pid, + oid); + } + + if (cmd->cont.num_descriptors == 1) { + sglist = &cmd->cont.descriptors[0].sglist; + indata = cmd->indata + cdb_cont_len; + ddt = DDT_SGL; + } else { + sglist = NULL; + indata = cmd->indata; + ddt = DDT_CONTIG; + } + ret = osd_create_and_write(osd, pid, requested_oid, len, - offset, cmd->indata, cdb_cont_len, sense, ddt); + offset, indata, cdb_cont_len, sglist, + sense, ddt); break; } case OSD_CREATE_COLLECTION: { @@ -1569,12 +1723,35 @@ static void exec_service_action(struct command *cmd) uint64_t oid = get_ntohll(&cdb[24]); uint64_t len = get_ntohll(&cdb[32]); uint64_t offset = get_ntohll(&cdb[40]); - uint8_t ddt = cdb[10]; + uint8_t ddt; + struct sg_list sgl, *sglist; + const uint8_t *indata; ret = verify_enough_input_data(cmd, len); if (ret) break; - ret = osd_write(osd, pid, oid, len, offset, cmd->indata, - cdb_cont_len, sense, ddt); + + /* osd2r04 6.40 - at most one sg list descriptor + and no other descriptors */ + if (cmd->cont.num_descriptors > 1 || + (cmd->cont.num_descriptors == 1 && + cmd->cont.descriptors[0].type != SCATTER_GATHER_LIST)) { + sense_basic_build(cmd->sense, OSD_SSK_ILLEGAL_REQUEST, + OSD_ASC_INVALID_FIELD_IN_CDB, pid, + oid); + } + + if (cmd->cont.num_descriptors == 1) { + sglist = &cmd->cont.descriptors[0].sglist; + indata = cmd->indata + cdb_cont_len; + ddt = DDT_SGL; + } else { + sglist = NULL; + indata = cmd->indata; + ddt = DDT_CONTIG; + } + + ret = osd_write(osd, pid, oid, len, offset, indata, + sglist, sense, ddt); if (ret) break; @@ -1604,6 +1781,7 @@ static void exec_service_action(struct command *cmd) if (ret) osd_debug("%s: done 0x%04x => %d", __func__, cmd->action, ret); + out_exec: /* * All the above return bytes of sense data put into sense[] * in case of an error. But sometimes they also return good @@ -1695,6 +1873,10 @@ int osdemu_cmd_submit(struct osd_device *osd, uint8_t *cdb, .get_used_outlen = 0, .sense = { 0 }, /* maybe no need to initialize to zero */ .senselen = 0, + .cont = { + .num_descriptors = 0, + .descriptors = NULL, + }, }; /* check cdb opcode and length */ @@ -1774,6 +1956,10 @@ int osdemu_cmd_submit(struct osd_device *osd, uint8_t *cdb, *data_out_len = 0; out: + if (cmd.cont.descriptors != NULL) { + free(cmd.cont.descriptors); + } + if (cmd.senselen == 0) { return SAM_STAT_GOOD; } else { diff --git a/osd-target/osd-types.h b/osd-target/osd-types.h index 47ed91b..5045325 100644 --- a/osd-target/osd-types.h +++ b/osd-target/osd-types.h @@ -221,6 +221,16 @@ enum { /* CDB continuation descriptors */ +struct sg_list_entry { + uint64_t offset; + uint64_t bytes_to_transfer; +}; + +struct sg_list { + uint64_t num_entries; + const struct sg_list_entry *entries; +}; + struct cdb_continuation_descriptor_header { uint16_t type; uint8_t reserved; diff --git a/osd-target/osd.c b/osd-target/osd.c index f476df1..cf52c12 100644 --- a/osd-target/osd.c +++ b/osd-target/osd.c @@ -1332,12 +1332,12 @@ int osd_append(struct osd_device *osd, uint64_t pid, uint64_t oid, switch(ddt) { case DDT_CONTIG: { - return contig_append(osd, pid, oid, len, appenddata, - sense); + return contig_append(osd, pid, oid, len, + appenddata+cdb_cont_len, sense); } case DDT_SGL: { - return sgl_append(osd, pid, oid, len, appenddata, - sense); + return sgl_append(osd, pid, oid, len, + appenddata+cdb_cont_len, sense); } case DDT_VEC: { return vec_append(osd, pid, oid, len, appenddata, @@ -1674,7 +1674,9 @@ int osd_create(struct osd_device *osd, uint64_t pid, uint64_t requested_oid, int osd_create_and_write(struct osd_device *osd, uint64_t pid, uint64_t oid, uint64_t len, uint64_t offset, - const uint8_t *data, uint32_t cdb_cont_len, uint8_t *sense, uint8_t ddt) + const uint8_t *data, uint32_t cdb_cont_len, + const struct sg_list *sglist, + uint8_t *sense, uint8_t ddt) { int ret; @@ -1683,7 +1685,7 @@ int osd_create_and_write(struct osd_device *osd, uint64_t pid, return ret; } - ret = osd_write(osd, pid, oid, len, offset, data, cdb_cont_len, sense, ddt); + ret = osd_write(osd, pid, oid, len, offset, data, sglist, sense, ddt); if (ret) { osd_remove(osd, pid, oid, cdb_cont_len, sense); return ret; @@ -2919,13 +2921,13 @@ static int contig_read(struct osd_device *osd, uint64_t pid, uint64_t oid, } static int sgl_read(struct osd_device *osd, uint64_t pid, uint64_t oid, - uint64_t len, uint64_t offset, const uint8_t *indata, + uint64_t len, uint64_t offset, const struct sg_list *sglist, uint8_t *outdata, uint64_t *used_outlen, uint8_t *sense) { ssize_t readlen; int ret, fd; char path[MAXNAMELEN]; - uint64_t inlen, pairs, hdr_offset, offset_val, data_offset, length; + uint64_t inlen, pairs, offset_val, data_offset, length; unsigned int i; osd_debug("%s: pid %llu oid %llu len %llu offset %llu", __func__, @@ -2934,7 +2936,7 @@ static int sgl_read(struct osd_device *osd, uint64_t pid, uint64_t oid, assert(osd && osd->root && osd->dbc && outdata && used_outlen && sense); - pairs = get_ntohll(indata); + pairs = sglist->num_entries; inlen = (pairs * sizeof(uint64_t) * 2) + sizeof(uint64_t); assert(pairs * sizeof(uint64_t) * 2 == inlen - sizeof(uint64_t)); @@ -2951,16 +2953,13 @@ static int sgl_read(struct osd_device *osd, uint64_t pid, uint64_t oid, goto out_cdb_err; - hdr_offset = sizeof(uint64_t); data_offset = 0; readlen = 0; - - for (i=0; ientries[i].offset); + length = get_ntohll(&sglist->entries[i].bytes_to_transfer); osd_debug("%s: Offset: %llu Length: %llu", __func__, llu(offset_val + offset), llu(length)); @@ -3089,7 +3088,8 @@ static int vec_read(struct osd_device *osd, uint64_t pid, uint64_t oid, int osd_read(struct osd_device *osd, uint64_t pid, uint64_t oid, uint64_t len, uint64_t offset, const uint8_t *indata, uint8_t *outdata, - uint64_t *used_outlen, uint32_t cdb_cont_len, uint8_t *sense, uint8_t ddt) + uint64_t *used_outlen, const struct sg_list *sglist, + uint8_t *sense, uint8_t ddt) { /*figure out what kind of write it is based on ddt and call appropriate write function*/ @@ -3100,7 +3100,7 @@ int osd_read(struct osd_device *osd, uint64_t pid, uint64_t oid, uint64_t len, used_outlen, sense); } case DDT_SGL: { - return sgl_read(osd, pid, oid, len, offset, indata, + return sgl_read(osd, pid, oid, len, offset, sglist, outdata, used_outlen, sense); } case DDT_VEC: { @@ -3688,12 +3688,13 @@ static int contig_write(struct osd_device *osd, uint64_t pid, uint64_t oid, static int sgl_write(struct osd_device *osd, uint64_t pid, uint64_t oid, uint64_t len, uint64_t offset, const uint8_t *dinbuf, + const struct sg_list *sglist, uint8_t *sense) { int ret; int fd; char path[MAXNAMELEN]; - uint64_t pairs, data_offset, offset_val, hdr_offset, length; + uint64_t pairs, data_offset, offset_val, length; unsigned int i; osd_debug("%s: pid %llu oid %llu len %llu offset %llu data %p", @@ -3701,13 +3702,10 @@ static int sgl_write(struct osd_device *osd, uint64_t pid, uint64_t oid, assert(osd && osd->root && osd->dbc && dinbuf && sense); - pairs = get_ntohll(dinbuf); - data_offset = (pairs * sizeof(uint64_t) * 2) + sizeof(uint64_t); + pairs = sglist->num_entries; assert(pairs != 0); osd_debug("%s: offset,len pairs %llu", __func__, llu(pairs)); - osd_debug("%s: data offset %llu", __func__, llu(data_offset)); - if (!(pid >= USEROBJECT_PID_LB && oid >= USEROBJECT_OID_LB)) goto out_cdb_err; @@ -3717,20 +3715,18 @@ static int sgl_write(struct osd_device *osd, uint64_t pid, uint64_t oid, if (fd < 0) goto out_cdb_err; - - hdr_offset = sizeof(uint64_t); /* skip count of offset/len pairs */ + data_offset = 0; for (i=0; ientries[i].offset); + length = get_ntohll(&sglist->entries[i].bytes_to_transfer); - length = get_ntohll(dinbuf + hdr_offset); /* length */ - hdr_offset += sizeof(uint64_t); + osd_debug("%s: Offset: %llu Length: %llu", + __func__, llu(offset_val + offset), llu(length)); - osd_debug("%s: Offset: %llu Length: %llu", __func__, llu(offset_val + offset), - llu(length)); - - osd_debug("%s: Position in data buffer: %llu", __func__, llu(data_offset)); + osd_debug("%s: Position in data buffer: %llu", + __func__, llu(data_offset)); osd_debug("%s: ------------------------------", __func__); ret = pwrite(fd, dinbuf+data_offset, length, offset_val+offset); @@ -3834,7 +3830,7 @@ static int vec_write(struct osd_device *osd, uint64_t pid, uint64_t oid, int osd_write(struct osd_device *osd, uint64_t pid, uint64_t oid, uint64_t len, uint64_t offset, const uint8_t *dinbuf, - uint32_t cdb_cont_len, uint8_t *sense, uint8_t ddt) + const struct sg_list *sglist, uint8_t *sense, uint8_t ddt) { /*figure out what kind of write it is based on ddt and call appropriate @@ -3847,7 +3843,7 @@ int osd_write(struct osd_device *osd, uint64_t pid, uint64_t oid, } case DDT_SGL: { return sgl_write(osd, pid, oid, len, offset, dinbuf, - sense); + sglist, sense); } case DDT_VEC: { return vec_write(osd, pid, oid, len, offset, dinbuf, @@ -3858,8 +3854,6 @@ int osd_write(struct osd_device *osd, uint64_t pid, uint64_t oid, OSD_ASC_INVALID_FIELD_IN_CDB, pid, oid); } } - - } /* diff --git a/osd-target/osd.h b/osd-target/osd.h index 22bb8a4..10a9cef 100644 --- a/osd-target/osd.h +++ b/osd-target/osd.h @@ -51,7 +51,9 @@ int osd_create(struct osd_device *osd, uint64_t pid, uint64_t requested_oid, uint16_t num, uint32_t cdb_cont_len, uint8_t *sense); int osd_create_and_write(struct osd_device *osd, uint64_t pid, uint64_t requested_oid, uint64_t len, uint64_t offset, - const uint8_t *data, uint32_t cdb_cont_len, uint8_t *sense, uint8_t ddt); + const uint8_t *data, uint32_t cdb_cont_len, + const struct sg_list *sglist, uint8_t *sense, + uint8_t ddt); int osd_create_collection(struct osd_device *osd, uint64_t pid, uint64_t requested_cid, uint32_t cdb_cont_len, uint8_t *sense); int osd_create_partition(struct osd_device *osd, uint64_t requested_pid, uint32_t cdb_cont_len, @@ -93,7 +95,7 @@ int osd_query(struct osd_device *osd, uint64_t pid, uint64_t cid, void *outdata, uint64_t *used_outlen, uint32_t cdb_cont_len, uint8_t *sense); int osd_read(struct osd_device *osd, uint64_t pid, uint64_t uid, uint64_t len, uint64_t offset, const uint8_t *indata, uint8_t *outdata, uint64_t *outlen, - uint32_t cdb_cont_len, uint8_t *sense, uint8_t ddt); + const struct sg_list *sglist, uint8_t *sense, uint8_t ddt); int osd_read_map(struct osd_device *osd, uint64_t pid, uint64_t oid, uint64_t alloc_len, uint64_t offset, uint16_t map_type, uint8_t *outdata, uint64_t *used_outlen, uint32_t cdb_cont_len, uint8_t *sense); @@ -119,7 +121,7 @@ int osd_set_member_attributes(struct osd_device *osd, uint64_t pid, int osd_write(struct osd_device *osd, uint64_t pid, uint64_t oid, uint64_t len, uint64_t offset, const uint8_t *data, - uint32_t cdb_cont_len, uint8_t *sense, uint8_t ddt); + const struct sg_list *sglist, uint8_t *sense, uint8_t ddt); int osd_cas(struct osd_device *osd, uint64_t pid, uint64_t oid, uint64_t cmp, uint64_t swap, uint8_t *doutbuf, uint64_t *used_outlen,