aboutsummaryrefslogtreecommitdiff
path: root/hw/scsi-disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/scsi-disk.c')
-rw-r--r--hw/scsi-disk.c152
1 files changed, 118 insertions, 34 deletions
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 3419c8e2d2..a2f299e951 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -25,6 +25,7 @@ do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
#define SENSE_NO_SENSE 0
#define SENSE_NOT_READY 2
+#define SENSE_HARDWARE_ERROR 4
#define SENSE_ILLEGAL_REQUEST 5
struct SCSIDevice
@@ -46,7 +47,13 @@ struct SCSIDevice
int buf_pos;
int buf_len;
int sense;
+ BlockDriverAIOCB *aiocb;
+ /* Data still to be transfered after this request completes. */
+ uint8_t *aiodata;
+ uint32_t aiolen;
char buf[512];
+ /* Completion functions may be called from either scsi_{read,write}_data
+ or from the AIO completion routines. */
scsi_completionfn completion;
void *opaque;
};
@@ -54,10 +61,49 @@ struct SCSIDevice
static void scsi_command_complete(SCSIDevice *s, int sense)
{
s->sense = sense;
- s->completion(s->opaque, s->tag, sense);
+ s->completion(s->opaque, SCSI_REASON_DONE, sense);
}
-/* Read data from a scsi device. Returns nonzero on failure. */
+static void scsi_transfer_complete(SCSIDevice *s)
+{
+ s->completion(s->opaque, SCSI_REASON_DATA, 0);
+ s->aiocb = NULL;
+}
+
+static void scsi_read_complete(void * opaque, int ret)
+{
+ SCSIDevice *s = (SCSIDevice *)opaque;
+
+ if (ret) {
+ DPRINTF("IO error\n");
+ scsi_command_complete(s, SENSE_HARDWARE_ERROR);
+ }
+
+ if (s->aiolen) {
+ /* Read the remaining data. Full and partial sectors are transferred
+ separately. */
+ scsi_read_data(s, s->aiodata, s->aiolen);
+ } else {
+ if (s->buf_len == 0 && s->sector_count == 0)
+ scsi_command_complete(s, SENSE_NO_SENSE);
+ else
+ scsi_transfer_complete(s);
+ }
+}
+
+/* Cancel a pending data transfer. */
+void scsi_cancel_io(SCSIDevice *s)
+{
+ if (!s->aiocb) {
+ BADF("Cancel with no pending IO\n");
+ return;
+ }
+ bdrv_aio_cancel(s->aiocb);
+ s->aiocb = NULL;
+}
+
+/* Read data from a scsi device. Returns nonzero on failure.
+ The transfer may complete asynchronously. */
int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
{
uint32_t n;
@@ -84,14 +130,19 @@ int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
n = s->sector_count;
if (n != 0) {
- bdrv_read(s->bdrv, s->sector, data, n);
- data += n * 512;
- len -= n * 512;
+ s->aiolen = len - n * 512;
+ s->aiodata = data + n * 512;
+ s->aiocb = bdrv_aio_read(s->bdrv, s->sector, data, n,
+ scsi_read_complete, s);
+ if (s->aiocb == NULL)
+ scsi_command_complete(s, SENSE_HARDWARE_ERROR);
s->sector += n;
s->sector_count -= n;
+ return 0;
}
if (len && s->sector_count) {
+ /* TODO: Make this use AIO. */
bdrv_read(s->bdrv, s->sector, s->buf, 1);
s->sector++;
s->sector_count--;
@@ -106,11 +157,53 @@ int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
if (s->buf_len == 0 && s->sector_count == 0)
scsi_command_complete(s, SENSE_NO_SENSE);
+ else
+ scsi_transfer_complete(s);
return 0;
}
-/* Read data to a scsi device. Returns nonzero on failure. */
+static void scsi_write_complete(void * opaque, int ret)
+{
+ SCSIDevice *s = (SCSIDevice *)opaque;
+
+ if (ret) {
+ fprintf(stderr, "scsi-disc: IO write error\n");
+ exit(1);
+ }
+
+ if (s->sector_count == 0)
+ scsi_command_complete(s, SENSE_NO_SENSE);
+ else
+ scsi_transfer_complete(s);
+}
+
+static uint32_t scsi_write_partial_sector(SCSIDevice *s, uint8_t *data,
+ uint32_t len)
+{
+ int n;
+
+ n = 512 - s->buf_len;
+ if (n > len)
+ n = len;
+
+ memcpy(s->buf + s->buf_len, data, n);
+ data += n;
+ s->buf_len += n;
+ len -= n;
+ if (s->buf_len == 512) {
+ /* A full sector has been accumulated. Write it to disk. */
+ /* TODO: Make this use async IO. */
+ bdrv_write(s->bdrv, s->sector, s->buf, 1);
+ s->buf_len = 0;
+ s->sector++;
+ s->sector_count--;
+ }
+ return n;
+}
+
+/* Write data to a scsi device. Returns nonzero on failure.
+ The transfer may complete asynchronously. */
int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
{
uint32_t n;
@@ -125,48 +218,39 @@ int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
return 1;
if (s->buf_len != 0 || len < 512) {
- n = 512 - s->buf_len;
- if (n > len)
- n = len;
-
- memcpy(s->buf + s->buf_len, data, n);
- data += n;
- s->buf_len += n;
+ n = scsi_write_partial_sector(s, data, len);
len -= n;
- if (s->buf_len == 512) {
- /* A full sector has been accumulated. Write it to disk. */
- bdrv_write(s->bdrv, s->sector, s->buf, 1);
- s->buf_len = 0;
- s->sector++;
- s->sector_count--;
- }
+ data += n;
}
n = len / 512;
if (n > s->sector_count)
- n = s->sector_count;
+ return 1;
if (n != 0) {
- bdrv_write(s->bdrv, s->sector, data, n);
+ s->aiocb = bdrv_aio_write(s->bdrv, s->sector, data, n,
+ scsi_write_complete, s);
+ if (s->aiocb == NULL)
+ scsi_command_complete(s, SENSE_HARDWARE_ERROR);
data += n * 512;
len -= n * 512;
s->sector += n;
s->sector_count -= n;
}
- if (len >= 512)
- return 1;
-
- if (len && s->sector_count) {
- /* Recurse to complete the partial write. */
- return scsi_write_data(s, data, len);
+ if (len) {
+ if (s->sector_count == 0)
+ return 1;
+ /* Complete a partial write. */
+ scsi_write_partial_sector(s, data, len);
+ }
+ if (n == 0) {
+ /* Transfer completes immediately. */
+ if (s->sector_count == 0)
+ scsi_command_complete(s, SENSE_NO_SENSE);
+ else
+ scsi_transfer_complete(s);
}
-
- if (len != 0)
- return 1;
-
- if (s->sector_count == 0)
- scsi_command_complete(s, SENSE_NO_SENSE);
return 0;
}