2006-08-02 00:21:11 +08:00
|
|
|
/*
|
2007-12-16 01:28:36 +08:00
|
|
|
* Block driver for RAW files (posix)
|
2007-09-17 05:08:06 +08:00
|
|
|
*
|
2006-08-02 00:21:11 +08:00
|
|
|
* Copyright (c) 2006 Fabrice Bellard
|
2007-09-17 05:08:06 +08:00
|
|
|
*
|
2006-08-02 00:21:11 +08:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
2007-11-11 10:51:17 +08:00
|
|
|
#include "qemu-common.h"
|
2012-12-18 01:20:00 +08:00
|
|
|
#include "qemu/timer.h"
|
|
|
|
#include "qemu/log.h"
|
2012-12-18 01:19:44 +08:00
|
|
|
#include "block/block_int.h"
|
2012-12-18 01:20:00 +08:00
|
|
|
#include "qemu/module.h"
|
2012-05-25 17:46:27 +08:00
|
|
|
#include "trace.h"
|
2012-12-18 01:19:44 +08:00
|
|
|
#include "block/thread-pool.h"
|
2012-12-18 01:20:00 +08:00
|
|
|
#include "qemu/iov.h"
|
2012-06-09 16:57:37 +08:00
|
|
|
#include "raw-aio.h"
|
2014-09-10 17:05:48 +08:00
|
|
|
#include "qapi/util.h"
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2011-11-11 02:40:06 +08:00
|
|
|
#if defined(__APPLE__) && (__MACH__)
|
2006-08-02 00:21:11 +08:00
|
|
|
#include <paths.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <IOKit/IOKitLib.h>
|
|
|
|
#include <IOKit/IOBSD.h>
|
|
|
|
#include <IOKit/storage/IOMediaBSDClient.h>
|
|
|
|
#include <IOKit/storage/IOMedia.h>
|
|
|
|
#include <IOKit/storage/IOCDMedia.h>
|
|
|
|
//#include <IOKit/storage/IOCDTypes.h>
|
|
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __sun__
|
2007-01-06 01:44:41 +08:00
|
|
|
#define _POSIX_PTHREAD_SEMANTICS 1
|
2006-08-02 00:21:11 +08:00
|
|
|
#include <sys/dkio.h>
|
|
|
|
#endif
|
2006-08-19 19:45:59 +08:00
|
|
|
#ifdef __linux__
|
2011-06-29 22:25:17 +08:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2006-08-19 19:45:59 +08:00
|
|
|
#include <sys/ioctl.h>
|
2010-09-06 23:06:02 +08:00
|
|
|
#include <sys/param.h>
|
2006-08-19 19:45:59 +08:00
|
|
|
#include <linux/cdrom.h>
|
|
|
|
#include <linux/fd.h>
|
2012-05-09 22:49:58 +08:00
|
|
|
#include <linux/fs.h>
|
qemu-img create: add 'nocow' option
Add 'nocow' option so that users could have a chance to set NOCOW flag to
newly created files. It's useful on btrfs file system to enhance performance.
Btrfs has low performance when hosting VM images, even more when the guest
in those VM are also using btrfs as file system. One way to mitigate this bad
performance is to turn off COW attributes on VM files. Generally, there are
two ways to turn off NOCOW on btrfs: a) by mounting fs with nodatacow, then
all newly created files will be NOCOW. b) per file. Add the NOCOW file
attribute. It could only be done to empty or new files.
This patch tries the second way, according to the option, it could add NOCOW
per file.
For most block drivers, since the create file step is in raw-posix.c, so we
can do setting NOCOW flag ioctl in raw-posix.c only.
But there are some exceptions, like block/vpc.c and block/vdi.c, they are
creating file by calling qemu_open directly. For them, do the same setting
NOCOW flag ioctl work in them separately.
[Fixed up 082.out due to the new 'nocow' creation option
--Stefan]
Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-30 14:29:58 +08:00
|
|
|
#ifndef FS_NOCOW_FL
|
|
|
|
#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
|
|
|
|
#endif
|
2012-05-09 22:49:58 +08:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_FIEMAP
|
|
|
|
#include <linux/fiemap.h>
|
2006-08-19 19:45:59 +08:00
|
|
|
#endif
|
2013-01-14 23:26:52 +08:00
|
|
|
#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
|
|
|
|
#include <linux/falloc.h>
|
|
|
|
#endif
|
2009-11-30 01:00:41 +08:00
|
|
|
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
|
2006-12-22 03:14:11 +08:00
|
|
|
#include <sys/disk.h>
|
2009-03-28 16:37:13 +08:00
|
|
|
#include <sys/cdio.h>
|
2006-12-22 03:14:11 +08:00
|
|
|
#endif
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2008-08-16 02:33:42 +08:00
|
|
|
#ifdef __OpenBSD__
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/disklabel.h>
|
|
|
|
#include <sys/dkio.h>
|
|
|
|
#endif
|
|
|
|
|
2011-05-23 20:31:17 +08:00
|
|
|
#ifdef __NetBSD__
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/disklabel.h>
|
|
|
|
#include <sys/dkio.h>
|
|
|
|
#include <sys/disk.h>
|
|
|
|
#endif
|
|
|
|
|
2009-03-08 04:06:23 +08:00
|
|
|
#ifdef __DragonFly__
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/diskslice.h>
|
|
|
|
#endif
|
|
|
|
|
2010-12-17 18:41:15 +08:00
|
|
|
#ifdef CONFIG_XFS
|
|
|
|
#include <xfs/xfs.h>
|
|
|
|
#endif
|
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
//#define DEBUG_FLOPPY
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2007-11-11 10:51:17 +08:00
|
|
|
//#define DEBUG_BLOCK
|
2008-09-15 23:51:35 +08:00
|
|
|
#if defined(DEBUG_BLOCK)
|
2009-05-14 01:53:17 +08:00
|
|
|
#define DEBUG_BLOCK_PRINT(formatCstr, ...) do { if (qemu_log_enabled()) \
|
|
|
|
{ qemu_log(formatCstr, ## __VA_ARGS__); qemu_log_flush(); } } while (0)
|
2007-09-13 20:29:23 +08:00
|
|
|
#else
|
2009-05-14 01:53:17 +08:00
|
|
|
#define DEBUG_BLOCK_PRINT(formatCstr, ...)
|
2007-09-13 20:29:23 +08:00
|
|
|
#endif
|
|
|
|
|
2008-10-15 02:00:38 +08:00
|
|
|
/* OS X does not have O_DSYNC */
|
|
|
|
#ifndef O_DSYNC
|
2009-07-02 01:28:32 +08:00
|
|
|
#ifdef O_SYNC
|
2008-10-15 02:14:47 +08:00
|
|
|
#define O_DSYNC O_SYNC
|
2009-07-02 01:28:32 +08:00
|
|
|
#elif defined(O_FSYNC)
|
|
|
|
#define O_DSYNC O_FSYNC
|
|
|
|
#endif
|
2008-10-15 02:00:38 +08:00
|
|
|
#endif
|
|
|
|
|
2008-10-14 22:42:54 +08:00
|
|
|
/* Approximate O_DIRECT with O_DSYNC if O_DIRECT isn't available */
|
|
|
|
#ifndef O_DIRECT
|
|
|
|
#define O_DIRECT O_DSYNC
|
|
|
|
#endif
|
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
#define FTYPE_FILE 0
|
|
|
|
#define FTYPE_CD 1
|
|
|
|
#define FTYPE_FD 2
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2010-10-23 23:24:07 +08:00
|
|
|
/* if the FD is not accessed during that time (in ns), we try to
|
2006-08-19 19:45:59 +08:00
|
|
|
reopen it to see if the disk has been changed */
|
2010-10-23 23:24:07 +08:00
|
|
|
#define FD_OPEN_TIMEOUT (1000000000)
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2010-09-13 05:43:21 +08:00
|
|
|
#define MAX_BLOCKSIZE 4096
|
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
typedef struct BDRVRawState {
|
|
|
|
int fd;
|
|
|
|
int type;
|
2009-06-15 19:53:26 +08:00
|
|
|
int open_flags;
|
2011-11-29 19:42:20 +08:00
|
|
|
size_t buf_align;
|
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
#if defined(__linux__)
|
|
|
|
/* linux floppy specific */
|
|
|
|
int64_t fd_open_time;
|
|
|
|
int64_t fd_error_time;
|
|
|
|
int fd_got_error;
|
|
|
|
int fd_media_changed;
|
2006-08-02 00:21:11 +08:00
|
|
|
#endif
|
2009-08-28 20:39:31 +08:00
|
|
|
#ifdef CONFIG_LINUX_AIO
|
2009-08-20 22:58:35 +08:00
|
|
|
int use_aio;
|
2009-10-26 20:03:08 +08:00
|
|
|
void *aio_ctx;
|
2009-08-28 20:39:31 +08:00
|
|
|
#endif
|
2010-12-17 18:41:15 +08:00
|
|
|
#ifdef CONFIG_XFS
|
2013-11-22 20:39:55 +08:00
|
|
|
bool is_xfs:1;
|
2010-12-17 18:41:15 +08:00
|
|
|
#endif
|
2013-11-22 20:39:55 +08:00
|
|
|
bool has_discard:1;
|
2013-11-22 20:39:57 +08:00
|
|
|
bool has_write_zeroes:1;
|
2013-11-22 20:39:55 +08:00
|
|
|
bool discard_zeroes:1;
|
2014-10-21 22:03:03 +08:00
|
|
|
bool needs_alignment;
|
2014-05-09 02:57:55 +08:00
|
|
|
#ifdef CONFIG_FIEMAP
|
|
|
|
bool skip_fiemap;
|
|
|
|
#endif
|
2006-08-19 19:45:59 +08:00
|
|
|
} BDRVRawState;
|
|
|
|
|
2012-09-21 03:13:25 +08:00
|
|
|
typedef struct BDRVRawReopenState {
|
|
|
|
int fd;
|
|
|
|
int open_flags;
|
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
int use_aio;
|
|
|
|
#endif
|
|
|
|
} BDRVRawReopenState;
|
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
static int fd_open(BlockDriverState *bs);
|
2009-06-27 01:51:24 +08:00
|
|
|
static int64_t raw_getlength(BlockDriverState *bs);
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2012-05-25 17:46:27 +08:00
|
|
|
typedef struct RawPosixAIOData {
|
|
|
|
BlockDriverState *bs;
|
|
|
|
int aio_fildes;
|
|
|
|
union {
|
|
|
|
struct iovec *aio_iov;
|
|
|
|
void *aio_ioctl_buf;
|
|
|
|
};
|
|
|
|
int aio_niov;
|
2013-01-14 23:26:55 +08:00
|
|
|
uint64_t aio_nbytes;
|
2012-05-25 17:46:27 +08:00
|
|
|
#define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */
|
|
|
|
off_t aio_offset;
|
|
|
|
int aio_type;
|
|
|
|
} RawPosixAIOData;
|
|
|
|
|
2009-11-30 01:00:41 +08:00
|
|
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
2009-06-15 19:55:19 +08:00
|
|
|
static int cdrom_reopen(BlockDriverState *bs);
|
2009-03-28 16:37:13 +08:00
|
|
|
#endif
|
|
|
|
|
2011-05-24 17:30:29 +08:00
|
|
|
#if defined(__NetBSD__)
|
|
|
|
static int raw_normalize_devicepath(const char **filename)
|
|
|
|
{
|
|
|
|
static char namebuf[PATH_MAX];
|
|
|
|
const char *dp, *fname;
|
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
fname = *filename;
|
|
|
|
dp = strrchr(fname, '/');
|
|
|
|
if (lstat(fname, &sb) < 0) {
|
|
|
|
fprintf(stderr, "%s: stat failed: %s\n",
|
|
|
|
fname, strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISBLK(sb.st_mode)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dp == NULL) {
|
|
|
|
snprintf(namebuf, PATH_MAX, "r%s", fname);
|
|
|
|
} else {
|
|
|
|
snprintf(namebuf, PATH_MAX, "%.*s/r%s",
|
|
|
|
(int)(dp - fname), fname, dp + 1);
|
|
|
|
}
|
|
|
|
fprintf(stderr, "%s is a block device", fname);
|
|
|
|
*filename = namebuf;
|
|
|
|
fprintf(stderr, ", using %s\n", *filename);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int raw_normalize_devicepath(const char **filename)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-07-16 23:48:17 +08:00
|
|
|
static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
|
2011-11-29 19:42:20 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
char *buf;
|
|
|
|
unsigned int sector_size;
|
|
|
|
|
|
|
|
/* For /dev/sg devices the alignment is not really used.
|
|
|
|
With buffered I/O, we don't have any restrictions. */
|
2014-10-21 22:03:03 +08:00
|
|
|
if (bs->sg || !s->needs_alignment) {
|
2011-11-29 19:42:20 +08:00
|
|
|
bs->request_alignment = 1;
|
|
|
|
s->buf_align = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try a few ioctls to get the right size */
|
|
|
|
bs->request_alignment = 0;
|
|
|
|
s->buf_align = 0;
|
|
|
|
|
|
|
|
#ifdef BLKSSZGET
|
2014-07-16 23:48:17 +08:00
|
|
|
if (ioctl(fd, BLKSSZGET, §or_size) >= 0) {
|
2011-11-29 19:42:20 +08:00
|
|
|
bs->request_alignment = sector_size;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef DKIOCGETBLOCKSIZE
|
2014-07-16 23:48:17 +08:00
|
|
|
if (ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) >= 0) {
|
2011-11-29 19:42:20 +08:00
|
|
|
bs->request_alignment = sector_size;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef DIOCGSECTORSIZE
|
2014-07-16 23:48:17 +08:00
|
|
|
if (ioctl(fd, DIOCGSECTORSIZE, §or_size) >= 0) {
|
2011-11-29 19:42:20 +08:00
|
|
|
bs->request_alignment = sector_size;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_XFS
|
|
|
|
if (s->is_xfs) {
|
|
|
|
struct dioattr da;
|
2014-07-16 23:48:17 +08:00
|
|
|
if (xfsctl(NULL, fd, XFS_IOC_DIOINFO, &da) >= 0) {
|
2011-11-29 19:42:20 +08:00
|
|
|
bs->request_alignment = da.d_miniosz;
|
|
|
|
/* The kernel returns wrong information for d_mem */
|
|
|
|
/* s->buf_align = da.d_mem; */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* If we could not get the sizes so far, we can only guess them */
|
|
|
|
if (!s->buf_align) {
|
|
|
|
size_t align;
|
|
|
|
buf = qemu_memalign(MAX_BLOCKSIZE, 2 * MAX_BLOCKSIZE);
|
|
|
|
for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) {
|
2014-07-16 23:48:17 +08:00
|
|
|
if (pread(fd, buf + align, MAX_BLOCKSIZE, 0) >= 0) {
|
2011-11-29 19:42:20 +08:00
|
|
|
s->buf_align = align;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qemu_vfree(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bs->request_alignment) {
|
|
|
|
size_t align;
|
|
|
|
buf = qemu_memalign(s->buf_align, MAX_BLOCKSIZE);
|
|
|
|
for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) {
|
2014-07-16 23:48:17 +08:00
|
|
|
if (pread(fd, buf, align, 0) >= 0) {
|
2011-11-29 19:42:20 +08:00
|
|
|
bs->request_alignment = align;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qemu_vfree(buf);
|
|
|
|
}
|
2014-07-16 23:48:17 +08:00
|
|
|
|
|
|
|
if (!s->buf_align || !bs->request_alignment) {
|
|
|
|
error_setg(errp, "Could not find working O_DIRECT alignment. "
|
|
|
|
"Try cache.direct=off.");
|
|
|
|
}
|
2011-11-29 19:42:20 +08:00
|
|
|
}
|
|
|
|
|
2012-09-21 03:13:21 +08:00
|
|
|
static void raw_parse_flags(int bdrv_flags, int *open_flags)
|
|
|
|
{
|
|
|
|
assert(open_flags != NULL);
|
|
|
|
|
|
|
|
*open_flags |= O_BINARY;
|
|
|
|
*open_flags &= ~O_ACCMODE;
|
|
|
|
if (bdrv_flags & BDRV_O_RDWR) {
|
|
|
|
*open_flags |= O_RDWR;
|
|
|
|
} else {
|
|
|
|
*open_flags |= O_RDONLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Use O_DSYNC for write-through caching, no flags for write-back caching,
|
|
|
|
* and O_DIRECT for no caching. */
|
|
|
|
if ((bdrv_flags & BDRV_O_NOCACHE)) {
|
|
|
|
*open_flags |= O_DIRECT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-08 22:34:47 +08:00
|
|
|
static void raw_detach_aio_context(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
if (s->use_aio) {
|
|
|
|
laio_detach_aio_context(s->aio_ctx, bdrv_get_aio_context(bs));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void raw_attach_aio_context(BlockDriverState *bs,
|
|
|
|
AioContext *new_context)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
if (s->use_aio) {
|
|
|
|
laio_attach_aio_context(s->aio_ctx, new_context);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-09-21 03:13:20 +08:00
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
static int raw_set_aio(void **aio_ctx, int *use_aio, int bdrv_flags)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
assert(aio_ctx != NULL);
|
|
|
|
assert(use_aio != NULL);
|
|
|
|
/*
|
|
|
|
* Currently Linux do AIO only for files opened with O_DIRECT
|
|
|
|
* specified so check NOCACHE flag too
|
|
|
|
*/
|
|
|
|
if ((bdrv_flags & (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) ==
|
|
|
|
(BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) {
|
|
|
|
|
|
|
|
/* if non-NULL, laio_init() has already been run */
|
|
|
|
if (*aio_ctx == NULL) {
|
|
|
|
*aio_ctx = laio_init();
|
|
|
|
if (!*aio_ctx) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*use_aio = 1;
|
|
|
|
} else {
|
|
|
|
*use_aio = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-03-06 05:41:37 +08:00
|
|
|
static void raw_parse_filename(const char *filename, QDict *options,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
/* The filename does not have to be prefixed by the protocol name, since
|
|
|
|
* "file" is the default protocol; therefore, the return value of this
|
|
|
|
* function call can be ignored. */
|
|
|
|
strstart(filename, "file:", &filename);
|
|
|
|
|
|
|
|
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
|
|
|
}
|
|
|
|
|
2013-04-02 16:47:40 +08:00
|
|
|
static QemuOptsList raw_runtime_opts = {
|
|
|
|
.name = "raw",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head),
|
|
|
|
.desc = {
|
|
|
|
{
|
|
|
|
.name = "filename",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "File name of the image",
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int raw_open_common(BlockDriverState *bs, QDict *options,
|
2013-10-11 17:37:01 +08:00
|
|
|
int bdrv_flags, int open_flags, Error **errp)
|
2006-08-02 00:21:11 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
2013-04-02 16:47:40 +08:00
|
|
|
QemuOpts *opts;
|
|
|
|
Error *local_err = NULL;
|
2014-04-12 01:16:36 +08:00
|
|
|
const char *filename = NULL;
|
2009-06-15 19:53:26 +08:00
|
|
|
int fd, ret;
|
2013-11-22 20:39:55 +08:00
|
|
|
struct stat st;
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2014-01-02 10:49:17 +08:00
|
|
|
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
|
2013-04-02 16:47:40 +08:00
|
|
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2013-10-11 17:37:01 +08:00
|
|
|
error_propagate(errp, local_err);
|
2013-04-02 16:47:40 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
filename = qemu_opt_get(opts, "filename");
|
|
|
|
|
2011-05-24 17:30:29 +08:00
|
|
|
ret = raw_normalize_devicepath(&filename);
|
|
|
|
if (ret != 0) {
|
2013-10-11 17:37:01 +08:00
|
|
|
error_setg_errno(errp, -ret, "Could not normalize device path");
|
2013-04-02 16:47:40 +08:00
|
|
|
goto fail;
|
2011-05-24 17:30:29 +08:00
|
|
|
}
|
|
|
|
|
2012-09-21 03:13:21 +08:00
|
|
|
s->open_flags = open_flags;
|
|
|
|
raw_parse_flags(bdrv_flags, &s->open_flags);
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2009-06-15 19:53:38 +08:00
|
|
|
s->fd = -1;
|
2009-12-02 19:24:42 +08:00
|
|
|
fd = qemu_open(filename, s->open_flags, 0644);
|
2006-08-19 19:45:59 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
ret = -errno;
|
2013-04-02 16:47:40 +08:00
|
|
|
if (ret == -EROFS) {
|
2006-08-19 19:45:59 +08:00
|
|
|
ret = -EACCES;
|
2013-04-02 16:47:40 +08:00
|
|
|
}
|
|
|
|
goto fail;
|
2006-08-19 19:45:59 +08:00
|
|
|
}
|
2006-08-02 00:21:11 +08:00
|
|
|
s->fd = fd;
|
2009-08-20 22:58:19 +08:00
|
|
|
|
2009-08-20 22:58:35 +08:00
|
|
|
#ifdef CONFIG_LINUX_AIO
|
2012-09-21 03:13:20 +08:00
|
|
|
if (raw_set_aio(&s->aio_ctx, &s->use_aio, bdrv_flags)) {
|
2012-05-25 00:03:13 +08:00
|
|
|
qemu_close(fd);
|
2013-04-02 16:47:40 +08:00
|
|
|
ret = -errno;
|
2013-10-11 17:37:01 +08:00
|
|
|
error_setg_errno(errp, -ret, "Could not set AIO state");
|
2013-04-02 16:47:40 +08:00
|
|
|
goto fail;
|
2009-08-20 22:58:19 +08:00
|
|
|
}
|
2012-09-21 03:13:20 +08:00
|
|
|
#endif
|
2009-08-20 22:58:19 +08:00
|
|
|
|
2013-11-22 20:39:47 +08:00
|
|
|
s->has_discard = true;
|
2013-11-22 20:39:57 +08:00
|
|
|
s->has_write_zeroes = true;
|
2014-10-21 22:03:03 +08:00
|
|
|
if ((bs->open_flags & BDRV_O_NOCACHE) != 0) {
|
|
|
|
s->needs_alignment = true;
|
|
|
|
}
|
2013-11-22 20:39:55 +08:00
|
|
|
|
|
|
|
if (fstat(s->fd, &st) < 0) {
|
|
|
|
error_setg_errno(errp, errno, "Could not stat file");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (S_ISREG(st.st_mode)) {
|
|
|
|
s->discard_zeroes = true;
|
|
|
|
}
|
2013-11-22 20:39:56 +08:00
|
|
|
if (S_ISBLK(st.st_mode)) {
|
|
|
|
#ifdef BLKDISCARDZEROES
|
|
|
|
unsigned int arg;
|
|
|
|
if (ioctl(s->fd, BLKDISCARDZEROES, &arg) == 0 && arg) {
|
|
|
|
s->discard_zeroes = true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
|
|
/* On Linux 3.10, BLKDISCARD leaves stale data in the page cache. Do
|
|
|
|
* not rely on the contents of discarded blocks unless using O_DIRECT.
|
2013-11-22 20:39:57 +08:00
|
|
|
* Same for BLKZEROOUT.
|
2013-11-22 20:39:56 +08:00
|
|
|
*/
|
|
|
|
if (!(bs->open_flags & BDRV_O_NOCACHE)) {
|
|
|
|
s->discard_zeroes = false;
|
2013-11-22 20:39:57 +08:00
|
|
|
s->has_write_zeroes = false;
|
2013-11-22 20:39:56 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2014-10-21 22:03:03 +08:00
|
|
|
#ifdef __FreeBSD__
|
|
|
|
if (S_ISCHR(st.st_mode)) {
|
|
|
|
/*
|
|
|
|
* The file is a char device (disk), which on FreeBSD isn't behind
|
|
|
|
* a pager, so force all requests to be aligned. This is needed
|
|
|
|
* so QEMU makes sure all IO operations on the device are aligned
|
|
|
|
* to sector size, or else FreeBSD will reject them with EINVAL.
|
|
|
|
*/
|
|
|
|
s->needs_alignment = true;
|
|
|
|
}
|
|
|
|
#endif
|
2013-11-22 20:39:55 +08:00
|
|
|
|
2010-12-17 18:41:15 +08:00
|
|
|
#ifdef CONFIG_XFS
|
|
|
|
if (platform_test_xfs_fd(s->fd)) {
|
2013-11-22 20:39:47 +08:00
|
|
|
s->is_xfs = true;
|
2010-12-17 18:41:15 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-05-08 22:34:47 +08:00
|
|
|
raw_attach_aio_context(bs, bdrv_get_aio_context(bs));
|
|
|
|
|
2013-04-02 16:47:40 +08:00
|
|
|
ret = 0;
|
|
|
|
fail:
|
2014-04-12 01:16:36 +08:00
|
|
|
if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) {
|
|
|
|
unlink(filename);
|
|
|
|
}
|
2013-04-02 16:47:40 +08:00
|
|
|
qemu_opts_del(opts);
|
|
|
|
return ret;
|
2006-08-02 00:21:11 +08:00
|
|
|
}
|
|
|
|
|
2013-09-05 20:22:29 +08:00
|
|
|
static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|
|
|
Error **errp)
|
2009-06-15 19:53:38 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
2013-10-11 17:37:01 +08:00
|
|
|
Error *local_err = NULL;
|
|
|
|
int ret;
|
2009-06-15 19:53:38 +08:00
|
|
|
|
|
|
|
s->type = FTYPE_FILE;
|
2013-10-11 17:37:01 +08:00
|
|
|
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2013-10-11 17:37:01 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
return ret;
|
2009-06-15 19:53:38 +08:00
|
|
|
}
|
|
|
|
|
2012-09-21 03:13:25 +08:00
|
|
|
static int raw_reopen_prepare(BDRVReopenState *state,
|
|
|
|
BlockReopenQueue *queue, Error **errp)
|
|
|
|
{
|
|
|
|
BDRVRawState *s;
|
|
|
|
BDRVRawReopenState *raw_s;
|
|
|
|
int ret = 0;
|
2014-07-16 23:48:17 +08:00
|
|
|
Error *local_err = NULL;
|
2012-09-21 03:13:25 +08:00
|
|
|
|
|
|
|
assert(state != NULL);
|
|
|
|
assert(state->bs != NULL);
|
|
|
|
|
|
|
|
s = state->bs->opaque;
|
|
|
|
|
block: Use g_new() & friends where that makes obvious sense
g_new(T, n) is neater than g_malloc(sizeof(T) * n). It's also safer,
for two reasons. One, it catches multiplication overflowing size_t.
Two, it returns T * rather than void *, which lets the compiler catch
more type errors.
Patch created with Coccinelle, with two manual changes on top:
* Add const to bdrv_iterate_format() to keep the types straight
* Convert the allocation in bdrv_drop_intermediate(), which Coccinelle
inexplicably misses
Coccinelle semantic patch:
@@
type T;
@@
-g_malloc(sizeof(T))
+g_new(T, 1)
@@
type T;
@@
-g_try_malloc(sizeof(T))
+g_try_new(T, 1)
@@
type T;
@@
-g_malloc0(sizeof(T))
+g_new0(T, 1)
@@
type T;
@@
-g_try_malloc0(sizeof(T))
+g_try_new0(T, 1)
@@
type T;
expression n;
@@
-g_malloc(sizeof(T) * (n))
+g_new(T, n)
@@
type T;
expression n;
@@
-g_try_malloc(sizeof(T) * (n))
+g_try_new(T, n)
@@
type T;
expression n;
@@
-g_malloc0(sizeof(T) * (n))
+g_new0(T, n)
@@
type T;
expression n;
@@
-g_try_malloc0(sizeof(T) * (n))
+g_try_new0(T, n)
@@
type T;
expression p, n;
@@
-g_realloc(p, sizeof(T) * (n))
+g_renew(T, p, n)
@@
type T;
expression p, n;
@@
-g_try_realloc(p, sizeof(T) * (n))
+g_try_renew(T, p, n)
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2014-08-19 16:31:08 +08:00
|
|
|
state->opaque = g_new0(BDRVRawReopenState, 1);
|
2012-09-21 03:13:25 +08:00
|
|
|
raw_s = state->opaque;
|
|
|
|
|
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
raw_s->use_aio = s->use_aio;
|
|
|
|
|
|
|
|
/* we can use s->aio_ctx instead of a copy, because the use_aio flag is
|
|
|
|
* valid in the 'false' condition even if aio_ctx is set, and raw_set_aio()
|
|
|
|
* won't override aio_ctx if aio_ctx is non-NULL */
|
|
|
|
if (raw_set_aio(&s->aio_ctx, &raw_s->use_aio, state->flags)) {
|
2013-10-11 17:37:01 +08:00
|
|
|
error_setg(errp, "Could not set AIO state");
|
2012-09-21 03:13:25 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-11-20 23:21:10 +08:00
|
|
|
if (s->type == FTYPE_FD || s->type == FTYPE_CD) {
|
|
|
|
raw_s->open_flags |= O_NONBLOCK;
|
|
|
|
}
|
|
|
|
|
2012-09-21 03:13:25 +08:00
|
|
|
raw_parse_flags(state->flags, &raw_s->open_flags);
|
|
|
|
|
|
|
|
raw_s->fd = -1;
|
|
|
|
|
2013-01-31 22:40:14 +08:00
|
|
|
int fcntl_flags = O_APPEND | O_NONBLOCK;
|
2012-09-21 03:13:25 +08:00
|
|
|
#ifdef O_NOATIME
|
|
|
|
fcntl_flags |= O_NOATIME;
|
|
|
|
#endif
|
|
|
|
|
2013-01-31 22:40:14 +08:00
|
|
|
#ifdef O_ASYNC
|
|
|
|
/* Not all operating systems have O_ASYNC, and those that don't
|
|
|
|
* will not let us track the state into raw_s->open_flags (typically
|
|
|
|
* you achieve the same effect with an ioctl, for example I_SETSIG
|
|
|
|
* on Solaris). But we do not use O_ASYNC, so that's fine.
|
|
|
|
*/
|
|
|
|
assert((s->open_flags & O_ASYNC) == 0);
|
|
|
|
#endif
|
|
|
|
|
2012-09-21 03:13:25 +08:00
|
|
|
if ((raw_s->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
|
|
|
|
/* dup the original fd */
|
|
|
|
/* TODO: use qemu fcntl wrapper */
|
|
|
|
#ifdef F_DUPFD_CLOEXEC
|
|
|
|
raw_s->fd = fcntl(s->fd, F_DUPFD_CLOEXEC, 0);
|
|
|
|
#else
|
|
|
|
raw_s->fd = dup(s->fd);
|
|
|
|
if (raw_s->fd != -1) {
|
|
|
|
qemu_set_cloexec(raw_s->fd);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (raw_s->fd >= 0) {
|
|
|
|
ret = fcntl_setfl(raw_s->fd, raw_s->open_flags);
|
|
|
|
if (ret) {
|
|
|
|
qemu_close(raw_s->fd);
|
|
|
|
raw_s->fd = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
|
|
|
|
if (raw_s->fd == -1) {
|
|
|
|
assert(!(raw_s->open_flags & O_CREAT));
|
|
|
|
raw_s->fd = qemu_open(state->bs->filename, raw_s->open_flags);
|
|
|
|
if (raw_s->fd == -1) {
|
2013-10-11 17:37:01 +08:00
|
|
|
error_setg_errno(errp, errno, "Could not reopen file");
|
2012-09-21 03:13:25 +08:00
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
}
|
2014-07-16 23:48:17 +08:00
|
|
|
|
|
|
|
/* Fail already reopen_prepare() if we can't get a working O_DIRECT
|
|
|
|
* alignment with the new fd. */
|
|
|
|
if (raw_s->fd != -1) {
|
|
|
|
raw_probe_alignment(state->bs, raw_s->fd, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
qemu_close(raw_s->fd);
|
|
|
|
raw_s->fd = -1;
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-21 03:13:25 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void raw_reopen_commit(BDRVReopenState *state)
|
|
|
|
{
|
|
|
|
BDRVRawReopenState *raw_s = state->opaque;
|
|
|
|
BDRVRawState *s = state->bs->opaque;
|
|
|
|
|
|
|
|
s->open_flags = raw_s->open_flags;
|
|
|
|
|
|
|
|
qemu_close(s->fd);
|
|
|
|
s->fd = raw_s->fd;
|
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
s->use_aio = raw_s->use_aio;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
g_free(state->opaque);
|
|
|
|
state->opaque = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void raw_reopen_abort(BDRVReopenState *state)
|
|
|
|
{
|
|
|
|
BDRVRawReopenState *raw_s = state->opaque;
|
|
|
|
|
|
|
|
/* nothing to do if NULL, we didn't get far enough */
|
|
|
|
if (raw_s == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (raw_s->fd >= 0) {
|
|
|
|
qemu_close(raw_s->fd);
|
|
|
|
raw_s->fd = -1;
|
|
|
|
}
|
|
|
|
g_free(state->opaque);
|
|
|
|
state->opaque = NULL;
|
|
|
|
}
|
|
|
|
|
2014-07-16 23:48:16 +08:00
|
|
|
static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
|
2011-11-29 19:42:20 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
2012-09-21 03:13:25 +08:00
|
|
|
|
2014-07-16 23:48:17 +08:00
|
|
|
raw_probe_alignment(bs, s->fd, errp);
|
2011-11-29 19:42:20 +08:00
|
|
|
bs->bl.opt_mem_alignment = s->buf_align;
|
|
|
|
}
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2012-05-25 17:46:27 +08:00
|
|
|
static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_ioctl_buf);
|
|
|
|
if (ret == -1) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
2013-01-10 22:28:35 +08:00
|
|
|
return 0;
|
2012-05-25 17:46:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t handle_aiocb_flush(RawPosixAIOData *aiocb)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = qemu_fdatasync(aiocb->aio_fildes);
|
|
|
|
if (ret == -1) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PREADV
|
|
|
|
|
|
|
|
static bool preadv_present = true;
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset)
|
|
|
|
{
|
|
|
|
return preadv(fd, iov, nr_iov, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset)
|
|
|
|
{
|
|
|
|
return pwritev(fd, iov, nr_iov, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
static bool preadv_present = false;
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset)
|
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset)
|
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static ssize_t handle_aiocb_rw_vector(RawPosixAIOData *aiocb)
|
|
|
|
{
|
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (aiocb->aio_type & QEMU_AIO_WRITE)
|
|
|
|
len = qemu_pwritev(aiocb->aio_fildes,
|
|
|
|
aiocb->aio_iov,
|
|
|
|
aiocb->aio_niov,
|
|
|
|
aiocb->aio_offset);
|
|
|
|
else
|
|
|
|
len = qemu_preadv(aiocb->aio_fildes,
|
|
|
|
aiocb->aio_iov,
|
|
|
|
aiocb->aio_niov,
|
|
|
|
aiocb->aio_offset);
|
|
|
|
} while (len == -1 && errno == EINTR);
|
|
|
|
|
|
|
|
if (len == -1) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read/writes the data to/from a given linear buffer.
|
|
|
|
*
|
|
|
|
* Returns the number of bytes handles or -errno in case of an error. Short
|
|
|
|
* reads are only returned if the end of the file is reached.
|
|
|
|
*/
|
|
|
|
static ssize_t handle_aiocb_rw_linear(RawPosixAIOData *aiocb, char *buf)
|
|
|
|
{
|
|
|
|
ssize_t offset = 0;
|
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
while (offset < aiocb->aio_nbytes) {
|
|
|
|
if (aiocb->aio_type & QEMU_AIO_WRITE) {
|
|
|
|
len = pwrite(aiocb->aio_fildes,
|
|
|
|
(const char *)buf + offset,
|
|
|
|
aiocb->aio_nbytes - offset,
|
|
|
|
aiocb->aio_offset + offset);
|
|
|
|
} else {
|
|
|
|
len = pread(aiocb->aio_fildes,
|
|
|
|
buf + offset,
|
|
|
|
aiocb->aio_nbytes - offset,
|
|
|
|
aiocb->aio_offset + offset);
|
|
|
|
}
|
|
|
|
if (len == -1 && errno == EINTR) {
|
|
|
|
continue;
|
2014-08-21 20:44:07 +08:00
|
|
|
} else if (len == -1 && errno == EINVAL &&
|
|
|
|
(aiocb->bs->open_flags & BDRV_O_NOCACHE) &&
|
|
|
|
!(aiocb->aio_type & QEMU_AIO_WRITE) &&
|
|
|
|
offset > 0) {
|
|
|
|
/* O_DIRECT pread() may fail with EINVAL when offset is unaligned
|
|
|
|
* after a short read. Assume that O_DIRECT short reads only occur
|
|
|
|
* at EOF. Therefore this is a short read, not an I/O error.
|
|
|
|
*/
|
|
|
|
break;
|
2012-05-25 17:46:27 +08:00
|
|
|
} else if (len == -1) {
|
|
|
|
offset = -errno;
|
|
|
|
break;
|
|
|
|
} else if (len == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
offset += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb)
|
|
|
|
{
|
|
|
|
ssize_t nbytes;
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
if (!(aiocb->aio_type & QEMU_AIO_MISALIGNED)) {
|
|
|
|
/*
|
|
|
|
* If there is just a single buffer, and it is properly aligned
|
|
|
|
* we can just use plain pread/pwrite without any problems.
|
|
|
|
*/
|
|
|
|
if (aiocb->aio_niov == 1) {
|
|
|
|
return handle_aiocb_rw_linear(aiocb, aiocb->aio_iov->iov_base);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We have more than one iovec, and all are properly aligned.
|
|
|
|
*
|
|
|
|
* Try preadv/pwritev first and fall back to linearizing the
|
|
|
|
* buffer if it's not supported.
|
|
|
|
*/
|
|
|
|
if (preadv_present) {
|
|
|
|
nbytes = handle_aiocb_rw_vector(aiocb);
|
|
|
|
if (nbytes == aiocb->aio_nbytes ||
|
|
|
|
(nbytes < 0 && nbytes != -ENOSYS)) {
|
|
|
|
return nbytes;
|
|
|
|
}
|
|
|
|
preadv_present = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX(hch): short read/write. no easy way to handle the reminder
|
|
|
|
* using these interfaces. For now retry using plain
|
|
|
|
* pread/pwrite?
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, we have to do it the hard way, copy all segments into
|
|
|
|
* a single aligned buffer.
|
|
|
|
*/
|
2014-05-22 00:02:42 +08:00
|
|
|
buf = qemu_try_blockalign(aiocb->bs, aiocb->aio_nbytes);
|
|
|
|
if (buf == NULL) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2012-05-25 17:46:27 +08:00
|
|
|
if (aiocb->aio_type & QEMU_AIO_WRITE) {
|
|
|
|
char *p = buf;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < aiocb->aio_niov; ++i) {
|
|
|
|
memcpy(p, aiocb->aio_iov[i].iov_base, aiocb->aio_iov[i].iov_len);
|
|
|
|
p += aiocb->aio_iov[i].iov_len;
|
|
|
|
}
|
2014-07-01 22:09:54 +08:00
|
|
|
assert(p - buf == aiocb->aio_nbytes);
|
2012-05-25 17:46:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
nbytes = handle_aiocb_rw_linear(aiocb, buf);
|
|
|
|
if (!(aiocb->aio_type & QEMU_AIO_WRITE)) {
|
|
|
|
char *p = buf;
|
|
|
|
size_t count = aiocb->aio_nbytes, copy;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < aiocb->aio_niov && count; ++i) {
|
|
|
|
copy = count;
|
|
|
|
if (copy > aiocb->aio_iov[i].iov_len) {
|
|
|
|
copy = aiocb->aio_iov[i].iov_len;
|
|
|
|
}
|
|
|
|
memcpy(aiocb->aio_iov[i].iov_base, p, copy);
|
2014-07-01 22:09:54 +08:00
|
|
|
assert(count >= copy);
|
2012-05-25 17:46:27 +08:00
|
|
|
p += copy;
|
|
|
|
count -= copy;
|
|
|
|
}
|
2014-07-01 22:09:54 +08:00
|
|
|
assert(count == 0);
|
2012-05-25 17:46:27 +08:00
|
|
|
}
|
|
|
|
qemu_vfree(buf);
|
|
|
|
|
|
|
|
return nbytes;
|
|
|
|
}
|
|
|
|
|
2013-01-14 23:26:55 +08:00
|
|
|
#ifdef CONFIG_XFS
|
2013-11-22 20:39:57 +08:00
|
|
|
static int xfs_write_zeroes(BDRVRawState *s, int64_t offset, uint64_t bytes)
|
|
|
|
{
|
|
|
|
struct xfs_flock64 fl;
|
|
|
|
|
|
|
|
memset(&fl, 0, sizeof(fl));
|
|
|
|
fl.l_whence = SEEK_SET;
|
|
|
|
fl.l_start = offset;
|
|
|
|
fl.l_len = bytes;
|
|
|
|
|
|
|
|
if (xfsctl(NULL, s->fd, XFS_IOC_ZERO_RANGE, &fl) < 0) {
|
|
|
|
DEBUG_BLOCK_PRINT("cannot write zero range (%s)\n", strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-14 23:26:55 +08:00
|
|
|
static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes)
|
|
|
|
{
|
|
|
|
struct xfs_flock64 fl;
|
|
|
|
|
|
|
|
memset(&fl, 0, sizeof(fl));
|
|
|
|
fl.l_whence = SEEK_SET;
|
|
|
|
fl.l_start = offset;
|
|
|
|
fl.l_len = bytes;
|
|
|
|
|
|
|
|
if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) {
|
|
|
|
DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-11-22 20:39:57 +08:00
|
|
|
static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb)
|
|
|
|
{
|
|
|
|
int ret = -EOPNOTSUPP;
|
|
|
|
BDRVRawState *s = aiocb->bs->opaque;
|
|
|
|
|
|
|
|
if (s->has_write_zeroes == 0) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aiocb->aio_type & QEMU_AIO_BLKDEV) {
|
|
|
|
#ifdef BLKZEROOUT
|
|
|
|
do {
|
|
|
|
uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes };
|
|
|
|
if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} while (errno == EINTR);
|
|
|
|
|
|
|
|
ret = -errno;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
#ifdef CONFIG_XFS
|
|
|
|
if (s->is_xfs) {
|
|
|
|
return xfs_write_zeroes(s, aiocb->aio_offset, aiocb->aio_nbytes);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == -ENODEV || ret == -ENOSYS || ret == -EOPNOTSUPP ||
|
|
|
|
ret == -ENOTTY) {
|
|
|
|
s->has_write_zeroes = false;
|
|
|
|
ret = -ENOTSUP;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-01-14 23:26:55 +08:00
|
|
|
static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb)
|
|
|
|
{
|
|
|
|
int ret = -EOPNOTSUPP;
|
|
|
|
BDRVRawState *s = aiocb->bs->opaque;
|
|
|
|
|
2013-11-22 20:39:47 +08:00
|
|
|
if (!s->has_discard) {
|
|
|
|
return -ENOTSUP;
|
2013-01-14 23:26:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (aiocb->aio_type & QEMU_AIO_BLKDEV) {
|
|
|
|
#ifdef BLKDISCARD
|
|
|
|
do {
|
|
|
|
uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes };
|
|
|
|
if (ioctl(aiocb->aio_fildes, BLKDISCARD, range) == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} while (errno == EINTR);
|
|
|
|
|
|
|
|
ret = -errno;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
#ifdef CONFIG_XFS
|
|
|
|
if (s->is_xfs) {
|
|
|
|
return xfs_discard(s, aiocb->aio_offset, aiocb->aio_nbytes);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
|
|
|
|
do {
|
|
|
|
if (fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
|
|
|
aiocb->aio_offset, aiocb->aio_nbytes) == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} while (errno == EINTR);
|
|
|
|
|
|
|
|
ret = -errno;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == -ENODEV || ret == -ENOSYS || ret == -EOPNOTSUPP ||
|
|
|
|
ret == -ENOTTY) {
|
2013-11-22 20:39:47 +08:00
|
|
|
s->has_discard = false;
|
|
|
|
ret = -ENOTSUP;
|
2013-01-14 23:26:55 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-05-25 17:46:27 +08:00
|
|
|
static int aio_worker(void *arg)
|
|
|
|
{
|
|
|
|
RawPosixAIOData *aiocb = arg;
|
|
|
|
ssize_t ret = 0;
|
|
|
|
|
|
|
|
switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
|
|
|
|
case QEMU_AIO_READ:
|
|
|
|
ret = handle_aiocb_rw(aiocb);
|
|
|
|
if (ret >= 0 && ret < aiocb->aio_nbytes && aiocb->bs->growable) {
|
|
|
|
iov_memset(aiocb->aio_iov, aiocb->aio_niov, ret,
|
|
|
|
0, aiocb->aio_nbytes - ret);
|
|
|
|
|
|
|
|
ret = aiocb->aio_nbytes;
|
|
|
|
}
|
|
|
|
if (ret == aiocb->aio_nbytes) {
|
|
|
|
ret = 0;
|
|
|
|
} else if (ret >= 0 && ret < aiocb->aio_nbytes) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case QEMU_AIO_WRITE:
|
|
|
|
ret = handle_aiocb_rw(aiocb);
|
|
|
|
if (ret == aiocb->aio_nbytes) {
|
|
|
|
ret = 0;
|
|
|
|
} else if (ret >= 0 && ret < aiocb->aio_nbytes) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case QEMU_AIO_FLUSH:
|
|
|
|
ret = handle_aiocb_flush(aiocb);
|
|
|
|
break;
|
|
|
|
case QEMU_AIO_IOCTL:
|
|
|
|
ret = handle_aiocb_ioctl(aiocb);
|
|
|
|
break;
|
2013-01-14 23:26:55 +08:00
|
|
|
case QEMU_AIO_DISCARD:
|
|
|
|
ret = handle_aiocb_discard(aiocb);
|
|
|
|
break;
|
2013-11-22 20:39:57 +08:00
|
|
|
case QEMU_AIO_WRITE_ZEROES:
|
|
|
|
ret = handle_aiocb_write_zeroes(aiocb);
|
|
|
|
break;
|
2012-05-25 17:46:27 +08:00
|
|
|
default:
|
|
|
|
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_slice_free(RawPosixAIOData, aiocb);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-11-22 20:39:55 +08:00
|
|
|
static int paio_submit_co(BlockDriverState *bs, int fd,
|
|
|
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
|
|
|
int type)
|
|
|
|
{
|
|
|
|
RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
|
|
|
|
ThreadPool *pool;
|
|
|
|
|
|
|
|
acb->bs = bs;
|
|
|
|
acb->aio_type = type;
|
|
|
|
acb->aio_fildes = fd;
|
|
|
|
|
2014-07-01 22:09:54 +08:00
|
|
|
acb->aio_nbytes = nb_sectors * BDRV_SECTOR_SIZE;
|
|
|
|
acb->aio_offset = sector_num * BDRV_SECTOR_SIZE;
|
|
|
|
|
2013-11-22 20:39:55 +08:00
|
|
|
if (qiov) {
|
|
|
|
acb->aio_iov = qiov->iov;
|
|
|
|
acb->aio_niov = qiov->niov;
|
2014-07-01 22:09:54 +08:00
|
|
|
assert(qiov->size == acb->aio_nbytes);
|
2013-11-22 20:39:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
trace_paio_submit_co(sector_num, nb_sectors, type);
|
|
|
|
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
|
|
|
|
return thread_pool_submit_co(pool, aio_worker, acb);
|
|
|
|
}
|
|
|
|
|
2014-10-07 19:59:14 +08:00
|
|
|
static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
2012-05-25 17:46:27 +08:00
|
|
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
2014-10-07 19:59:15 +08:00
|
|
|
BlockCompletionFunc *cb, void *opaque, int type)
|
2012-05-25 17:46:27 +08:00
|
|
|
{
|
|
|
|
RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
|
2013-03-07 20:41:49 +08:00
|
|
|
ThreadPool *pool;
|
2012-05-25 17:46:27 +08:00
|
|
|
|
|
|
|
acb->bs = bs;
|
|
|
|
acb->aio_type = type;
|
|
|
|
acb->aio_fildes = fd;
|
|
|
|
|
2014-07-01 22:09:54 +08:00
|
|
|
acb->aio_nbytes = nb_sectors * BDRV_SECTOR_SIZE;
|
|
|
|
acb->aio_offset = sector_num * BDRV_SECTOR_SIZE;
|
|
|
|
|
2012-05-25 17:46:27 +08:00
|
|
|
if (qiov) {
|
|
|
|
acb->aio_iov = qiov->iov;
|
|
|
|
acb->aio_niov = qiov->niov;
|
2014-07-01 22:09:54 +08:00
|
|
|
assert(qiov->size == acb->aio_nbytes);
|
2012-05-25 17:46:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
trace_paio_submit(acb, opaque, sector_num, nb_sectors, type);
|
2013-03-07 20:41:49 +08:00
|
|
|
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
|
|
|
|
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
|
2012-05-25 17:46:27 +08:00
|
|
|
}
|
|
|
|
|
2014-10-07 19:59:14 +08:00
|
|
|
static BlockAIOCB *raw_aio_submit(BlockDriverState *bs,
|
2009-08-20 22:58:19 +08:00
|
|
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
2014-10-07 19:59:15 +08:00
|
|
|
BlockCompletionFunc *cb, void *opaque, int type)
|
2006-08-02 00:21:11 +08:00
|
|
|
{
|
2006-08-07 10:38:06 +08:00
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
if (fd_open(bs) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2009-04-08 02:43:24 +08:00
|
|
|
/*
|
2014-10-21 22:03:03 +08:00
|
|
|
* Check if the underlying device requires requests to be aligned,
|
|
|
|
* and if the request we are trying to submit is aligned or not.
|
|
|
|
* If this is the case tell the low-level driver that it needs
|
|
|
|
* to copy the buffer.
|
2009-04-08 02:43:24 +08:00
|
|
|
*/
|
2014-10-21 22:03:03 +08:00
|
|
|
if (s->needs_alignment) {
|
2013-01-11 23:41:27 +08:00
|
|
|
if (!bdrv_qiov_is_aligned(bs, qiov)) {
|
2009-08-20 22:58:35 +08:00
|
|
|
type |= QEMU_AIO_MISALIGNED;
|
2009-08-28 20:39:31 +08:00
|
|
|
#ifdef CONFIG_LINUX_AIO
|
2009-08-20 22:58:35 +08:00
|
|
|
} else if (s->use_aio) {
|
|
|
|
return laio_submit(bs, s->aio_ctx, s->fd, sector_num, qiov,
|
2009-08-28 20:39:31 +08:00
|
|
|
nb_sectors, cb, opaque, type);
|
|
|
|
#endif
|
2009-08-20 22:58:35 +08:00
|
|
|
}
|
2009-08-20 22:58:19 +08:00
|
|
|
}
|
2009-04-08 02:43:24 +08:00
|
|
|
|
2009-10-26 20:03:08 +08:00
|
|
|
return paio_submit(bs, s->fd, sector_num, qiov, nb_sectors,
|
2009-08-20 22:58:19 +08:00
|
|
|
cb, opaque, type);
|
2006-08-02 00:21:11 +08:00
|
|
|
}
|
|
|
|
|
2014-07-04 18:04:34 +08:00
|
|
|
static void raw_aio_plug(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
if (s->use_aio) {
|
|
|
|
laio_io_plug(bs, s->aio_ctx);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void raw_aio_unplug(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
if (s->use_aio) {
|
|
|
|
laio_io_unplug(bs, s->aio_ctx, true);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void raw_aio_flush_io_queue(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
if (s->use_aio) {
|
|
|
|
laio_io_unplug(bs, s->aio_ctx, false);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-10-07 19:59:14 +08:00
|
|
|
static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
|
2009-04-08 02:43:24 +08:00
|
|
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
2014-10-07 19:59:15 +08:00
|
|
|
BlockCompletionFunc *cb, void *opaque)
|
2006-08-02 00:21:11 +08:00
|
|
|
{
|
2009-08-20 22:58:19 +08:00
|
|
|
return raw_aio_submit(bs, sector_num, qiov, nb_sectors,
|
|
|
|
cb, opaque, QEMU_AIO_READ);
|
2006-08-02 00:21:11 +08:00
|
|
|
}
|
|
|
|
|
2014-10-07 19:59:14 +08:00
|
|
|
static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
|
2009-04-08 02:43:24 +08:00
|
|
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
2014-10-07 19:59:15 +08:00
|
|
|
BlockCompletionFunc *cb, void *opaque)
|
2006-08-02 00:21:11 +08:00
|
|
|
{
|
2009-08-20 22:58:19 +08:00
|
|
|
return raw_aio_submit(bs, sector_num, qiov, nb_sectors,
|
|
|
|
cb, opaque, QEMU_AIO_WRITE);
|
2006-08-02 00:21:11 +08:00
|
|
|
}
|
2008-09-26 23:59:29 +08:00
|
|
|
|
2014-10-07 19:59:14 +08:00
|
|
|
static BlockAIOCB *raw_aio_flush(BlockDriverState *bs,
|
2014-10-07 19:59:15 +08:00
|
|
|
BlockCompletionFunc *cb, void *opaque)
|
2009-09-05 01:01:49 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
if (fd_open(bs) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2009-10-26 20:03:08 +08:00
|
|
|
return paio_submit(bs, s->fd, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH);
|
2009-09-05 01:01:49 +08:00
|
|
|
}
|
|
|
|
|
2006-08-02 00:21:11 +08:00
|
|
|
static void raw_close(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
2014-05-08 22:34:47 +08:00
|
|
|
|
|
|
|
raw_detach_aio_context(bs);
|
|
|
|
|
2014-05-08 22:34:48 +08:00
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
if (s->use_aio) {
|
|
|
|
laio_cleanup(s->aio_ctx);
|
|
|
|
}
|
|
|
|
#endif
|
2006-08-19 19:45:59 +08:00
|
|
|
if (s->fd >= 0) {
|
2012-08-15 04:43:46 +08:00
|
|
|
qemu_close(s->fd);
|
2006-08-19 19:45:59 +08:00
|
|
|
s->fd = -1;
|
|
|
|
}
|
2006-08-02 00:21:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
2011-09-21 07:10:37 +08:00
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (fstat(s->fd, &st)) {
|
2006-08-02 00:21:11 +08:00
|
|
|
return -errno;
|
2011-09-21 07:10:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISREG(st.st_mode)) {
|
|
|
|
if (ftruncate(s->fd, offset) < 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
} else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
|
|
|
|
if (offset > raw_getlength(bs)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2006-08-02 00:21:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-08-16 02:33:42 +08:00
|
|
|
#ifdef __OpenBSD__
|
|
|
|
static int64_t raw_getlength(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
int fd = s->fd;
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (fstat(fd, &st))
|
2014-06-26 19:23:16 +08:00
|
|
|
return -errno;
|
2008-08-16 02:33:42 +08:00
|
|
|
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
|
|
|
|
struct disklabel dl;
|
|
|
|
|
|
|
|
if (ioctl(fd, DIOCGDINFO, &dl))
|
2014-06-26 19:23:16 +08:00
|
|
|
return -errno;
|
2008-08-16 02:33:42 +08:00
|
|
|
return (uint64_t)dl.d_secsize *
|
|
|
|
dl.d_partitions[DISKPART(st.st_rdev)].p_size;
|
|
|
|
} else
|
|
|
|
return st.st_size;
|
|
|
|
}
|
2011-05-23 20:31:17 +08:00
|
|
|
#elif defined(__NetBSD__)
|
|
|
|
static int64_t raw_getlength(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
int fd = s->fd;
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (fstat(fd, &st))
|
2014-06-26 19:23:16 +08:00
|
|
|
return -errno;
|
2011-05-23 20:31:17 +08:00
|
|
|
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
|
|
|
|
struct dkwedge_info dkw;
|
|
|
|
|
|
|
|
if (ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1) {
|
|
|
|
return dkw.dkw_size * 512;
|
|
|
|
} else {
|
|
|
|
struct disklabel dl;
|
|
|
|
|
|
|
|
if (ioctl(fd, DIOCGDINFO, &dl))
|
2014-06-26 19:23:16 +08:00
|
|
|
return -errno;
|
2011-05-23 20:31:17 +08:00
|
|
|
return (uint64_t)dl.d_secsize *
|
|
|
|
dl.d_partitions[DISKPART(st.st_rdev)].p_size;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
return st.st_size;
|
|
|
|
}
|
2010-04-07 01:13:44 +08:00
|
|
|
#elif defined(__sun__)
|
|
|
|
static int64_t raw_getlength(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
struct dk_minfo minfo;
|
|
|
|
int ret;
|
2014-06-26 19:23:16 +08:00
|
|
|
int64_t size;
|
2010-04-07 01:13:44 +08:00
|
|
|
|
|
|
|
ret = fd_open(bs);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use the DKIOCGMEDIAINFO ioctl to read the size.
|
|
|
|
*/
|
|
|
|
ret = ioctl(s->fd, DKIOCGMEDIAINFO, &minfo);
|
|
|
|
if (ret != -1) {
|
|
|
|
return minfo.dki_lbsize * minfo.dki_capacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There are reports that lseek on some devices fails, but
|
|
|
|
* irc discussion said that contingency on contingency was overkill.
|
|
|
|
*/
|
2014-06-26 19:23:16 +08:00
|
|
|
size = lseek(s->fd, 0, SEEK_END);
|
|
|
|
if (size < 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return size;
|
2010-04-07 01:13:44 +08:00
|
|
|
}
|
|
|
|
#elif defined(CONFIG_BSD)
|
|
|
|
static int64_t raw_getlength(BlockDriverState *bs)
|
2006-08-02 00:21:11 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
int fd = s->fd;
|
|
|
|
int64_t size;
|
|
|
|
struct stat sb;
|
2009-11-30 01:00:41 +08:00
|
|
|
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
|
2009-03-28 16:37:13 +08:00
|
|
|
int reopened = 0;
|
2006-08-02 00:21:11 +08:00
|
|
|
#endif
|
2006-08-19 19:45:59 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = fd_open(bs);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2009-11-30 01:00:41 +08:00
|
|
|
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
|
2009-03-28 16:37:13 +08:00
|
|
|
again:
|
|
|
|
#endif
|
2006-08-02 00:21:11 +08:00
|
|
|
if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) {
|
|
|
|
#ifdef DIOCGMEDIASIZE
|
|
|
|
if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size))
|
2009-03-08 04:06:23 +08:00
|
|
|
#elif defined(DIOCGPART)
|
|
|
|
{
|
|
|
|
struct partinfo pi;
|
|
|
|
if (ioctl(fd, DIOCGPART, &pi) == 0)
|
|
|
|
size = pi.media_size;
|
|
|
|
else
|
|
|
|
size = 0;
|
|
|
|
}
|
|
|
|
if (size == 0)
|
2006-08-02 00:21:11 +08:00
|
|
|
#endif
|
2011-11-11 02:40:06 +08:00
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
2014-05-24 00:15:41 +08:00
|
|
|
size = LLONG_MAX;
|
2006-08-02 00:21:11 +08:00
|
|
|
#else
|
|
|
|
size = lseek(fd, 0LL, SEEK_END);
|
2014-06-26 19:23:16 +08:00
|
|
|
if (size < 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
2009-03-28 16:37:13 +08:00
|
|
|
#endif
|
2009-11-30 01:00:41 +08:00
|
|
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
2009-03-28 16:37:13 +08:00
|
|
|
switch(s->type) {
|
|
|
|
case FTYPE_CD:
|
|
|
|
/* XXX FreeBSD acd returns UINT_MAX sectors for an empty drive */
|
|
|
|
if (size == 2048LL * (unsigned)-1)
|
|
|
|
size = 0;
|
|
|
|
/* XXX no disc? maybe we need to reopen... */
|
2009-06-15 19:55:19 +08:00
|
|
|
if (size <= 0 && !reopened && cdrom_reopen(bs) >= 0) {
|
2009-03-28 16:37:13 +08:00
|
|
|
reopened = 1;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
}
|
2006-08-02 00:21:11 +08:00
|
|
|
#endif
|
2010-04-07 01:13:44 +08:00
|
|
|
} else {
|
2006-08-02 00:21:11 +08:00
|
|
|
size = lseek(fd, 0, SEEK_END);
|
2014-06-26 19:23:16 +08:00
|
|
|
if (size < 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
2006-08-02 00:21:11 +08:00
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
2010-04-07 01:13:44 +08:00
|
|
|
#else
|
|
|
|
static int64_t raw_getlength(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
int ret;
|
2014-06-26 19:23:16 +08:00
|
|
|
int64_t size;
|
2010-04-07 01:13:44 +08:00
|
|
|
|
|
|
|
ret = fd_open(bs);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-06-26 19:23:16 +08:00
|
|
|
size = lseek(s->fd, 0, SEEK_END);
|
|
|
|
if (size < 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return size;
|
2010-04-07 01:13:44 +08:00
|
|
|
}
|
2008-08-16 02:33:42 +08:00
|
|
|
#endif
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2011-07-12 19:56:39 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-06-05 17:21:01 +08:00
|
|
|
static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
2006-08-02 00:21:11 +08:00
|
|
|
{
|
|
|
|
int fd;
|
2009-07-11 22:43:37 +08:00
|
|
|
int result = 0;
|
2009-05-18 22:42:10 +08:00
|
|
|
int64_t total_size = 0;
|
qemu-img create: add 'nocow' option
Add 'nocow' option so that users could have a chance to set NOCOW flag to
newly created files. It's useful on btrfs file system to enhance performance.
Btrfs has low performance when hosting VM images, even more when the guest
in those VM are also using btrfs as file system. One way to mitigate this bad
performance is to turn off COW attributes on VM files. Generally, there are
two ways to turn off NOCOW on btrfs: a) by mounting fs with nodatacow, then
all newly created files will be NOCOW. b) per file. Add the NOCOW file
attribute. It could only be done to empty or new files.
This patch tries the second way, according to the option, it could add NOCOW
per file.
For most block drivers, since the create file step is in raw-posix.c, so we
can do setting NOCOW flag ioctl in raw-posix.c only.
But there are some exceptions, like block/vpc.c and block/vdi.c, they are
creating file by calling qemu_open directly. For them, do the same setting
NOCOW flag ioctl work in them separately.
[Fixed up 082.out due to the new 'nocow' creation option
--Stefan]
Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-30 14:29:58 +08:00
|
|
|
bool nocow = false;
|
2014-09-10 17:05:48 +08:00
|
|
|
PreallocMode prealloc;
|
|
|
|
char *buf = NULL;
|
|
|
|
Error *local_err = NULL;
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2014-03-06 05:41:38 +08:00
|
|
|
strstart(filename, "file:", &filename);
|
|
|
|
|
2009-05-18 22:42:10 +08:00
|
|
|
/* Read out options */
|
2014-09-10 17:05:46 +08:00
|
|
|
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
|
|
|
BDRV_SECTOR_SIZE);
|
qemu-img create: add 'nocow' option
Add 'nocow' option so that users could have a chance to set NOCOW flag to
newly created files. It's useful on btrfs file system to enhance performance.
Btrfs has low performance when hosting VM images, even more when the guest
in those VM are also using btrfs as file system. One way to mitigate this bad
performance is to turn off COW attributes on VM files. Generally, there are
two ways to turn off NOCOW on btrfs: a) by mounting fs with nodatacow, then
all newly created files will be NOCOW. b) per file. Add the NOCOW file
attribute. It could only be done to empty or new files.
This patch tries the second way, according to the option, it could add NOCOW
per file.
For most block drivers, since the create file step is in raw-posix.c, so we
can do setting NOCOW flag ioctl in raw-posix.c only.
But there are some exceptions, like block/vpc.c and block/vdi.c, they are
creating file by calling qemu_open directly. For them, do the same setting
NOCOW flag ioctl work in them separately.
[Fixed up 082.out due to the new 'nocow' creation option
--Stefan]
Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-30 14:29:58 +08:00
|
|
|
nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
|
2014-09-10 17:05:48 +08:00
|
|
|
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
|
|
|
prealloc = qapi_enum_parse(PreallocMode_lookup, buf,
|
|
|
|
PREALLOC_MODE_MAX, PREALLOC_MODE_OFF,
|
|
|
|
&local_err);
|
|
|
|
g_free(buf);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
result = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2006-08-02 00:21:11 +08:00
|
|
|
|
2012-08-15 04:43:45 +08:00
|
|
|
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
|
|
|
0644);
|
2009-07-11 22:43:37 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
result = -errno;
|
2013-10-11 17:37:01 +08:00
|
|
|
error_setg_errno(errp, -result, "Could not create file");
|
2014-09-10 17:05:48 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nocow) {
|
qemu-img create: add 'nocow' option
Add 'nocow' option so that users could have a chance to set NOCOW flag to
newly created files. It's useful on btrfs file system to enhance performance.
Btrfs has low performance when hosting VM images, even more when the guest
in those VM are also using btrfs as file system. One way to mitigate this bad
performance is to turn off COW attributes on VM files. Generally, there are
two ways to turn off NOCOW on btrfs: a) by mounting fs with nodatacow, then
all newly created files will be NOCOW. b) per file. Add the NOCOW file
attribute. It could only be done to empty or new files.
This patch tries the second way, according to the option, it could add NOCOW
per file.
For most block drivers, since the create file step is in raw-posix.c, so we
can do setting NOCOW flag ioctl in raw-posix.c only.
But there are some exceptions, like block/vpc.c and block/vdi.c, they are
creating file by calling qemu_open directly. For them, do the same setting
NOCOW flag ioctl work in them separately.
[Fixed up 082.out due to the new 'nocow' creation option
--Stefan]
Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-30 14:29:58 +08:00
|
|
|
#ifdef __linux__
|
2014-09-10 17:05:48 +08:00
|
|
|
/* Set NOCOW flag to solve performance issue on fs like btrfs.
|
|
|
|
* This is an optimisation. The FS_IOC_SETFLAGS ioctl return value
|
|
|
|
* will be ignored since any failure of this operation should not
|
|
|
|
* block the left work.
|
|
|
|
*/
|
|
|
|
int attr;
|
|
|
|
if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) {
|
|
|
|
attr |= FS_NOCOW_FL;
|
|
|
|
ioctl(fd, FS_IOC_SETFLAGS, &attr);
|
qemu-img create: add 'nocow' option
Add 'nocow' option so that users could have a chance to set NOCOW flag to
newly created files. It's useful on btrfs file system to enhance performance.
Btrfs has low performance when hosting VM images, even more when the guest
in those VM are also using btrfs as file system. One way to mitigate this bad
performance is to turn off COW attributes on VM files. Generally, there are
two ways to turn off NOCOW on btrfs: a) by mounting fs with nodatacow, then
all newly created files will be NOCOW. b) per file. Add the NOCOW file
attribute. It could only be done to empty or new files.
This patch tries the second way, according to the option, it could add NOCOW
per file.
For most block drivers, since the create file step is in raw-posix.c, so we
can do setting NOCOW flag ioctl in raw-posix.c only.
But there are some exceptions, like block/vpc.c and block/vdi.c, they are
creating file by calling qemu_open directly. For them, do the same setting
NOCOW flag ioctl work in them separately.
[Fixed up 082.out due to the new 'nocow' creation option
--Stefan]
Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-30 14:29:58 +08:00
|
|
|
}
|
2014-09-10 17:05:48 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ftruncate(fd, total_size) != 0) {
|
|
|
|
result = -errno;
|
|
|
|
error_setg_errno(errp, -result, "Could not resize file");
|
|
|
|
goto out_close;
|
|
|
|
}
|
qemu-img create: add 'nocow' option
Add 'nocow' option so that users could have a chance to set NOCOW flag to
newly created files. It's useful on btrfs file system to enhance performance.
Btrfs has low performance when hosting VM images, even more when the guest
in those VM are also using btrfs as file system. One way to mitigate this bad
performance is to turn off COW attributes on VM files. Generally, there are
two ways to turn off NOCOW on btrfs: a) by mounting fs with nodatacow, then
all newly created files will be NOCOW. b) per file. Add the NOCOW file
attribute. It could only be done to empty or new files.
This patch tries the second way, according to the option, it could add NOCOW
per file.
For most block drivers, since the create file step is in raw-posix.c, so we
can do setting NOCOW flag ioctl in raw-posix.c only.
But there are some exceptions, like block/vpc.c and block/vdi.c, they are
creating file by calling qemu_open directly. For them, do the same setting
NOCOW flag ioctl work in them separately.
[Fixed up 082.out due to the new 'nocow' creation option
--Stefan]
Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-30 14:29:58 +08:00
|
|
|
|
2014-09-29 23:12:59 +08:00
|
|
|
switch (prealloc) {
|
|
|
|
#ifdef CONFIG_POSIX_FALLOCATE
|
|
|
|
case PREALLOC_MODE_FALLOC:
|
2014-09-10 17:05:48 +08:00
|
|
|
/* posix_fallocate() doesn't set errno. */
|
|
|
|
result = -posix_fallocate(fd, 0, total_size);
|
|
|
|
if (result != 0) {
|
|
|
|
error_setg_errno(errp, -result,
|
|
|
|
"Could not preallocate data for the new file");
|
2009-07-11 22:43:37 +08:00
|
|
|
}
|
2014-09-29 23:12:59 +08:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case PREALLOC_MODE_FULL:
|
|
|
|
{
|
2014-09-10 17:05:48 +08:00
|
|
|
int64_t num = 0, left = total_size;
|
2014-09-29 23:12:59 +08:00
|
|
|
buf = g_malloc0(65536);
|
2014-09-10 17:05:48 +08:00
|
|
|
|
|
|
|
while (left > 0) {
|
|
|
|
num = MIN(left, 65536);
|
|
|
|
result = write(fd, buf, num);
|
|
|
|
if (result < 0) {
|
|
|
|
result = -errno;
|
|
|
|
error_setg_errno(errp, -result,
|
|
|
|
"Could not write to the new file");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
left -= num;
|
2009-07-11 22:43:37 +08:00
|
|
|
}
|
2014-09-10 17:05:48 +08:00
|
|
|
fsync(fd);
|
|
|
|
g_free(buf);
|
2014-09-29 23:12:59 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case PREALLOC_MODE_OFF:
|
|
|
|
break;
|
|
|
|
default:
|
2014-09-10 17:05:48 +08:00
|
|
|
result = -EINVAL;
|
|
|
|
error_setg(errp, "Unsupported preallocation mode: %s",
|
|
|
|
PreallocMode_lookup[prealloc]);
|
2014-09-29 23:12:59 +08:00
|
|
|
break;
|
2009-07-11 22:43:37 +08:00
|
|
|
}
|
2014-09-10 17:05:48 +08:00
|
|
|
|
|
|
|
out_close:
|
|
|
|
if (qemu_close(fd) != 0 && result == 0) {
|
|
|
|
result = -errno;
|
|
|
|
error_setg_errno(errp, -result, "Could not close the new file");
|
|
|
|
}
|
|
|
|
out:
|
2009-07-11 22:43:37 +08:00
|
|
|
return result;
|
2006-08-02 00:21:11 +08:00
|
|
|
}
|
|
|
|
|
2014-05-09 02:57:55 +08:00
|
|
|
static int64_t try_fiemap(BlockDriverState *bs, off_t start, off_t *data,
|
|
|
|
off_t *hole, int nb_sectors, int *pnum)
|
2012-05-09 22:49:58 +08:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_FIEMAP
|
2012-06-20 06:02:51 +08:00
|
|
|
BDRVRawState *s = bs->opaque;
|
2014-05-09 02:57:55 +08:00
|
|
|
int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
|
2012-05-09 22:49:58 +08:00
|
|
|
struct {
|
|
|
|
struct fiemap fm;
|
|
|
|
struct fiemap_extent fe;
|
|
|
|
} f;
|
2012-06-20 06:02:51 +08:00
|
|
|
|
2014-05-09 02:57:55 +08:00
|
|
|
if (s->skip_fiemap) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2012-05-09 22:49:58 +08:00
|
|
|
f.fm.fm_start = start;
|
|
|
|
f.fm.fm_length = (int64_t)nb_sectors * BDRV_SECTOR_SIZE;
|
2014-09-26 07:14:11 +08:00
|
|
|
f.fm.fm_flags = FIEMAP_FLAG_SYNC;
|
2012-05-09 22:49:58 +08:00
|
|
|
f.fm.fm_extent_count = 1;
|
|
|
|
f.fm.fm_reserved = 0;
|
|
|
|
if (ioctl(s->fd, FS_IOC_FIEMAP, &f) == -1) {
|
2014-05-09 02:57:55 +08:00
|
|
|
s->skip_fiemap = true;
|
|
|
|
return -errno;
|
2012-05-09 22:49:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (f.fm.fm_mapped_extents == 0) {
|
|
|
|
/* No extents found, data is beyond f.fm.fm_start + f.fm.fm_length.
|
|
|
|
* f.fm.fm_start + f.fm.fm_length must be clamped to the file size!
|
|
|
|
*/
|
|
|
|
off_t length = lseek(s->fd, 0, SEEK_END);
|
2014-05-09 02:57:55 +08:00
|
|
|
*hole = f.fm.fm_start;
|
|
|
|
*data = MIN(f.fm.fm_start + f.fm.fm_length, length);
|
2012-05-09 22:49:58 +08:00
|
|
|
} else {
|
2014-05-09 02:57:55 +08:00
|
|
|
*data = f.fe.fe_logical;
|
|
|
|
*hole = f.fe.fe_logical + f.fe.fe_length;
|
2013-09-05 01:00:36 +08:00
|
|
|
if (f.fe.fe_flags & FIEMAP_EXTENT_UNWRITTEN) {
|
|
|
|
ret |= BDRV_BLOCK_ZERO;
|
|
|
|
}
|
2012-05-09 22:49:58 +08:00
|
|
|
}
|
2012-06-20 06:02:51 +08:00
|
|
|
|
2014-05-09 02:57:55 +08:00
|
|
|
return ret;
|
|
|
|
#else
|
|
|
|
return -ENOTSUP;
|
|
|
|
#endif
|
|
|
|
}
|
2012-06-20 06:02:51 +08:00
|
|
|
|
2014-05-09 02:57:55 +08:00
|
|
|
static int64_t try_seek_hole(BlockDriverState *bs, off_t start, off_t *data,
|
|
|
|
off_t *hole, int *pnum)
|
|
|
|
{
|
|
|
|
#if defined SEEK_HOLE && defined SEEK_DATA
|
2012-06-20 06:02:51 +08:00
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
2014-05-09 02:57:55 +08:00
|
|
|
*hole = lseek(s->fd, start, SEEK_HOLE);
|
|
|
|
if (*hole == -1) {
|
|
|
|
return -errno;
|
2012-05-09 22:49:58 +08:00
|
|
|
}
|
|
|
|
|
2014-05-09 02:57:55 +08:00
|
|
|
if (*hole > start) {
|
|
|
|
*data = start;
|
2012-05-09 22:49:58 +08:00
|
|
|
} else {
|
|
|
|
/* On a hole. We need another syscall to find its end. */
|
2014-05-09 02:57:55 +08:00
|
|
|
*data = lseek(s->fd, start, SEEK_DATA);
|
|
|
|
if (*data == -1) {
|
|
|
|
*data = lseek(s->fd, 0, SEEK_END);
|
2012-05-09 22:49:58 +08:00
|
|
|
}
|
|
|
|
}
|
2014-05-09 02:57:55 +08:00
|
|
|
|
|
|
|
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
|
2012-05-09 22:49:58 +08:00
|
|
|
#else
|
2014-05-09 02:57:55 +08:00
|
|
|
return -ENOTSUP;
|
2012-05-09 22:49:58 +08:00
|
|
|
#endif
|
2014-05-09 02:57:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns true iff the specified sector is present in the disk image. Drivers
|
|
|
|
* not implementing the functionality are assumed to not support backing files,
|
|
|
|
* hence all their sectors are reported as allocated.
|
|
|
|
*
|
|
|
|
* If 'sector_num' is beyond the end of the disk image the return value is 0
|
|
|
|
* and 'pnum' is set to 0.
|
|
|
|
*
|
|
|
|
* 'pnum' is set to the number of sectors (including and immediately following
|
|
|
|
* the specified sector) that are known to be in the same
|
|
|
|
* allocated/unallocated state.
|
|
|
|
*
|
|
|
|
* 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes
|
|
|
|
* beyond the end of the disk image it will be clamped.
|
|
|
|
*/
|
|
|
|
static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
|
|
|
int64_t sector_num,
|
|
|
|
int nb_sectors, int *pnum)
|
|
|
|
{
|
|
|
|
off_t start, data = 0, hole = 0;
|
2014-10-24 18:57:58 +08:00
|
|
|
int64_t total_size;
|
2014-05-09 02:57:55 +08:00
|
|
|
int64_t ret;
|
|
|
|
|
|
|
|
ret = fd_open(bs);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
start = sector_num * BDRV_SECTOR_SIZE;
|
2014-10-24 18:57:58 +08:00
|
|
|
total_size = bdrv_getlength(bs);
|
|
|
|
if (total_size < 0) {
|
|
|
|
return total_size;
|
|
|
|
} else if (start >= total_size) {
|
|
|
|
*pnum = 0;
|
|
|
|
return 0;
|
|
|
|
} else if (start + nb_sectors * BDRV_SECTOR_SIZE > total_size) {
|
|
|
|
nb_sectors = DIV_ROUND_UP(total_size - start, BDRV_SECTOR_SIZE);
|
|
|
|
}
|
2014-05-09 02:57:55 +08:00
|
|
|
|
2014-09-26 07:14:12 +08:00
|
|
|
ret = try_seek_hole(bs, start, &data, &hole, pnum);
|
2014-05-09 02:57:55 +08:00
|
|
|
if (ret < 0) {
|
2014-09-26 07:14:12 +08:00
|
|
|
ret = try_fiemap(bs, start, &data, &hole, nb_sectors, pnum);
|
2014-05-09 02:57:55 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
/* Assume everything is allocated. */
|
|
|
|
data = 0;
|
|
|
|
hole = start + nb_sectors * BDRV_SECTOR_SIZE;
|
|
|
|
ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
|
|
|
|
}
|
|
|
|
}
|
2012-05-09 22:49:58 +08:00
|
|
|
|
|
|
|
if (data <= start) {
|
|
|
|
/* On a data extent, compute sectors to the end of the extent. */
|
|
|
|
*pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE);
|
|
|
|
} else {
|
|
|
|
/* On a hole, compute sectors to the beginning of the next extent. */
|
|
|
|
*pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
|
2013-09-05 01:00:35 +08:00
|
|
|
ret &= ~BDRV_BLOCK_DATA;
|
|
|
|
ret |= BDRV_BLOCK_ZERO;
|
2012-05-09 22:49:58 +08:00
|
|
|
}
|
2013-09-05 01:00:35 +08:00
|
|
|
|
|
|
|
return ret;
|
2012-05-09 22:49:58 +08:00
|
|
|
}
|
|
|
|
|
2014-10-07 19:59:14 +08:00
|
|
|
static coroutine_fn BlockAIOCB *raw_aio_discard(BlockDriverState *bs,
|
2013-01-14 23:26:55 +08:00
|
|
|
int64_t sector_num, int nb_sectors,
|
2014-10-07 19:59:15 +08:00
|
|
|
BlockCompletionFunc *cb, void *opaque)
|
2010-12-17 18:41:15 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
2013-01-14 23:26:55 +08:00
|
|
|
return paio_submit(bs, s->fd, sector_num, NULL, nb_sectors,
|
|
|
|
cb, opaque, QEMU_AIO_DISCARD);
|
2010-12-17 18:41:15 +08:00
|
|
|
}
|
2009-05-18 22:42:10 +08:00
|
|
|
|
2013-11-22 20:39:55 +08:00
|
|
|
static int coroutine_fn raw_co_write_zeroes(
|
|
|
|
BlockDriverState *bs, int64_t sector_num,
|
|
|
|
int nb_sectors, BdrvRequestFlags flags)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
if (!(flags & BDRV_REQ_MAY_UNMAP)) {
|
2013-11-22 20:39:57 +08:00
|
|
|
return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors,
|
|
|
|
QEMU_AIO_WRITE_ZEROES);
|
|
|
|
} else if (s->discard_zeroes) {
|
|
|
|
return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors,
|
|
|
|
QEMU_AIO_DISCARD);
|
2013-11-22 20:39:55 +08:00
|
|
|
}
|
2013-11-22 20:39:57 +08:00
|
|
|
return -ENOTSUP;
|
2013-11-22 20:39:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
bdi->unallocated_blocks_are_zero = s->discard_zeroes;
|
|
|
|
bdi->can_write_zeroes_with_unmap = s->discard_zeroes;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-05 17:21:01 +08:00
|
|
|
static QemuOptsList raw_create_opts = {
|
|
|
|
.name = "raw-create-opts",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
|
|
|
|
.desc = {
|
|
|
|
{
|
|
|
|
.name = BLOCK_OPT_SIZE,
|
|
|
|
.type = QEMU_OPT_SIZE,
|
|
|
|
.help = "Virtual disk size"
|
|
|
|
},
|
qemu-img create: add 'nocow' option
Add 'nocow' option so that users could have a chance to set NOCOW flag to
newly created files. It's useful on btrfs file system to enhance performance.
Btrfs has low performance when hosting VM images, even more when the guest
in those VM are also using btrfs as file system. One way to mitigate this bad
performance is to turn off COW attributes on VM files. Generally, there are
two ways to turn off NOCOW on btrfs: a) by mounting fs with nodatacow, then
all newly created files will be NOCOW. b) per file. Add the NOCOW file
attribute. It could only be done to empty or new files.
This patch tries the second way, according to the option, it could add NOCOW
per file.
For most block drivers, since the create file step is in raw-posix.c, so we
can do setting NOCOW flag ioctl in raw-posix.c only.
But there are some exceptions, like block/vpc.c and block/vdi.c, they are
creating file by calling qemu_open directly. For them, do the same setting
NOCOW flag ioctl work in them separately.
[Fixed up 082.out due to the new 'nocow' creation option
--Stefan]
Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-06-30 14:29:58 +08:00
|
|
|
{
|
|
|
|
.name = BLOCK_OPT_NOCOW,
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "Turn off copy-on-write (valid only on btrfs)"
|
|
|
|
},
|
2014-09-10 17:05:48 +08:00
|
|
|
{
|
|
|
|
.name = BLOCK_OPT_PREALLOC,
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "Preallocation mode (allowed values: off, falloc, full)"
|
|
|
|
},
|
2014-06-05 17:21:01 +08:00
|
|
|
{ /* end of list */ }
|
|
|
|
}
|
2009-05-18 22:42:10 +08:00
|
|
|
};
|
|
|
|
|
2010-04-08 04:30:24 +08:00
|
|
|
static BlockDriver bdrv_file = {
|
|
|
|
.format_name = "file",
|
|
|
|
.protocol_name = "file",
|
2009-04-08 01:57:09 +08:00
|
|
|
.instance_size = sizeof(BDRVRawState),
|
2013-09-24 23:07:04 +08:00
|
|
|
.bdrv_needs_filename = true,
|
2009-04-08 01:57:09 +08:00
|
|
|
.bdrv_probe = NULL, /* no probe for protocols */
|
2014-03-06 05:41:37 +08:00
|
|
|
.bdrv_parse_filename = raw_parse_filename,
|
2010-04-14 20:17:38 +08:00
|
|
|
.bdrv_file_open = raw_open,
|
2012-09-21 03:13:25 +08:00
|
|
|
.bdrv_reopen_prepare = raw_reopen_prepare,
|
|
|
|
.bdrv_reopen_commit = raw_reopen_commit,
|
|
|
|
.bdrv_reopen_abort = raw_reopen_abort,
|
2009-04-08 01:57:09 +08:00
|
|
|
.bdrv_close = raw_close,
|
2014-06-05 17:21:11 +08:00
|
|
|
.bdrv_create = raw_create,
|
2013-06-28 18:47:42 +08:00
|
|
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
2013-09-05 01:00:28 +08:00
|
|
|
.bdrv_co_get_block_status = raw_co_get_block_status,
|
2013-11-22 20:39:55 +08:00
|
|
|
.bdrv_co_write_zeroes = raw_co_write_zeroes,
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2009-04-08 02:43:24 +08:00
|
|
|
.bdrv_aio_readv = raw_aio_readv,
|
|
|
|
.bdrv_aio_writev = raw_aio_writev,
|
2009-09-05 01:01:49 +08:00
|
|
|
.bdrv_aio_flush = raw_aio_flush,
|
2013-01-14 23:26:55 +08:00
|
|
|
.bdrv_aio_discard = raw_aio_discard,
|
2011-11-29 19:42:20 +08:00
|
|
|
.bdrv_refresh_limits = raw_refresh_limits,
|
2014-07-04 18:04:34 +08:00
|
|
|
.bdrv_io_plug = raw_aio_plug,
|
|
|
|
.bdrv_io_unplug = raw_aio_unplug,
|
|
|
|
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
|
2008-12-13 00:41:40 +08:00
|
|
|
|
2006-08-02 00:21:11 +08:00
|
|
|
.bdrv_truncate = raw_truncate,
|
|
|
|
.bdrv_getlength = raw_getlength,
|
2013-11-22 20:39:55 +08:00
|
|
|
.bdrv_get_info = raw_get_info,
|
2011-07-12 19:56:39 +08:00
|
|
|
.bdrv_get_allocated_file_size
|
|
|
|
= raw_get_allocated_file_size,
|
2009-05-18 22:42:10 +08:00
|
|
|
|
2014-05-08 22:34:47 +08:00
|
|
|
.bdrv_detach_aio_context = raw_detach_aio_context,
|
|
|
|
.bdrv_attach_aio_context = raw_attach_aio_context,
|
|
|
|
|
2014-06-05 17:21:01 +08:00
|
|
|
.create_opts = &raw_create_opts,
|
2006-08-02 00:21:11 +08:00
|
|
|
};
|
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
/***********************************************/
|
|
|
|
/* host device */
|
|
|
|
|
2011-11-11 02:40:06 +08:00
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
2006-08-19 19:45:59 +08:00
|
|
|
static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
|
|
|
|
static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
|
|
|
|
|
|
|
|
kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
|
|
|
|
{
|
2007-09-17 05:08:06 +08:00
|
|
|
kern_return_t kernResult;
|
2006-08-19 19:45:59 +08:00
|
|
|
mach_port_t masterPort;
|
|
|
|
CFMutableDictionaryRef classesToMatch;
|
|
|
|
|
|
|
|
kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
|
|
|
|
if ( KERN_SUCCESS != kernResult ) {
|
|
|
|
printf( "IOMasterPort returned %d\n", kernResult );
|
|
|
|
}
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2007-09-17 05:08:06 +08:00
|
|
|
classesToMatch = IOServiceMatching( kIOCDMediaClass );
|
2006-08-19 19:45:59 +08:00
|
|
|
if ( classesToMatch == NULL ) {
|
|
|
|
printf( "IOServiceMatching returned a NULL dictionary.\n" );
|
|
|
|
} else {
|
|
|
|
CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
|
|
|
|
}
|
|
|
|
kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
|
|
|
|
if ( KERN_SUCCESS != kernResult )
|
|
|
|
{
|
|
|
|
printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
|
|
|
|
}
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
return kernResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
|
|
|
|
{
|
|
|
|
io_object_t nextMedia;
|
|
|
|
kern_return_t kernResult = KERN_FAILURE;
|
|
|
|
*bsdPath = '\0';
|
|
|
|
nextMedia = IOIteratorNext( mediaIterator );
|
|
|
|
if ( nextMedia )
|
|
|
|
{
|
|
|
|
CFTypeRef bsdPathAsCFString;
|
|
|
|
bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 );
|
|
|
|
if ( bsdPathAsCFString ) {
|
|
|
|
size_t devPathLength;
|
|
|
|
strcpy( bsdPath, _PATH_DEV );
|
|
|
|
strcat( bsdPath, "r" );
|
|
|
|
devPathLength = strlen( bsdPath );
|
|
|
|
if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) {
|
|
|
|
kernResult = KERN_SUCCESS;
|
|
|
|
}
|
|
|
|
CFRelease( bsdPathAsCFString );
|
|
|
|
}
|
|
|
|
IOObjectRelease( nextMedia );
|
|
|
|
}
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
return kernResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2009-06-15 20:04:22 +08:00
|
|
|
static int hdev_probe_device(const char *filename)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
/* allow a dedicated CD-ROM driver to match with a higher priority */
|
|
|
|
if (strstart(filename, "/dev/cdrom", NULL))
|
|
|
|
return 50;
|
|
|
|
|
|
|
|
if (stat(filename, &st) >= 0 &&
|
|
|
|
(S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) {
|
|
|
|
return 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-05 19:28:33 +08:00
|
|
|
static int check_hdev_writable(BDRVRawState *s)
|
|
|
|
{
|
|
|
|
#if defined(BLKROGET)
|
|
|
|
/* Linux block devices can be configured "read-only" using blockdev(8).
|
|
|
|
* This is independent of device node permissions and therefore open(2)
|
|
|
|
* with O_RDWR succeeds. Actual writes fail with EPERM.
|
|
|
|
*
|
|
|
|
* bdrv_open() is supposed to fail if the disk is read-only. Explicitly
|
|
|
|
* check for read-only block devices so that Linux block devices behave
|
|
|
|
* properly.
|
|
|
|
*/
|
|
|
|
struct stat st;
|
|
|
|
int readonly = 0;
|
|
|
|
|
|
|
|
if (fstat(s->fd, &st)) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISBLK(st.st_mode)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl(s->fd, BLKROGET, &readonly) < 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (readonly) {
|
|
|
|
return -EACCES;
|
|
|
|
}
|
|
|
|
#endif /* defined(BLKROGET) */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-08 07:39:41 +08:00
|
|
|
static void hdev_parse_filename(const char *filename, QDict *options,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
/* The prefix is optional, just as for "file". */
|
|
|
|
strstart(filename, "host_device:", &filename);
|
|
|
|
|
|
|
|
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
|
|
|
}
|
|
|
|
|
2013-09-05 20:22:29 +08:00
|
|
|
static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
|
|
|
Error **errp)
|
2006-08-19 19:45:59 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
2013-10-11 17:37:01 +08:00
|
|
|
Error *local_err = NULL;
|
2013-02-05 19:28:33 +08:00
|
|
|
int ret;
|
2013-04-02 16:47:40 +08:00
|
|
|
const char *filename = qdict_get_str(options, "filename");
|
2008-09-23 03:17:18 +08:00
|
|
|
|
2011-11-11 02:40:06 +08:00
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
2006-08-19 19:45:59 +08:00
|
|
|
if (strstart(filename, "/dev/cdrom", NULL)) {
|
|
|
|
kern_return_t kernResult;
|
|
|
|
io_iterator_t mediaIterator;
|
|
|
|
char bsdPath[ MAXPATHLEN ];
|
|
|
|
int fd;
|
2007-09-17 05:08:06 +08:00
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
kernResult = FindEjectableCDMedia( &mediaIterator );
|
|
|
|
kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
if ( bsdPath[ 0 ] != '\0' ) {
|
|
|
|
strcat(bsdPath,"s0");
|
|
|
|
/* some CDs don't have a partition 0 */
|
2012-08-15 04:43:45 +08:00
|
|
|
fd = qemu_open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE);
|
2006-08-19 19:45:59 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
bsdPath[strlen(bsdPath)-1] = '1';
|
|
|
|
} else {
|
2012-08-15 04:43:46 +08:00
|
|
|
qemu_close(fd);
|
2006-08-19 19:45:59 +08:00
|
|
|
}
|
|
|
|
filename = bsdPath;
|
2013-06-11 16:44:58 +08:00
|
|
|
qdict_put(options, "filename", qstring_from_str(filename));
|
2006-08-19 19:45:59 +08:00
|
|
|
}
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
if ( mediaIterator )
|
|
|
|
IOObjectRelease( mediaIterator );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
s->type = FTYPE_FILE;
|
2009-08-11 05:39:39 +08:00
|
|
|
#if defined(__linux__)
|
2010-09-06 23:06:02 +08:00
|
|
|
{
|
|
|
|
char resolved_path[ MAXPATHLEN ], *temp;
|
|
|
|
|
|
|
|
temp = realpath(filename, resolved_path);
|
|
|
|
if (temp && strstart(temp, "/dev/sg", NULL)) {
|
|
|
|
bs->sg = 1;
|
|
|
|
}
|
2006-08-19 19:45:59 +08:00
|
|
|
}
|
|
|
|
#endif
|
2009-06-15 19:53:38 +08:00
|
|
|
|
2013-10-11 17:37:01 +08:00
|
|
|
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
2013-02-05 19:28:33 +08:00
|
|
|
if (ret < 0) {
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2013-10-11 17:37:01 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
2013-02-05 19:28:33 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & BDRV_O_RDWR) {
|
|
|
|
ret = check_hdev_writable(s);
|
|
|
|
if (ret < 0) {
|
|
|
|
raw_close(bs);
|
2013-10-11 17:37:01 +08:00
|
|
|
error_setg_errno(errp, -ret, "The device is not writable");
|
2013-02-05 19:28:33 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2006-08-19 19:45:59 +08:00
|
|
|
}
|
|
|
|
|
2008-09-15 23:51:35 +08:00
|
|
|
#if defined(__linux__)
|
2006-08-19 19:45:59 +08:00
|
|
|
/* Note: we do not have a reliable method to detect if the floppy is
|
|
|
|
present. The current method is to try to open the floppy at every
|
|
|
|
I/O and to keep it opened during a few hundreds of ms. */
|
|
|
|
static int fd_open(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
int last_media_present;
|
|
|
|
|
|
|
|
if (s->type != FTYPE_FD)
|
|
|
|
return 0;
|
|
|
|
last_media_present = (s->fd >= 0);
|
2007-09-17 05:08:06 +08:00
|
|
|
if (s->fd >= 0 &&
|
2010-10-23 23:24:07 +08:00
|
|
|
(get_clock() - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
|
2012-08-15 04:43:46 +08:00
|
|
|
qemu_close(s->fd);
|
2006-08-19 19:45:59 +08:00
|
|
|
s->fd = -1;
|
|
|
|
#ifdef DEBUG_FLOPPY
|
|
|
|
printf("Floppy closed\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if (s->fd < 0) {
|
2007-09-17 05:08:06 +08:00
|
|
|
if (s->fd_got_error &&
|
2010-10-23 23:24:07 +08:00
|
|
|
(get_clock() - s->fd_error_time) < FD_OPEN_TIMEOUT) {
|
2006-08-19 19:45:59 +08:00
|
|
|
#ifdef DEBUG_FLOPPY
|
|
|
|
printf("No floppy (open delayed)\n");
|
|
|
|
#endif
|
|
|
|
return -EIO;
|
|
|
|
}
|
2012-08-15 04:43:45 +08:00
|
|
|
s->fd = qemu_open(bs->filename, s->open_flags & ~O_NONBLOCK);
|
2006-08-19 19:45:59 +08:00
|
|
|
if (s->fd < 0) {
|
2010-10-23 23:24:07 +08:00
|
|
|
s->fd_error_time = get_clock();
|
2006-08-19 19:45:59 +08:00
|
|
|
s->fd_got_error = 1;
|
|
|
|
if (last_media_present)
|
|
|
|
s->fd_media_changed = 1;
|
|
|
|
#ifdef DEBUG_FLOPPY
|
|
|
|
printf("No floppy\n");
|
|
|
|
#endif
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_FLOPPY
|
|
|
|
printf("Floppy opened\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if (!last_media_present)
|
|
|
|
s->fd_media_changed = 1;
|
2010-10-23 23:24:07 +08:00
|
|
|
s->fd_open_time = get_clock();
|
2006-08-19 19:45:59 +08:00
|
|
|
s->fd_got_error = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-15 20:04:34 +08:00
|
|
|
static int hdev_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
2007-12-25 00:10:43 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
return ioctl(s->fd, req, buf);
|
|
|
|
}
|
2009-03-29 01:28:41 +08:00
|
|
|
|
2014-10-07 19:59:14 +08:00
|
|
|
static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
|
2009-03-29 01:28:41 +08:00
|
|
|
unsigned long int req, void *buf,
|
2014-10-07 19:59:15 +08:00
|
|
|
BlockCompletionFunc *cb, void *opaque)
|
2009-03-29 01:28:41 +08:00
|
|
|
{
|
2009-04-08 02:43:24 +08:00
|
|
|
BDRVRawState *s = bs->opaque;
|
2012-11-02 23:14:20 +08:00
|
|
|
RawPosixAIOData *acb;
|
2013-03-07 20:41:49 +08:00
|
|
|
ThreadPool *pool;
|
2009-03-29 01:28:41 +08:00
|
|
|
|
2009-04-08 02:43:24 +08:00
|
|
|
if (fd_open(bs) < 0)
|
|
|
|
return NULL;
|
2012-11-02 23:14:20 +08:00
|
|
|
|
|
|
|
acb = g_slice_new(RawPosixAIOData);
|
|
|
|
acb->bs = bs;
|
|
|
|
acb->aio_type = QEMU_AIO_IOCTL;
|
|
|
|
acb->aio_fildes = s->fd;
|
|
|
|
acb->aio_offset = 0;
|
|
|
|
acb->aio_ioctl_buf = buf;
|
|
|
|
acb->aio_ioctl_cmd = req;
|
2013-03-07 20:41:49 +08:00
|
|
|
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
|
|
|
|
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
|
2009-03-29 01:28:41 +08:00
|
|
|
}
|
|
|
|
|
2009-11-30 01:00:41 +08:00
|
|
|
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
2009-03-28 16:37:13 +08:00
|
|
|
static int fd_open(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
/* this is just to ensure s->fd is sane (its called by io ops) */
|
|
|
|
if (s->fd >= 0)
|
|
|
|
return 0;
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
#else /* !linux && !FreeBSD */
|
2006-08-19 19:45:59 +08:00
|
|
|
|
2008-09-16 00:48:11 +08:00
|
|
|
static int fd_open(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-29 01:28:41 +08:00
|
|
|
#endif /* !linux && !FreeBSD */
|
2009-03-13 03:57:12 +08:00
|
|
|
|
2014-10-07 19:59:14 +08:00
|
|
|
static coroutine_fn BlockAIOCB *hdev_aio_discard(BlockDriverState *bs,
|
2013-01-18 23:43:35 +08:00
|
|
|
int64_t sector_num, int nb_sectors,
|
2014-10-07 19:59:15 +08:00
|
|
|
BlockCompletionFunc *cb, void *opaque)
|
2013-01-18 23:43:35 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
if (fd_open(bs) < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return paio_submit(bs, s->fd, sector_num, NULL, nb_sectors,
|
|
|
|
cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV);
|
|
|
|
}
|
|
|
|
|
2013-11-22 20:39:56 +08:00
|
|
|
static coroutine_fn int hdev_co_write_zeroes(BlockDriverState *bs,
|
|
|
|
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = fd_open(bs);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
if (!(flags & BDRV_REQ_MAY_UNMAP)) {
|
2013-11-22 20:39:57 +08:00
|
|
|
return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors,
|
|
|
|
QEMU_AIO_WRITE_ZEROES|QEMU_AIO_BLKDEV);
|
|
|
|
} else if (s->discard_zeroes) {
|
|
|
|
return paio_submit_co(bs, s->fd, sector_num, NULL, nb_sectors,
|
|
|
|
QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV);
|
2013-11-22 20:39:56 +08:00
|
|
|
}
|
2013-11-22 20:39:57 +08:00
|
|
|
return -ENOTSUP;
|
2013-11-22 20:39:56 +08:00
|
|
|
}
|
|
|
|
|
2014-06-05 17:21:01 +08:00
|
|
|
static int hdev_create(const char *filename, QemuOpts *opts,
|
2013-09-05 20:26:05 +08:00
|
|
|
Error **errp)
|
2009-04-06 01:40:43 +08:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int ret = 0;
|
|
|
|
struct stat stat_buf;
|
2009-05-18 22:42:10 +08:00
|
|
|
int64_t total_size = 0;
|
2014-03-08 07:39:44 +08:00
|
|
|
bool has_prefix;
|
|
|
|
|
|
|
|
/* This function is used by all three protocol block drivers and therefore
|
|
|
|
* any of these three prefixes may be given.
|
|
|
|
* The return value has to be stored somewhere, otherwise this is an error
|
|
|
|
* due to -Werror=unused-value. */
|
|
|
|
has_prefix =
|
|
|
|
strstart(filename, "host_device:", &filename) ||
|
|
|
|
strstart(filename, "host_cdrom:" , &filename) ||
|
|
|
|
strstart(filename, "host_floppy:", &filename);
|
|
|
|
|
|
|
|
(void)has_prefix;
|
2009-04-06 01:40:43 +08:00
|
|
|
|
2009-05-18 22:42:10 +08:00
|
|
|
/* Read out options */
|
2014-09-10 17:05:46 +08:00
|
|
|
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
|
|
|
BDRV_SECTOR_SIZE);
|
2009-04-06 01:40:43 +08:00
|
|
|
|
2012-08-15 04:43:45 +08:00
|
|
|
fd = qemu_open(filename, O_WRONLY | O_BINARY);
|
2013-10-11 17:37:01 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
error_setg_errno(errp, -ret, "Could not open device");
|
|
|
|
return ret;
|
|
|
|
}
|
2009-04-06 01:40:43 +08:00
|
|
|
|
2013-10-11 17:37:01 +08:00
|
|
|
if (fstat(fd, &stat_buf) < 0) {
|
2010-03-12 20:52:31 +08:00
|
|
|
ret = -errno;
|
2013-10-11 17:37:01 +08:00
|
|
|
error_setg_errno(errp, -ret, "Could not stat device");
|
|
|
|
} else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) {
|
|
|
|
error_setg(errp,
|
|
|
|
"The given file is neither a block nor a character device");
|
2010-03-12 20:52:31 +08:00
|
|
|
ret = -ENODEV;
|
2014-09-10 17:05:46 +08:00
|
|
|
} else if (lseek(fd, 0, SEEK_END) < total_size) {
|
2013-10-11 17:37:01 +08:00
|
|
|
error_setg(errp, "Device is too small");
|
2009-04-06 01:40:43 +08:00
|
|
|
ret = -ENOSPC;
|
2013-10-11 17:37:01 +08:00
|
|
|
}
|
2009-04-06 01:40:43 +08:00
|
|
|
|
2012-08-15 04:43:46 +08:00
|
|
|
qemu_close(fd);
|
2009-04-06 01:40:43 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-05-10 06:03:42 +08:00
|
|
|
static BlockDriver bdrv_host_device = {
|
2009-10-01 18:35:49 +08:00
|
|
|
.format_name = "host_device",
|
2010-04-08 04:30:24 +08:00
|
|
|
.protocol_name = "host_device",
|
2009-10-01 18:35:49 +08:00
|
|
|
.instance_size = sizeof(BDRVRawState),
|
2013-09-24 23:07:04 +08:00
|
|
|
.bdrv_needs_filename = true,
|
2009-10-01 18:35:49 +08:00
|
|
|
.bdrv_probe_device = hdev_probe_device,
|
2014-03-08 07:39:41 +08:00
|
|
|
.bdrv_parse_filename = hdev_parse_filename,
|
2010-04-14 20:17:38 +08:00
|
|
|
.bdrv_file_open = hdev_open,
|
2009-10-01 18:35:49 +08:00
|
|
|
.bdrv_close = raw_close,
|
2012-11-20 23:21:10 +08:00
|
|
|
.bdrv_reopen_prepare = raw_reopen_prepare,
|
|
|
|
.bdrv_reopen_commit = raw_reopen_commit,
|
|
|
|
.bdrv_reopen_abort = raw_reopen_abort,
|
2014-06-05 17:21:11 +08:00
|
|
|
.bdrv_create = hdev_create,
|
2014-06-05 17:21:01 +08:00
|
|
|
.create_opts = &raw_create_opts,
|
2013-11-22 20:39:56 +08:00
|
|
|
.bdrv_co_write_zeroes = hdev_co_write_zeroes,
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2009-04-08 02:43:24 +08:00
|
|
|
.bdrv_aio_readv = raw_aio_readv,
|
|
|
|
.bdrv_aio_writev = raw_aio_writev,
|
2009-09-05 01:01:49 +08:00
|
|
|
.bdrv_aio_flush = raw_aio_flush,
|
2013-01-14 23:26:55 +08:00
|
|
|
.bdrv_aio_discard = hdev_aio_discard,
|
2011-11-29 19:42:20 +08:00
|
|
|
.bdrv_refresh_limits = raw_refresh_limits,
|
2014-07-04 18:04:34 +08:00
|
|
|
.bdrv_io_plug = raw_aio_plug,
|
|
|
|
.bdrv_io_unplug = raw_aio_unplug,
|
|
|
|
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
|
2008-12-13 00:41:40 +08:00
|
|
|
|
2011-09-21 07:10:37 +08:00
|
|
|
.bdrv_truncate = raw_truncate,
|
2009-03-08 06:00:29 +08:00
|
|
|
.bdrv_getlength = raw_getlength,
|
2013-11-22 20:39:55 +08:00
|
|
|
.bdrv_get_info = raw_get_info,
|
2011-07-12 19:56:39 +08:00
|
|
|
.bdrv_get_allocated_file_size
|
|
|
|
= raw_get_allocated_file_size,
|
2006-08-19 19:45:59 +08:00
|
|
|
|
2014-05-08 22:34:47 +08:00
|
|
|
.bdrv_detach_aio_context = raw_detach_aio_context,
|
|
|
|
.bdrv_attach_aio_context = raw_attach_aio_context,
|
|
|
|
|
2009-06-15 19:55:19 +08:00
|
|
|
/* generic scsi device */
|
2009-06-15 20:04:34 +08:00
|
|
|
#ifdef __linux__
|
|
|
|
.bdrv_ioctl = hdev_ioctl,
|
|
|
|
.bdrv_aio_ioctl = hdev_aio_ioctl,
|
|
|
|
#endif
|
2009-06-15 19:55:19 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef __linux__
|
2014-03-08 07:39:42 +08:00
|
|
|
static void floppy_parse_filename(const char *filename, QDict *options,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
/* The prefix is optional, just as for "file". */
|
|
|
|
strstart(filename, "host_floppy:", &filename);
|
|
|
|
|
|
|
|
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
|
|
|
}
|
|
|
|
|
2013-09-05 20:22:29 +08:00
|
|
|
static int floppy_open(BlockDriverState *bs, QDict *options, int flags,
|
|
|
|
Error **errp)
|
2009-06-15 19:55:19 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
2013-10-11 17:37:01 +08:00
|
|
|
Error *local_err = NULL;
|
2009-06-15 19:55:19 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
s->type = FTYPE_FD;
|
|
|
|
|
2009-06-17 23:27:44 +08:00
|
|
|
/* open will not fail even if no floppy is inserted, so add O_NONBLOCK */
|
2013-10-11 17:37:01 +08:00
|
|
|
ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
|
|
|
|
if (ret) {
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2013-10-11 17:37:01 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
2009-06-15 19:55:19 +08:00
|
|
|
return ret;
|
2013-10-11 17:37:01 +08:00
|
|
|
}
|
2009-06-15 19:55:19 +08:00
|
|
|
|
|
|
|
/* close fd so that we can reopen it as needed */
|
2012-08-15 04:43:46 +08:00
|
|
|
qemu_close(s->fd);
|
2009-06-15 19:55:19 +08:00
|
|
|
s->fd = -1;
|
|
|
|
s->fd_media_changed = 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-15 20:04:22 +08:00
|
|
|
static int floppy_probe_device(const char *filename)
|
|
|
|
{
|
2010-01-15 00:19:41 +08:00
|
|
|
int fd, ret;
|
|
|
|
int prio = 0;
|
|
|
|
struct floppy_struct fdparam;
|
2011-06-29 22:25:17 +08:00
|
|
|
struct stat st;
|
2010-01-15 00:19:41 +08:00
|
|
|
|
2012-08-15 04:43:44 +08:00
|
|
|
if (strstart(filename, "/dev/fd", NULL) &&
|
|
|
|
!strstart(filename, "/dev/fdset/", NULL)) {
|
2010-01-15 00:19:41 +08:00
|
|
|
prio = 50;
|
2012-08-15 04:43:44 +08:00
|
|
|
}
|
2010-01-15 00:19:41 +08:00
|
|
|
|
2012-08-15 04:43:45 +08:00
|
|
|
fd = qemu_open(filename, O_RDONLY | O_NONBLOCK);
|
2010-01-15 00:19:41 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2011-06-29 22:25:17 +08:00
|
|
|
ret = fstat(fd, &st);
|
|
|
|
if (ret == -1 || !S_ISBLK(st.st_mode)) {
|
|
|
|
goto outc;
|
|
|
|
}
|
2010-01-15 00:19:41 +08:00
|
|
|
|
|
|
|
/* Attempt to detect via a floppy specific ioctl */
|
|
|
|
ret = ioctl(fd, FDGETPRM, &fdparam);
|
|
|
|
if (ret >= 0)
|
|
|
|
prio = 100;
|
|
|
|
|
2011-06-29 22:25:17 +08:00
|
|
|
outc:
|
2012-08-15 04:43:46 +08:00
|
|
|
qemu_close(fd);
|
2010-01-15 00:19:41 +08:00
|
|
|
out:
|
|
|
|
return prio;
|
2009-06-15 20:04:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-15 19:55:19 +08:00
|
|
|
static int floppy_is_inserted(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return fd_open(bs) >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int floppy_media_changed(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: we do not have a true media changed indication.
|
|
|
|
* It does not work if the floppy is changed without trying to read it.
|
|
|
|
*/
|
|
|
|
fd_open(bs);
|
|
|
|
ret = s->fd_media_changed;
|
|
|
|
s->fd_media_changed = 0;
|
|
|
|
#ifdef DEBUG_FLOPPY
|
|
|
|
printf("Floppy changed=%d\n", ret);
|
|
|
|
#endif
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-02-04 02:24:53 +08:00
|
|
|
static void floppy_eject(BlockDriverState *bs, bool eject_flag)
|
2009-06-15 19:55:19 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (s->fd >= 0) {
|
2012-08-15 04:43:46 +08:00
|
|
|
qemu_close(s->fd);
|
2009-06-15 19:55:19 +08:00
|
|
|
s->fd = -1;
|
|
|
|
}
|
2012-08-15 04:43:45 +08:00
|
|
|
fd = qemu_open(bs->filename, s->open_flags | O_NONBLOCK);
|
2009-06-15 19:55:19 +08:00
|
|
|
if (fd >= 0) {
|
|
|
|
if (ioctl(fd, FDEJECT, 0) < 0)
|
|
|
|
perror("FDEJECT");
|
2012-08-15 04:43:46 +08:00
|
|
|
qemu_close(fd);
|
2009-06-15 19:55:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static BlockDriver bdrv_host_floppy = {
|
|
|
|
.format_name = "host_floppy",
|
2010-04-08 04:30:24 +08:00
|
|
|
.protocol_name = "host_floppy",
|
2009-06-15 19:55:19 +08:00
|
|
|
.instance_size = sizeof(BDRVRawState),
|
2013-09-24 23:07:04 +08:00
|
|
|
.bdrv_needs_filename = true,
|
2009-06-15 20:04:22 +08:00
|
|
|
.bdrv_probe_device = floppy_probe_device,
|
2014-03-08 07:39:42 +08:00
|
|
|
.bdrv_parse_filename = floppy_parse_filename,
|
2010-04-14 20:17:38 +08:00
|
|
|
.bdrv_file_open = floppy_open,
|
2009-06-15 19:55:19 +08:00
|
|
|
.bdrv_close = raw_close,
|
2012-11-20 23:21:10 +08:00
|
|
|
.bdrv_reopen_prepare = raw_reopen_prepare,
|
|
|
|
.bdrv_reopen_commit = raw_reopen_commit,
|
|
|
|
.bdrv_reopen_abort = raw_reopen_abort,
|
2014-06-05 17:21:11 +08:00
|
|
|
.bdrv_create = hdev_create,
|
2014-06-05 17:21:01 +08:00
|
|
|
.create_opts = &raw_create_opts,
|
2009-06-15 19:55:19 +08:00
|
|
|
|
|
|
|
.bdrv_aio_readv = raw_aio_readv,
|
|
|
|
.bdrv_aio_writev = raw_aio_writev,
|
2009-09-05 01:01:49 +08:00
|
|
|
.bdrv_aio_flush = raw_aio_flush,
|
2011-11-29 19:42:20 +08:00
|
|
|
.bdrv_refresh_limits = raw_refresh_limits,
|
2014-07-04 18:04:34 +08:00
|
|
|
.bdrv_io_plug = raw_aio_plug,
|
|
|
|
.bdrv_io_unplug = raw_aio_unplug,
|
|
|
|
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
|
2009-06-15 19:55:19 +08:00
|
|
|
|
2011-09-21 07:10:37 +08:00
|
|
|
.bdrv_truncate = raw_truncate,
|
block: Avoid unecessary drv->bdrv_getlength() calls
The block layer generally keeps the size of an image cached in
bs->total_sectors so that it doesn't have to perform expensive
operations to get the size whenever it needs it.
This doesn't work however when using a backend that can change its size
without qemu being aware of it, i.e. passthrough of removable media like
CD-ROMs or floppy disks. For this reason, the caching is disabled when a
removable device is used.
It is obvious that checking whether the _guest_ device has removable
media isn't the right thing to do when we want to know whether the size
of the host backend can change. To make things worse, non-top-level
BlockDriverStates never have any device attached, which makes qemu
assume they are removable, so drv->bdrv_getlength() is always called on
the protocol layer. In the case of raw-posix, this causes unnecessary
lseek() system calls, which turned out to be rather expensive.
This patch completely changes the logic and disables bs->total_sectors
caching only for certain block driver types, for which a size change is
expected: host_cdrom and host_floppy on POSIX, host_device on win32; also
the raw format in case it sits on top of one of these protocols, but in
the common case the nested bdrv_getlength() call on the protocol driver
will use the cache again and avoid an expensive drv->bdrv_getlength()
call.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
2013-10-29 19:18:58 +08:00
|
|
|
.bdrv_getlength = raw_getlength,
|
|
|
|
.has_variable_length = true,
|
2011-07-12 19:56:39 +08:00
|
|
|
.bdrv_get_allocated_file_size
|
|
|
|
= raw_get_allocated_file_size,
|
2009-06-15 19:55:19 +08:00
|
|
|
|
2014-05-08 22:34:47 +08:00
|
|
|
.bdrv_detach_aio_context = raw_detach_aio_context,
|
|
|
|
.bdrv_attach_aio_context = raw_attach_aio_context,
|
|
|
|
|
2009-06-15 19:55:19 +08:00
|
|
|
/* removable device support */
|
|
|
|
.bdrv_is_inserted = floppy_is_inserted,
|
|
|
|
.bdrv_media_changed = floppy_media_changed,
|
|
|
|
.bdrv_eject = floppy_eject,
|
|
|
|
};
|
2014-03-08 07:39:43 +08:00
|
|
|
#endif
|
2009-06-15 19:55:19 +08:00
|
|
|
|
2014-03-08 07:39:43 +08:00
|
|
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
|
|
|
static void cdrom_parse_filename(const char *filename, QDict *options,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
/* The prefix is optional, just as for "file". */
|
|
|
|
strstart(filename, "host_cdrom:", &filename);
|
|
|
|
|
|
|
|
qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __linux__
|
2013-09-05 20:22:29 +08:00
|
|
|
static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
|
|
|
|
Error **errp)
|
2009-06-15 19:55:19 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
2013-10-11 17:37:01 +08:00
|
|
|
Error *local_err = NULL;
|
|
|
|
int ret;
|
2009-06-15 19:55:19 +08:00
|
|
|
|
|
|
|
s->type = FTYPE_CD;
|
|
|
|
|
2009-06-17 23:27:44 +08:00
|
|
|
/* open will not fail even if no CD is inserted, so add O_NONBLOCK */
|
2013-10-11 17:37:01 +08:00
|
|
|
ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2013-10-11 17:37:01 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
return ret;
|
2009-06-15 19:55:19 +08:00
|
|
|
}
|
|
|
|
|
2009-06-15 20:04:22 +08:00
|
|
|
static int cdrom_probe_device(const char *filename)
|
|
|
|
{
|
2010-01-15 00:19:40 +08:00
|
|
|
int fd, ret;
|
|
|
|
int prio = 0;
|
2011-06-29 22:25:17 +08:00
|
|
|
struct stat st;
|
2010-01-15 00:19:40 +08:00
|
|
|
|
2012-08-15 04:43:45 +08:00
|
|
|
fd = qemu_open(filename, O_RDONLY | O_NONBLOCK);
|
2010-01-15 00:19:40 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2011-06-29 22:25:17 +08:00
|
|
|
ret = fstat(fd, &st);
|
|
|
|
if (ret == -1 || !S_ISBLK(st.st_mode)) {
|
|
|
|
goto outc;
|
|
|
|
}
|
2010-01-15 00:19:40 +08:00
|
|
|
|
|
|
|
/* Attempt to detect via a CDROM specific ioctl */
|
|
|
|
ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
|
|
|
|
if (ret >= 0)
|
|
|
|
prio = 100;
|
|
|
|
|
2011-06-29 22:25:17 +08:00
|
|
|
outc:
|
2012-08-15 04:43:46 +08:00
|
|
|
qemu_close(fd);
|
2010-01-15 00:19:40 +08:00
|
|
|
out:
|
|
|
|
return prio;
|
2009-06-15 20:04:22 +08:00
|
|
|
}
|
|
|
|
|
2009-06-15 19:55:19 +08:00
|
|
|
static int cdrom_is_inserted(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
|
|
|
|
if (ret == CDS_DISC_OK)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-04 02:24:53 +08:00
|
|
|
static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
|
2009-06-15 19:55:19 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
if (eject_flag) {
|
|
|
|
if (ioctl(s->fd, CDROMEJECT, NULL) < 0)
|
|
|
|
perror("CDROMEJECT");
|
|
|
|
} else {
|
|
|
|
if (ioctl(s->fd, CDROMCLOSETRAY, NULL) < 0)
|
|
|
|
perror("CDROMEJECT");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-07 00:58:47 +08:00
|
|
|
static void cdrom_lock_medium(BlockDriverState *bs, bool locked)
|
2009-06-15 19:55:19 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
if (ioctl(s->fd, CDROM_LOCKDOOR, locked) < 0) {
|
|
|
|
/*
|
|
|
|
* Note: an error can happen if the distribution automatically
|
|
|
|
* mounts the CD-ROM
|
|
|
|
*/
|
|
|
|
/* perror("CDROM_LOCKDOOR"); */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static BlockDriver bdrv_host_cdrom = {
|
|
|
|
.format_name = "host_cdrom",
|
2010-04-08 04:30:24 +08:00
|
|
|
.protocol_name = "host_cdrom",
|
2009-06-15 19:55:19 +08:00
|
|
|
.instance_size = sizeof(BDRVRawState),
|
2013-09-24 23:07:04 +08:00
|
|
|
.bdrv_needs_filename = true,
|
2009-06-15 20:04:22 +08:00
|
|
|
.bdrv_probe_device = cdrom_probe_device,
|
2014-03-08 07:39:43 +08:00
|
|
|
.bdrv_parse_filename = cdrom_parse_filename,
|
2010-04-14 20:17:38 +08:00
|
|
|
.bdrv_file_open = cdrom_open,
|
2009-06-15 19:55:19 +08:00
|
|
|
.bdrv_close = raw_close,
|
2012-11-20 23:21:10 +08:00
|
|
|
.bdrv_reopen_prepare = raw_reopen_prepare,
|
|
|
|
.bdrv_reopen_commit = raw_reopen_commit,
|
|
|
|
.bdrv_reopen_abort = raw_reopen_abort,
|
2014-06-05 17:21:11 +08:00
|
|
|
.bdrv_create = hdev_create,
|
2014-06-05 17:21:01 +08:00
|
|
|
.create_opts = &raw_create_opts,
|
2009-06-15 19:55:19 +08:00
|
|
|
|
|
|
|
.bdrv_aio_readv = raw_aio_readv,
|
|
|
|
.bdrv_aio_writev = raw_aio_writev,
|
2009-09-05 01:01:49 +08:00
|
|
|
.bdrv_aio_flush = raw_aio_flush,
|
2011-11-29 19:42:20 +08:00
|
|
|
.bdrv_refresh_limits = raw_refresh_limits,
|
2014-07-04 18:04:34 +08:00
|
|
|
.bdrv_io_plug = raw_aio_plug,
|
|
|
|
.bdrv_io_unplug = raw_aio_unplug,
|
|
|
|
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
|
2009-06-15 19:55:19 +08:00
|
|
|
|
2011-09-21 07:10:37 +08:00
|
|
|
.bdrv_truncate = raw_truncate,
|
block: Avoid unecessary drv->bdrv_getlength() calls
The block layer generally keeps the size of an image cached in
bs->total_sectors so that it doesn't have to perform expensive
operations to get the size whenever it needs it.
This doesn't work however when using a backend that can change its size
without qemu being aware of it, i.e. passthrough of removable media like
CD-ROMs or floppy disks. For this reason, the caching is disabled when a
removable device is used.
It is obvious that checking whether the _guest_ device has removable
media isn't the right thing to do when we want to know whether the size
of the host backend can change. To make things worse, non-top-level
BlockDriverStates never have any device attached, which makes qemu
assume they are removable, so drv->bdrv_getlength() is always called on
the protocol layer. In the case of raw-posix, this causes unnecessary
lseek() system calls, which turned out to be rather expensive.
This patch completely changes the logic and disables bs->total_sectors
caching only for certain block driver types, for which a size change is
expected: host_cdrom and host_floppy on POSIX, host_device on win32; also
the raw format in case it sits on top of one of these protocols, but in
the common case the nested bdrv_getlength() call on the protocol driver
will use the cache again and avoid an expensive drv->bdrv_getlength()
call.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
2013-10-29 19:18:58 +08:00
|
|
|
.bdrv_getlength = raw_getlength,
|
|
|
|
.has_variable_length = true,
|
2011-07-12 19:56:39 +08:00
|
|
|
.bdrv_get_allocated_file_size
|
|
|
|
= raw_get_allocated_file_size,
|
2009-06-15 19:55:19 +08:00
|
|
|
|
2014-05-08 22:34:47 +08:00
|
|
|
.bdrv_detach_aio_context = raw_detach_aio_context,
|
|
|
|
.bdrv_attach_aio_context = raw_attach_aio_context,
|
|
|
|
|
2009-06-15 19:55:19 +08:00
|
|
|
/* removable device support */
|
|
|
|
.bdrv_is_inserted = cdrom_is_inserted,
|
|
|
|
.bdrv_eject = cdrom_eject,
|
2011-09-07 00:58:47 +08:00
|
|
|
.bdrv_lock_medium = cdrom_lock_medium,
|
2009-06-15 19:55:19 +08:00
|
|
|
|
|
|
|
/* generic scsi device */
|
2009-06-15 20:04:34 +08:00
|
|
|
.bdrv_ioctl = hdev_ioctl,
|
|
|
|
.bdrv_aio_ioctl = hdev_aio_ioctl,
|
2009-06-15 19:55:19 +08:00
|
|
|
};
|
|
|
|
#endif /* __linux__ */
|
|
|
|
|
2009-11-30 01:00:41 +08:00
|
|
|
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
|
2013-11-01 05:41:46 +08:00
|
|
|
static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
|
|
|
|
Error **errp)
|
2009-06-15 19:55:19 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
2013-10-11 17:37:01 +08:00
|
|
|
Error *local_err = NULL;
|
2009-06-15 19:55:19 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
s->type = FTYPE_CD;
|
|
|
|
|
2013-10-11 17:37:01 +08:00
|
|
|
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
|
|
|
if (ret) {
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2013-10-11 17:37:01 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
2009-06-15 19:55:19 +08:00
|
|
|
return ret;
|
2013-10-11 17:37:01 +08:00
|
|
|
}
|
2009-06-15 19:55:19 +08:00
|
|
|
|
2011-11-22 18:06:25 +08:00
|
|
|
/* make sure the door isn't locked at this time */
|
2009-06-15 19:55:19 +08:00
|
|
|
ioctl(s->fd, CDIOCALLOW);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-15 20:04:22 +08:00
|
|
|
static int cdrom_probe_device(const char *filename)
|
|
|
|
{
|
|
|
|
if (strstart(filename, "/dev/cd", NULL) ||
|
|
|
|
strstart(filename, "/dev/acd", NULL))
|
|
|
|
return 100;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-15 19:55:19 +08:00
|
|
|
static int cdrom_reopen(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Force reread of possibly changed/newly loaded disc,
|
|
|
|
* FreeBSD seems to not notice sometimes...
|
|
|
|
*/
|
|
|
|
if (s->fd >= 0)
|
2012-08-15 04:43:46 +08:00
|
|
|
qemu_close(s->fd);
|
2012-08-15 04:43:45 +08:00
|
|
|
fd = qemu_open(bs->filename, s->open_flags, 0644);
|
2009-06-15 19:55:19 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
s->fd = -1;
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
s->fd = fd;
|
|
|
|
|
2011-11-22 18:06:25 +08:00
|
|
|
/* make sure the door isn't locked at this time */
|
2009-06-15 19:55:19 +08:00
|
|
|
ioctl(s->fd, CDIOCALLOW);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cdrom_is_inserted(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return raw_getlength(bs) > 0;
|
|
|
|
}
|
|
|
|
|
2012-02-04 02:24:53 +08:00
|
|
|
static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
|
2009-06-15 19:55:19 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
if (s->fd < 0)
|
2011-07-21 00:23:42 +08:00
|
|
|
return;
|
2009-06-15 19:55:19 +08:00
|
|
|
|
|
|
|
(void) ioctl(s->fd, CDIOCALLOW);
|
|
|
|
|
|
|
|
if (eject_flag) {
|
|
|
|
if (ioctl(s->fd, CDIOCEJECT) < 0)
|
|
|
|
perror("CDIOCEJECT");
|
|
|
|
} else {
|
|
|
|
if (ioctl(s->fd, CDIOCCLOSE) < 0)
|
|
|
|
perror("CDIOCCLOSE");
|
|
|
|
}
|
|
|
|
|
2011-07-21 00:23:42 +08:00
|
|
|
cdrom_reopen(bs);
|
2009-06-15 19:55:19 +08:00
|
|
|
}
|
|
|
|
|
2011-09-07 00:58:47 +08:00
|
|
|
static void cdrom_lock_medium(BlockDriverState *bs, bool locked)
|
2009-06-15 19:55:19 +08:00
|
|
|
{
|
|
|
|
BDRVRawState *s = bs->opaque;
|
|
|
|
|
|
|
|
if (s->fd < 0)
|
2011-07-21 00:23:41 +08:00
|
|
|
return;
|
2009-06-15 19:55:19 +08:00
|
|
|
if (ioctl(s->fd, (locked ? CDIOCPREVENT : CDIOCALLOW)) < 0) {
|
|
|
|
/*
|
|
|
|
* Note: an error can happen if the distribution automatically
|
|
|
|
* mounts the CD-ROM
|
|
|
|
*/
|
|
|
|
/* perror("CDROM_LOCKDOOR"); */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static BlockDriver bdrv_host_cdrom = {
|
|
|
|
.format_name = "host_cdrom",
|
2010-04-08 04:30:24 +08:00
|
|
|
.protocol_name = "host_cdrom",
|
2009-06-15 19:55:19 +08:00
|
|
|
.instance_size = sizeof(BDRVRawState),
|
2013-09-24 23:07:04 +08:00
|
|
|
.bdrv_needs_filename = true,
|
2009-06-15 20:04:22 +08:00
|
|
|
.bdrv_probe_device = cdrom_probe_device,
|
2014-03-08 07:39:43 +08:00
|
|
|
.bdrv_parse_filename = cdrom_parse_filename,
|
2010-04-14 20:17:38 +08:00
|
|
|
.bdrv_file_open = cdrom_open,
|
2009-06-15 19:55:19 +08:00
|
|
|
.bdrv_close = raw_close,
|
2012-11-20 23:21:10 +08:00
|
|
|
.bdrv_reopen_prepare = raw_reopen_prepare,
|
|
|
|
.bdrv_reopen_commit = raw_reopen_commit,
|
|
|
|
.bdrv_reopen_abort = raw_reopen_abort,
|
2014-06-05 17:21:11 +08:00
|
|
|
.bdrv_create = hdev_create,
|
2014-06-05 17:21:01 +08:00
|
|
|
.create_opts = &raw_create_opts,
|
2009-06-15 19:55:19 +08:00
|
|
|
|
|
|
|
.bdrv_aio_readv = raw_aio_readv,
|
|
|
|
.bdrv_aio_writev = raw_aio_writev,
|
2009-09-05 01:01:49 +08:00
|
|
|
.bdrv_aio_flush = raw_aio_flush,
|
2011-11-29 19:42:20 +08:00
|
|
|
.bdrv_refresh_limits = raw_refresh_limits,
|
2014-07-04 18:04:34 +08:00
|
|
|
.bdrv_io_plug = raw_aio_plug,
|
|
|
|
.bdrv_io_unplug = raw_aio_unplug,
|
|
|
|
.bdrv_flush_io_queue = raw_aio_flush_io_queue,
|
2009-06-15 19:55:19 +08:00
|
|
|
|
2011-09-21 07:10:37 +08:00
|
|
|
.bdrv_truncate = raw_truncate,
|
block: Avoid unecessary drv->bdrv_getlength() calls
The block layer generally keeps the size of an image cached in
bs->total_sectors so that it doesn't have to perform expensive
operations to get the size whenever it needs it.
This doesn't work however when using a backend that can change its size
without qemu being aware of it, i.e. passthrough of removable media like
CD-ROMs or floppy disks. For this reason, the caching is disabled when a
removable device is used.
It is obvious that checking whether the _guest_ device has removable
media isn't the right thing to do when we want to know whether the size
of the host backend can change. To make things worse, non-top-level
BlockDriverStates never have any device attached, which makes qemu
assume they are removable, so drv->bdrv_getlength() is always called on
the protocol layer. In the case of raw-posix, this causes unnecessary
lseek() system calls, which turned out to be rather expensive.
This patch completely changes the logic and disables bs->total_sectors
caching only for certain block driver types, for which a size change is
expected: host_cdrom and host_floppy on POSIX, host_device on win32; also
the raw format in case it sits on top of one of these protocols, but in
the common case the nested bdrv_getlength() call on the protocol driver
will use the cache again and avoid an expensive drv->bdrv_getlength()
call.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
2013-10-29 19:18:58 +08:00
|
|
|
.bdrv_getlength = raw_getlength,
|
|
|
|
.has_variable_length = true,
|
2011-07-12 19:56:39 +08:00
|
|
|
.bdrv_get_allocated_file_size
|
|
|
|
= raw_get_allocated_file_size,
|
2009-06-15 19:55:19 +08:00
|
|
|
|
2014-05-08 22:34:47 +08:00
|
|
|
.bdrv_detach_aio_context = raw_detach_aio_context,
|
|
|
|
.bdrv_attach_aio_context = raw_attach_aio_context,
|
|
|
|
|
2006-08-19 19:45:59 +08:00
|
|
|
/* removable device support */
|
2009-06-15 19:55:19 +08:00
|
|
|
.bdrv_is_inserted = cdrom_is_inserted,
|
|
|
|
.bdrv_eject = cdrom_eject,
|
2011-09-07 00:58:47 +08:00
|
|
|
.bdrv_lock_medium = cdrom_lock_medium,
|
2006-08-19 19:45:59 +08:00
|
|
|
};
|
2009-06-15 19:55:19 +08:00
|
|
|
#endif /* __FreeBSD__ */
|
2009-05-10 06:03:42 +08:00
|
|
|
|
2010-04-08 04:30:24 +08:00
|
|
|
static void bdrv_file_init(void)
|
2009-05-10 06:03:42 +08:00
|
|
|
{
|
2009-06-15 20:04:22 +08:00
|
|
|
/*
|
|
|
|
* Register all the drivers. Note that order is important, the driver
|
|
|
|
* registered last will get probed first.
|
|
|
|
*/
|
2010-04-08 04:30:24 +08:00
|
|
|
bdrv_register(&bdrv_file);
|
2009-05-10 06:03:42 +08:00
|
|
|
bdrv_register(&bdrv_host_device);
|
2009-06-15 19:55:19 +08:00
|
|
|
#ifdef __linux__
|
|
|
|
bdrv_register(&bdrv_host_floppy);
|
|
|
|
bdrv_register(&bdrv_host_cdrom);
|
|
|
|
#endif
|
2009-11-30 01:00:41 +08:00
|
|
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
2009-06-15 19:55:19 +08:00
|
|
|
bdrv_register(&bdrv_host_cdrom);
|
|
|
|
#endif
|
2009-05-10 06:03:42 +08:00
|
|
|
}
|
|
|
|
|
2010-04-08 04:30:24 +08:00
|
|
|
block_init(bdrv_file_init);
|