X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=block-raw-posix.c;h=b35c9d09b2a79e287276e52dd5c799d4feaa5da3;hb=ed6a9b307b006d066beaf875bbd0b5b9164349c5;hp=97dd30af613b326407d6a2a245b6fc3034e39b7d;hpb=828899865fa31f3fc1c2d4c4491a1292ed70c838;p=qemu diff --git a/block-raw-posix.c b/block-raw-posix.c index 97dd30a..b35c9d0 100644 --- a/block-raw-posix.c +++ b/block-raw-posix.c @@ -25,10 +25,9 @@ #include "qemu-timer.h" #include "qemu-char.h" #include "block_int.h" -#include "compatfd.h" #include #ifdef CONFIG_AIO -#include +#include "posix-aio-compat.h" #endif #ifdef CONFIG_COCOA @@ -56,6 +55,7 @@ #ifdef __FreeBSD__ #include #include +#include #endif #ifdef __OpenBSD__ @@ -64,16 +64,31 @@ #include #endif +#ifdef __DragonFly__ +#include +#include +#endif + //#define DEBUG_FLOPPY //#define DEBUG_BLOCK #if defined(DEBUG_BLOCK) -#define DEBUG_BLOCK_PRINT(formatCstr, args...) do { if (loglevel != 0) \ - { fprintf(logfile, formatCstr, ##args); fflush(logfile); } } while (0) +#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 @@ -96,15 +111,22 @@ typedef struct BDRVRawState { int fd_got_error; int fd_media_changed; #endif -#if defined(O_DIRECT) - uint8_t* aligned_buf; +#if defined(__FreeBSD__) + int cd_open_flags; #endif + uint8_t* aligned_buf; } BDRVRawState; static int posix_aio_init(void); static int fd_open(BlockDriverState *bs); +#if defined(__FreeBSD__) +static int cd_open(BlockDriverState *bs); +#endif + +static int raw_is_inserted(BlockDriverState *bs); + static int raw_open(BlockDriverState *bs, const char *filename, int flags) { BDRVRawState *s = bs->opaque; @@ -123,10 +145,13 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) } 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; @@ -138,9 +163,8 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) return ret; } s->fd = fd; -#if defined(O_DIRECT) s->aligned_buf = NULL; - if (flags & BDRV_O_DIRECT) { + if ((flags & BDRV_O_NOCACHE)) { s->aligned_buf = qemu_memalign(512, ALIGNED_BUFFER_SIZE); if (s->aligned_buf == NULL) { ret = -errno; @@ -148,7 +172,6 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) return ret; } } -#endif return 0; } @@ -244,7 +267,7 @@ static int raw_pwrite_aligned(BlockDriverState *bs, int64_t offset, ret = fd_open(bs); if (ret < 0) - return ret; + return -errno; if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) { ++(s->lseek_err_cnt); @@ -254,7 +277,7 @@ static int raw_pwrite_aligned(BlockDriverState *bs, int64_t offset, s->fd, bs->filename, offset, buf, count, bs->total_sectors, errno, strerror(errno)); } - return -1; + return -EIO; } s->lseek_err_cnt = 0; @@ -269,11 +292,10 @@ static int raw_pwrite_aligned(BlockDriverState *bs, int64_t offset, label__raw_write__success: - return ret; + return (ret < 0) ? -errno : ret; } -#if defined(O_DIRECT) /* * offset and count are in bytes and possibly not aligned. For files opened * with O_DIRECT, necessary alignments are ensured before calling @@ -346,6 +368,17 @@ static int raw_pread(BlockDriverState *bs, int64_t offset, return raw_pread_aligned(bs, offset, buf, count) + sum; } +static int raw_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + int ret; + + ret = raw_pread(bs, sector_num * 512, buf, nb_sectors * 512); + if (ret == (nb_sectors * 512)) + ret = 0; + return ret; +} + /* * offset and count are in bytes and possibly not aligned. For files opened * with O_DIRECT, necessary alignments are ensured before calling @@ -424,11 +457,15 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset, return raw_pwrite_aligned(bs, offset, buf, count) + sum; } -#else -#define raw_pread raw_pread_aligned -#define raw_pwrite raw_pwrite_aligned -#endif - +static int raw_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + int ret; + ret = raw_pwrite(bs, sector_num * 512, buf, nb_sectors * 512); + if (ret == (nb_sectors * 512)) + ret = 0; + return ret; +} #ifdef CONFIG_AIO /***********************************************************/ @@ -436,14 +473,14 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset, typedef struct RawAIOCB { BlockDriverAIOCB common; - struct aiocb aiocb; + struct qemu_paiocb aiocb; struct RawAIOCB *next; int ret; } RawAIOCB; typedef struct PosixAioState { - int fd; + int rfd, wfd; RawAIOCB *first_aio; } PosixAioState; @@ -452,29 +489,18 @@ static void posix_aio_read(void *opaque) PosixAioState *s = opaque; RawAIOCB *acb, **pacb; int ret; - size_t offset; - union { - struct qemu_signalfd_siginfo siginfo; - char buf[128]; - } sig; - - /* try to read from signalfd, don't freak out if we can't read anything */ - offset = 0; - while (offset < 128) { - ssize_t len; - - len = read(s->fd, sig.buf + offset, 128 - offset); - if (len == -1 && errno == EINTR) - continue; - if (len == -1 && errno == EAGAIN) { - /* there is no natural reason for this to happen, - * so we'll spin hard until we get everything just - * to be on the safe side. */ - if (offset > 0) - continue; - } + ssize_t len; - offset += 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(;;) { @@ -483,7 +509,7 @@ static void posix_aio_read(void *opaque) 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; @@ -491,7 +517,7 @@ static void posix_aio_read(void *opaque) } 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 @@ -521,42 +547,53 @@ static int posix_aio_flush(void *opaque) static PosixAioState *posix_aio_state; +static void aio_signal_handler(int signum) +{ + if (posix_aio_state) { + char byte = 0; + + write(posix_aio_state->wfd, &byte, sizeof(byte)); + } + + qemu_service_io(); +} + static int posix_aio_init(void) { - sigset_t mask; + struct sigaction act; PosixAioState *s; + int fds[2]; + struct qemu_paioinit ai; if (posix_aio_state) return 0; s = qemu_malloc(sizeof(PosixAioState)); - if (s == NULL) - return -ENOMEM; - - /* Make sure to block AIO signal */ - sigemptyset(&mask); - sigaddset(&mask, SIGUSR2); - sigprocmask(SIG_BLOCK, &mask, 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; - s->fd = qemu_signalfd(&mask); + if (pipe(fds) == -1) { + fprintf(stderr, "failed to create pipe\n"); + return -errno; + } - fcntl(s->fd, F_SETFL, O_NONBLOCK); + s->rfd = fds[0]; + s->wfd = fds[1]; - qemu_aio_set_fd_handler(s->fd, posix_aio_read, NULL, posix_aio_flush, s); + 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); -#if defined(__linux__) && defined(__GLIBC_PREREQ) && !__GLIBC_PREREQ(2, 4) - { - /* 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); - } -#endif posix_aio_state = s; return 0; @@ -576,8 +613,7 @@ static RawAIOCB *raw_aio_setup(BlockDriverState *bs, if (!acb) return NULL; acb->aiocb.aio_fildes = s->fd; - acb->aiocb.aio_sigevent.sigev_signo = SIGUSR2; - acb->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + acb->aiocb.ev_signo = SIGUSR2; acb->aiocb.aio_buf = buf; if (nb_sectors < 0) acb->aiocb.aio_nbytes = -nb_sectors; @@ -596,6 +632,25 @@ static void raw_aio_em_cb(void* opaque) 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) { + fprintf(stderr, "raw_aio_remove: aio request not found!\n"); + break; + } else if (*pacb == acb) { + *pacb = acb->next; + qemu_aio_release(acb); + break; + } + pacb = &(*pacb)->next; + } +} + static BlockDriverAIOCB *raw_aio_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) @@ -606,7 +661,6 @@ static BlockDriverAIOCB *raw_aio_read(BlockDriverState *bs, * If O_DIRECT is used and the buffer is not aligned fall back * to synchronous IO. */ -#if defined(O_DIRECT) BDRVRawState *s = bs->opaque; if (unlikely(s->aligned_buf != NULL && ((uintptr_t) buf % 512))) { @@ -617,13 +671,12 @@ static BlockDriverAIOCB *raw_aio_read(BlockDriverState *bs, qemu_bh_schedule(bh); return &acb->common; } -#endif 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; @@ -639,7 +692,6 @@ static BlockDriverAIOCB *raw_aio_write(BlockDriverState *bs, * If O_DIRECT is used and the buffer is not aligned fall back * to synchronous IO. */ -#if defined(O_DIRECT) BDRVRawState *s = bs->opaque; if (unlikely(s->aligned_buf != NULL && ((uintptr_t) buf % 512))) { @@ -650,13 +702,12 @@ static BlockDriverAIOCB *raw_aio_write(BlockDriverState *bs, qemu_bh_schedule(bh); return &acb->common; } -#endif 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; @@ -666,45 +717,32 @@ static void raw_aio_cancel(BlockDriverAIOCB *blockacb) { 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 = &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; - } + raw_aio_remove(acb); } - #else /* CONFIG_AIO */ static int posix_aio_init(void) { + return 0; } #endif /* CONFIG_AIO */ + static void raw_close(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; if (s->fd >= 0) { close(s->fd); s->fd = -1; -#if defined(O_DIRECT) if (s->aligned_buf != NULL) qemu_free(s->aligned_buf); -#endif } } @@ -743,8 +781,11 @@ static int64_t raw_getlength(BlockDriverState *bs) BDRVRawState *s = bs->opaque; int fd = s->fd; int64_t size; -#ifdef _BSD +#ifdef HOST_BSD struct stat sb; +#ifdef __FreeBSD__ + int reopened = 0; +#endif #endif #ifdef __sun__ struct dk_minfo minfo; @@ -756,16 +797,41 @@ static int64_t raw_getlength(BlockDriverState *bs) if (ret < 0) return ret; -#ifdef _BSD +#ifdef HOST_BSD +#ifdef __FreeBSD__ +again: +#endif if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { #ifdef DIOCGMEDIASIZE if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) +#elif defined(DIOCGPART) + { + struct partinfo pi; + if (ioctl(fd, DIOCGPART, &pi) == 0) + size = pi.media_size; + else + size = 0; + } + if (size == 0) #endif #ifdef CONFIG_COCOA size = LONG_LONG_MAX; #else size = lseek(fd, 0LL, SEEK_END); #endif +#ifdef __FreeBSD__ + 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... */ + if (size <= 0 && !reopened && cd_open(bs) >= 0) { + reopened = 1; + goto again; + } + } +#endif } else #endif #ifdef __sun__ @@ -826,8 +892,9 @@ BlockDriver bdrv_raw = { .bdrv_aio_cancel = raw_aio_cancel, .aiocb_size = sizeof(RawAIOCB), #endif - .bdrv_pread = raw_pread, - .bdrv_pwrite = raw_pwrite, + + .bdrv_read = raw_read, + .bdrv_write = raw_write, .bdrv_truncate = raw_truncate, .bdrv_getlength = raw_getlength, }; @@ -933,10 +1000,12 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) 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__) @@ -953,6 +1022,14 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) bs->sg = 1; } #endif +#if defined(__FreeBSD__) + if (strstart(filename, "/dev/cd", NULL) || + strstart(filename, "/dev/acd", NULL)) { + s->type = FTYPE_CD; + s->cd_open_flags = open_flags; + } +#endif + s->fd = -1; fd = open(filename, open_flags, 0644); if (fd < 0) { ret = -errno; @@ -961,6 +1038,11 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) return ret; } s->fd = fd; +#if defined(__FreeBSD__) + /* make sure the door isnt locked at this time */ + if (s->type == FTYPE_CD) + ioctl (s->fd, CDIOCALLOW); +#endif #if defined(__linux__) /* close fd so that we can reopen it as needed */ if (s->type == FTYPE_FD) { @@ -973,7 +1055,6 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) } #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. */ @@ -1128,7 +1209,136 @@ static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) return ioctl(s->fd, req, buf); } -#else + +static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs, + unsigned long int req, void *buf, + BlockDriverCompletionFunc *cb, void *opaque) +{ + RawAIOCB *acb; + + acb = raw_aio_setup(bs, 0, buf, 0, cb, opaque); + if (!acb) + return NULL; + + acb->aiocb.aio_ioctl_cmd = req; + if (qemu_paio_ioctl(&acb->aiocb) < 0) { + raw_aio_remove(acb); + return NULL; + } + + return &acb->common; +} + +#elif defined(__FreeBSD__) + +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; +} + +static int cd_open(BlockDriverState *bs) +{ +#if defined(__FreeBSD__) + BDRVRawState *s = bs->opaque; + int fd; + + switch(s->type) { + case FTYPE_CD: + /* XXX force reread of possibly changed/newly loaded disc, + * FreeBSD seems to not notice sometimes... */ + if (s->fd >= 0) + close (s->fd); + fd = open(bs->filename, s->cd_open_flags, 0644); + if (fd < 0) { + s->fd = -1; + return -EIO; + } + s->fd = fd; + /* make sure the door isnt locked at this time */ + ioctl (s->fd, CDIOCALLOW); + } +#endif + return 0; +} + +static int raw_is_inserted(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + + switch(s->type) { + case FTYPE_CD: + return (raw_getlength(bs) > 0); + case FTYPE_FD: + /* XXX handle this */ + /* FALLTHRU */ + default: + return 1; + } +} + +static int raw_media_changed(BlockDriverState *bs) +{ + return -ENOTSUP; +} + +static int raw_eject(BlockDriverState *bs, int eject_flag) +{ + BDRVRawState *s = bs->opaque; + + switch(s->type) { + case FTYPE_CD: + if (s->fd < 0) + return -ENOTSUP; + (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"); + } + if (cd_open(bs) < 0) + return -ENOTSUP; + break; + case FTYPE_FD: + /* XXX handle this */ + /* FALLTHRU */ + default: + return -ENOTSUP; + } + return 0; +} + +static int raw_set_locked(BlockDriverState *bs, int locked) +{ + BDRVRawState *s = bs->opaque; + + switch(s->type) { + case FTYPE_CD: + if (s->fd < 0) + return -ENOTSUP; + if (ioctl (s->fd, (locked ? CDIOCPREVENT : CDIOCALLOW)) < 0) { + /* Note: an error can happen if the distribution automatically + mounts the CD-ROM */ + // perror("CDROM_LOCKDOOR"); + } + break; + default: + return -ENOTSUP; + } + return 0; +} + +static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) +{ + return -ENOTSUP; +} +#else /* !linux && !FreeBSD */ static int fd_open(BlockDriverState *bs) { @@ -1159,34 +1369,39 @@ static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) { return -ENOTSUP; } -#endif /* !linux */ + +static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs, + unsigned long int req, void *buf, + BlockDriverCompletionFunc *cb, void *opaque) +{ + return -ENOTSUP; +} +#endif /* !linux && !FreeBSD */ BlockDriver bdrv_host_device = { - "host_device", - sizeof(BDRVRawState), - NULL, /* no probe for protocols */ - hdev_open, - NULL, - NULL, - raw_close, - NULL, - raw_flush, + .format_name = "host_device", + .instance_size = sizeof(BDRVRawState), + .bdrv_open = hdev_open, + .bdrv_close = raw_close, + .bdrv_flush = 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), + .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_read = raw_read, + .bdrv_write = raw_write, + .bdrv_getlength = raw_getlength, /* removable device support */ - .bdrv_is_inserted = raw_is_inserted, - .bdrv_media_changed = raw_media_changed, - .bdrv_eject = raw_eject, - .bdrv_set_locked = raw_set_locked, + .bdrv_is_inserted = raw_is_inserted, + .bdrv_media_changed = raw_media_changed, + .bdrv_eject = raw_eject, + .bdrv_set_locked = raw_set_locked, /* generic scsi device */ - .bdrv_ioctl = raw_ioctl, + .bdrv_ioctl = raw_ioctl, + .bdrv_aio_ioctl = raw_aio_ioctl, };