Commit f48a7a6e authored by Paolo Bonzini's avatar Paolo Bonzini Committed by Kevin Wolf
Browse files

scsi: remove devs array from SCSIBus



Change the devs array into a linked list, and add a scsi_device_find
function to navigate the children list instead.  This lets the SCSI
bus use more complex addressing, and HBAs can talk to the correct device
when there are multiple LUNs per target.

scsi_device_find may return another LUN on the same target if none is
found that matches exactly.

Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
parent d8bb00d6
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -217,7 +217,8 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
        s->async_len = 0;
    }

    if (target >= ESP_MAX_DEVS || !s->bus.devs[target]) {
    s->current_dev = scsi_device_find(&s->bus, target, 0);
    if (!s->current_dev) {
        // No such drive
        s->rregs[ESP_RSTAT] = 0;
        s->rregs[ESP_RINTR] = INTR_DC;
@@ -225,7 +226,6 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
        esp_raise_irq(s);
        return 0;
    }
    s->current_dev = s->bus.devs[target];
    return dmalen;
}

@@ -233,10 +233,12 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
{
    int32_t datalen;
    int lun;
    SCSIDevice *current_lun;

    trace_esp_do_busid_cmd(busid);
    lun = busid & 7;
    s->current_req = scsi_req_new(s->current_dev, 0, lun, buf, NULL);
    current_lun = scsi_device_find(&s->bus, s->current_dev->id, lun);
    s->current_req = scsi_req_new(current_lun, 0, lun, buf, NULL);
    datalen = scsi_req_enqueue(s->current_req);
    s->ti_size = datalen;
    if (datalen != 0) {
+7 −15
Original line number Diff line number Diff line
@@ -531,7 +531,7 @@ static void lsi_bad_selection(LSIState *s, uint32_t id)
/* Initiate a SCSI layer data transfer.  */
static void lsi_do_dma(LSIState *s, int out)
{
    uint32_t count, id;
    uint32_t count;
    target_phys_addr_t addr;
    SCSIDevice *dev;

@@ -542,12 +542,8 @@ static void lsi_do_dma(LSIState *s, int out)
        return;
    }

    id = (s->current->tag >> 8) & 0xf;
    dev = s->bus.devs[id];
    if (!dev) {
        lsi_bad_selection(s, id);
        return;
    }
    dev = s->current->req->dev;
    assert(dev);

    count = s->dbc;
    if (count > s->current->dma_len)
@@ -771,7 +767,7 @@ static void lsi_do_command(LSIState *s)
    s->command_complete = 0;

    id = (s->select_tag >> 8) & 0xf;
    dev = s->bus.devs[id];
    dev = scsi_device_find(&s->bus, id, s->current_lun);
    if (!dev) {
        lsi_bad_selection(s, id);
        return;
@@ -1202,7 +1198,7 @@ again:
                }
                s->sstat0 |= LSI_SSTAT0_WOA;
                s->scntl1 &= ~LSI_SCNTL1_IARB;
                if (id >= LSI_MAX_DEVS || !s->bus.devs[id]) {
                if (!scsi_device_find(&s->bus, id, 0)) {
                    lsi_bad_selection(s, id);
                    break;
                }
@@ -1684,14 +1680,10 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
        if (val & LSI_SCNTL1_RST) {
            if (!(s->sstat0 & LSI_SSTAT0_RST)) {
                DeviceState *dev;
                int id;

                for (id = 0; id < LSI_MAX_DEVS; id++) {
                    if (s->bus.devs[id]) {
                        dev = &s->bus.devs[id]->qdev;
                QTAILQ_FOREACH(dev, &s->bus.qbus.children, sibling) {
                    dev->info->reset(dev);
                }
                }
                s->sstat0 |= LSI_SSTAT0_RST;
                lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0);
            }
+1 −1
Original line number Diff line number Diff line
@@ -73,7 +73,7 @@ struct BusState {
    const char *name;
    int allow_hotplug;
    int qdev_allocated;
    QTAILQ_HEAD(, DeviceState) children;
    QTAILQ_HEAD(ChildrenHead, DeviceState) children;
    QLIST_ENTRY(BusState) sibling;
};

+30 −23
Original line number Diff line number Diff line
@@ -37,30 +37,31 @@ static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base)
    SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
    SCSIDeviceInfo *info = DO_UPCAST(SCSIDeviceInfo, qdev, base);
    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
    SCSIDevice *olddev;
    int rc = -1;

    if (dev->id == -1) {
        for (dev->id = 0; dev->id < bus->info->ndev; dev->id++) {
            if (bus->devs[dev->id] == NULL)
        int id;
        for (id = 0; id < bus->info->ndev; id++) {
            if (!scsi_device_find(bus, id, 0)) {
                dev->id = id;
                break;
            }
        }
    }
    if (dev->id >= bus->info->ndev) {
        error_report("bad scsi device id: %d", dev->id);
        goto err;
    }

    if (bus->devs[dev->id]) {
        qdev_free(&bus->devs[dev->id]->qdev);
    olddev = scsi_device_find(bus, dev->id, dev->lun);
    if (olddev && dev->lun == olddev->lun) {
        qdev_free(&olddev->qdev);
    }
    bus->devs[dev->id] = dev;

    dev->info = info;
    QTAILQ_INIT(&dev->requests);
    rc = dev->info->init(dev);
    if (rc != 0) {
        bus->devs[dev->id] = NULL;
    }

err:
    return rc;
@@ -69,13 +70,10 @@ err:
static int scsi_qdev_exit(DeviceState *qdev)
{
    SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);

    assert(bus->devs[dev->id] != NULL);
    if (bus->devs[dev->id]->info->destroy) {
        bus->devs[dev->id]->info->destroy(bus->devs[dev->id]);
    if (dev->info->destroy) {
        dev->info->destroy(dev);
    }
    bus->devs[dev->id] = NULL;
    return 0;
}

@@ -1157,19 +1155,28 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
static char *scsibus_get_fw_dev_path(DeviceState *dev)
{
    SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
    SCSIBus *bus = scsi_bus_from_device(d);
    char path[100];
    int i;

    for (i = 0; i < bus->info->ndev; i++) {
        if (bus->devs[i] == d) {
            break;
        }
    snprintf(path, sizeof(path), "%s@%d:%d:%d", qdev_fw_name(dev),
             0, d->id, d->lun);

    return strdup(path);
}

    assert(i != bus->info->ndev);
SCSIDevice *scsi_device_find(SCSIBus *bus, int id, int lun)
{
    DeviceState *qdev;
    SCSIDevice *target_dev = NULL;

    snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), i);
    QTAILQ_FOREACH_REVERSE(qdev, &bus->qbus.children, ChildrenHead, sibling) {
        SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);

    return strdup(path);
        if (dev->id == id) {
            if (dev->lun == lun) {
                return dev;
            }
            target_dev = dev;
        }
    }
    return target_dev;
}
+1 −2
Original line number Diff line number Diff line
@@ -110,8 +110,6 @@ struct SCSIBus {

    SCSISense unit_attention;
    const SCSIBusInfo *info;

    SCSIDevice *devs[MAX_SCSI_DEVS];
};

void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info);
@@ -195,5 +193,6 @@ void scsi_req_abort(SCSIRequest *req, int status);
void scsi_req_cancel(SCSIRequest *req);
void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense);
int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
SCSIDevice *scsi_device_find(SCSIBus *bus, int target, int lun);

#endif
Loading