#include "qemu-common.h"
#include "qemu-error.h"
-#include "block_int.h"
+#include "block/block_int.h"
#include <rbd/librbd.h>
* leading "\".
*/
+/* rbd_aio_discard added in 0.1.2 */
+#if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 2)
+#define LIBRBD_SUPPORTS_DISCARD
+#else
+#undef LIBRBD_SUPPORTS_DISCARD
+#endif
+
#define OBJ_MAX_SIZE (1UL << OBJ_DEFAULT_OBJ_ORDER)
#define RBD_MAX_CONF_NAME_SIZE 128
#define RBD_MAX_SNAP_NAME_SIZE 128
#define RBD_MAX_SNAPS 100
+typedef enum {
+ RBD_AIO_READ,
+ RBD_AIO_WRITE,
+ RBD_AIO_DISCARD
+} RBDAIOCmd;
+
typedef struct RBDAIOCB {
BlockDriverAIOCB common;
QEMUBH *bh;
- int ret;
+ int64_t ret;
QEMUIOVector *qiov;
char *bounce;
- int write;
+ RBDAIOCmd cmd;
int64_t sector_num;
int error;
struct BDRVRBDState *s;
int cancelled;
+ int status;
} RBDAIOCB;
typedef struct RADOSCB {
int done;
int64_t size;
char *buf;
- int ret;
+ int64_t ret;
} RADOSCB;
#define RBD_FD_READ 0
RBDAIOCB *acb = rcb->acb;
int64_t r;
- if (acb->cancelled) {
- qemu_vfree(acb->bounce);
- qemu_aio_release(acb);
- goto done;
- }
-
r = rcb->ret;
- if (acb->write) {
+ if (acb->cmd == RBD_AIO_WRITE ||
+ acb->cmd == RBD_AIO_DISCARD) {
if (r < 0) {
acb->ret = r;
acb->error = 1;
/* Note that acb->bh can be NULL in case where the aio was cancelled */
acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb);
qemu_bh_schedule(acb->bh);
-done:
g_free(rcb);
}
s->snap = g_strdup(snap_buf);
}
+ /*
+ * Fallback to more conservative semantics if setting cache
+ * options fails. Ignore errors from setting rbd_cache because the
+ * only possible error is that the option does not exist, and
+ * librbd defaults to no caching. If write through caching cannot
+ * be set up, fall back to no caching.
+ */
+ if (flags & BDRV_O_NOCACHE) {
+ rados_conf_set(s->cluster, "rbd_cache", "false");
+ } else {
+ rados_conf_set(s->cluster, "rbd_cache", "true");
+ }
+
if (strstr(conf, "conf=") == NULL) {
/* try default location, but ignore failure */
rados_conf_read_file(s->cluster, NULL);
fcntl(s->fds[0], F_SETFL, O_NONBLOCK);
fcntl(s->fds[1], F_SETFL, O_NONBLOCK);
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], qemu_rbd_aio_event_reader,
- NULL, qemu_rbd_aio_flush_cb, NULL, s);
+ NULL, qemu_rbd_aio_flush_cb, s);
return 0;
close(s->fds[0]);
close(s->fds[1]);
- qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL , NULL, NULL, NULL,
- NULL);
+ qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL, NULL, NULL, NULL);
rbd_close(s->image);
rados_ioctx_destroy(s->io_ctx);
{
RBDAIOCB *acb = (RBDAIOCB *) blockacb;
acb->cancelled = 1;
+
+ while (acb->status == -EINPROGRESS) {
+ qemu_aio_wait();
+ }
+
+ qemu_aio_release(acb);
}
-static AIOPool rbd_aio_pool = {
+static const AIOCBInfo rbd_aiocb_info = {
.aiocb_size = sizeof(RBDAIOCB),
.cancel = qemu_rbd_aio_cancel,
};
{
RBDAIOCB *acb = opaque;
- if (!acb->write) {
- qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size);
+ if (acb->cmd == RBD_AIO_READ) {
+ qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
}
qemu_vfree(acb->bounce);
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
qemu_bh_delete(acb->bh);
acb->bh = NULL;
+ acb->status = 0;
- qemu_aio_release(acb);
+ if (!acb->cancelled) {
+ qemu_aio_release(acb);
+ }
}
-static BlockDriverAIOCB *rbd_aio_rw_vector(BlockDriverState *bs,
- int64_t sector_num,
- QEMUIOVector *qiov,
- int nb_sectors,
- BlockDriverCompletionFunc *cb,
- void *opaque, int write)
+static int rbd_aio_discard_wrapper(rbd_image_t image,
+ uint64_t off,
+ uint64_t len,
+ rbd_completion_t comp)
+{
+#ifdef LIBRBD_SUPPORTS_DISCARD
+ return rbd_aio_discard(image, off, len, comp);
+#else
+ return -ENOTSUP;
+#endif
+}
+
+static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque,
+ RBDAIOCmd cmd)
{
RBDAIOCB *acb;
RADOSCB *rcb;
BDRVRBDState *s = bs->opaque;
- acb = qemu_aio_get(&rbd_aio_pool, bs, cb, opaque);
- acb->write = write;
+ acb = qemu_aio_get(&rbd_aiocb_info, bs, cb, opaque);
+ acb->cmd = cmd;
acb->qiov = qiov;
- acb->bounce = qemu_blockalign(bs, qiov->size);
+ if (cmd == RBD_AIO_DISCARD) {
+ acb->bounce = NULL;
+ } else {
+ acb->bounce = qemu_blockalign(bs, qiov->size);
+ }
acb->ret = 0;
acb->error = 0;
acb->s = s;
acb->cancelled = 0;
acb->bh = NULL;
+ acb->status = -EINPROGRESS;
- if (write) {
- qemu_iovec_to_buffer(acb->qiov, acb->bounce);
+ if (cmd == RBD_AIO_WRITE) {
+ qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size);
}
buf = acb->bounce;
goto failed;
}
- if (write) {
+ switch (cmd) {
+ case RBD_AIO_WRITE:
r = rbd_aio_write(s->image, off, size, buf, c);
- } else {
+ break;
+ case RBD_AIO_READ:
r = rbd_aio_read(s->image, off, size, buf, c);
+ break;
+ case RBD_AIO_DISCARD:
+ r = rbd_aio_discard_wrapper(s->image, off, size, c);
+ break;
+ default:
+ r = -EINVAL;
}
if (r < 0) {
BlockDriverCompletionFunc *cb,
void *opaque)
{
- return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
+ return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
+ RBD_AIO_READ);
}
static BlockDriverAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
BlockDriverCompletionFunc *cb,
void *opaque)
{
- return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
+ return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
+ RBD_AIO_WRITE);
}
static int qemu_rbd_co_flush(BlockDriverState *bs)
return 0;
}
+static int qemu_rbd_snap_remove(BlockDriverState *bs,
+ const char *snapshot_name)
+{
+ BDRVRBDState *s = bs->opaque;
+ int r;
+
+ r = rbd_snap_remove(s->image, snapshot_name);
+ return r;
+}
+
+static int qemu_rbd_snap_rollback(BlockDriverState *bs,
+ const char *snapshot_name)
+{
+ BDRVRBDState *s = bs->opaque;
+ int r;
+
+ r = rbd_snap_rollback(s->image, snapshot_name);
+ return r;
+}
+
static int qemu_rbd_snap_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_tab)
{
return snap_count;
}
+#ifdef LIBRBD_SUPPORTS_DISCARD
+static BlockDriverAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs,
+ int64_t sector_num,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ return rbd_start_aio(bs, sector_num, NULL, nb_sectors, cb, opaque,
+ RBD_AIO_DISCARD);
+}
+#endif
+
static QEMUOptionParameter qemu_rbd_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
.bdrv_aio_writev = qemu_rbd_aio_writev,
.bdrv_co_flush_to_disk = qemu_rbd_co_flush,
+#ifdef LIBRBD_SUPPORTS_DISCARD
+ .bdrv_aio_discard = qemu_rbd_aio_discard,
+#endif
+
.bdrv_snapshot_create = qemu_rbd_snap_create,
+ .bdrv_snapshot_delete = qemu_rbd_snap_remove,
.bdrv_snapshot_list = qemu_rbd_snap_list,
+ .bdrv_snapshot_goto = qemu_rbd_snap_rollback,
};
static void bdrv_rbd_init(void)