reopen it to see if the disk has been changed */
#define FD_OPEN_TIMEOUT 1000
+/* posix-aio doesn't allow multiple outstanding requests to a single file
+ * descriptor. we implement a pool of dup()'d file descriptors to work
+ * around this */
+#define RAW_FD_POOL_SIZE 64
+
typedef struct BDRVRawState {
int fd;
int type;
unsigned int lseek_err_cnt;
+ int fd_pool[RAW_FD_POOL_SIZE];
#if defined(__linux__)
/* linux floppy specific */
int fd_open_flags;
#endif
} 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;
+ int i;
+
+ posix_aio_init();
s->lseek_err_cnt = 0;
return ret;
}
s->fd = fd;
+ for (i = 0; i < RAW_FD_POOL_SIZE; i++)
+ s->fd_pool[i] = -1;
#if defined(O_DIRECT)
s->aligned_buf = NULL;
if (flags & BDRV_O_DIRECT) {
typedef struct RawAIOCB {
BlockDriverAIOCB common;
+ int fd;
struct aiocb aiocb;
struct RawAIOCB *next;
int ret;
} RawAIOCB;
-static int aio_sig_fd = -1;
-static int aio_sig_num = SIGUSR2;
-static RawAIOCB *first_aio; /* AIO issued */
-static int aio_initialized = 0;
+typedef struct PosixAioState
+{
+ int fd;
+ RawAIOCB *first_aio;
+} PosixAioState;
+
+static int raw_fd_pool_get(BDRVRawState *s)
+{
+ int i;
+
+ for (i = 0; i < RAW_FD_POOL_SIZE; i++) {
+ /* already in use */
+ if (s->fd_pool[i] != -1)
+ continue;
+
+ /* try to dup file descriptor */
+ s->fd_pool[i] = dup(s->fd);
+ if (s->fd_pool[i] != -1)
+ return s->fd_pool[i];
+ }
+
+ /* we couldn't dup the file descriptor so just use the main one */
+ return s->fd;
+}
-static void qemu_aio_poll(void *opaque)
+static void raw_fd_pool_put(RawAIOCB *acb)
{
+ BDRVRawState *s = acb->common.bs->opaque;
+ int i;
+
+ for (i = 0; i < RAW_FD_POOL_SIZE; i++) {
+ if (s->fd_pool[i] == acb->fd) {
+ close(s->fd_pool[i]);
+ s->fd_pool[i] = -1;
+ }
+ }
+}
+
+static void posix_aio_read(void *opaque)
+{
+ PosixAioState *s = opaque;
RawAIOCB *acb, **pacb;
int ret;
size_t offset;
while (offset < 128) {
ssize_t len;
- len = read(aio_sig_fd, sig.buf + offset, 128 - offset);
+ len = read(s->fd, sig.buf + offset, 128 - offset);
if (len == -1 && errno == EINTR)
continue;
if (len == -1 && errno == EAGAIN) {
}
for(;;) {
- pacb = &first_aio;
+ pacb = &s->first_aio;
for(;;) {
acb = *pacb;
if (!acb)
if (ret == ECANCELED) {
/* remove the request */
*pacb = acb->next;
+ raw_fd_pool_put(acb);
qemu_aio_release(acb);
} else if (ret != EINPROGRESS) {
/* end of aio */
*pacb = acb->next;
/* call the callback */
acb->common.cb(acb->common.opaque, ret);
+ raw_fd_pool_put(acb);
qemu_aio_release(acb);
break;
} else {
the_end: ;
}
-void qemu_aio_init(void)
+static int posix_aio_flush(void *opaque)
+{
+ PosixAioState *s = opaque;
+ return !!s->first_aio;
+}
+
+static PosixAioState *posix_aio_state;
+
+static int posix_aio_init(void)
{
sigset_t mask;
+ PosixAioState *s;
+ struct aioinit ai;
+
+ if (posix_aio_state)
+ return 0;
- aio_initialized = 1;
+ s = qemu_malloc(sizeof(PosixAioState));
+ if (s == NULL)
+ return -ENOMEM;
/* Make sure to block AIO signal */
sigemptyset(&mask);
- sigaddset(&mask, aio_sig_num);
+ sigaddset(&mask, SIGUSR2);
sigprocmask(SIG_BLOCK, &mask, NULL);
- aio_sig_fd = qemu_signalfd(&mask);
+ s->first_aio = NULL;
+ s->fd = qemu_signalfd(&mask);
- fcntl(aio_sig_fd, F_SETFL, O_NONBLOCK);
+ fcntl(s->fd, F_SETFL, O_NONBLOCK);
- qemu_set_fd_handler2(aio_sig_fd, NULL, qemu_aio_poll, NULL, NULL);
+ qemu_aio_set_fd_handler(s->fd, posix_aio_read, NULL, posix_aio_flush, s);
-#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);
- }
+ memset(&ai, 0, sizeof(ai));
+#if !defined(__linux__) || (defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 4))
+ ai.aio_threads = 5;
+ ai.aio_num = 1;
+#else
+ /* XXX: aio thread exit seems to hang on RedHat 9 and this init
+ seems to fix the problem. */
+ ai.aio_threads = 1;
+ ai.aio_num = 1;
+ ai.aio_idle_time = 365 * 100000;
#endif
-}
-
-/* Wait for all IO requests to complete. */
-void qemu_aio_flush(void)
-{
- qemu_aio_poll(NULL);
- while (first_aio) {
- qemu_aio_wait();
- }
-}
-
-void qemu_aio_wait(void)
-{
- int ret;
-
- if (qemu_bh_poll())
- return;
-
- if (!first_aio)
- return;
+ aio_init(&ai);
+ posix_aio_state = s;
- do {
- fd_set rdfds;
-
- FD_ZERO(&rdfds);
- FD_SET(aio_sig_fd, &rdfds);
-
- ret = select(aio_sig_fd + 1, &rdfds, NULL, NULL, NULL);
- if (ret == -1 && errno == EINTR)
- continue;
- } while (ret == 0);
-
- qemu_aio_poll(NULL);
+ return 0;
}
static RawAIOCB *raw_aio_setup(BlockDriverState *bs,
acb = qemu_aio_get(bs, cb, opaque);
if (!acb)
return NULL;
- acb->aiocb.aio_fildes = s->fd;
- acb->aiocb.aio_sigevent.sigev_signo = aio_sig_num;
+ acb->fd = raw_fd_pool_get(s);
+ acb->aiocb.aio_fildes = acb->fd;
+ acb->aiocb.aio_sigevent.sigev_signo = SIGUSR2;
acb->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
acb->aiocb.aio_buf = buf;
if (nb_sectors < 0)
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;
}
}
/* remove the callback from the queue */
- pacb = &first_aio;
+ pacb = &posix_aio_state->first_aio;
for(;;) {
if (*pacb == NULL) {
break;
} else if (*pacb == acb) {
*pacb = acb->next;
+ raw_fd_pool_put(acb);
qemu_aio_release(acb);
break;
}
}
}
-# else /* CONFIG_AIO */
-
-void qemu_aio_init(void)
+#else /* CONFIG_AIO */
+static int posix_aio_init(void)
{
}
+#endif /* CONFIG_AIO */
-void qemu_aio_flush(void)
+static void raw_close_fd_pool(BDRVRawState *s)
{
-}
+ int i;
-void qemu_aio_wait(void)
-{
- qemu_bh_poll();
+ for (i = 0; i < RAW_FD_POOL_SIZE; i++) {
+ if (s->fd_pool[i] != -1) {
+ close(s->fd_pool[i]);
+ s->fd_pool[i] = -1;
+ }
+ }
}
-#endif /* CONFIG_AIO */
-
static void raw_close(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
qemu_free(s->aligned_buf);
#endif
}
+ raw_close_fd_pool(s);
}
static int raw_truncate(BlockDriverState *bs, int64_t offset)
static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVRawState *s = bs->opaque;
- int fd, open_flags, ret;
+ int fd, open_flags, ret, i;
+
+ posix_aio_init();
#ifdef CONFIG_COCOA
if (strstart(filename, "/dev/cdrom", NULL)) {
return ret;
}
s->fd = fd;
+ for (i = 0; i < RAW_FD_POOL_SIZE; i++)
+ s->fd_pool[i] = -1;
#if defined(__linux__)
/* close fd so that we can reopen it as needed */
if (s->type == FTYPE_FD) {
}
#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. */
(qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
close(s->fd);
s->fd = -1;
+ raw_close_fd_pool(s);
#ifdef DEBUG_FLOPPY
printf("Floppy closed\n");
#endif
if (s->fd >= 0) {
close(s->fd);
s->fd = -1;
+ raw_close_fd_pool(s);
}
fd = open(bs->filename, s->fd_open_flags | O_NONBLOCK);
if (fd >= 0) {