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
This commit is contained in:
commit
53810bab3a
1
block.h
1
block.h
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
|
@ -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 = {
|
||||
@ -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);
|
||||
|
11
block/qed.c
11
block/qed.c
@ -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;
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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");
|
||||
|
@ -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 {
|
||||
|
@ -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,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);
|
||||
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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. */
|
||||
|
@ -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(;;) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -26,6 +26,12 @@ incompatible_features 0x1
|
||||
== Repairing the image file must succeed ==
|
||||
ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0
|
||||
Repairing cluster 5 refcount=0 reference=1
|
||||
The following inconsistencies were found and repaired:
|
||||
|
||||
0 leaked clusters
|
||||
1 corruptions
|
||||
|
||||
Double checking the fixed image now...
|
||||
No errors were found on the image.
|
||||
incompatible_features 0x0
|
||||
|
||||
|
@ -105,16 +105,16 @@ _make_test_img()
|
||||
|
||||
# XXX(hch): have global image options?
|
||||
$QEMU_IMG create -f $IMGFMT $extra_img_options $TEST_IMG $image_size | \
|
||||
sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" | \
|
||||
sed -e "s#$TEST_DIR#TEST_DIR#g" | \
|
||||
sed -e "s#$IMGFMT#IMGFMT#g" | \
|
||||
sed -e "s# encryption=off##g" | \
|
||||
sed -e "s# cluster_size=[0-9]\\+##g" | \
|
||||
sed -e "s# table_size=[0-9]\\+##g" | \
|
||||
sed -e "s# compat='[^']*'##g" | \
|
||||
sed -e "s# compat6=\\(on\\|off\\)##g" | \
|
||||
sed -e "s# static=\\(on\\|off\\)##g" | \
|
||||
sed -e "s# lazy_refcounts=\\(on\\|off\\)##g"
|
||||
sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
|
||||
-e "s#$TEST_DIR#TEST_DIR#g" \
|
||||
-e "s#$IMGFMT#IMGFMT#g" \
|
||||
-e "s# encryption=off##g" \
|
||||
-e "s# cluster_size=[0-9]\\+##g" \
|
||||
-e "s# table_size=[0-9]\\+##g" \
|
||||
-e "s# compat='[^']*'##g" \
|
||||
-e "s# compat6=\\(on\\|off\\)##g" \
|
||||
-e "s# static=\\(on\\|off\\)##g" \
|
||||
-e "s# lazy_refcounts=\\(on\\|off\\)##g"
|
||||
}
|
||||
|
||||
_cleanup_test_img()
|
||||
@ -297,6 +297,20 @@ _supported_os()
|
||||
_notrun "not suitable for this OS: $HOSTOS"
|
||||
}
|
||||
|
||||
_unsupported_qemu_io_options()
|
||||
{
|
||||
for bad_opt
|
||||
do
|
||||
for opt in $QEMU_IO_OPTIONS
|
||||
do
|
||||
if [ "$bad_opt" = "$opt" ]
|
||||
then
|
||||
_notrun "not suitable for qemu-io option: $bad_opt"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
# this test requires that a specified command (executable) exists
|
||||
#
|
||||
_require_command()
|
||||
|
Loading…
Reference in New Issue
Block a user