aboutsummaryrefslogtreecommitdiff
path: root/hw/lsi53c895a.c
diff options
context:
space:
mode:
authorHannes Reinecke <hare@suse.de>2011-04-18 12:35:39 +0200
committerPaolo Bonzini <pbonzini@redhat.com>2011-05-26 12:14:15 +0200
commit5c6c0e513600ba57c3e73b7151d3c0664438f7b5 (patch)
treef08a9d140398fc3558ab6112ab03cc3831fed6f7 /hw/lsi53c895a.c
parent11257187e122f1b33e4983b881a2b6009f5993ca (diff)
scsi: Use 'SCSIRequest' directly
Currently the SCSIRequest structure is abstracted away and cannot accessed directly from the driver. This requires the handler to do a lookup on an abstract 'tag' which identifies the SCSIRequest structure. With this patch the SCSIRequest structure is exposed to the driver. This allows use to use it directly as an argument to the SCSIDeviceInfo callback functions and remove the lookup. A new callback function 'alloc_req' is introduced matching 'free req'; unref'ing to free up resources after use is moved into the scsi_command_complete callbacks. This temporarily introduces a leak of requests that are cancelled, when they are removed from the queue and not from the driver. This is fixed later by introducing scsi_req_cancel. That patch in turn depends on this one, because the argument to scsi_req_cancel is a SCSIRequest. Signed-off-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'hw/lsi53c895a.c')
-rw-r--r--hw/lsi53c895a.c56
1 files changed, 32 insertions, 24 deletions
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 1ebcde74ef..56234f836c 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -174,6 +174,7 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0)
#define LSI_TAG_VALID (1 << 16)
typedef struct lsi_request {
+ SCSIRequest *req;
uint32_t tag;
uint32_t dma_len;
uint8_t *dma_buf;
@@ -567,11 +568,9 @@ static void lsi_do_dma(LSIState *s, int out)
s->csbc += count;
s->dnad += count;
s->dbc -= count;
-
- if (s->current->dma_buf == NULL) {
- s->current->dma_buf = dev->info->get_buf(dev, s->current->tag);
+ if (s->current->dma_buf == NULL) {
+ s->current->dma_buf = dev->info->get_buf(s->current->req);
}
-
/* ??? Set SFBR to first data byte. */
if (out) {
cpu_physical_memory_read(addr, s->current->dma_buf, count);
@@ -583,10 +582,10 @@ static void lsi_do_dma(LSIState *s, int out)
s->current->dma_buf = NULL;
if (out) {
/* Write the data. */
- dev->info->write_data(dev, s->current->tag);
+ dev->info->write_data(s->current->req);
} else {
/* Request any remaining data. */
- dev->info->read_data(dev, s->current->tag);
+ dev->info->read_data(s->current->req);
}
} else {
s->current->dma_buf += count;
@@ -698,12 +697,10 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
return 1;
}
}
-
-/* Callback to indicate that the SCSI layer has completed a transfer. */
-static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
- uint32_t arg)
+ /* Callback to indicate that the SCSI layer has completed a transfer. */
+static void lsi_command_complete(SCSIRequest *req, int reason, uint32_t arg)
{
- LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent);
+ LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
@@ -718,21 +715,24 @@ static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
lsi_set_phase(s, PHASE_ST);
}
- qemu_free(s->current);
- s->current = NULL;
-
+ if (s->current && req == s->current->req) {
+ scsi_req_unref(s->current->req);
+ qemu_free(s->current);
+ s->current = NULL;
+ }
lsi_resume_script(s);
return;
}
- if (s->waiting == 1 || !s->current || tag != s->current->tag ||
+ if (s->waiting == 1 || !s->current || req->tag != s->current->tag ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
- if (lsi_queue_tag(s, tag, arg))
+ if (lsi_queue_tag(s, req->tag, arg)) {
return;
+ }
}
/* host adapter (re)connected */
- DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg);
+ DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, arg);
s->current->dma_len = arg;
s->command_complete = 1;
if (!s->waiting)
@@ -768,14 +768,16 @@ static void lsi_do_command(LSIState *s)
assert(s->current == NULL);
s->current = qemu_mallocz(sizeof(lsi_request));
s->current->tag = s->select_tag;
+ s->current->req = dev->info->alloc_req(dev, s->current->tag,
+ s->current_lun);
- n = dev->info->send_command(dev, s->current->tag, buf, s->current_lun);
+ n = dev->info->send_command(s->current->req, buf);
if (n > 0) {
lsi_set_phase(s, PHASE_DI);
- dev->info->read_data(dev, s->current->tag);
+ dev->info->read_data(s->current->req);
} else if (n < 0) {
lsi_set_phase(s, PHASE_DO);
- dev->info->write_data(dev, s->current->tag);
+ dev->info->write_data(s->current->req);
}
if (!s->command_complete) {
@@ -868,13 +870,15 @@ static void lsi_do_msgout(LSIState *s)
int len;
uint32_t current_tag;
SCSIDevice *current_dev;
- lsi_request *p, *p_next;
+ lsi_request *current_req, *p, *p_next;
int id;
if (s->current) {
current_tag = s->current->tag;
+ current_req = s->current;
} else {
current_tag = s->select_tag;
+ current_req = lsi_find_by_tag(s, current_tag);
}
id = (current_tag >> 8) & 0xf;
current_dev = s->bus.devs[id];
@@ -926,7 +930,9 @@ static void lsi_do_msgout(LSIState *s)
case 0x0d:
/* The ABORT TAG message clears the current I/O process only. */
DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag);
- current_dev->info->cancel_io(current_dev, current_tag);
+ if (current_req) {
+ current_dev->info->cancel_io(current_req->req);
+ }
lsi_disconnect(s);
break;
case 0x06:
@@ -949,7 +955,9 @@ static void lsi_do_msgout(LSIState *s)
}
/* clear the current I/O process */
- current_dev->info->cancel_io(current_dev, current_tag);
+ if (s->current) {
+ current_dev->info->cancel_io(s->current->req);
+ }
/* As the current implemented devices scsi_disk and scsi_generic
only support one LUN, we don't need to keep track of LUNs.
@@ -961,7 +969,7 @@ static void lsi_do_msgout(LSIState *s)
id = current_tag & 0x0000ff00;
QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) {
if ((p->tag & 0x0000ff00) == id) {
- current_dev->info->cancel_io(current_dev, p->tag);
+ current_dev->info->cancel_io(p->req);
QTAILQ_REMOVE(&s->queue, p, next);
}
}