static void bs_sync_sync_range(struct scsi_cmd *cmd, uint32_t length, int *result, uint8_t *key, uint16_t *asc) { int ret; ret = fdatasync(cmd->dev->fd); if (ret) set_medium_error(result, key, asc); }
static void bs_sync_sync_range(struct scsi_cmd *cmd, uint32_t length, int *result, uint8_t *key, uint16_t *asc) { int ret; ret = rbd_flush(RBDP(cmd->dev->fd)->rbd_image); if (ret) set_medium_error(result, key, asc); }
static int foo_handle_cmd( struct tcmu_device *dev, uint8_t *cdb, struct iovec *iovec, size_t iov_cnt, uint8_t *sense) { struct foo_state *state = tcmu_get_dev_private(dev); uint8_t cmd; cmd = cdb[0]; switch (cmd) { case INQUIRY: return tcmu_emulate_inquiry(dev, NULL, cdb, iovec, iov_cnt, sense); break; case TEST_UNIT_READY: return tcmu_emulate_test_unit_ready(cdb, iovec, iov_cnt, sense); break; case SERVICE_ACTION_IN_16: if (cdb[1] == READ_CAPACITY_16) return tcmu_emulate_read_capacity_16(state->num_lbas, state->block_size, cdb, iovec, iov_cnt, sense); else return TCMU_NOT_HANDLED; break; case MODE_SENSE: case MODE_SENSE_10: return tcmu_emulate_mode_sense(dev, cdb, iovec, iov_cnt, sense); break; case MODE_SELECT: case MODE_SELECT_10: return tcmu_emulate_mode_select(dev, cdb, iovec, iov_cnt, sense); break; case READ_6: case READ_10: case READ_12: case READ_16: // A real "read" implementation goes here! return set_medium_error(sense); case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_16: // A real "write" implemention goes here! return SAM_STAT_GOOD; default: tcmu_err("unknown command %x\n", cdb[0]); return TCMU_NOT_HANDLED; } }
static void bs_rbd_request(struct scsi_cmd *cmd) { int ret; uint32_t length; int result = SAM_STAT_GOOD; uint8_t key; uint16_t asc; #if 0 /* * This should go in the sense data on error for COMPARE_AND_WRITE, but * there doesn't seem to be any attempt to do so... */ uint32_t info = 0; #endif char *tmpbuf; size_t blocksize; uint64_t offset = cmd->offset; uint32_t tl = cmd->tl; int do_verify = 0; int i; char *ptr; const char *write_buf = NULL; ret = length = 0; key = asc = 0; struct active_rbd *rbd = RBDP(cmd->dev->fd); switch (cmd->scb[0]) { case ORWRITE_16: length = scsi_get_out_length(cmd); tmpbuf = malloc(length); if (!tmpbuf) { result = SAM_STAT_CHECK_CONDITION; key = HARDWARE_ERROR; asc = ASC_INTERNAL_TGT_FAILURE; break; } ret = rbd_read(rbd->rbd_image, offset, length, tmpbuf); if (ret != length) { set_medium_error(&result, &key, &asc); free(tmpbuf); break; } ptr = scsi_get_out_buffer(cmd); for (i = 0; i < length; i++) ptr[i] |= tmpbuf[i]; free(tmpbuf); write_buf = scsi_get_out_buffer(cmd); goto write; case COMPARE_AND_WRITE: /* Blocks are transferred twice, first the set that * we compare to the existing data, and second the set * to write if the compare was successful. */ length = scsi_get_out_length(cmd) / 2; if (length != cmd->tl) { result = SAM_STAT_CHECK_CONDITION; key = ILLEGAL_REQUEST; asc = ASC_INVALID_FIELD_IN_CDB; break; } tmpbuf = malloc(length); if (!tmpbuf) { result = SAM_STAT_CHECK_CONDITION; key = HARDWARE_ERROR; asc = ASC_INTERNAL_TGT_FAILURE; break; } ret = rbd_read(rbd->rbd_image, offset, length, tmpbuf); if (ret != length) { set_medium_error(&result, &key, &asc); free(tmpbuf); break; } if (memcmp(scsi_get_out_buffer(cmd), tmpbuf, length)) { uint32_t pos = 0; char *spos = scsi_get_out_buffer(cmd); char *dpos = tmpbuf; /* * Data differed, this is assumed to be 'rare' * so use a much more expensive byte-by-byte * comparasion to find out at which offset the * data differs. */ for (pos = 0; pos < length && *spos++ == *dpos++; pos++) ; #if 0 /* See comment above at declaration */ info = pos; #endif result = SAM_STAT_CHECK_CONDITION; key = MISCOMPARE; asc = ASC_MISCOMPARE_DURING_VERIFY_OPERATION; free(tmpbuf); break; } /* no DPO bit (cache retention advice) support */ free(tmpbuf); write_buf = scsi_get_out_buffer(cmd) + length; goto write; case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE_16: /* TODO */ length = (cmd->scb[0] == SYNCHRONIZE_CACHE) ? 0 : 0; if (cmd->scb[1] & 0x2) { result = SAM_STAT_CHECK_CONDITION; key = ILLEGAL_REQUEST; asc = ASC_INVALID_FIELD_IN_CDB; } else bs_sync_sync_range(cmd, length, &result, &key, &asc); break; case WRITE_VERIFY: case WRITE_VERIFY_12: case WRITE_VERIFY_16: do_verify = 1; case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_16: length = scsi_get_out_length(cmd); write_buf = scsi_get_out_buffer(cmd); write: ret = rbd_write(rbd->rbd_image, offset, length, write_buf); if (ret == length) { struct mode_pg *pg; /* * it would be better not to access to pg * directy. */ pg = find_mode_page(cmd->dev, 0x08, 0); if (pg == NULL) { result = SAM_STAT_CHECK_CONDITION; key = ILLEGAL_REQUEST; asc = ASC_INVALID_FIELD_IN_CDB; break; } if (((cmd->scb[0] != WRITE_6) && (cmd->scb[1] & 0x8)) || !(pg->mode_data[0] & 0x04)) bs_sync_sync_range(cmd, length, &result, &key, &asc); } else set_medium_error(&result, &key, &asc); if (do_verify) goto verify; break; case WRITE_SAME: case WRITE_SAME_16: /* WRITE_SAME used to punch hole in file */ if (cmd->scb[1] & 0x08) { ret = rbd_discard(rbd->rbd_image, offset, tl); if (ret != 0) { eprintf("Failed to punch hole for WRITE_SAME" " command\n"); result = SAM_STAT_CHECK_CONDITION; key = HARDWARE_ERROR; asc = ASC_INTERNAL_TGT_FAILURE; break; } break; } while (tl > 0) { blocksize = 1 << cmd->dev->blk_shift; tmpbuf = scsi_get_out_buffer(cmd); switch (cmd->scb[1] & 0x06) { case 0x02: /* PBDATA==0 LBDATA==1 */ put_unaligned_be32(offset, tmpbuf); break; case 0x04: /* PBDATA==1 LBDATA==0 */ /* physical sector format */ put_unaligned_be64(offset, tmpbuf); break; } ret = rbd_write(rbd->rbd_image, offset, blocksize, tmpbuf); if (ret != blocksize) set_medium_error(&result, &key, &asc); offset += blocksize; tl -= blocksize; } break; case READ_6: case READ_10: case READ_12: case READ_16: length = scsi_get_in_length(cmd); ret = rbd_read(rbd->rbd_image, offset, length, scsi_get_in_buffer(cmd)); if (ret != length) set_medium_error(&result, &key, &asc); break; case PRE_FETCH_10: case PRE_FETCH_16: break; case VERIFY_10: case VERIFY_12: case VERIFY_16: verify: length = scsi_get_out_length(cmd); tmpbuf = malloc(length); if (!tmpbuf) { result = SAM_STAT_CHECK_CONDITION; key = HARDWARE_ERROR; asc = ASC_INTERNAL_TGT_FAILURE; break; } ret = rbd_read(rbd->rbd_image, offset, length, tmpbuf); if (ret != length) set_medium_error(&result, &key, &asc); else if (memcmp(scsi_get_out_buffer(cmd), tmpbuf, length)) { result = SAM_STAT_CHECK_CONDITION; key = MISCOMPARE; asc = ASC_MISCOMPARE_DURING_VERIFY_OPERATION; } free(tmpbuf); break; case UNMAP: if (!cmd->dev->attrs.thinprovisioning) { result = SAM_STAT_CHECK_CONDITION; key = ILLEGAL_REQUEST; asc = ASC_INVALID_FIELD_IN_CDB; break; } length = scsi_get_out_length(cmd); tmpbuf = scsi_get_out_buffer(cmd); if (length < 8) break; length -= 8; tmpbuf += 8; while (length >= 16) { offset = get_unaligned_be64(&tmpbuf[0]); offset = offset << cmd->dev->blk_shift; tl = get_unaligned_be32(&tmpbuf[8]); tl = tl << cmd->dev->blk_shift; if (offset + tl > cmd->dev->size) { eprintf("UNMAP beyond EOF\n"); result = SAM_STAT_CHECK_CONDITION; key = ILLEGAL_REQUEST; asc = ASC_LBA_OUT_OF_RANGE; break; } if (tl > 0) { if (rbd_discard(rbd->rbd_image, offset, tl) != 0) { eprintf("Failed to punch hole for" " UNMAP at offset:%" PRIu64 " length:%d\n", offset, tl); result = SAM_STAT_CHECK_CONDITION; key = HARDWARE_ERROR; asc = ASC_INTERNAL_TGT_FAILURE; break; } } length -= 16; tmpbuf += 16; } break; default: break; } dprintf("io done %p %x %d %u\n", cmd, cmd->scb[0], ret, length); scsi_set_result(cmd, result); if (result != SAM_STAT_GOOD) { eprintf("io error %p %x %d %d %" PRIu64 ", %m\n", cmd, cmd->scb[0], ret, length, offset); sense_data_build(cmd, key, asc); } }
/* * Return scsi status or TCMU_NOT_HANDLED */ int tcmu_glfs_handle_cmd( struct tcmu_device *dev, struct tcmulib_cmd *tcmulib_cmd) { uint8_t *cdb = tcmulib_cmd->cdb; struct iovec *iovec = tcmulib_cmd->iovec; size_t iov_cnt = tcmulib_cmd->iov_cnt; uint8_t *sense = tcmulib_cmd->sense_buf; struct glfs_state *state = tcmu_get_dev_private(dev); uint8_t cmd; glfs_fd_t *gfd = state->gfd; int ret; uint32_t length; int result = SAM_STAT_GOOD; char *tmpbuf; uint64_t offset = state->block_size * tcmu_get_lba(cdb); uint32_t tl = state->block_size * tcmu_get_xfer_length(cdb); int do_verify = 0; uint32_t cmp_offset; ret = length = 0; cmd = cdb[0]; switch (cmd) { case INQUIRY: return tcmu_emulate_inquiry(dev, cdb, iovec, iov_cnt, sense); break; case TEST_UNIT_READY: return tcmu_emulate_test_unit_ready(cdb, iovec, iov_cnt, sense); break; case SERVICE_ACTION_IN_16: if (cdb[1] == READ_CAPACITY_16) { long long size; unsigned long long num_lbas; size = tcmu_get_device_size(dev); if (size == -1) { errp("Could not get device size\n"); return TCMU_NOT_HANDLED; } num_lbas = size / state->block_size; return tcmu_emulate_read_capacity_16(num_lbas, state->block_size, cdb, iovec, iov_cnt, sense); } else { return TCMU_NOT_HANDLED; } break; case MODE_SENSE: case MODE_SENSE_10: return tcmu_emulate_mode_sense(cdb, iovec, iov_cnt, sense); break; case MODE_SELECT: case MODE_SELECT_10: return tcmu_emulate_mode_select(cdb, iovec, iov_cnt, sense); break; case COMPARE_AND_WRITE: /* Blocks are transferred twice, first the set that * we compare to the existing data, and second the set * to write if the compare was successful. */ length = tl / 2; tmpbuf = malloc(length); if (!tmpbuf) { result = tcmu_set_sense_data(sense, HARDWARE_ERROR, ASC_INTERNAL_TARGET_FAILURE, NULL); break; } ret = glfs_pread(gfd, tmpbuf, length, offset, SEEK_SET); if (ret != length) { result = set_medium_error(sense); free(tmpbuf); break; } cmp_offset = tcmu_compare_with_iovec(tmpbuf, iovec, length); if (cmp_offset != -1) { result = tcmu_set_sense_data(sense, MISCOMPARE, ASC_MISCOMPARE_DURING_VERIFY_OPERATION, &cmp_offset); free(tmpbuf); break; } free(tmpbuf); tcmu_seek_in_iovec(iovec, length); goto write; case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE_16: if (cdb[1] & 0x2) result = tcmu_set_sense_data(sense, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB, NULL); else glfs_fdatasync(gfd); break; case WRITE_VERIFY: case WRITE_VERIFY_12: case WRITE_VERIFY_16: do_verify = 1; case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_16: length = tl; write: ret = glfs_pwritev(gfd, iovec, iov_cnt, offset, ALLOWED_BSOFLAGS); if (ret == length) { /* Sync if FUA */ if ((cmd != WRITE_6) && (cdb[1] & 0x8)) glfs_fdatasync(gfd); } else { errp("Error on write %x %x\n", ret, length); result = set_medium_error(sense); break; } if (!do_verify) break; tmpbuf = malloc(length); if (!tmpbuf) { result = tcmu_set_sense_data(sense, HARDWARE_ERROR, ASC_INTERNAL_TARGET_FAILURE, NULL); break; } ret = glfs_pread(gfd, tmpbuf, length, offset, ALLOWED_BSOFLAGS); if (ret != length) { result = set_medium_error(sense); free(tmpbuf); break; } cmp_offset = tcmu_compare_with_iovec(tmpbuf, iovec, length); if (cmp_offset != -1) result = tcmu_set_sense_data(sense, MISCOMPARE, ASC_MISCOMPARE_DURING_VERIFY_OPERATION, &cmp_offset); free(tmpbuf); break; case WRITE_SAME: case WRITE_SAME_16: errp("WRITE_SAME called, but has vpd b2 been implemented?\n"); result = tcmu_set_sense_data(sense, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB, NULL); break; #if 0 /* WRITE_SAME used to punch hole in file */ if (cdb[1] & 0x08) { ret = glfs_discard(gfd, offset, tl); if (ret != 0) { result = tcmu_set_sense_data(sense, HARDWARE_ERROR, ASC_INTERNAL_TARGET_FAILURE, NULL); } break; } while (tl > 0) { size_t blocksize = state->block_size; uint32_t val32; uint64_t val64; assert(iovec->iov_len >= 8); switch (cdb[1] & 0x06) { case 0x02: /* PBDATA==0 LBDATA==1 */ val32 = htobe32(offset); memcpy(iovec->iov_base, &val32, 4); break; case 0x04: /* PBDATA==1 LBDATA==0 */ /* physical sector format */ /* hey this is wrong val! But how to fix? */ val64 = htobe64(offset); memcpy(iovec->iov_base, &val64, 8); break; default: /* FIXME */ errp("PBDATA and LBDATA set!!!\n"); } ret = glfs_pwritev(gfd, iovec, blocksize, offset, ALLOWED_BSOFLAGS); if (ret != blocksize) result = set_medium_error(sense); offset += blocksize; tl -= blocksize; } break; #endif case READ_6: case READ_10: case READ_12: case READ_16: length = tcmu_iovec_length(iovec, iov_cnt); ret = glfs_preadv(gfd, iovec, iov_cnt, offset, SEEK_SET); if (ret != length) { errp("Error on read %x %x\n", ret, length); result = set_medium_error(sense); } break; case UNMAP: /* TODO: implement UNMAP */ result = tcmu_set_sense_data(sense, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB, NULL); break; default: result = TCMU_NOT_HANDLED; break; } dbgp("io done %p %x %d %u\n", cdb, cmd, result, length); if (result == TCMU_NOT_HANDLED) dbgp("io not handled %p %x %x %d %d %llu\n", cdb, result, cmd, ret, length, (unsigned long long)offset); else if (result != SAM_STAT_GOOD) { errp("io error %p %x %x %d %d %llu\n", cdb, result, cmd, ret, length, (unsigned long long)offset); } return result; }
/* * Return scsi status or TCMU_NOT_HANDLED */ static int file_handle_cmd( struct tcmu_device *dev, uint8_t *cdb, struct iovec *iovec, size_t iov_cnt, uint8_t *sense) { struct file_state *state = tcmu_get_dev_private(dev); uint8_t cmd; int remaining; size_t ret; cmd = cdb[0]; switch (cmd) { case INQUIRY: return tcmu_emulate_inquiry(dev, cdb, iovec, iov_cnt, sense); break; case TEST_UNIT_READY: return tcmu_emulate_test_unit_ready(cdb, iovec, iov_cnt, sense); break; case SERVICE_ACTION_IN_16: if (cdb[1] == READ_CAPACITY_16) return tcmu_emulate_read_capacity_16(state->num_lbas, state->block_size, cdb, iovec, iov_cnt, sense); else return TCMU_NOT_HANDLED; break; case MODE_SENSE: case MODE_SENSE_10: return tcmu_emulate_mode_sense(cdb, iovec, iov_cnt, sense); break; case MODE_SELECT: case MODE_SELECT_10: return tcmu_emulate_mode_select(cdb, iovec, iov_cnt, sense); break; case READ_6: case READ_10: case READ_12: case READ_16: { void *buf; uint64_t offset = state->block_size * tcmu_get_lba(cdb); int length = tcmu_get_xfer_length(cdb) * state->block_size; ret = lseek(state->fd, offset, SEEK_SET); if (ret == -1) { errp("lseek failed: %m\n"); return set_medium_error(sense); } /* Using this buf DTRT even if seek is beyond EOF */ buf = malloc(length); if (!buf) return set_medium_error(sense); memset(buf, 0, length); ret = read(state->fd, buf, length); if (ret == -1) { errp("read failed: %m\n"); free(buf); return set_medium_error(sense); } tcmu_memcpy_into_iovec(iovec, iov_cnt, buf, length); free(buf); return SAM_STAT_GOOD; } break; case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_16: { uint64_t offset = state->block_size * tcmu_get_lba(cdb); int length = be16toh(*((uint16_t *)&cdb[7])) * state->block_size; ret = lseek(state->fd, offset, SEEK_SET); if (ret == -1) { errp("lseek failed: %m\n"); return set_medium_error(sense); } remaining = length; while (remaining) { unsigned int to_copy; to_copy = (remaining > iovec->iov_len) ? iovec->iov_len : remaining; ret = write(state->fd, iovec->iov_base, to_copy); if (ret == -1) { errp("Could not write: %m\n"); return set_medium_error(sense); } remaining -= to_copy; iovec++; } return SAM_STAT_GOOD; } break; default: errp("unknown command %x\n", cdb[0]); return TCMU_NOT_HANDLED; } }
/* * Return scsi status or TCMU_NOT_HANDLED */ int file_handle_cmd( struct tcmu_device *dev, uint8_t *cdb, struct iovec *iovec, size_t iov_cnt, uint8_t *sense) { struct file_state *state = dev->hm_private; uint8_t cmd; int remaining; size_t ret; cmd = cdb[0]; if (cmd == READ_10) { void *buf; void *tmp_ptr; int lba = tcmu_get_lba(cdb); int length = tcmu_get_xfer_length(cdb) * state->block_size; ret = lseek(state->fd, lba * state->block_size, SEEK_SET); if (ret == -1) { printf("lseek failed: %m\n"); return set_medium_error(sense); } /* Using this buf DTRT even if seek is beyond EOF */ buf = malloc(length); if (!buf) return set_medium_error(sense); memset(buf, 0, length); ret = read(state->fd, buf, length); if (ret == -1) { printf("read failed: %m\n"); free(buf); return set_medium_error(sense); } tmp_ptr = buf; remaining = length; while (remaining) { unsigned int to_copy; to_copy = (remaining > iovec->iov_len) ? iovec->iov_len : remaining; memcpy(iovec->iov_base, tmp_ptr, to_copy); tmp_ptr += to_copy; remaining -= iovec->iov_len; iovec++; } free(buf); return SAM_STAT_GOOD; } else if (cmd == WRITE_10) { int lba = be32toh(*((u_int32_t *)&cdb[2])); int length = be16toh(*((uint16_t *)&cdb[7])) * state->block_size; ret = lseek(state->fd, lba * state->block_size, SEEK_SET); if (ret == -1) { printf("lseek failed: %m\n"); return set_medium_error(sense); } remaining = length; while (remaining) { unsigned int to_copy; to_copy = (remaining > iovec->iov_len) ? iovec->iov_len : remaining; ret = write(state->fd, iovec->iov_base, to_copy); if (ret == -1) { printf("Could not write: %m\n"); return set_medium_error(sense); } remaining -= to_copy; iovec++; } return SAM_STAT_GOOD; } printf("unknown command %x\n", cdb[0]); return TCMU_NOT_HANDLED; }