Merge remote-tracking branch 'kwolf/for-anthony' into staging

This commit is contained in:
Anthony Liguori 2011-07-22 09:23:43 -05:00
commit bf1cd9b4f5
28 changed files with 2427 additions and 1778 deletions

19
block.c
View File

@ -1146,6 +1146,25 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
return ret; return ret;
} }
/**
* Length of a allocated file in bytes. Sparse files are counted by actual
* allocated space. Return < 0 if error or unknown.
*/
int64_t bdrv_get_allocated_file_size(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
if (!drv) {
return -ENOMEDIUM;
}
if (drv->bdrv_get_allocated_file_size) {
return drv->bdrv_get_allocated_file_size(bs);
}
if (bs->file) {
return bdrv_get_allocated_file_size(bs->file);
}
return -ENOTSUP;
}
/** /**
* Length of a file in bytes. Return < 0 if error or unknown. * Length of a file in bytes. Return < 0 if error or unknown.
*/ */

View File

@ -89,6 +89,7 @@ int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors); const uint8_t *buf, int nb_sectors);
int bdrv_truncate(BlockDriverState *bs, int64_t offset); int bdrv_truncate(BlockDriverState *bs, int64_t offset);
int64_t bdrv_getlength(BlockDriverState *bs); int64_t bdrv_getlength(BlockDriverState *bs);
int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs); void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs);
int bdrv_commit(BlockDriverState *bs); int bdrv_commit(BlockDriverState *bs);

View File

@ -312,3 +312,15 @@ found:
c->entries[i].dirty = true; c->entries[i].dirty = true;
} }
bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
bool enable)
{
bool old = c->writethrough;
if (!old && enable) {
qcow2_cache_flush(bs, c);
}
c->writethrough = enable;
return old;
}

View File

@ -705,8 +705,15 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated; uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated;
int64_t old_offset, old_l2_offset; int64_t old_offset, old_l2_offset;
int i, j, l1_modified, nb_csectors, refcount; int i, j, l1_modified = 0, nb_csectors, refcount;
int ret; int ret;
bool old_l2_writethrough, old_refcount_writethrough;
/* Switch caches to writeback mode during update */
old_l2_writethrough =
qcow2_cache_set_writethrough(bs, s->l2_table_cache, false);
old_refcount_writethrough =
qcow2_cache_set_writethrough(bs, s->refcount_block_cache, false);
l2_table = NULL; l2_table = NULL;
l1_table = NULL; l1_table = NULL;
@ -720,7 +727,11 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
l1_allocated = 1; l1_allocated = 1;
if (bdrv_pread(bs->file, l1_table_offset, if (bdrv_pread(bs->file, l1_table_offset,
l1_table, l1_size2) != l1_size2) l1_table, l1_size2) != l1_size2)
{
ret = -EIO;
goto fail; goto fail;
}
for(i = 0;i < l1_size; i++) for(i = 0;i < l1_size; i++)
be64_to_cpus(&l1_table[i]); be64_to_cpus(&l1_table[i]);
} else { } else {
@ -729,7 +740,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
l1_allocated = 0; l1_allocated = 0;
} }
l1_modified = 0;
for(i = 0; i < l1_size; i++) { for(i = 0; i < l1_size; i++) {
l2_offset = l1_table[i]; l2_offset = l1_table[i];
if (l2_offset) { if (l2_offset) {
@ -773,6 +783,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
} }
if (refcount < 0) { if (refcount < 0) {
ret = -EIO;
goto fail; goto fail;
} }
} }
@ -803,6 +814,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
refcount = get_refcount(bs, l2_offset >> s->cluster_bits); refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
} }
if (refcount < 0) { if (refcount < 0) {
ret = -EIO;
goto fail; goto fail;
} else if (refcount == 1) { } else if (refcount == 1) {
l2_offset |= QCOW_OFLAG_COPIED; l2_offset |= QCOW_OFLAG_COPIED;
@ -813,6 +825,18 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
} }
} }
} }
ret = 0;
fail:
if (l2_table) {
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
}
/* Enable writethrough cache mode again */
qcow2_cache_set_writethrough(bs, s->l2_table_cache, old_l2_writethrough);
qcow2_cache_set_writethrough(bs, s->refcount_block_cache,
old_refcount_writethrough);
if (l1_modified) { if (l1_modified) {
for(i = 0; i < l1_size; i++) for(i = 0; i < l1_size; i++)
cpu_to_be64s(&l1_table[i]); cpu_to_be64s(&l1_table[i]);
@ -824,15 +848,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
} }
if (l1_allocated) if (l1_allocated)
qemu_free(l1_table); qemu_free(l1_table);
return 0; return ret;
fail:
if (l2_table) {
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
}
if (l1_allocated)
qemu_free(l1_table);
return -EIO;
} }

View File

@ -228,6 +228,8 @@ int qcow2_read_snapshots(BlockDriverState *bs);
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
bool writethrough); bool writethrough);
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
bool enable);
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table);
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c);

View File

@ -793,6 +793,17 @@ static int64_t raw_getlength(BlockDriverState *bs)
} }
#endif #endif
static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
{
struct stat st;
BDRVRawState *s = bs->opaque;
if (fstat(s->fd, &st) < 0) {
return -errno;
}
return (int64_t)st.st_blocks * 512;
}
static int raw_create(const char *filename, QEMUOptionParameter *options) static int raw_create(const char *filename, QEMUOptionParameter *options)
{ {
int fd; int fd;
@ -888,6 +899,8 @@ static BlockDriver bdrv_file = {
.bdrv_truncate = raw_truncate, .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
.create_options = raw_create_options, .create_options = raw_create_options,
}; };
@ -1156,6 +1169,8 @@ static BlockDriver bdrv_host_device = {
.bdrv_read = raw_read, .bdrv_read = raw_read,
.bdrv_write = raw_write, .bdrv_write = raw_write,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
/* generic scsi device */ /* generic scsi device */
#ifdef __linux__ #ifdef __linux__
@ -1277,6 +1292,8 @@ static BlockDriver bdrv_host_floppy = {
.bdrv_read = raw_read, .bdrv_read = raw_read,
.bdrv_write = raw_write, .bdrv_write = raw_write,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
/* removable device support */ /* removable device support */
.bdrv_is_inserted = floppy_is_inserted, .bdrv_is_inserted = floppy_is_inserted,
@ -1380,6 +1397,8 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_read = raw_read, .bdrv_read = raw_read,
.bdrv_write = raw_write, .bdrv_write = raw_write,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
/* removable device support */ /* removable device support */
.bdrv_is_inserted = cdrom_is_inserted, .bdrv_is_inserted = cdrom_is_inserted,
@ -1503,6 +1522,8 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_read = raw_read, .bdrv_read = raw_read,
.bdrv_write = raw_write, .bdrv_write = raw_write,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
/* removable device support */ /* removable device support */
.bdrv_is_inserted = cdrom_is_inserted, .bdrv_is_inserted = cdrom_is_inserted,

View File

@ -213,6 +213,31 @@ static int64_t raw_getlength(BlockDriverState *bs)
return l.QuadPart; return l.QuadPart;
} }
static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
{
typedef DWORD (WINAPI * get_compressed_t)(const char *filename,
DWORD * high);
get_compressed_t get_compressed;
struct _stati64 st;
const char *filename = bs->filename;
/* WinNT support GetCompressedFileSize to determine allocate size */
get_compressed =
(get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"),
"GetCompressedFileSizeA");
if (get_compressed) {
DWORD high, low;
low = get_compressed(filename, &high);
if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) {
return (((int64_t) high) << 32) + low;
}
}
if (_stati64(filename, &st) < 0) {
return -1;
}
return st.st_size;
}
static int raw_create(const char *filename, QEMUOptionParameter *options) static int raw_create(const char *filename, QEMUOptionParameter *options)
{ {
int fd; int fd;
@ -257,6 +282,8 @@ static BlockDriver bdrv_file = {
.bdrv_write = raw_write, .bdrv_write = raw_write,
.bdrv_truncate = raw_truncate, .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
.create_options = raw_create_options, .create_options = raw_create_options,
}; };
@ -419,6 +446,8 @@ static BlockDriver bdrv_host_device = {
.bdrv_read = raw_read, .bdrv_read = raw_read,
.bdrv_write = raw_write, .bdrv_write = raw_write,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
}; };
static void bdrv_file_init(void) static void bdrv_file_init(void)

View File

@ -1286,6 +1286,49 @@ static int do_sd_create(char *filename, int64_t vdi_size,
return 0; return 0;
} }
static int sd_prealloc(const char *filename)
{
BlockDriverState *bs = NULL;
uint32_t idx, max_idx;
int64_t vdi_size;
void *buf = qemu_mallocz(SD_DATA_OBJ_SIZE);
int ret;
ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR);
if (ret < 0) {
goto out;
}
vdi_size = bdrv_getlength(bs);
if (vdi_size < 0) {
ret = vdi_size;
goto out;
}
max_idx = DIV_ROUND_UP(vdi_size, SD_DATA_OBJ_SIZE);
for (idx = 0; idx < max_idx; idx++) {
/*
* The created image can be a cloned image, so we need to read
* a data from the source image.
*/
ret = bdrv_pread(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE);
if (ret < 0) {
goto out;
}
ret = bdrv_pwrite(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE);
if (ret < 0) {
goto out;
}
}
out:
if (bs) {
bdrv_delete(bs);
}
qemu_free(buf);
return ret;
}
static int sd_create(const char *filename, QEMUOptionParameter *options) static int sd_create(const char *filename, QEMUOptionParameter *options)
{ {
int ret; int ret;
@ -1295,13 +1338,15 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
BDRVSheepdogState s; BDRVSheepdogState s;
char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid; uint32_t snapid;
int prealloc = 0;
const char *vdiname;
strstart(filename, "sheepdog:", (const char **)&filename); strstart(filename, "sheepdog:", &vdiname);
memset(&s, 0, sizeof(s)); memset(&s, 0, sizeof(s));
memset(vdi, 0, sizeof(vdi)); memset(vdi, 0, sizeof(vdi));
memset(tag, 0, sizeof(tag)); memset(tag, 0, sizeof(tag));
if (parse_vdiname(&s, filename, vdi, &snapid, tag) < 0) { if (parse_vdiname(&s, vdiname, vdi, &snapid, tag) < 0) {
error_report("invalid filename"); error_report("invalid filename");
return -EINVAL; return -EINVAL;
} }
@ -1311,6 +1356,16 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
vdi_size = options->value.n; vdi_size = options->value.n;
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
backing_file = options->value.s; backing_file = options->value.s;
} else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
if (!options->value.s || !strcmp(options->value.s, "off")) {
prealloc = 0;
} else if (!strcmp(options->value.s, "full")) {
prealloc = 1;
} else {
error_report("Invalid preallocation mode: '%s'",
options->value.s);
return -EINVAL;
}
} }
options++; options++;
} }
@ -1348,7 +1403,12 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
bdrv_delete(bs); bdrv_delete(bs);
} }
return do_sd_create((char *)vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port); ret = do_sd_create(vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port);
if (!prealloc || ret) {
return ret;
}
return sd_prealloc(filename);
} }
static void sd_close(BlockDriverState *bs) static void sd_close(BlockDriverState *bs)
@ -1984,6 +2044,11 @@ static QEMUOptionParameter sd_create_options[] = {
.type = OPT_STRING, .type = OPT_STRING,
.help = "File name of a base image" .help = "File name of a base image"
}, },
{
.name = BLOCK_OPT_PREALLOC,
.type = OPT_STRING,
.help = "Preallocation mode (allowed values: off, full)"
},
{ NULL } { NULL }
}; };

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,7 @@
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size" #define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
#define BLOCK_OPT_TABLE_SIZE "table_size" #define BLOCK_OPT_TABLE_SIZE "table_size"
#define BLOCK_OPT_PREALLOC "preallocation" #define BLOCK_OPT_PREALLOC "preallocation"
#define BLOCK_OPT_SUBFMT "subformat"
typedef struct AIOPool { typedef struct AIOPool {
void (*cancel)(BlockDriverAIOCB *acb); void (*cancel)(BlockDriverAIOCB *acb);
@ -85,6 +86,7 @@ struct BlockDriver {
const char *protocol_name; const char *protocol_name;
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset); int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
int64_t (*bdrv_getlength)(BlockDriverState *bs); int64_t (*bdrv_getlength)(BlockDriverState *bs);
int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs);
int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num, int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors); const uint8_t *buf, int nb_sectors);

View File

@ -244,7 +244,7 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
DPRINTF("do_busid_cmd: busid 0x%x\n", busid); DPRINTF("do_busid_cmd: busid 0x%x\n", busid);
lun = busid & 7; lun = busid & 7;
s->current_req = scsi_req_new(s->current_dev, 0, lun); s->current_req = scsi_req_new(s->current_dev, 0, lun, NULL);
datalen = scsi_req_enqueue(s->current_req, buf); datalen = scsi_req_enqueue(s->current_req, buf);
s->ti_size = datalen; s->ti_size = datalen;
if (datalen != 0) { if (datalen != 0) {

View File

@ -661,7 +661,7 @@ static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
static void lsi_request_cancelled(SCSIRequest *req) static void lsi_request_cancelled(SCSIRequest *req)
{ {
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
lsi_request *p; lsi_request *p = req->hba_private;
if (s->current && req == s->current->req) { if (s->current && req == s->current->req) {
scsi_req_unref(req); scsi_req_unref(req);
@ -670,7 +670,6 @@ static void lsi_request_cancelled(SCSIRequest *req)
return; return;
} }
p = lsi_find_by_tag(s, req->tag);
if (p) { if (p) {
QTAILQ_REMOVE(&s->queue, p, next); QTAILQ_REMOVE(&s->queue, p, next);
scsi_req_unref(req); scsi_req_unref(req);
@ -680,18 +679,12 @@ static void lsi_request_cancelled(SCSIRequest *req)
/* Record that data is available for a queued command. Returns zero if /* Record that data is available for a queued command. Returns zero if
the device was reselected, nonzero if the IO is deferred. */ the device was reselected, nonzero if the IO is deferred. */
static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t len) static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len)
{ {
lsi_request *p; lsi_request *p = req->hba_private;
p = lsi_find_by_tag(s, tag);
if (!p) {
BADF("IO with unknown tag %d\n", tag);
return 1;
}
if (p->pending) { if (p->pending) {
BADF("Multiple IO pending for tag %d\n", tag); BADF("Multiple IO pending for request %p\n", p);
} }
p->pending = len; p->pending = len;
/* Reselect if waiting for it, or if reselection triggers an IRQ /* Reselect if waiting for it, or if reselection triggers an IRQ
@ -743,9 +736,9 @@ static void lsi_transfer_data(SCSIRequest *req, uint32_t len)
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out; int out;
if (s->waiting == 1 || !s->current || req->tag != s->current->tag || if (s->waiting == 1 || !s->current || req->hba_private != s->current ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
if (lsi_queue_tag(s, req->tag, len)) { if (lsi_queue_req(s, req, len)) {
return; return;
} }
} }
@ -789,7 +782,8 @@ static void lsi_do_command(LSIState *s)
assert(s->current == NULL); assert(s->current == NULL);
s->current = qemu_mallocz(sizeof(lsi_request)); s->current = qemu_mallocz(sizeof(lsi_request));
s->current->tag = s->select_tag; s->current->tag = s->select_tag;
s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun); s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun,
s->current);
n = scsi_req_enqueue(s->current->req, buf); n = scsi_req_enqueue(s->current->req, buf);
if (n) { if (n) {

View File

@ -131,7 +131,8 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
return res; return res;
} }
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun) SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag,
uint32_t lun, void *hba_private)
{ {
SCSIRequest *req; SCSIRequest *req;
@ -141,14 +142,16 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
req->dev = d; req->dev = d;
req->tag = tag; req->tag = tag;
req->lun = lun; req->lun = lun;
req->hba_private = hba_private;
req->status = -1; req->status = -1;
trace_scsi_req_alloc(req->dev->id, req->lun, req->tag); trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
return req; return req;
} }
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun) SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
void *hba_private)
{ {
return d->info->alloc_req(d, tag, lun); return d->info->alloc_req(d, tag, lun, hba_private);
} }
uint8_t *scsi_req_get_buf(SCSIRequest *req) uint8_t *scsi_req_get_buf(SCSIRequest *req)

View File

@ -81,13 +81,13 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type);
static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf); static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf);
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag,
uint32_t lun) uint32_t lun, void *hba_private)
{ {
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *req; SCSIRequest *req;
SCSIDiskReq *r; SCSIDiskReq *r;
req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun); req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun, hba_private);
r = DO_UPCAST(SCSIDiskReq, req, req); r = DO_UPCAST(SCSIDiskReq, req, req);
r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE); r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE);
return req; return req;
@ -398,7 +398,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
"buffer size %zd\n", req->cmd.xfer); "buffer size %zd\n", req->cmd.xfer);
pages = buflen++; pages = buflen++;
outbuf[buflen++] = 0x00; // list of supported pages (this page) outbuf[buflen++] = 0x00; // list of supported pages (this page)
outbuf[buflen++] = 0x80; // unit serial number if (s->serial)
outbuf[buflen++] = 0x80; // unit serial number
outbuf[buflen++] = 0x83; // device identification outbuf[buflen++] = 0x83; // device identification
if (s->drive_kind == SCSI_HD) { if (s->drive_kind == SCSI_HD) {
outbuf[buflen++] = 0xb0; // block limits outbuf[buflen++] = 0xb0; // block limits
@ -409,8 +410,14 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
} }
case 0x80: /* Device serial number, optional */ case 0x80: /* Device serial number, optional */
{ {
int l = strlen(s->serial); int l;
if (!s->serial) {
DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
return -1;
}
l = strlen(s->serial);
if (l > req->cmd.xfer) if (l > req->cmd.xfer)
l = req->cmd.xfer; l = req->cmd.xfer;
if (l > 20) if (l > 20)
@ -1007,7 +1014,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
command = buf[0]; command = buf[0];
outbuf = (uint8_t *)r->iov.iov_base; outbuf = (uint8_t *)r->iov.iov_base;
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", req->lun, req->tag, buf[0]);
if (scsi_req_parse(&r->req, buf) != 0) { if (scsi_req_parse(&r->req, buf) != 0) {
BADF("Unsupported command length, command %x\n", command); BADF("Unsupported command length, command %x\n", command);
@ -1203,7 +1210,9 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind)
if (!s->serial) { if (!s->serial) {
/* try to fall back to value set with legacy -drive serial=... */ /* try to fall back to value set with legacy -drive serial=... */
dinfo = drive_get_by_blockdev(s->bs); dinfo = drive_get_by_blockdev(s->bs);
s->serial = qemu_strdup(*dinfo->serial ? dinfo->serial : "0"); if (*dinfo->serial) {
s->serial = qemu_strdup(dinfo->serial);
}
} }
if (!s->version) { if (!s->version) {

View File

@ -96,11 +96,12 @@ static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
return size; return size;
} }
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
void *hba_private)
{ {
SCSIRequest *req; SCSIRequest *req;
req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun); req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun, hba_private);
return req; return req;
} }

View File

@ -43,6 +43,7 @@ struct SCSIRequest {
} cmd; } cmd;
BlockDriverAIOCB *aiocb; BlockDriverAIOCB *aiocb;
bool enqueued; bool enqueued;
void *hba_private;
QTAILQ_ENTRY(SCSIRequest) next; QTAILQ_ENTRY(SCSIRequest) next;
}; };
@ -67,7 +68,8 @@ struct SCSIDeviceInfo {
DeviceInfo qdev; DeviceInfo qdev;
scsi_qdev_initfn init; scsi_qdev_initfn init;
void (*destroy)(SCSIDevice *s); void (*destroy)(SCSIDevice *s);
SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun); SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun,
void *hba_private);
void (*free_req)(SCSIRequest *req); void (*free_req)(SCSIRequest *req);
int32_t (*send_command)(SCSIRequest *req, uint8_t *buf); int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
void (*read_data)(SCSIRequest *req); void (*read_data)(SCSIRequest *req);
@ -138,8 +140,10 @@ extern const struct SCSISense sense_code_LUN_FAILURE;
int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed); int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense); int scsi_sense_valid(SCSISense sense);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag,
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun); uint32_t lun, void *hba_private);
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
void *hba_private);
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf); int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf);
void scsi_req_free(SCSIRequest *req); void scsi_req_free(SCSIRequest *req);
SCSIRequest *scsi_req_ref(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req);

View File

@ -121,7 +121,7 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
return NULL; return NULL;
} }
static void vscsi_put_req(VSCSIState *s, vscsi_req *req) static void vscsi_put_req(vscsi_req *req)
{ {
if (req->sreq != NULL) { if (req->sreq != NULL) {
scsi_req_unref(req->sreq); scsi_req_unref(req->sreq);
@ -130,15 +130,6 @@ static void vscsi_put_req(VSCSIState *s, vscsi_req *req)
req->active = 0; req->active = 0;
} }
static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req)
{
uint32_t tag = req->tag;
if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) {
return NULL;
}
return &s->reqs[tag];
}
static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun) static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun)
{ {
/* XXX Figure that one out properly ! This is crackpot */ /* XXX Figure that one out properly ! This is crackpot */
@ -454,7 +445,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
if (n) { if (n) {
req->senselen = n; req->senselen = n;
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
vscsi_put_req(s, req); vscsi_put_req(req);
return; return;
} }
@ -483,7 +474,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len) static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
{ {
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = vscsi_find_req(s, sreq); vscsi_req *req = sreq->hba_private;
uint8_t *buf; uint8_t *buf;
int rc = 0; int rc = 0;
@ -531,7 +522,7 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status) static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
{ {
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = vscsi_find_req(s, sreq); vscsi_req *req = sreq->hba_private;
int32_t res_in = 0, res_out = 0; int32_t res_in = 0, res_out = 0;
dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n", dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n",
@ -563,15 +554,14 @@ static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
} }
} }
vscsi_send_rsp(s, req, 0, res_in, res_out); vscsi_send_rsp(s, req, 0, res_in, res_out);
vscsi_put_req(s, req); vscsi_put_req(req);
} }
static void vscsi_request_cancelled(SCSIRequest *sreq) static void vscsi_request_cancelled(SCSIRequest *sreq)
{ {
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); vscsi_req *req = sreq->hba_private;
vscsi_req *req = vscsi_find_req(s, sreq);
vscsi_put_req(s, req); vscsi_put_req(req);
} }
static void vscsi_process_login(VSCSIState *s, vscsi_req *req) static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
@ -659,7 +649,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
} }
req->lun = lun; req->lun = lun;
req->sreq = scsi_req_new(sdev, req->qtag, lun); req->sreq = scsi_req_new(sdev, req->qtag, lun, req);
n = scsi_req_enqueue(req->sreq, srp->cmd.cdb); n = scsi_req_enqueue(req->sreq, srp->cmd.cdb);
dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n", dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n",
@ -858,7 +848,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq)
} }
if (done) { if (done) {
vscsi_put_req(s, req); vscsi_put_req(req);
} }
} }

View File

@ -216,10 +216,6 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet; USBPacket *p = s->packet;
if (req->tag != s->tag) {
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
}
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
s->scsi_len = len; s->scsi_len = len;
s->scsi_buf = scsi_req_get_buf(req); s->scsi_buf = scsi_req_get_buf(req);
@ -241,9 +237,6 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet; USBPacket *p = s->packet;
if (req->tag != s->tag) {
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
}
DPRINTF("Command complete %d\n", status); DPRINTF("Command complete %d\n", status);
s->residue = s->data_len; s->residue = s->data_len;
s->result = status != 0; s->result = status != 0;
@ -387,7 +380,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->tag, cbw.flags, cbw.cmd_len, s->data_len);
s->residue = 0; s->residue = 0;
s->scsi_len = 0; s->scsi_len = 0;
s->req = scsi_req_new(s->scsi_dev, s->tag, 0); s->req = scsi_req_new(s->scsi_dev, s->tag, 0, NULL);
scsi_req_enqueue(s->req, cbw.cmd); scsi_req_enqueue(s->req, cbw.cmd);
/* ??? Should check that USB and SCSI data transfer /* ??? Should check that USB and SCSI data transfer
directions match. */ directions match. */

View File

@ -657,7 +657,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_
/* copy in packet. ugh */ /* copy in packet. ugh */
len = iov_from_buf(sg, elem.in_num, len = iov_from_buf(sg, elem.in_num,
buf + offset, size - offset); buf + offset, 0, size - offset);
total += len; total += len;
offset += len; offset += len;
/* If buffers can't be merged, at this point we /* If buffers can't be merged, at this point we

View File

@ -104,7 +104,7 @@ static size_t write_to_port(VirtIOSerialPort *port,
} }
len = iov_from_buf(elem.in_sg, elem.in_num, len = iov_from_buf(elem.in_sg, elem.in_num,
buf + offset, size - offset); buf + offset, 0, size - offset);
offset += len; offset += len;
virtqueue_push(vq, &elem, len); virtqueue_push(vq, &elem, len);

69
iov.c
View File

@ -14,56 +14,61 @@
#include "iov.h" #include "iov.h"
size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt, size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt,
const void *buf, size_t size) const void *buf, size_t iov_off, size_t size)
{ {
size_t offset; size_t iovec_off, buf_off;
unsigned int i; unsigned int i;
offset = 0; iovec_off = 0;
for (i = 0; offset < size && i < iovcnt; i++) {
size_t len;
len = MIN(iov[i].iov_len, size - offset);
memcpy(iov[i].iov_base, buf + offset, len);
offset += len;
}
return offset;
}
size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt,
void *buf, size_t offset, size_t size)
{
uint8_t *ptr;
size_t iov_off, buf_off;
unsigned int i;
ptr = buf;
iov_off = 0;
buf_off = 0; buf_off = 0;
for (i = 0; i < iovcnt && size; i++) { for (i = 0; i < iov_cnt && size; i++) {
if (offset < (iov_off + iov[i].iov_len)) { if (iov_off < (iovec_off + iov[i].iov_len)) {
size_t len = MIN((iov_off + iov[i].iov_len) - offset , size); size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off, size);
memcpy(ptr + buf_off, iov[i].iov_base + (offset - iov_off), len); memcpy(iov[i].iov_base + (iov_off - iovec_off), buf + buf_off, len);
buf_off += len; buf_off += len;
offset += len; iov_off += len;
size -= len; size -= len;
} }
iov_off += iov[i].iov_len; iovec_off += iov[i].iov_len;
} }
return buf_off; return buf_off;
} }
size_t iov_size(const struct iovec *iov, const unsigned int iovcnt) size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
void *buf, size_t iov_off, size_t size)
{
uint8_t *ptr;
size_t iovec_off, buf_off;
unsigned int i;
ptr = buf;
iovec_off = 0;
buf_off = 0;
for (i = 0; i < iov_cnt && size; i++) {
if (iov_off < (iovec_off + iov[i].iov_len)) {
size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size);
memcpy(ptr + buf_off, iov[i].iov_base + (iov_off - iovec_off), len);
buf_off += len;
iov_off += len;
size -= len;
}
iovec_off += iov[i].iov_len;
}
return buf_off;
}
size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt)
{ {
size_t len; size_t len;
unsigned int i; unsigned int i;
len = 0; len = 0;
for (i = 0; i < iovcnt; i++) { for (i = 0; i < iov_cnt; i++) {
len += iov[i].iov_len; len += iov[i].iov_len;
} }
return len; return len;

10
iov.h
View File

@ -12,8 +12,8 @@
#include "qemu-common.h" #include "qemu-common.h"
size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt, size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt,
const void *buf, size_t size); const void *buf, size_t iov_off, size_t size);
size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt, size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
void *buf, size_t offset, size_t size); void *buf, size_t iov_off, size_t size);
size_t iov_size(const struct iovec *iov, const unsigned int iovcnt); size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt);

View File

@ -23,6 +23,7 @@ static QemuOptsList qemu_drive_opts = {
},{ },{
.name = "index", .name = "index",
.type = QEMU_OPT_NUMBER, .type = QEMU_OPT_NUMBER,
.help = "index number",
},{ },{
.name = "cyls", .name = "cyls",
.type = QEMU_OPT_NUMBER, .type = QEMU_OPT_NUMBER,
@ -46,6 +47,7 @@ static QemuOptsList qemu_drive_opts = {
},{ },{
.name = "snapshot", .name = "snapshot",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
.help = "enable/disable snapshot mode",
},{ },{
.name = "file", .name = "file",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
@ -65,12 +67,15 @@ static QemuOptsList qemu_drive_opts = {
},{ },{
.name = "serial", .name = "serial",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
.help = "disk serial number",
},{ },{
.name = "rerror", .name = "rerror",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
.help = "read error action",
},{ },{
.name = "werror", .name = "werror",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
.help = "write error action",
},{ },{
.name = "addr", .name = "addr",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
@ -78,6 +83,7 @@ static QemuOptsList qemu_drive_opts = {
},{ },{
.name = "readonly", .name = "readonly",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
.help = "open drive file as read-only",
}, },
{ /* end of list */ } { /* end of list */ }
}, },

View File

@ -30,7 +30,7 @@ ETEXI
DEF("convert", img_convert, DEF("convert", img_convert,
"convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename") "convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename")
STEXI STEXI
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} @item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
ETEXI ETEXI
DEF("info", img_info, DEF("info", img_info,
@ -48,7 +48,7 @@ ETEXI
DEF("rebase", img_rebase, DEF("rebase", img_rebase,
"rebase [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename") "rebase [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
STEXI STEXI
@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} @item rebase [-f @var{fmt}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
ETEXI ETEXI
DEF("resize", img_resize, DEF("resize", img_resize,

View File

@ -1024,35 +1024,6 @@ out:
return 0; return 0;
} }
#ifdef _WIN32
static int64_t get_allocated_file_size(const char *filename)
{
typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high);
get_compressed_t get_compressed;
struct _stati64 st;
/* WinNT support GetCompressedFileSize to determine allocate size */
get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
if (get_compressed) {
DWORD high, low;
low = get_compressed(filename, &high);
if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR)
return (((int64_t) high) << 32) + low;
}
if (_stati64(filename, &st) < 0)
return -1;
return st.st_size;
}
#else
static int64_t get_allocated_file_size(const char *filename)
{
struct stat st;
if (stat(filename, &st) < 0)
return -1;
return (int64_t)st.st_blocks * 512;
}
#endif
static void dump_snapshots(BlockDriverState *bs) static void dump_snapshots(BlockDriverState *bs)
{ {
@ -1112,7 +1083,7 @@ static int img_info(int argc, char **argv)
bdrv_get_format(bs, fmt_name, sizeof(fmt_name)); bdrv_get_format(bs, fmt_name, sizeof(fmt_name));
bdrv_get_geometry(bs, &total_sectors); bdrv_get_geometry(bs, &total_sectors);
get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512); get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512);
allocated_size = get_allocated_file_size(filename); allocated_size = bdrv_get_allocated_file_size(bs);
if (allocated_size < 0) { if (allocated_size < 0) {
snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
} else { } else {

View File

@ -38,6 +38,8 @@ by the used format or see the format descriptions below for details.
indicates that target image must be compressed (qcow format only) indicates that target image must be compressed (qcow format only)
@item -h @item -h
with or without a command shows help and lists the supported formats with or without a command shows help and lists the supported formats
@item -p
display progress bar (convert and rebase commands only)
@end table @end table
Parameters to snapshot subcommand: Parameters to snapshot subcommand:
@ -84,7 +86,7 @@ it doesn't need to be specified separately in this case.
Commit the changes recorded in @var{filename} in its base image. Commit the changes recorded in @var{filename} in its base image.
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} @item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename} Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
using format @var{output_fmt}. It can be optionally compressed (@code{-c} using format @var{output_fmt}. It can be optionally compressed (@code{-c}
@ -114,7 +116,7 @@ they are displayed too.
List, apply, create or delete snapshots in image @var{filename}. List, apply, create or delete snapshots in image @var{filename}.
@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} @item rebase [-f @var{fmt}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
Changes the backing file of an image. Only the formats @code{qcow2} and Changes the backing file of an image. Only the formats @code{qcow2} and
@code{qed} support changing the backing file. @code{qed} support changing the backing file.

2421
qemu-io.c

File diff suppressed because it is too large Load Diff

View File

@ -160,6 +160,14 @@ an untrusted format header.
This option specifies the serial number to assign to the device. This option specifies the serial number to assign to the device.
@item addr=@var{addr} @item addr=@var{addr}
Specify the controller's PCI address (if=virtio only). Specify the controller's PCI address (if=virtio only).
@item werror=@var{action},rerror=@var{action}
Specify which @var{action} to take on write and read errors. Valid actions are:
"ignore" (ignore the error and try to continue), "stop" (pause QEMU),
"report" (report the error to the guest), "enospc" (pause QEMU only if the
host disk is full; report the error to the guest otherwise).
The default setting is @option{werror=enospc} and @option{rerror=report}.
@item readonly
Open drive @option{file} as read-only. Guest write attempts will fail.
@end table @end table
By default, writethrough caching is used for all block device. This means that By default, writethrough caching is used for all block device. This means that