From patchwork Wed Jun 15 14:03:11 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Wolf X-Patchwork-Id: 100543 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id A68CEB6F92 for ; Thu, 16 Jun 2011 01:14:29 +1000 (EST) Received: from localhost ([::1]:40834 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QWrnV-0005eU-6A for incoming@patchwork.ozlabs.org; Wed, 15 Jun 2011 11:14:25 -0400 Received: from eggs.gnu.org ([140.186.70.92]:35185) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QWqeE-0003lw-45 for qemu-devel@nongnu.org; Wed, 15 Jun 2011 10:00:55 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QWqe8-0005uw-1o for qemu-devel@nongnu.org; Wed, 15 Jun 2011 10:00:45 -0400 Received: from mx1.redhat.com ([209.132.183.28]:31416) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QWqe6-0005uT-LL for qemu-devel@nongnu.org; Wed, 15 Jun 2011 10:00:39 -0400 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p5FE0aXB006074 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 15 Jun 2011 10:00:37 -0400 Received: from dhcp-5-188.str.redhat.com (dhcp-5-175.str.redhat.com [10.32.5.175]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id p5FE0Krk024634; Wed, 15 Jun 2011 10:00:36 -0400 From: Kevin Wolf To: anthony@codemonkey.ws Date: Wed, 15 Jun 2011 16:03:11 +0200 Message-Id: <1308146593-19842-13-git-send-email-kwolf@redhat.com> In-Reply-To: <1308146593-19842-1-git-send-email-kwolf@redhat.com> References: <1308146593-19842-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.12 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [Qemu-devel] [PATCH 12/14] ide: add TRIM support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Christoph Hellwig Add support for TRIM sub function of the data set management command, and wire it up to the qemu discard infrastructure. Signed-off-by: Christoph Hellwig Signed-off-by: Kevin Wolf --- hw/ide/core.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++- hw/ide/internal.h | 14 +++++++- hw/ide/macio.c | 4 ++ hw/ide/pci.c | 9 ++++- hw/ide/qdev.c | 5 +++ 5 files changed, 124 insertions(+), 5 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 14bda82..ca17a43 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -78,7 +78,7 @@ static void ide_identify(IDEState *s) { uint16_t *p; unsigned int oldsize; - IDEDevice *dev; + IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master; if (s->identify_set) { memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data)); @@ -124,6 +124,9 @@ static void ide_identify(IDEState *s) put_le16(p + 66, 120); put_le16(p + 67, 120); put_le16(p + 68, 120); + if (dev && dev->conf.discard_granularity) { + put_le16(p + 69, (1 << 14)); /* determinate TRIM behavior */ + } if (s->ncq_queues) { put_le16(p + 75, s->ncq_queues - 1); @@ -154,9 +157,12 @@ static void ide_identify(IDEState *s) put_le16(p + 101, s->nb_sectors >> 16); put_le16(p + 102, s->nb_sectors >> 32); put_le16(p + 103, s->nb_sectors >> 48); - dev = s->unit ? s->bus->slave : s->bus->master; + if (dev && dev->conf.physical_block_size) put_le16(p + 106, 0x6000 | get_physical_block_exp(&dev->conf)); + if (dev && dev->conf.discard_granularity) { + put_le16(p + 169, 1); /* TRIM support */ + } memcpy(s->identify_data, p, sizeof(s->identify_data)); s->identify_set = 1; @@ -299,6 +305,74 @@ static void ide_set_signature(IDEState *s) } } +typedef struct TrimAIOCB { + BlockDriverAIOCB common; + QEMUBH *bh; + int ret; +} TrimAIOCB; + +static void trim_aio_cancel(BlockDriverAIOCB *acb) +{ + TrimAIOCB *iocb = container_of(acb, TrimAIOCB, common); + + qemu_bh_delete(iocb->bh); + iocb->bh = NULL; + qemu_aio_release(iocb); +} + +static AIOPool trim_aio_pool = { + .aiocb_size = sizeof(TrimAIOCB), + .cancel = trim_aio_cancel, +}; + +static void ide_trim_bh_cb(void *opaque) +{ + TrimAIOCB *iocb = opaque; + + iocb->common.cb(iocb->common.opaque, iocb->ret); + + qemu_bh_delete(iocb->bh); + iocb->bh = NULL; + + qemu_aio_release(iocb); +} + +BlockDriverAIOCB *ide_issue_trim(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + TrimAIOCB *iocb; + int i, j, ret; + + iocb = qemu_aio_get(&trim_aio_pool, bs, cb, opaque); + iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb); + iocb->ret = 0; + + for (j = 0; j < qiov->niov; j++) { + uint64_t *buffer = qiov->iov[j].iov_base; + + for (i = 0; i < qiov->iov[j].iov_len / 8; i++) { + /* 6-byte LBA + 2-byte range per entry */ + uint64_t entry = le64_to_cpu(buffer[i]); + uint64_t sector = entry & 0x0000ffffffffffffULL; + uint16_t count = entry >> 48; + + if (count == 0) { + break; + } + + ret = bdrv_discard(bs, sector, count); + if (!iocb->ret) { + iocb->ret = ret; + } + } + } + + qemu_bh_schedule(iocb->bh); + + return &iocb->common; +} + static inline void ide_abort_command(IDEState *s) { s->status = READY_STAT | ERR_STAT; @@ -474,6 +548,9 @@ handle_rw_error: if (s->dma_cmd == IDE_DMA_READ) op |= BM_STATUS_RETRY_READ; + else if (s->dma_cmd == IDE_DMA_TRIM) + op |= BM_STATUS_RETRY_TRIM; + if (ide_handle_rw_error(s, -ret, op)) { return; } @@ -519,6 +596,10 @@ handle_rw_error: s->bus->dma->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, ide_dma_cb, s); break; + case IDE_DMA_TRIM: + s->bus->dma->aiocb = dma_bdrv_io(s->bs, &s->sg, sector_num, + ide_issue_trim, ide_dma_cb, s, 1); + break; } if (!s->bus->dma->aiocb) { @@ -818,6 +899,18 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) return; switch(val) { + case WIN_DSM: + switch (s->feature) { + case DSM_TRIM: + if (!s->bs) { + goto abort_cmd; + } + ide_sector_start_dma(s, IDE_DMA_TRIM); + break; + default: + goto abort_cmd; + } + break; case WIN_IDENTIFY: if (s->bs && s->drive_kind != IDE_CD) { if (s->drive_kind != IDE_CFATA) diff --git a/hw/ide/internal.h b/hw/ide/internal.h index ea3edf5..02e805f 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -62,7 +62,11 @@ typedef struct IDEDMAOps IDEDMAOps; */ #define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */ /* - * 0x04->0x07 Reserved + * 0x04->0x05 Reserved + */ +#define WIN_DSM 0x06 +/* + * 0x07 Reserved */ #define WIN_SRST 0x08 /* ATAPI soft reset command */ #define WIN_DEVICE_RESET 0x08 @@ -190,6 +194,9 @@ typedef struct IDEDMAOps IDEDMAOps; #define IDE_DMA_BUF_SECTORS 256 +/* feature values for Data Set Management */ +#define DSM_TRIM 0x01 + #if (IDE_DMA_BUF_SECTORS < MAX_MULT_SECTORS) #error "IDE_DMA_BUF_SECTORS must be bigger or equal to MAX_MULT_SECTORS" #endif @@ -382,6 +389,7 @@ struct unreported_events { enum ide_dma_cmd { IDE_DMA_READ, IDE_DMA_WRITE, + IDE_DMA_TRIM, }; #define ide_cmd_is_read(s) \ @@ -521,6 +529,7 @@ struct IDEDeviceInfo { #define BM_STATUS_PIO_RETRY 0x10 #define BM_STATUS_RETRY_READ 0x20 #define BM_STATUS_RETRY_FLUSH 0x40 +#define BM_STATUS_RETRY_TRIM 0x80 #define BM_MIGRATION_COMPAT_STATUS_BITS \ (BM_STATUS_DMA_RETRY | BM_STATUS_PIO_RETRY | \ @@ -591,6 +600,9 @@ void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func); void ide_transfer_stop(IDEState *s); void ide_set_inactive(IDEState *s); +BlockDriverAIOCB *ide_issue_trim(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque); /* hw/ide/atapi.c */ void ide_atapi_cmd(IDEState *s); diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 099b8cb..7daeb31 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -154,6 +154,10 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) m->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, pmac_ide_transfer_cb, io); break; + case IDE_DMA_TRIM: + m->aiocb = dma_bdrv_io(s->bs, &s->sg, sector_num, + ide_issue_trim, pmac_ide_transfer_cb, s, 1); + break; } if (!m->aiocb) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 80c5794..c940375 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -200,8 +200,13 @@ static void bmdma_restart_bh(void *opaque) is_read = !!(bus->error_status & BM_STATUS_RETRY_READ); if (bus->error_status & BM_STATUS_DMA_RETRY) { - bus->error_status &= ~(BM_STATUS_DMA_RETRY | BM_STATUS_RETRY_READ); - bmdma_restart_dma(bm, is_read ? IDE_DMA_READ : IDE_DMA_WRITE); + if (bus->error_status & BM_STATUS_RETRY_TRIM) { + bus->error_status &= ~BM_STATUS_RETRY_TRIM; + bmdma_restart_dma(bm, IDE_DMA_TRIM); + } else { + bus->error_status &= ~(BM_STATUS_DMA_RETRY | BM_STATUS_RETRY_READ); + bmdma_restart_dma(bm, is_read ? IDE_DMA_READ : IDE_DMA_WRITE); + } } else if (bus->error_status & BM_STATUS_PIO_RETRY) { bus->error_status &= ~(BM_STATUS_PIO_RETRY | BM_STATUS_RETRY_READ); if (is_read) { diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 3f9dc89..d9b8f24 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -125,6 +125,11 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) const char *serial; DriveInfo *dinfo; + if (dev->conf.discard_granularity && dev->conf.discard_granularity != 512) { + error_report("discard_granularity must be 512 for ide"); + return -1; + } + serial = dev->serial; if (!serial) { /* try to fall back to value set with legacy -drive serial=... */