提交 71544d30 编写于 作者: P Paolo Bonzini 提交者: Kevin Wolf

scsi: push request restart to SCSIDevice

The request restart mechanism is generic and could be reused for
scsi-generic.  In the meanwhile, pushing it to SCSIDevice avoids
that scsi_dma_restart_bh looks at SCSIGenericReqs when working on
a scsi-block device.

The code is the same that is already in hw/scsi-disk.c, with
the type flags replaced by req->cmd.mode and a more generic way to
requeue SCSI_XFER_NONE commands.

I also added a missing call to qemu_del_vm_change_state_handler.
Signed-off-by: NPaolo Bonzini <pbonzini@redhat.com>
Signed-off-by: NKevin Wolf <kwolf@redhat.com>
上级 c9501c95
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
static char *scsibus_get_fw_dev_path(DeviceState *dev); static char *scsibus_get_fw_dev_path(DeviceState *dev);
static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf);
static void scsi_req_dequeue(SCSIRequest *req);
static int scsi_build_sense(uint8_t *in_buf, int in_len, static int scsi_build_sense(uint8_t *in_buf, int in_len,
uint8_t *buf, int len, bool fixed); uint8_t *buf, int len, bool fixed);
...@@ -33,6 +34,53 @@ void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info) ...@@ -33,6 +34,53 @@ void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info)
bus->qbus.allow_hotplug = 1; bus->qbus.allow_hotplug = 1;
} }
static void scsi_dma_restart_bh(void *opaque)
{
SCSIDevice *s = opaque;
SCSIRequest *req, *next;
qemu_bh_delete(s->bh);
s->bh = NULL;
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
scsi_req_ref(req);
if (req->retry) {
req->retry = false;
switch (req->cmd.mode) {
case SCSI_XFER_FROM_DEV:
case SCSI_XFER_TO_DEV:
scsi_req_continue(req);
break;
case SCSI_XFER_NONE:
scsi_req_dequeue(req);
scsi_req_enqueue(req);
break;
}
}
scsi_req_unref(req);
}
}
void scsi_req_retry(SCSIRequest *req)
{
/* No need to save a reference, because scsi_dma_restart_bh just
* looks at the request list. */
req->retry = true;
}
static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
{
SCSIDevice *s = opaque;
if (!running) {
return;
}
if (!s->bh) {
s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
qemu_bh_schedule(s->bh);
}
}
static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base) static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base)
{ {
SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
...@@ -83,6 +131,10 @@ static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base) ...@@ -83,6 +131,10 @@ static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base)
dev->info = info; dev->info = info;
QTAILQ_INIT(&dev->requests); QTAILQ_INIT(&dev->requests);
rc = dev->info->init(dev); rc = dev->info->init(dev);
if (rc == 0) {
dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb,
dev);
}
err: err:
return rc; return rc;
...@@ -92,6 +144,9 @@ static int scsi_qdev_exit(DeviceState *qdev) ...@@ -92,6 +144,9 @@ static int scsi_qdev_exit(DeviceState *qdev)
{ {
SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
if (dev->vmsentry) {
qemu_del_vm_change_state_handler(dev->vmsentry);
}
if (dev->info->destroy) { if (dev->info->destroy) {
dev->info->destroy(dev); dev->info->destroy(dev);
} }
...@@ -586,6 +641,7 @@ int32_t scsi_req_enqueue(SCSIRequest *req) ...@@ -586,6 +641,7 @@ int32_t scsi_req_enqueue(SCSIRequest *req)
static void scsi_req_dequeue(SCSIRequest *req) static void scsi_req_dequeue(SCSIRequest *req)
{ {
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
req->retry = false;
if (req->enqueued) { if (req->enqueued) {
QTAILQ_REMOVE(&req->dev->requests, req, next); QTAILQ_REMOVE(&req->dev->requests, req, next);
req->enqueued = false; req->enqueued = false;
......
...@@ -42,12 +42,6 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) ...@@ -42,12 +42,6 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#define SCSI_DMA_BUF_SIZE 131072 #define SCSI_DMA_BUF_SIZE 131072
#define SCSI_MAX_INQUIRY_LEN 256 #define SCSI_MAX_INQUIRY_LEN 256
#define SCSI_REQ_STATUS_RETRY 0x01
#define SCSI_REQ_STATUS_RETRY_TYPE_MASK 0x06
#define SCSI_REQ_STATUS_RETRY_READ 0x00
#define SCSI_REQ_STATUS_RETRY_WRITE 0x02
#define SCSI_REQ_STATUS_RETRY_FLUSH 0x04
typedef struct SCSIDiskState SCSIDiskState; typedef struct SCSIDiskState SCSIDiskState;
typedef struct SCSIDiskReq { typedef struct SCSIDiskReq {
...@@ -58,7 +52,6 @@ typedef struct SCSIDiskReq { ...@@ -58,7 +52,6 @@ typedef struct SCSIDiskReq {
uint32_t buflen; uint32_t buflen;
struct iovec iov; struct iovec iov;
QEMUIOVector qiov; QEMUIOVector qiov;
uint32_t status;
BlockAcctCookie acct; BlockAcctCookie acct;
} SCSIDiskReq; } SCSIDiskReq;
...@@ -75,8 +68,7 @@ struct SCSIDiskState ...@@ -75,8 +68,7 @@ struct SCSIDiskState
bool tray_locked; bool tray_locked;
}; };
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); static int scsi_handle_rw_error(SCSIDiskReq *r, int error);
static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf);
static void scsi_free_request(SCSIRequest *req) static void scsi_free_request(SCSIRequest *req)
{ {
...@@ -102,7 +94,6 @@ static void scsi_cancel_io(SCSIRequest *req) ...@@ -102,7 +94,6 @@ static void scsi_cancel_io(SCSIRequest *req)
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
DPRINTF("Cancel tag=0x%x\n", req->tag); DPRINTF("Cancel tag=0x%x\n", req->tag);
r->status &= ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK);
if (r->req.aiocb) { if (r->req.aiocb) {
bdrv_aio_cancel(r->req.aiocb); bdrv_aio_cancel(r->req.aiocb);
...@@ -139,7 +130,7 @@ static void scsi_read_complete(void * opaque, int ret) ...@@ -139,7 +130,7 @@ static void scsi_read_complete(void * opaque, int ret)
} }
if (ret) { if (ret) {
if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) { if (scsi_handle_rw_error(r, -ret)) {
goto done; goto done;
} }
} }
...@@ -168,7 +159,7 @@ static void scsi_flush_complete(void * opaque, int ret) ...@@ -168,7 +159,7 @@ static void scsi_flush_complete(void * opaque, int ret)
} }
if (ret < 0) { if (ret < 0) {
if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_FLUSH)) { if (scsi_handle_rw_error(r, -ret)) {
goto done; goto done;
} }
} }
...@@ -233,9 +224,9 @@ static void scsi_read_data(SCSIRequest *req) ...@@ -233,9 +224,9 @@ static void scsi_read_data(SCSIRequest *req)
* scsi_handle_rw_error always manages its reference counts, independent * scsi_handle_rw_error always manages its reference counts, independent
* of the return value. * of the return value.
*/ */
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
{ {
int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); int is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read); BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read);
...@@ -247,17 +238,10 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) ...@@ -247,17 +238,10 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC) if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
|| action == BLOCK_ERR_STOP_ANY) { || action == BLOCK_ERR_STOP_ANY) {
type &= SCSI_REQ_STATUS_RETRY_TYPE_MASK;
r->status |= SCSI_REQ_STATUS_RETRY | type;
bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read); bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read);
vm_stop(RUN_STATE_IO_ERROR); vm_stop(RUN_STATE_IO_ERROR);
bdrv_iostatus_set_err(s->qdev.conf.bs, error); bdrv_iostatus_set_err(s->qdev.conf.bs, error);
scsi_req_retry(&r->req);
/* No need to save a reference, because scsi_dma_restart_bh just
* looks at the request list. If a request is canceled, the
* retry request is just dropped.
*/
} else { } else {
switch (error) { switch (error) {
case ENOMEDIUM: case ENOMEDIUM:
...@@ -290,7 +274,7 @@ static void scsi_write_complete(void * opaque, int ret) ...@@ -290,7 +274,7 @@ static void scsi_write_complete(void * opaque, int ret)
} }
if (ret) { if (ret) {
if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) { if (scsi_handle_rw_error(r, -ret)) {
goto done; goto done;
} }
} }
...@@ -347,51 +331,6 @@ static void scsi_write_data(SCSIRequest *req) ...@@ -347,51 +331,6 @@ static void scsi_write_data(SCSIRequest *req)
} }
} }
static void scsi_dma_restart_bh(void *opaque)
{
SCSIDiskState *s = opaque;
SCSIRequest *req;
SCSIDiskReq *r;
qemu_bh_delete(s->bh);
s->bh = NULL;
QTAILQ_FOREACH(req, &s->qdev.requests, next) {
r = DO_UPCAST(SCSIDiskReq, req, req);
if (r->status & SCSI_REQ_STATUS_RETRY) {
int status = r->status;
r->status &=
~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK);
switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) {
case SCSI_REQ_STATUS_RETRY_READ:
scsi_read_data(&r->req);
break;
case SCSI_REQ_STATUS_RETRY_WRITE:
scsi_write_data(&r->req);
break;
case SCSI_REQ_STATUS_RETRY_FLUSH:
scsi_send_command(&r->req, r->req.cmd.buf);
break;
}
}
}
}
static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
{
SCSIDiskState *s = opaque;
if (!running) {
return;
}
if (!s->bh) {
s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
qemu_bh_schedule(s->bh);
}
}
/* Return a pointer to the data buffer. */ /* Return a pointer to the data buffer. */
static uint8_t *scsi_get_buf(SCSIRequest *req) static uint8_t *scsi_get_buf(SCSIRequest *req)
{ {
...@@ -1591,7 +1530,6 @@ static int scsi_initfn(SCSIDevice *dev) ...@@ -1591,7 +1530,6 @@ static int scsi_initfn(SCSIDevice *dev)
} }
bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize); bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
bdrv_iostatus_enable(s->qdev.conf.bs); bdrv_iostatus_enable(s->qdev.conf.bs);
add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0");
return 0; return 0;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include "qdev.h" #include "qdev.h"
#include "block.h" #include "block.h"
#include "sysemu.h"
#define MAX_SCSI_DEVS 255 #define MAX_SCSI_DEVS 255
...@@ -52,6 +53,7 @@ struct SCSIRequest { ...@@ -52,6 +53,7 @@ struct SCSIRequest {
uint32_t sense_len; uint32_t sense_len;
bool enqueued; bool enqueued;
bool io_canceled; bool io_canceled;
bool retry;
void *hba_private; void *hba_private;
QTAILQ_ENTRY(SCSIRequest) next; QTAILQ_ENTRY(SCSIRequest) next;
}; };
...@@ -59,6 +61,8 @@ struct SCSIRequest { ...@@ -59,6 +61,8 @@ struct SCSIRequest {
struct SCSIDevice struct SCSIDevice
{ {
DeviceState qdev; DeviceState qdev;
VMChangeStateEntry *vmsentry;
QEMUBH *bh;
uint32_t id; uint32_t id;
BlockConf conf; BlockConf conf;
SCSIDeviceInfo *info; SCSIDeviceInfo *info;
...@@ -194,6 +198,7 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req); ...@@ -194,6 +198,7 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req);
int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len); int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len);
void scsi_req_abort(SCSIRequest *req, int status); void scsi_req_abort(SCSIRequest *req, int status);
void scsi_req_cancel(SCSIRequest *req); void scsi_req_cancel(SCSIRequest *req);
void scsi_req_retry(SCSIRequest *req);
void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense); void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense);
int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册
新手
引导
客服 返回
顶部