From patchwork Mon Oct 25 15:17:33 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Wolf X-Patchwork-Id: 69106 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id ECDD2B70AF for ; Tue, 26 Oct 2010 02:27:21 +1100 (EST) Received: from localhost ([127.0.0.1]:35062 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PAOxC-0007lw-HA for incoming@patchwork.ozlabs.org; Mon, 25 Oct 2010 11:27:18 -0400 Received: from [140.186.70.92] (port=47598 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PAOnA-0001eE-Rf for qemu-devel@nongnu.org; Mon, 25 Oct 2010 11:16:58 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PAOn8-0008Bk-UP for qemu-devel@nongnu.org; Mon, 25 Oct 2010 11:16:56 -0400 Received: from mx1.redhat.com ([209.132.183.28]:1027) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PAOn8-0008BN-LV for qemu-devel@nongnu.org; Mon, 25 Oct 2010 11:16:54 -0400 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o9PFGsCk001312 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 25 Oct 2010 11:16:54 -0400 Received: from dhcp-5-188.str.redhat.com (dhcp-5-175.str.redhat.com [10.32.5.175]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id o9PFGpGO030378; Mon, 25 Oct 2010 11:16:53 -0400 From: Kevin Wolf To: qemu-devel@nongnu.org Date: Mon, 25 Oct 2010 17:17:33 +0200 Message-Id: <1288019856-16649-2-git-send-email-kwolf@redhat.com> In-Reply-To: <1288019856-16649-1-git-send-email-kwolf@redhat.com> References: <1288019856-16649-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. Cc: kwolf@redhat.com Subject: [Qemu-devel] [PATCH 1/4] scsi-disk: Implement rerror option X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This implements the rerror option for SCSI disks. Signed-off-by: Kevin Wolf --- blockdev.c | 2 +- hw/scsi-disk.c | 91 ++++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/blockdev.c b/blockdev.c index ff7602b..160fa84 100644 --- a/blockdev.c +++ b/blockdev.c @@ -326,7 +326,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) on_read_error = BLOCK_ERR_REPORT; if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { - if (type != IF_IDE && type != IF_VIRTIO && type != IF_NONE) { + if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) { fprintf(stderr, "rerror is no supported by this format\n"); return NULL; } diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 9628b39..55ba558 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -41,7 +41,10 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #define SCSI_DMA_BUF_SIZE 131072 #define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_REQ_STATUS_RETRY 0x01 +#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 typedef struct SCSIDiskState SCSIDiskState; @@ -70,6 +73,8 @@ struct SCSIDiskState char *serial; }; +static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); + static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag, uint32_t lun) { @@ -127,34 +132,30 @@ static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) static void scsi_read_complete(void * opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; + int n; r->req.aiocb = NULL; if (ret) { - DPRINTF("IO error\n"); - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0); - scsi_command_complete(r, CHECK_CONDITION, NO_SENSE); - return; + if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) { + return; + } } + DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->iov.iov_len); + n = r->iov.iov_len / 512; + r->sector += n; + r->sector_count -= n; r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); } -/* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIDevice *d, uint32_t tag) + +static void scsi_read_request(SCSIDiskReq *r) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad read tag 0x%x\n", tag); - /* ??? This is the wrong error. */ - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - return; - } if (r->sector_count == (uint32_t)-1) { DPRINTF("Read buf_len=%zd\n", r->iov.iov_len); r->sector_count = 0; @@ -177,14 +178,34 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) scsi_read_complete, r); if (r->req.aiocb == NULL) scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - r->sector += n; - r->sector_count -= n; } -static int scsi_handle_write_error(SCSIDiskReq *r, int error) +/* Read more data from scsi device into buffer. */ +static void scsi_read_data(SCSIDevice *d, uint32_t tag) { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); + SCSIDiskReq *r; + + r = scsi_find_request(s, tag); + if (!r) { + BADF("Bad read tag 0x%x\n", tag); + /* ??? This is the wrong error. */ + scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); + return; + } + + if (r->req.aiocb) { + BADF("Data transfer already in progress\n"); + } + + scsi_read_request(r); +} + +static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) +{ + int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - BlockErrorAction action = bdrv_get_on_error(s->bs, 0); + BlockErrorAction action = bdrv_get_on_error(s->bs, is_read); if (action == BLOCK_ERR_IGNORE) { bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, 0); @@ -193,10 +214,16 @@ static int scsi_handle_write_error(SCSIDiskReq *r, int error) if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC) || action == BLOCK_ERR_STOP_ANY) { - r->status |= SCSI_REQ_STATUS_RETRY; + + type &= SCSI_REQ_STATUS_RETRY_TYPE_MASK; + r->status |= SCSI_REQ_STATUS_RETRY | type; + bdrv_mon_event(s->bs, BDRV_ACTION_STOP, 0); vm_stop(0); } else { + if (type == SCSI_REQ_STATUS_RETRY_READ) { + r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0); + } scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, 0); @@ -214,8 +241,9 @@ static void scsi_write_complete(void * opaque, int ret) r->req.aiocb = NULL; if (ret) { - if (scsi_handle_write_error(r, -ret)) + if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) { return; + } } n = r->iov.iov_len / 512; @@ -288,8 +316,18 @@ static void scsi_dma_restart_bh(void *opaque) QTAILQ_FOREACH(req, &s->qdev.requests, next) { r = DO_UPCAST(SCSIDiskReq, req, req); if (r->status & SCSI_REQ_STATUS_RETRY) { - r->status &= ~SCSI_REQ_STATUS_RETRY; - scsi_write_request(r); + 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_request(r); + break; + case SCSI_REQ_STATUS_RETRY_WRITE: + scsi_write_request(r); + break; + } } } } @@ -1152,11 +1190,6 @@ static int scsi_disk_initfn(SCSIDevice *dev) return -1; } - if (bdrv_get_on_error(s->bs, 1) != BLOCK_ERR_REPORT) { - error_report("Device doesn't support drive option rerror"); - return -1; - } - if (!s->serial) { /* try to fall back to value set with legacy -drive serial=... */ dinfo = drive_get_by_blockdev(s->bs);