* THE SOFTWARE.
*/
#include "qemu-common.h"
-#ifndef QEMU_IMG
#include "qemu-timer.h"
-#include "exec-all.h"
-#endif
+#include "qemu-char.h"
#include "block_int.h"
#include <assert.h>
-#include <aio.h>
+#ifdef CONFIG_AIO
+#include "posix-aio-compat.h"
+#endif
#ifdef CONFIG_COCOA
#include <paths.h>
#include <linux/fd.h>
#endif
#ifdef __FreeBSD__
+#include <signal.h>
#include <sys/disk.h>
#endif
+#ifdef __OpenBSD__
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#endif
+
//#define DEBUG_FLOPPY
//#define DEBUG_BLOCK
-#if defined(DEBUG_BLOCK) && !defined(QEMU_IMG)
-#define DEBUG_BLOCK_PRINT(formatCstr, args...) do { if (loglevel != 0) \
- { fprintf(logfile, formatCstr, ##args); fflush(logfile); } } while (0)
+#if defined(DEBUG_BLOCK)
+#define DEBUG_BLOCK_PRINT(formatCstr, args...) do { if (qemu_log_enabled()) \
+ { qemu_log(formatCstr, ##args); qemu_log_flush(); } } while (0)
#else
#define DEBUG_BLOCK_PRINT(formatCstr, args...)
#endif
+/* OS X does not have O_DSYNC */
+#ifndef O_DSYNC
+#define O_DSYNC O_SYNC
+#endif
+
+/* Approximate O_DIRECT with O_DSYNC if O_DIRECT isn't available */
+#ifndef O_DIRECT
+#define O_DIRECT O_DSYNC
+#endif
+
#define FTYPE_FILE 0
#define FTYPE_CD 1
#define FTYPE_FD 2
+#define ALIGNED_BUFFER_SIZE (32 * 512)
+
/* if the FD is not accessed during that time (in ms), we try to
reopen it to see if the disk has been changed */
#define FD_OPEN_TIMEOUT 1000
int fd_got_error;
int fd_media_changed;
#endif
+ uint8_t* aligned_buf;
} BDRVRawState;
+static int posix_aio_init(void);
+
static int fd_open(BlockDriverState *bs);
static int raw_open(BlockDriverState *bs, const char *filename, int flags)
BDRVRawState *s = bs->opaque;
int fd, open_flags, ret;
+ posix_aio_init();
+
s->lseek_err_cnt = 0;
open_flags = O_BINARY;
}
if (flags & BDRV_O_CREAT)
open_flags |= O_CREAT | O_TRUNC;
-#ifdef O_DIRECT
- if (flags & BDRV_O_DIRECT)
+
+ /* Use O_DSYNC for write-through caching, no flags for write-back caching,
+ * and O_DIRECT for no caching. */
+ if ((flags & BDRV_O_NOCACHE))
open_flags |= O_DIRECT;
-#endif
+ else if (!(flags & BDRV_O_CACHE_WB))
+ open_flags |= O_DSYNC;
s->type = FTYPE_FILE;
return ret;
}
s->fd = fd;
+ s->aligned_buf = NULL;
+ if ((flags & BDRV_O_NOCACHE)) {
+ s->aligned_buf = qemu_memalign(512, ALIGNED_BUFFER_SIZE);
+ if (s->aligned_buf == NULL) {
+ ret = -errno;
+ close(fd);
+ return ret;
+ }
+ }
return 0;
}
#endif
*/
-static int raw_pread(BlockDriverState *bs, int64_t offset,
+/*
+ * offset and count are in bytes, but must be multiples of 512 for files
+ * opened with O_DIRECT. buf must be aligned to 512 bytes then.
+ *
+ * This function may be called without alignment if the caller ensures
+ * that O_DIRECT is not in effect.
+ */
+static int raw_pread_aligned(BlockDriverState *bs, int64_t offset,
uint8_t *buf, int count)
{
BDRVRawState *s = bs->opaque;
if (ret < 0)
return ret;
- if (lseek(s->fd, offset, SEEK_SET) == (off_t)-1) {
+ if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) {
++(s->lseek_err_cnt);
if(s->lseek_err_cnt <= 10) {
DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
return ret;
}
-static int raw_pwrite(BlockDriverState *bs, int64_t offset,
+/*
+ * offset and count are in bytes, but must be multiples of 512 for files
+ * opened with O_DIRECT. buf must be aligned to 512 bytes then.
+ *
+ * This function may be called without alignment if the caller ensures
+ * that O_DIRECT is not in effect.
+ */
+static int raw_pwrite_aligned(BlockDriverState *bs, int64_t offset,
const uint8_t *buf, int count)
{
BDRVRawState *s = bs->opaque;
ret = fd_open(bs);
if (ret < 0)
- return ret;
+ return -errno;
- if (lseek(s->fd, offset, SEEK_SET) == (off_t)-1) {
+ if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) {
++(s->lseek_err_cnt);
if(s->lseek_err_cnt) {
DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%"
s->fd, bs->filename, offset, buf, count,
bs->total_sectors, errno, strerror(errno));
}
- return -1;
+ return -EIO;
}
s->lseek_err_cnt = 0;
label__raw_write__success:
- return ret;
+ return (ret < 0) ? -errno : ret;
}
-/***********************************************************/
-/* Unix AIO using POSIX AIO */
-typedef struct RawAIOCB {
- BlockDriverAIOCB common;
- struct aiocb aiocb;
- struct RawAIOCB *next;
-} RawAIOCB;
+/*
+ * offset and count are in bytes and possibly not aligned. For files opened
+ * with O_DIRECT, necessary alignments are ensured before calling
+ * raw_pread_aligned to do the actual read.
+ */
+static int raw_pread(BlockDriverState *bs, int64_t offset,
+ uint8_t *buf, int count)
+{
+ BDRVRawState *s = bs->opaque;
+ int size, ret, shift, sum;
-static int aio_sig_num = SIGUSR2;
-static RawAIOCB *first_aio; /* AIO issued */
-static int aio_initialized = 0;
+ sum = 0;
-static void aio_signal_handler(int signum)
-{
-#ifndef QEMU_IMG
- CPUState *env = cpu_single_env;
- if (env) {
- /* stop the currently executing cpu because a timer occured */
- cpu_interrupt(env, CPU_INTERRUPT_EXIT);
-#ifdef USE_KQEMU
- if (env->kqemu_enabled) {
- kqemu_cpu_interrupt(env);
+ if (s->aligned_buf != NULL) {
+
+ if (offset & 0x1ff) {
+ /* align offset on a 512 bytes boundary */
+
+ shift = offset & 0x1ff;
+ size = (shift + count + 0x1ff) & ~0x1ff;
+ if (size > ALIGNED_BUFFER_SIZE)
+ size = ALIGNED_BUFFER_SIZE;
+ ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, size);
+ if (ret < 0)
+ return ret;
+
+ size = 512 - shift;
+ if (size > count)
+ size = count;
+ memcpy(buf, s->aligned_buf + shift, size);
+
+ buf += size;
+ offset += size;
+ count -= size;
+ sum += size;
+
+ if (count == 0)
+ return sum;
+ }
+ if (count & 0x1ff || (uintptr_t) buf & 0x1ff) {
+
+ /* read on aligned buffer */
+
+ while (count) {
+
+ size = (count + 0x1ff) & ~0x1ff;
+ if (size > ALIGNED_BUFFER_SIZE)
+ size = ALIGNED_BUFFER_SIZE;
+
+ ret = raw_pread_aligned(bs, offset, s->aligned_buf, size);
+ if (ret < 0)
+ return ret;
+
+ size = ret;
+ if (size > count)
+ size = count;
+
+ memcpy(buf, s->aligned_buf, size);
+
+ buf += size;
+ offset += size;
+ count -= size;
+ sum += size;
+ }
+
+ return sum;
}
-#endif
}
-#endif
+
+ return raw_pread_aligned(bs, offset, buf, count) + sum;
}
-void qemu_aio_init(void)
+/*
+ * offset and count are in bytes and possibly not aligned. For files opened
+ * with O_DIRECT, necessary alignments are ensured before calling
+ * raw_pwrite_aligned to do the actual write.
+ */
+static int raw_pwrite(BlockDriverState *bs, int64_t offset,
+ const uint8_t *buf, int count)
{
- struct sigaction act;
+ BDRVRawState *s = bs->opaque;
+ int size, ret, shift, sum;
- aio_initialized = 1;
+ sum = 0;
- sigfillset(&act.sa_mask);
- act.sa_flags = 0; /* do not restart syscalls to interrupt select() */
- act.sa_handler = aio_signal_handler;
- sigaction(aio_sig_num, &act, NULL);
+ if (s->aligned_buf != NULL) {
-#if defined(__GLIBC__) && defined(__linux__)
- {
- /* XXX: aio thread exit seems to hang on RedHat 9 and this init
- seems to fix the problem. */
- struct aioinit ai;
- memset(&ai, 0, sizeof(ai));
- ai.aio_threads = 1;
- ai.aio_num = 1;
- ai.aio_idle_time = 365 * 100000;
- aio_init(&ai);
+ if (offset & 0x1ff) {
+ /* align offset on a 512 bytes boundary */
+ shift = offset & 0x1ff;
+ ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, 512);
+ if (ret < 0)
+ return ret;
+
+ size = 512 - shift;
+ if (size > count)
+ size = count;
+ memcpy(s->aligned_buf + shift, buf, size);
+
+ ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf, 512);
+ if (ret < 0)
+ return ret;
+
+ buf += size;
+ offset += size;
+ count -= size;
+ sum += size;
+
+ if (count == 0)
+ return sum;
+ }
+ if (count & 0x1ff || (uintptr_t) buf & 0x1ff) {
+
+ while ((size = (count & ~0x1ff)) != 0) {
+
+ if (size > ALIGNED_BUFFER_SIZE)
+ size = ALIGNED_BUFFER_SIZE;
+
+ memcpy(s->aligned_buf, buf, size);
+
+ ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, size);
+ if (ret < 0)
+ return ret;
+
+ buf += ret;
+ offset += ret;
+ count -= ret;
+ sum += ret;
+ }
+ /* here, count < 512 because (count & ~0x1ff) == 0 */
+ if (count) {
+ ret = raw_pread_aligned(bs, offset, s->aligned_buf, 512);
+ if (ret < 0)
+ return ret;
+ memcpy(s->aligned_buf, buf, count);
+
+ ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, 512);
+ if (ret < 0)
+ return ret;
+ if (count < ret)
+ ret = count;
+
+ sum += ret;
+ }
+ return sum;
+ }
}
-#endif
+ return raw_pwrite_aligned(bs, offset, buf, count) + sum;
}
-void qemu_aio_poll(void)
+#ifdef CONFIG_AIO
+/***********************************************************/
+/* Unix AIO using POSIX AIO */
+
+typedef struct RawAIOCB {
+ BlockDriverAIOCB common;
+ struct qemu_paiocb aiocb;
+ struct RawAIOCB *next;
+ int ret;
+} RawAIOCB;
+
+typedef struct PosixAioState
+{
+ int rfd, wfd;
+ RawAIOCB *first_aio;
+} PosixAioState;
+
+static void posix_aio_read(void *opaque)
{
+ PosixAioState *s = opaque;
RawAIOCB *acb, **pacb;
int ret;
+ ssize_t len;
+
+ /* read all bytes from signal pipe */
+ for (;;) {
+ char bytes[16];
+
+ len = read(s->rfd, bytes, sizeof(bytes));
+ if (len == -1 && errno == EINTR)
+ continue; /* try again */
+ if (len == sizeof(bytes))
+ continue; /* more to read */
+ break;
+ }
for(;;) {
- pacb = &first_aio;
+ pacb = &s->first_aio;
for(;;) {
acb = *pacb;
if (!acb)
goto the_end;
- ret = aio_error(&acb->aiocb);
+ ret = qemu_paio_error(&acb->aiocb);
if (ret == ECANCELED) {
/* remove the request */
*pacb = acb->next;
} else if (ret != EINPROGRESS) {
/* end of aio */
if (ret == 0) {
- ret = aio_return(&acb->aiocb);
+ ret = qemu_paio_return(&acb->aiocb);
if (ret == acb->aiocb.aio_nbytes)
ret = 0;
else
the_end: ;
}
-/* Wait for all IO requests to complete. */
-void qemu_aio_flush(void)
+static int posix_aio_flush(void *opaque)
{
- qemu_aio_wait_start();
- qemu_aio_poll();
- while (first_aio) {
- qemu_aio_wait();
- }
- qemu_aio_wait_end();
+ PosixAioState *s = opaque;
+ return !!s->first_aio;
}
-/* wait until at least one AIO was handled */
-static sigset_t wait_oset;
+static PosixAioState *posix_aio_state;
-void qemu_aio_wait_start(void)
+static void aio_signal_handler(int signum)
{
- sigset_t set;
+ if (posix_aio_state) {
+ char byte = 0;
- if (!aio_initialized)
- qemu_aio_init();
- sigemptyset(&set);
- sigaddset(&set, aio_sig_num);
- sigprocmask(SIG_BLOCK, &set, &wait_oset);
+ write(posix_aio_state->wfd, &byte, sizeof(byte));
+ }
+
+ qemu_service_io();
}
-void qemu_aio_wait(void)
+static int posix_aio_init(void)
{
- sigset_t set;
- int nb_sigs;
+ struct sigaction act;
+ PosixAioState *s;
+ int fds[2];
+ struct qemu_paioinit ai;
+
+ if (posix_aio_state)
+ return 0;
-#ifndef QEMU_IMG
- if (qemu_bh_poll())
- return;
-#endif
- sigemptyset(&set);
- sigaddset(&set, aio_sig_num);
- sigwait(&set, &nb_sigs);
- qemu_aio_poll();
-}
+ s = qemu_malloc(sizeof(PosixAioState));
-void qemu_aio_wait_end(void)
-{
- sigprocmask(SIG_SETMASK, &wait_oset, NULL);
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0; /* do not restart syscalls to interrupt select() */
+ act.sa_handler = aio_signal_handler;
+ sigaction(SIGUSR2, &act, NULL);
+
+ s->first_aio = NULL;
+ if (pipe(fds) == -1) {
+ fprintf(stderr, "failed to create pipe\n");
+ return -errno;
+ }
+
+ s->rfd = fds[0];
+ s->wfd = fds[1];
+
+ fcntl(s->rfd, F_SETFL, O_NONBLOCK);
+ fcntl(s->wfd, F_SETFL, O_NONBLOCK);
+
+ qemu_aio_set_fd_handler(s->rfd, posix_aio_read, NULL, posix_aio_flush, s);
+
+ memset(&ai, 0, sizeof(ai));
+ ai.aio_threads = 64;
+ ai.aio_num = 64;
+ qemu_paio_init(&ai);
+
+ posix_aio_state = s;
+
+ return 0;
}
static RawAIOCB *raw_aio_setup(BlockDriverState *bs,
if (!acb)
return NULL;
acb->aiocb.aio_fildes = s->fd;
- acb->aiocb.aio_sigevent.sigev_signo = aio_sig_num;
- acb->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+ acb->aiocb.ev_signo = SIGUSR2;
acb->aiocb.aio_buf = buf;
- acb->aiocb.aio_nbytes = nb_sectors * 512;
+ if (nb_sectors < 0)
+ acb->aiocb.aio_nbytes = -nb_sectors;
+ else
+ acb->aiocb.aio_nbytes = nb_sectors * 512;
acb->aiocb.aio_offset = sector_num * 512;
- acb->next = first_aio;
- first_aio = acb;
+ acb->next = posix_aio_state->first_aio;
+ posix_aio_state->first_aio = acb;
return acb;
}
+static void raw_aio_em_cb(void* opaque)
+{
+ RawAIOCB *acb = opaque;
+ acb->common.cb(acb->common.opaque, acb->ret);
+ qemu_aio_release(acb);
+}
+
+static void raw_aio_remove(RawAIOCB *acb)
+{
+ RawAIOCB **pacb;
+
+ /* remove the callback from the queue */
+ pacb = &posix_aio_state->first_aio;
+ for(;;) {
+ if (*pacb == NULL) {
+ break;
+ } else if (*pacb == acb) {
+ *pacb = acb->next;
+ qemu_aio_release(acb);
+ break;
+ }
+ pacb = &acb->next;
+ }
+}
+
static BlockDriverAIOCB *raw_aio_read(BlockDriverState *bs,
int64_t sector_num, uint8_t *buf, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
RawAIOCB *acb;
+ /*
+ * If O_DIRECT is used and the buffer is not aligned fall back
+ * to synchronous IO.
+ */
+ BDRVRawState *s = bs->opaque;
+
+ if (unlikely(s->aligned_buf != NULL && ((uintptr_t) buf % 512))) {
+ QEMUBH *bh;
+ acb = qemu_aio_get(bs, cb, opaque);
+ acb->ret = raw_pread(bs, 512 * sector_num, buf, 512 * nb_sectors);
+ bh = qemu_bh_new(raw_aio_em_cb, acb);
+ qemu_bh_schedule(bh);
+ return &acb->common;
+ }
+
acb = raw_aio_setup(bs, sector_num, buf, nb_sectors, cb, opaque);
if (!acb)
return NULL;
- if (aio_read(&acb->aiocb) < 0) {
- qemu_aio_release(acb);
+ if (qemu_paio_read(&acb->aiocb) < 0) {
+ raw_aio_remove(acb);
return NULL;
}
return &acb->common;
{
RawAIOCB *acb;
+ /*
+ * If O_DIRECT is used and the buffer is not aligned fall back
+ * to synchronous IO.
+ */
+ BDRVRawState *s = bs->opaque;
+
+ if (unlikely(s->aligned_buf != NULL && ((uintptr_t) buf % 512))) {
+ QEMUBH *bh;
+ acb = qemu_aio_get(bs, cb, opaque);
+ acb->ret = raw_pwrite(bs, 512 * sector_num, buf, 512 * nb_sectors);
+ bh = qemu_bh_new(raw_aio_em_cb, acb);
+ qemu_bh_schedule(bh);
+ return &acb->common;
+ }
+
acb = raw_aio_setup(bs, sector_num, (uint8_t*)buf, nb_sectors, cb, opaque);
if (!acb)
return NULL;
- if (aio_write(&acb->aiocb) < 0) {
- qemu_aio_release(acb);
+ if (qemu_paio_write(&acb->aiocb) < 0) {
+ raw_aio_remove(acb);
return NULL;
}
return &acb->common;
{
int ret;
RawAIOCB *acb = (RawAIOCB *)blockacb;
- RawAIOCB **pacb;
- ret = aio_cancel(acb->aiocb.aio_fildes, &acb->aiocb);
- if (ret == AIO_NOTCANCELED) {
+ ret = qemu_paio_cancel(acb->aiocb.aio_fildes, &acb->aiocb);
+ if (ret == QEMU_PAIO_NOTCANCELED) {
/* fail safe: if the aio could not be canceled, we wait for
it */
- while (aio_error(&acb->aiocb) == EINPROGRESS);
+ while (qemu_paio_error(&acb->aiocb) == EINPROGRESS);
}
- /* remove the callback from the queue */
- pacb = &first_aio;
- for(;;) {
- if (*pacb == NULL) {
- break;
- } else if (*pacb == acb) {
- *pacb = acb->next;
- qemu_aio_release(acb);
- break;
- }
- pacb = &acb->next;
- }
+ raw_aio_remove(acb);
+}
+#else /* CONFIG_AIO */
+static int posix_aio_init(void)
+{
+ return 0;
}
+#endif /* CONFIG_AIO */
+
static void raw_close(BlockDriverState *bs)
{
if (s->fd >= 0) {
close(s->fd);
s->fd = -1;
+ if (s->aligned_buf != NULL)
+ qemu_free(s->aligned_buf);
}
}
return 0;
}
+#ifdef __OpenBSD__
+static int64_t raw_getlength(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ int fd = s->fd;
+ struct stat st;
+
+ if (fstat(fd, &st))
+ return -1;
+ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
+ struct disklabel dl;
+
+ if (ioctl(fd, DIOCGDINFO, &dl))
+ return -1;
+ return (uint64_t)dl.d_secsize *
+ dl.d_partitions[DISKPART(st.st_rdev)].p_size;
+ } else
+ return st.st_size;
+}
+#else /* !__OpenBSD__ */
static int64_t raw_getlength(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
}
return size;
}
+#endif
static int raw_create(const char *filename, int64_t total_size,
const char *backing_file, int flags)
raw_create,
raw_flush,
+#ifdef CONFIG_AIO
.bdrv_aio_read = raw_aio_read,
.bdrv_aio_write = raw_aio_write,
.bdrv_aio_cancel = raw_aio_cancel,
.aiocb_size = sizeof(RawAIOCB),
- .protocol_name = "file",
+#endif
+
.bdrv_pread = raw_pread,
.bdrv_pwrite = raw_pwrite,
.bdrv_truncate = raw_truncate,
BDRVRawState *s = bs->opaque;
int fd, open_flags, ret;
+ posix_aio_init();
+
#ifdef CONFIG_COCOA
if (strstart(filename, "/dev/cdrom", NULL)) {
kern_return_t kernResult;
open_flags |= O_RDONLY;
bs->read_only = 1;
}
-#ifdef O_DIRECT
- if (flags & BDRV_O_DIRECT)
+ /* Use O_DSYNC for write-through caching, no flags for write-back caching,
+ * and O_DIRECT for no caching. */
+ if ((flags & BDRV_O_NOCACHE))
open_flags |= O_DIRECT;
-#endif
+ else if (!(flags & BDRV_O_CACHE_WB))
+ open_flags |= O_DSYNC;
s->type = FTYPE_FILE;
#if defined(__linux__)
s->fd_open_flags = open_flags;
/* open will not fail even if no floppy is inserted */
open_flags |= O_NONBLOCK;
+ } else if (strstart(filename, "/dev/sg", NULL)) {
+ bs->sg = 1;
}
#endif
fd = open(filename, open_flags, 0644);
return 0;
}
-#if defined(__linux__) && !defined(QEMU_IMG)
-
+#if defined(__linux__)
/* 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. */
s->fd_got_error = 0;
return 0;
}
-#else
-static int fd_open(BlockDriverState *bs)
-{
- return 0;
-}
-#endif
-
-#if defined(__linux__)
static int raw_is_inserted(BlockDriverState *bs)
{
return 0;
}
+static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+ BDRVRawState *s = bs->opaque;
+
+ return ioctl(s->fd, req, buf);
+}
#else
+static int fd_open(BlockDriverState *bs)
+{
+ return 0;
+}
+
static int raw_is_inserted(BlockDriverState *bs)
{
return 1;
return -ENOTSUP;
}
+static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+ return -ENOTSUP;
+}
#endif /* !linux */
BlockDriver bdrv_host_device = {
NULL,
raw_flush,
+#ifdef CONFIG_AIO
.bdrv_aio_read = raw_aio_read,
.bdrv_aio_write = raw_aio_write,
.bdrv_aio_cancel = raw_aio_cancel,
.aiocb_size = sizeof(RawAIOCB),
+#endif
+
.bdrv_pread = raw_pread,
.bdrv_pwrite = raw_pwrite,
.bdrv_getlength = raw_getlength,
.bdrv_media_changed = raw_media_changed,
.bdrv_eject = raw_eject,
.bdrv_set_locked = raw_set_locked,
+ /* generic scsi device */
+ .bdrv_ioctl = raw_ioctl,
};