aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2012-08-11 19:48:50 -0500
committerAnthony Liguori <aliguori@us.ibm.com>2012-08-11 19:48:50 -0500
commit53810bab3acd73b9844807e53f02d867c1ad1d2a (patch)
treed39df6643c81aa0f9f6c3da3d549cb6bc56c2e4e /hw
parent312942619af7bd81bda57769224a8280684dd1cf (diff)
parent166f3c7b7026f9cd55a7daeec3b3444ec41092ab (diff)
Merge remote-tracking branch 'kwolf/for-anthony' into staging
* kwolf/for-anthony: qemu-iotests: skip 039 with ./check -nocache block: add BLOCK_O_CHECK for qemu-img check qcow2: mark image clean after repair succeeds qed: mark image clean after repair succeeds blockdev: flip default cache mode from writethrough to writeback virtio-blk: disable write cache if not negotiated virtio-blk: support VIRTIO_BLK_F_CONFIG_WCE qemu-iotests: Save some sed processes ahci: Fix sglist memleak in ahci_dma_rw_buf() ahci: Fix ahci cdrom read corruptions for reads > 128k virtio-blk: fix use-after-free while handling scsi commands
Diffstat (limited to 'hw')
-rw-r--r--hw/ide/ahci.c44
-rw-r--r--hw/ide/internal.h1
-rw-r--r--hw/virtio-blk.c31
-rw-r--r--hw/virtio-blk.h4
4 files changed, 70 insertions, 10 deletions
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index efea93f0b4..5ea3cadb01 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -636,7 +636,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
}
}
-static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist)
+static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset)
{
AHCICmdHdr *cmd = ad->cur_cmd;
uint32_t opts = le32_to_cpu(cmd->opts);
@@ -647,6 +647,10 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist)
uint8_t *prdt;
int i;
int r = 0;
+ int sum = 0;
+ int off_idx = -1;
+ int off_pos = -1;
+ int tbl_entry_size;
if (!sglist_alloc_hint) {
DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts);
@@ -669,10 +673,31 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist)
/* Get entries in the PRDT, init a qemu sglist accordingly */
if (sglist_alloc_hint > 0) {
AHCI_SG *tbl = (AHCI_SG *)prdt;
-
- qemu_sglist_init(sglist, sglist_alloc_hint, ad->hba->dma);
+ sum = 0;
for (i = 0; i < sglist_alloc_hint; i++) {
/* flags_size is zero-based */
+ tbl_entry_size = (le32_to_cpu(tbl[i].flags_size) + 1);
+ if (offset <= (sum + tbl_entry_size)) {
+ off_idx = i;
+ off_pos = offset - sum;
+ break;
+ }
+ sum += tbl_entry_size;
+ }
+ if ((off_idx == -1) || (off_pos < 0) || (off_pos > tbl_entry_size)) {
+ DPRINTF(ad->port_no, "%s: Incorrect offset! "
+ "off_idx: %d, off_pos: %d\n",
+ __func__, off_idx, off_pos);
+ r = -1;
+ goto out;
+ }
+
+ qemu_sglist_init(sglist, (sglist_alloc_hint - off_idx), ad->hba->dma);
+ qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr + off_pos),
+ le32_to_cpu(tbl[off_idx].flags_size) + 1 - off_pos);
+
+ for (i = off_idx + 1; i < sglist_alloc_hint; i++) {
+ /* flags_size is zero-based */
qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
le32_to_cpu(tbl[i].flags_size) + 1);
}
@@ -745,7 +770,7 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2,
s->dev[port].port.ifs[0].nb_sectors - 1);
- ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist);
+ ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist, 0);
ncq_tfs->tag = tag;
switch(ncq_fis->command) {
@@ -970,7 +995,7 @@ static int ahci_start_transfer(IDEDMA *dma)
goto out;
}
- if (!ahci_populate_sglist(ad, &s->sg)) {
+ if (!ahci_populate_sglist(ad, &s->sg, 0)) {
has_sglist = 1;
}
@@ -1015,6 +1040,7 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s,
DPRINTF(ad->port_no, "\n");
ad->dma_cb = dma_cb;
ad->dma_status |= BM_STATUS_DMAING;
+ s->io_buffer_offset = 0;
dma_cb(s, 0);
}
@@ -1023,7 +1049,7 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write)
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
IDEState *s = &ad->port.ifs[0];
- ahci_populate_sglist(ad, &s->sg);
+ ahci_populate_sglist(ad, &s->sg, 0);
s->io_buffer_size = s->sg.size;
DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size);
@@ -1037,7 +1063,7 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
uint8_t *p = s->io_buffer + s->io_buffer_index;
int l = s->io_buffer_size - s->io_buffer_index;
- if (ahci_populate_sglist(ad, &s->sg)) {
+ if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset)) {
return 0;
}
@@ -1047,9 +1073,13 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
dma_buf_write(p, l, &s->sg);
}
+ /* free sglist that was created in ahci_populate_sglist() */
+ qemu_sglist_destroy(&s->sg);
+
/* update number of transferred bytes */
ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l);
s->io_buffer_index += l;
+ s->io_buffer_offset += l;
DPRINTF(ad->port_no, "len=%#x\n", l);
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 7170bd9cd0..bf7d313cf4 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -393,6 +393,7 @@ struct IDEState {
struct iovec iov;
QEMUIOVector qiov;
/* ATA DMA state */
+ int io_buffer_offset;
int io_buffer_size;
QEMUSGList sg;
/* PIO transfer handling */
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index f21757ed55..fd8fa90792 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -254,6 +254,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
virtio_blk_req_complete(req, status);
g_free(req);
+ return;
#else
abort();
#endif
@@ -509,9 +510,19 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
blkcfg.size_max = 0;
blkcfg.physical_block_exp = get_physical_block_exp(s->conf);
blkcfg.alignment_offset = 0;
+ blkcfg.wce = bdrv_enable_write_cache(s->bs);
memcpy(config, &blkcfg, sizeof(struct virtio_blk_config));
}
+static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+ VirtIOBlock *s = to_virtio_blk(vdev);
+ struct virtio_blk_config blkcfg;
+
+ memcpy(&blkcfg, config, sizeof(blkcfg));
+ bdrv_set_enable_write_cache(s->bs, blkcfg.wce != 0);
+}
+
static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features)
{
VirtIOBlock *s = to_virtio_blk(vdev);
@@ -522,15 +533,29 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features)
features |= (1 << VIRTIO_BLK_F_BLK_SIZE);
features |= (1 << VIRTIO_BLK_F_SCSI);
+ features |= (1 << VIRTIO_BLK_F_CONFIG_WCE);
if (bdrv_enable_write_cache(s->bs))
- features |= (1 << VIRTIO_BLK_F_WCACHE);
-
+ features |= (1 << VIRTIO_BLK_F_WCE);
+
if (bdrv_is_read_only(s->bs))
features |= 1 << VIRTIO_BLK_F_RO;
return features;
}
+static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status)
+{
+ VirtIOBlock *s = to_virtio_blk(vdev);
+ uint32_t features;
+
+ if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+ return;
+ }
+
+ features = vdev->guest_features;
+ bdrv_set_enable_write_cache(s->bs, !!(features & (1 << VIRTIO_BLK_F_WCE)));
+}
+
static void virtio_blk_save(QEMUFile *f, void *opaque)
{
VirtIOBlock *s = opaque;
@@ -609,7 +634,9 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk)
sizeof(VirtIOBlock));
s->vdev.get_config = virtio_blk_update_config;
+ s->vdev.set_config = virtio_blk_set_config;
s->vdev.get_features = virtio_blk_get_features;
+ s->vdev.set_status = virtio_blk_set_status;
s->vdev.reset = virtio_blk_reset;
s->bs = blk->conf.bs;
s->conf = &blk->conf;
diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h
index 79ebccc95b..35834cf493 100644
--- a/hw/virtio-blk.h
+++ b/hw/virtio-blk.h
@@ -31,8 +31,9 @@
#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/
#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */
/* #define VIRTIO_BLK_F_IDENTIFY 8 ATA IDENTIFY supported, DEPRECATED */
-#define VIRTIO_BLK_F_WCACHE 9 /* write cache enabled */
+#define VIRTIO_BLK_F_WCE 9 /* write cache enabled */
#define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */
+#define VIRTIO_BLK_F_CONFIG_WCE 11 /* write cache configurable */
#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */
@@ -49,6 +50,7 @@ struct virtio_blk_config
uint8_t alignment_offset;
uint16_t min_io_size;
uint32_t opt_io_size;
+ uint8_t wce;
} QEMU_PACKED;
/* These two define direction. */