scsi-disk: split scsi-disk reqops

Only checks for present medium were still done in scsi_send_command
for emulated commands.  So move those to scsi_disk_emulate_command
and return different SCSIReqOps depending on the kind of command.

Checks for present medium can be done unconditionally for the
scsi_disk_dma_reqops case.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2012-02-08 09:53:42 +01:00
parent 101aa85f98
commit b08d0ea044
1 changed files with 123 additions and 68 deletions

View File

@ -1262,14 +1262,39 @@ static int scsi_disk_emulate_start_stop(SCSIDiskReq *r)
return 0; return 0;
} }
static int scsi_disk_emulate_command(SCSIDiskReq *r) static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
{ {
SCSIRequest *req = &r->req; SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
uint64_t nb_sectors; uint64_t nb_sectors;
uint8_t *outbuf; uint8_t *outbuf;
int buflen = 0; int buflen = 0;
switch (req->cmd.buf[0]) {
case INQUIRY:
case MODE_SENSE:
case MODE_SENSE_10:
case RESERVE:
case RESERVE_10:
case RELEASE:
case RELEASE_10:
case START_STOP:
case ALLOW_MEDIUM_REMOVAL:
case GET_CONFIGURATION:
case GET_EVENT_STATUS_NOTIFICATION:
case MECHANISM_STATUS:
case REQUEST_SENSE:
break;
default:
if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
return 0;
}
break;
}
assert(req->cmd.mode != SCSI_XFER_TO_DEV);
if (!r->iov.iov_base) { if (!r->iov.iov_base) {
/* /*
* FIXME: we shouldn't return anything bigger than 4k, but the code * FIXME: we shouldn't return anything bigger than 4k, but the code
@ -1332,7 +1357,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
break; break;
case START_STOP: case START_STOP:
if (scsi_disk_emulate_start_stop(r) < 0) { if (scsi_disk_emulate_start_stop(r) < 0) {
return -1; return 0;
} }
break; break;
case ALLOW_MEDIUM_REMOVAL: case ALLOW_MEDIUM_REMOVAL:
@ -1503,18 +1528,23 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
scsi_aio_complete, r); scsi_aio_complete, r);
return 0; return 0;
default: default:
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
return -1; return 0;
} }
assert(r->sector_count == 0); assert(!r->req.aiocb && r->sector_count == 0);
buflen = MIN(buflen, req->cmd.xfer); r->iov.iov_len = MIN(buflen, req->cmd.xfer);
return buflen; r->sector_count = -1;
if (r->iov.iov_len == 0) {
scsi_req_complete(&r->req, GOOD);
}
return r->iov.iov_len;
illegal_request: illegal_request:
if (r->req.status == -1) { if (r->req.status == -1) {
scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
} }
return -1; return 0;
illegal_lba: illegal_lba:
scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
@ -1526,49 +1556,18 @@ illegal_lba:
(eg. disk reads), negative for transfers to the device (eg. disk writes), (eg. disk reads), negative for transfers to the device (eg. disk writes),
and zero if the command does not transfer any data. */ and zero if the command does not transfer any data. */
static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
{ {
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
int32_t len; int32_t len;
uint8_t command; uint8_t command;
int rc;
command = buf[0]; command = buf[0];
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", req->lun, req->tag, buf[0]);
#ifdef DEBUG_SCSI if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
{ scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
int i; return 0;
for (i = 1; i < r->req.cmd.len; i++) {
printf(" 0x%02x", buf[i]);
}
printf("\n");
}
#endif
switch (command) {
case INQUIRY:
case MODE_SENSE:
case MODE_SENSE_10:
case RESERVE:
case RESERVE_10:
case RELEASE:
case RELEASE_10:
case START_STOP:
case ALLOW_MEDIUM_REMOVAL:
case GET_CONFIGURATION:
case GET_EVENT_STATUS_NOTIFICATION:
case MECHANISM_STATUS:
case REQUEST_SENSE:
break;
default:
if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
return 0;
}
break;
} }
switch (command) { switch (command) {
@ -1605,30 +1604,19 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
r->sector_count = len * (s->qdev.blocksize / 512); r->sector_count = len * (s->qdev.blocksize / 512);
break; break;
default: default:
rc = scsi_disk_emulate_command(r); abort();
if (rc < 0) {
return 0;
}
if (r->req.aiocb) {
return 0;
}
r->iov.iov_len = rc;
break;
illegal_lba: illegal_lba:
scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
return 0; return 0;
} }
if (r->sector_count == 0 && r->iov.iov_len == 0) { if (r->sector_count == 0) {
scsi_req_complete(&r->req, GOOD); scsi_req_complete(&r->req, GOOD);
} }
len = r->sector_count * 512 + r->iov.iov_len; assert(r->iov.iov_len == 0);
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
return -len; return -r->sector_count * 512;
} else { } else {
if (!r->sector_count) { return r->sector_count * 512;
r->sector_count = -1;
}
return len;
} }
} }
@ -1793,10 +1781,19 @@ static int scsi_disk_initfn(SCSIDevice *dev)
} }
} }
static const SCSIReqOps scsi_disk_reqops = { static const SCSIReqOps scsi_disk_emulate_reqops = {
.size = sizeof(SCSIDiskReq), .size = sizeof(SCSIDiskReq),
.free_req = scsi_free_request, .free_req = scsi_free_request,
.send_command = scsi_send_command, .send_command = scsi_disk_emulate_command,
.read_data = scsi_read_data,
.write_data = scsi_write_data,
.get_buf = scsi_get_buf,
};
static const SCSIReqOps scsi_disk_dma_reqops = {
.size = sizeof(SCSIDiskReq),
.free_req = scsi_free_request,
.send_command = scsi_disk_dma_command,
.read_data = scsi_read_data, .read_data = scsi_read_data,
.write_data = scsi_write_data, .write_data = scsi_write_data,
.cancel_io = scsi_cancel_io, .cancel_io = scsi_cancel_io,
@ -1805,13 +1802,72 @@ static const SCSIReqOps scsi_disk_reqops = {
.save_request = scsi_disk_save_request, .save_request = scsi_disk_save_request,
}; };
static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = {
[TEST_UNIT_READY] = &scsi_disk_emulate_reqops,
[INQUIRY] = &scsi_disk_emulate_reqops,
[MODE_SENSE] = &scsi_disk_emulate_reqops,
[MODE_SENSE_10] = &scsi_disk_emulate_reqops,
[START_STOP] = &scsi_disk_emulate_reqops,
[ALLOW_MEDIUM_REMOVAL] = &scsi_disk_emulate_reqops,
[READ_CAPACITY_10] = &scsi_disk_emulate_reqops,
[READ_TOC] = &scsi_disk_emulate_reqops,
[READ_DVD_STRUCTURE] = &scsi_disk_emulate_reqops,
[READ_DISC_INFORMATION] = &scsi_disk_emulate_reqops,
[GET_CONFIGURATION] = &scsi_disk_emulate_reqops,
[GET_EVENT_STATUS_NOTIFICATION] = &scsi_disk_emulate_reqops,
[MECHANISM_STATUS] = &scsi_disk_emulate_reqops,
[SERVICE_ACTION_IN_16] = &scsi_disk_emulate_reqops,
[REQUEST_SENSE] = &scsi_disk_emulate_reqops,
[SYNCHRONIZE_CACHE] = &scsi_disk_emulate_reqops,
[SEEK_10] = &scsi_disk_emulate_reqops,
#if 0
[MODE_SELECT] = &scsi_disk_emulate_reqops,
[MODE_SELECT_10] = &scsi_disk_emulate_reqops,
#endif
[WRITE_SAME_10] = &scsi_disk_emulate_reqops,
[WRITE_SAME_16] = &scsi_disk_emulate_reqops,
[READ_6] = &scsi_disk_dma_reqops,
[READ_10] = &scsi_disk_dma_reqops,
[READ_12] = &scsi_disk_dma_reqops,
[READ_16] = &scsi_disk_dma_reqops,
[VERIFY_10] = &scsi_disk_dma_reqops,
[VERIFY_12] = &scsi_disk_dma_reqops,
[VERIFY_16] = &scsi_disk_dma_reqops,
[WRITE_6] = &scsi_disk_dma_reqops,
[WRITE_10] = &scsi_disk_dma_reqops,
[WRITE_12] = &scsi_disk_dma_reqops,
[WRITE_16] = &scsi_disk_dma_reqops,
[WRITE_VERIFY_10] = &scsi_disk_dma_reqops,
[WRITE_VERIFY_12] = &scsi_disk_dma_reqops,
[WRITE_VERIFY_16] = &scsi_disk_dma_reqops,
};
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
uint8_t *buf, void *hba_private) uint8_t *buf, void *hba_private)
{ {
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *req; SCSIRequest *req;
const SCSIReqOps *ops;
uint8_t command;
req = scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun, hba_private); #ifdef DEBUG_SCSI
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, buf[0]);
{
int i;
for (i = 1; i < r->req.cmd.len; i++) {
printf(" 0x%02x", buf[i]);
}
printf("\n");
}
#endif
command = buf[0];
ops = scsi_disk_reqops_dispatch[command];
if (!ops) {
ops = &scsi_disk_emulate_reqops;
}
req = scsi_req_alloc(ops, &s->qdev, tag, lun, hba_private);
return req; return req;
} }
@ -1925,15 +1981,14 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
* unreliable, too. It is even possible that reads deliver random data * unreliable, too. It is even possible that reads deliver random data
* from the host page cache (this is probably a Linux bug). * from the host page cache (this is probably a Linux bug).
* *
* We might use scsi_disk_reqops as long as no writing commands are * We might use scsi_disk_dma_reqops as long as no writing commands are
* seen, but performance usually isn't paramount on optical media. So, * seen, but performance usually isn't paramount on optical media. So,
* just make scsi-block operate the same as scsi-generic for them. * just make scsi-block operate the same as scsi-generic for them.
*/ */
if (s->qdev.type == TYPE_ROM) { if (s->qdev.type != TYPE_ROM) {
break; return scsi_req_alloc(&scsi_disk_dma_reqops, &s->qdev, tag, lun,
} hba_private);
return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun, }
hba_private);
} }
return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun, return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun,