static int bs_paio_cmd_submit(struct scsi_cmd *cmd) { struct scsi_lu *lu = cmd->dev; struct aiocb *io=NULL; int ret = 0; switch (cmd->scb[0]) { case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE_16: break; case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_16: io=calloc(1, sizeof(*io)+sizeof(cmd)); io->aio_fildes=lu->fd; io->aio_buf=scsi_get_out_buffer(cmd); io->aio_offset=cmd->offset; io->aio_nbytes=scsi_get_out_length(cmd); io->aio_sigevent.sigev_notify=SIGEV_THREAD; io->aio_sigevent.sigev_notify_function=paio_done; io->aio_sigevent.sigev_notify_attributes=NULL; io->aio_sigevent.sigev_value.sival_ptr=io; *(struct scsi_cmd **)(io+1)=cmd; ret=aio_write(io); if(ret==0){ set_cmd_async(cmd); return 0; } break; case READ_6: case READ_10: case READ_12: case READ_16: io=calloc(1, sizeof(*io)+sizeof(cmd)); io->aio_fildes=lu->fd; io->aio_buf=scsi_get_in_buffer(cmd); io->aio_offset=cmd->offset; io->aio_nbytes=scsi_get_in_length(cmd); io->aio_sigevent.sigev_notify=SIGEV_THREAD; io->aio_sigevent.sigev_notify_function=paio_done; io->aio_sigevent.sigev_notify_attributes=NULL; io->aio_sigevent.sigev_value.sival_ptr=io; *(struct scsi_cmd **)(io+1)=cmd; ret=aio_read(io); if(ret==0){ set_cmd_async(cmd); return 0; } break; default: break; } if(ret!=0){ sense_data_build(cmd, MEDIUM_ERROR, 0); ret = SAM_STAT_CHECK_CONDITION; } return ret; }
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); } }
static void tape_rdwr_request(struct scsi_cmd *cmd) { struct ssc_info *ssc = dtype_priv(cmd->dev); struct blk_header_info *h = &ssc->c_blk; int ret, code; uint32_t length, i, transfer_length, residue; int result = SAM_STAT_GOOD; uint8_t *buf; int32_t count; int8_t fixed; int8_t sti; uint32_t block_length = ssc_get_block_length(cmd->dev); ret = 0; length = 0; i = 0; transfer_length = 0; residue = 0; code = 0; ssc = dtype_priv(cmd->dev); switch (cmd->scb[0]) { case REZERO_UNIT: dprintf("**** Rewind ****\n"); if (resp_rewind(cmd->dev)) { sense_data_build(cmd, MEDIUM_ERROR, ASC_SEQUENTIAL_POSITION_ERR); result = SAM_STAT_CHECK_CONDITION; } break; case WRITE_FILEMARKS: ret = get_unaligned_be24(&cmd->scb[2]); dprintf("*** Write %d filemark%s ***\n", ret, ((ret > 1) || (ret < 0)) ? "s" : ""); for (i = 0; i < ret; i++) append_blk(cmd, scsi_get_out_buffer(cmd), 0, 0, BLK_FILEMARK); fsync(cmd->dev->fd); break; case READ_6: fixed = cmd->scb[1] & 1; sti = cmd->scb[1] & 2; if (fixed && sti) { sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB); result = SAM_STAT_CHECK_CONDITION; break; } length = scsi_get_in_length(cmd); count = get_unaligned_be24(&cmd->scb[2]); buf = scsi_get_in_buffer(cmd); dprintf("*** READ_6: length %d, count %d, fixed block %s," " %" PRIu64 " %d\n", length, count, (fixed) ? "Yes" : "No", h->curr, sti); if (fixed) result = resp_fixed_read(cmd, buf, length, &ret); else result = resp_var_read(cmd, buf, length, &ret); eprintf("Executed READ_6, Read %d bytes, %" PRIu64 "\n", ret, h->curr); break; case WRITE_6: fixed = cmd->scb[1] & 1; buf = scsi_get_out_buffer(cmd); count = get_unaligned_be24(&cmd->scb[2]); length = scsi_get_out_length(cmd); if (!fixed) { block_length = length; count = 1; } for (i = 0, ret = 0; i < count; i++) { if (append_blk(cmd, buf, block_length, block_length, BLK_UNCOMPRESS_DATA)) { sense_data_build(cmd, MEDIUM_ERROR, ASC_WRITE_ERROR); result = SAM_STAT_CHECK_CONDITION; break; } buf += block_length; ret += block_length; } dprintf("*** WRITE_6 count: %d, length: %d, ret: %d, fixed: %s," " ssc->blk_sz: %d\n", count, length, ret, (fixed) ? "Yes" : "No", block_length); /* Check for end of media */ if (current_size(cmd) > ssc->mam.max_capacity) { sense_data_build(cmd, NO_SENSE|SENSE_EOM, NO_ADDITIONAL_SENSE); result = SAM_STAT_CHECK_CONDITION; break; } if (ret != length) { sense_data_build(cmd, MEDIUM_ERROR, ASC_WRITE_ERROR); result = SAM_STAT_CHECK_CONDITION; } break; case SPACE: code = cmd->scb[1] & 0xf; count = be24_to_2comp(&cmd->scb[2]); if (code == 0) { /* Logical Blocks */ result = space_blocks(cmd, count); break; } else if (code == 1) { /* Filemarks */ result = space_filemark(cmd, count); break; } else if (code == 3) { /* End of data */ while (h->blk_type != BLK_EOD) if (skip_next_header(cmd->dev)) { sense_data_build(cmd, MEDIUM_ERROR, ASC_MEDIUM_FORMAT_CORRUPT); result = SAM_STAT_CHECK_CONDITION; break; } } else { /* Unsupported */ sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB); result = SAM_STAT_CHECK_CONDITION; } break; case READ_POSITION: { int service_action = cmd->scb[1] & 0x1f; uint8_t *data = scsi_get_in_buffer(cmd); int len = scsi_get_in_length(cmd); dprintf("Size of in_buffer = %d\n", len); dprintf("Sizeof(buf): %zd\n", sizeof(buf)); dprintf("service action: 0x%02x\n", service_action); if (service_action == 0) { /* Short form - block ID */ memset(data, 0, 20); data[0] = 20; } else if (service_action == 1) { /* Short form - vendor uniq */ memset(data, 0, 20); data[0] = 20; } else if (service_action == 6) { /* Long form */ memset(data, 0, 32); data[0] = 32; } else { sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB); result = SAM_STAT_CHECK_CONDITION; } break; } default: eprintf("Unknown op code - should never see this\n"); sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE); result = SAM_STAT_CHECK_CONDITION; 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, cmd->offset); }