Skip to content

Commit

Permalink
Merge remote-tracking branch 'kwolf/for-anthony' into staging
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Anthony Liguori committed Aug 12, 2012
2 parents 3129426 + 166f3c7 commit 53810ba
Show file tree
Hide file tree
Showing 15 changed files with 155 additions and 45 deletions.
1 change: 1 addition & 0 deletions block.h
Expand Up @@ -79,6 +79,7 @@ typedef struct BlockDevOps {
#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */
#define BDRV_O_COPY_ON_READ 0x0400 /* copy read backing sectors into image */
#define BDRV_O_INCOMING 0x0800 /* consistency hint for incoming migration */
#define BDRV_O_CHECK 0x1000 /* open solely for consistency check */

#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)

Expand Down
32 changes: 17 additions & 15 deletions block/qcow2.c
Expand Up @@ -270,6 +270,20 @@ static int qcow2_mark_clean(BlockDriverState *bs)
return 0;
}

static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
BdrvCheckMode fix)
{
int ret = qcow2_check_refcounts(bs, result, fix);
if (ret < 0) {
return ret;
}

if (fix && result->check_errors == 0 && result->corruptions == 0) {
return qcow2_mark_clean(bs);
}
return ret;
}

static int qcow2_open(BlockDriverState *bs, int flags)
{
BDRVQcowState *s = bs->opaque;
Expand Down Expand Up @@ -470,16 +484,11 @@ static int qcow2_open(BlockDriverState *bs, int flags)
qemu_co_mutex_init(&s->lock);

/* Repair image if dirty */
if ((s->incompatible_features & QCOW2_INCOMPAT_DIRTY) &&
!bs->read_only) {
if (!(flags & BDRV_O_CHECK) && !bs->read_only &&
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
BdrvCheckResult result = {0};

ret = qcow2_check_refcounts(bs, &result, BDRV_FIX_ERRORS);
if (ret < 0) {
goto fail;
}

ret = qcow2_mark_clean(bs);
ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS);
if (ret < 0) {
goto fail;
}
Expand Down Expand Up @@ -1568,13 +1577,6 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return 0;
}


static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
BdrvCheckMode fix)
{
return qcow2_check_refcounts(bs, result, fix);
}

#if 0
static void dump_refcounts(BlockDriverState *bs)
{
Expand Down
26 changes: 26 additions & 0 deletions block/qed-check.c
Expand Up @@ -194,6 +194,28 @@ static void qed_check_for_leaks(QEDCheck *check)
}
}

/**
* Mark an image clean once it passes check or has been repaired
*/
static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result)
{
/* Skip if there were unfixable corruptions or I/O errors */
if (result->corruptions > 0 || result->check_errors > 0) {
return;
}

/* Skip if image is already marked clean */
if (!(s->header.features & QED_F_NEED_CHECK)) {
return;
}

/* Ensure fixes reach storage before clearing check bit */
bdrv_flush(s->bs);

s->header.features &= ~QED_F_NEED_CHECK;
qed_write_header_sync(s);
}

int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
{
QEDCheck check = {
Expand All @@ -215,6 +237,10 @@ int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
if (ret == 0) {
/* Only check for leaks if entire image was scanned successfully */
qed_check_for_leaks(&check);

if (fix) {
qed_check_mark_clean(s, result);
}
}

g_free(check.used_clusters);
Expand Down
11 changes: 2 additions & 9 deletions block/qed.c
Expand Up @@ -89,7 +89,7 @@ static void qed_header_cpu_to_le(const QEDHeader *cpu, QEDHeader *le)
le->backing_filename_size = cpu_to_le32(cpu->backing_filename_size);
}

static int qed_write_header_sync(BDRVQEDState *s)
int qed_write_header_sync(BDRVQEDState *s)
{
QEDHeader le;
int ret;
Expand Down Expand Up @@ -477,7 +477,7 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
}

/* If image was not closed cleanly, check consistency */
if (s->header.features & QED_F_NEED_CHECK) {
if (!(flags & BDRV_O_CHECK) && (s->header.features & QED_F_NEED_CHECK)) {
/* Read-only images cannot be fixed. There is no risk of corruption
* since write operations are not possible. Therefore, allow
* potentially inconsistent images to be opened read-only. This can
Expand All @@ -491,13 +491,6 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
if (ret) {
goto out;
}
if (!result.corruptions && !result.check_errors) {
/* Ensure fixes reach storage before clearing check bit */
bdrv_flush(s->bs);

s->header.features &= ~QED_F_NEED_CHECK;
qed_write_header_sync(s);
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions block/qed.h
Expand Up @@ -210,6 +210,11 @@ typedef struct {
void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque);
void gencb_complete(void *opaque, int ret);

/**
* Header functions
*/
int qed_write_header_sync(BDRVQEDState *s);

/**
* L2 cache functions
*/
Expand Down
1 change: 1 addition & 0 deletions blockdev.c
Expand Up @@ -377,6 +377,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
}
}

bdrv_flags |= BDRV_O_CACHE_WB;
if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) {
error_report("invalid cache option");
Expand Down
1 change: 1 addition & 0 deletions dma-helpers.c
Expand Up @@ -65,6 +65,7 @@ void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len)
void qemu_sglist_destroy(QEMUSGList *qsg)
{
g_free(qsg->sg);
memset(qsg, 0, sizeof(*qsg));
}

typedef struct {
Expand Down
44 changes: 37 additions & 7 deletions hw/ide/ahci.c
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -669,9 +673,30 @@ 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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
}

Expand All @@ -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);
Expand All @@ -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;
}

Expand All @@ -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);

Expand Down
1 change: 1 addition & 0 deletions hw/ide/internal.h
Expand Up @@ -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 */
Expand Down
31 changes: 29 additions & 2 deletions hw/virtio-blk.c
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion hw/virtio-blk.h
Expand Up @@ -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 */

Expand All @@ -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. */
Expand Down
2 changes: 1 addition & 1 deletion qemu-img.c
Expand Up @@ -379,7 +379,7 @@ static int img_check(int argc, char **argv)
BlockDriverState *bs;
BdrvCheckResult result;
int fix = 0;
int flags = BDRV_O_FLAGS;
int flags = BDRV_O_FLAGS | BDRV_O_CHECK;

fmt = NULL;
for(;;) {
Expand Down
1 change: 1 addition & 0 deletions tests/qemu-iotests/039
Expand Up @@ -44,6 +44,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow2
_supported_proto generic
_supported_os Linux
_unsupported_qemu_io_options --nocache

size=128M

Expand Down

0 comments on commit 53810ba

Please sign in to comment.