Skip to content

Commit

Permalink
ide: add TRIM support
Browse files Browse the repository at this point in the history
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 <hch@lst.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
  • Loading branch information
Christoph Hellwig authored and kevmw committed Jun 15, 2011
1 parent 4e1e005 commit d353fb7
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 5 deletions.
97 changes: 95 additions & 2 deletions hw/ide/core.c
Expand Up @@ -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));
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -474,6 +548,9 @@ void ide_dma_cb(void *opaque, int ret)

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;
}
Expand Down Expand Up @@ -519,6 +596,10 @@ void ide_dma_cb(void *opaque, int ret)
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) {
Expand Down Expand Up @@ -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)
Expand Down
14 changes: 13 additions & 1 deletion hw/ide/internal.h
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) \
Expand Down Expand Up @@ -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 | \
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions hw/ide/macio.c
Expand Up @@ -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)
Expand Down
9 changes: 7 additions & 2 deletions hw/ide/pci.c
Expand Up @@ -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) {
Expand Down
5 changes: 5 additions & 0 deletions hw/ide/qdev.c
Expand Up @@ -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=... */
Expand Down

0 comments on commit d353fb7

Please sign in to comment.