lsi: Handle removal of selected devices
We must not store references to selected devices as they may be hot-removed. Instead, look up the device based on its tag right before using it. If the device disappeared, throw an interrupt and disconnect. Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
		
							parent
							
								
									12aa6dd61c
								
							
						
					
					
						commit
						64d564094c
					
				@ -175,7 +175,6 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
typedef struct lsi_request {
 | 
					typedef struct lsi_request {
 | 
				
			||||||
    uint32_t tag;
 | 
					    uint32_t tag;
 | 
				
			||||||
    SCSIDevice *dev;
 | 
					 | 
				
			||||||
    uint32_t dma_len;
 | 
					    uint32_t dma_len;
 | 
				
			||||||
    uint8_t *dma_buf;
 | 
					    uint8_t *dma_buf;
 | 
				
			||||||
    uint32_t pending;
 | 
					    uint32_t pending;
 | 
				
			||||||
@ -202,7 +201,6 @@ typedef struct {
 | 
				
			|||||||
     * 3 if a DMA operation is in progress.  */
 | 
					     * 3 if a DMA operation is in progress.  */
 | 
				
			||||||
    int waiting;
 | 
					    int waiting;
 | 
				
			||||||
    SCSIBus bus;
 | 
					    SCSIBus bus;
 | 
				
			||||||
    SCSIDevice *select_dev;
 | 
					 | 
				
			||||||
    int current_lun;
 | 
					    int current_lun;
 | 
				
			||||||
    /* The tag is a combination of the device ID and the SCSI tag.  */
 | 
					    /* The tag is a combination of the device ID and the SCSI tag.  */
 | 
				
			||||||
    uint32_t select_tag;
 | 
					    uint32_t select_tag;
 | 
				
			||||||
@ -518,11 +516,25 @@ static void lsi_resume_script(LSIState *s)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void lsi_disconnect(LSIState *s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    s->scntl1 &= ~LSI_SCNTL1_CON;
 | 
				
			||||||
 | 
					    s->sstat1 &= ~PHASE_MASK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void lsi_bad_selection(LSIState *s, uint32_t id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DPRINTF("Selected absent target %d\n", id);
 | 
				
			||||||
 | 
					    lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO);
 | 
				
			||||||
 | 
					    lsi_disconnect(s);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Initiate a SCSI layer data transfer.  */
 | 
					/* Initiate a SCSI layer data transfer.  */
 | 
				
			||||||
static void lsi_do_dma(LSIState *s, int out)
 | 
					static void lsi_do_dma(LSIState *s, int out)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    uint32_t count;
 | 
					    uint32_t count, id;
 | 
				
			||||||
    target_phys_addr_t addr;
 | 
					    target_phys_addr_t addr;
 | 
				
			||||||
 | 
					    SCSIDevice *dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert(s->current);
 | 
					    assert(s->current);
 | 
				
			||||||
    if (!s->current->dma_len) {
 | 
					    if (!s->current->dma_len) {
 | 
				
			||||||
@ -531,6 +543,13 @@ static void lsi_do_dma(LSIState *s, int out)
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    id = s->current->tag >> 8;
 | 
				
			||||||
 | 
					    dev = s->bus.devs[id];
 | 
				
			||||||
 | 
					    if (!dev) {
 | 
				
			||||||
 | 
					        lsi_bad_selection(s, id);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    count = s->dbc;
 | 
					    count = s->dbc;
 | 
				
			||||||
    if (count > s->current->dma_len)
 | 
					    if (count > s->current->dma_len)
 | 
				
			||||||
        count = s->current->dma_len;
 | 
					        count = s->current->dma_len;
 | 
				
			||||||
@ -550,8 +569,7 @@ static void lsi_do_dma(LSIState *s, int out)
 | 
				
			|||||||
    s->dbc -= count;
 | 
					    s->dbc -= count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (s->current->dma_buf == NULL) {
 | 
					    if (s->current->dma_buf == NULL) {
 | 
				
			||||||
        s->current->dma_buf = s->current->dev->info->get_buf(s->current->dev,
 | 
					        s->current->dma_buf = dev->info->get_buf(dev, s->current->tag);
 | 
				
			||||||
                                                             s->current->tag);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* ??? Set SFBR to first data byte.  */
 | 
					    /* ??? Set SFBR to first data byte.  */
 | 
				
			||||||
@ -565,10 +583,10 @@ static void lsi_do_dma(LSIState *s, int out)
 | 
				
			|||||||
        s->current->dma_buf = NULL;
 | 
					        s->current->dma_buf = NULL;
 | 
				
			||||||
        if (out) {
 | 
					        if (out) {
 | 
				
			||||||
            /* Write the data.  */
 | 
					            /* Write the data.  */
 | 
				
			||||||
            s->current->dev->info->write_data(s->current->dev, s->current->tag);
 | 
					            dev->info->write_data(dev, s->current->tag);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            /* Request any remaining data.  */
 | 
					            /* Request any remaining data.  */
 | 
				
			||||||
            s->current->dev->info->read_data(s->current->dev, s->current->tag);
 | 
					            dev->info->read_data(dev, s->current->tag);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        s->current->dma_buf += count;
 | 
					        s->current->dma_buf += count;
 | 
				
			||||||
@ -715,7 +733,9 @@ static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static void lsi_do_command(LSIState *s)
 | 
					static void lsi_do_command(LSIState *s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    SCSIDevice *dev;
 | 
				
			||||||
    uint8_t buf[16];
 | 
					    uint8_t buf[16];
 | 
				
			||||||
 | 
					    uint32_t id;
 | 
				
			||||||
    int n;
 | 
					    int n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DPRINTF("Send command len=%d\n", s->dbc);
 | 
					    DPRINTF("Send command len=%d\n", s->dbc);
 | 
				
			||||||
@ -725,19 +745,24 @@ static void lsi_do_command(LSIState *s)
 | 
				
			|||||||
    s->sfbr = buf[0];
 | 
					    s->sfbr = buf[0];
 | 
				
			||||||
    s->command_complete = 0;
 | 
					    s->command_complete = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    id = s->select_tag >> 8;
 | 
				
			||||||
 | 
					    dev = s->bus.devs[id];
 | 
				
			||||||
 | 
					    if (!dev) {
 | 
				
			||||||
 | 
					        lsi_bad_selection(s, id);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert(s->current == NULL);
 | 
					    assert(s->current == NULL);
 | 
				
			||||||
    s->current = qemu_mallocz(sizeof(lsi_request));
 | 
					    s->current = qemu_mallocz(sizeof(lsi_request));
 | 
				
			||||||
    s->current->tag = s->select_tag;
 | 
					    s->current->tag = s->select_tag;
 | 
				
			||||||
    s->current->dev = s->select_dev;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    n = s->current->dev->info->send_command(s->current->dev, s->current->tag, buf,
 | 
					    n = dev->info->send_command(dev, s->current->tag, buf, s->current_lun);
 | 
				
			||||||
                                            s->current_lun);
 | 
					 | 
				
			||||||
    if (n > 0) {
 | 
					    if (n > 0) {
 | 
				
			||||||
        lsi_set_phase(s, PHASE_DI);
 | 
					        lsi_set_phase(s, PHASE_DI);
 | 
				
			||||||
        s->current->dev->info->read_data(s->current->dev, s->current->tag);
 | 
					        dev->info->read_data(dev, s->current->tag);
 | 
				
			||||||
    } else if (n < 0) {
 | 
					    } else if (n < 0) {
 | 
				
			||||||
        lsi_set_phase(s, PHASE_DO);
 | 
					        lsi_set_phase(s, PHASE_DO);
 | 
				
			||||||
        s->current->dev->info->write_data(s->current->dev, s->current->tag);
 | 
					        dev->info->write_data(dev, s->current->tag);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!s->command_complete) {
 | 
					    if (!s->command_complete) {
 | 
				
			||||||
@ -771,12 +796,6 @@ static void lsi_do_status(LSIState *s)
 | 
				
			|||||||
    lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */
 | 
					    lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void lsi_disconnect(LSIState *s)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    s->scntl1 &= ~LSI_SCNTL1_CON;
 | 
					 | 
				
			||||||
    s->sstat1 &= ~PHASE_MASK;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void lsi_do_msgin(LSIState *s)
 | 
					static void lsi_do_msgin(LSIState *s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int len;
 | 
					    int len;
 | 
				
			||||||
@ -1092,9 +1111,7 @@ again:
 | 
				
			|||||||
                s->sstat0 |= LSI_SSTAT0_WOA;
 | 
					                s->sstat0 |= LSI_SSTAT0_WOA;
 | 
				
			||||||
                s->scntl1 &= ~LSI_SCNTL1_IARB;
 | 
					                s->scntl1 &= ~LSI_SCNTL1_IARB;
 | 
				
			||||||
                if (id >= LSI_MAX_DEVS || !s->bus.devs[id]) {
 | 
					                if (id >= LSI_MAX_DEVS || !s->bus.devs[id]) {
 | 
				
			||||||
                    DPRINTF("Selected absent target %d\n", id);
 | 
					                    lsi_bad_selection(s, id);
 | 
				
			||||||
                    lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO);
 | 
					 | 
				
			||||||
                    lsi_disconnect(s);
 | 
					 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                DPRINTF("Selected target %d%s\n",
 | 
					                DPRINTF("Selected target %d%s\n",
 | 
				
			||||||
@ -1102,7 +1119,6 @@ again:
 | 
				
			|||||||
                /* ??? Linux drivers compain when this is set.  Maybe
 | 
					                /* ??? Linux drivers compain when this is set.  Maybe
 | 
				
			||||||
                   it only applies in low-level mode (unimplemented).
 | 
					                   it only applies in low-level mode (unimplemented).
 | 
				
			||||||
                lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */
 | 
					                lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */
 | 
				
			||||||
                s->select_dev = s->bus.devs[id];
 | 
					 | 
				
			||||||
                s->select_tag = id << 8;
 | 
					                s->select_tag = id << 8;
 | 
				
			||||||
                s->scntl1 |= LSI_SCNTL1_CON;
 | 
					                s->scntl1 |= LSI_SCNTL1_CON;
 | 
				
			||||||
                if (insn & (1 << 3)) {
 | 
					                if (insn & (1 << 3)) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user