mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
Mainline kernel support for eventfd(2) in linux aio was added between 2.6.21 and 2.6.22. Libaio after 0.3.107 has the header file, but presently few systems support it. Neither do we rely on an up-to-date libc6. Instead, this patch adds a header which defines custom iocb_common struct, and works around a potentially missing sys/eventfd.h. Signed-off-by: Daniel Stodden <daniel.stodden@citrix.com>
740 lines
15 KiB
C
740 lines
15 KiB
C
/*
|
|
* Copyright (c) 2008, XenSource Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of XenSource Inc. nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <libaio.h>
|
|
#ifdef __linux__
|
|
#include <linux/version.h>
|
|
#endif
|
|
|
|
#include "tapdisk.h"
|
|
#include "tapdisk-log.h"
|
|
#include "tapdisk-queue.h"
|
|
#include "tapdisk-filter.h"
|
|
#include "tapdisk-server.h"
|
|
#include "tapdisk-utils.h"
|
|
|
|
#include "libaio-compat.h"
|
|
#include "atomicio.h"
|
|
|
|
#define WARN(_f, _a...) tlog_write(TLOG_WARN, _f, ##_a)
|
|
#define DBG(_f, _a...) tlog_write(TLOG_DBG, _f, ##_a)
|
|
#define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a)
|
|
|
|
/*
|
|
* We used a kernel patch to return an fd associated with the AIO context
|
|
* so that we can concurrently poll on synchronous and async descriptors.
|
|
* This is signalled by passing 1 as the io context to io_setup.
|
|
*/
|
|
#define REQUEST_ASYNC_FD ((io_context_t)1)
|
|
|
|
static inline void
|
|
queue_tiocb(struct tqueue *queue, struct tiocb *tiocb)
|
|
{
|
|
struct iocb *iocb = &tiocb->iocb;
|
|
|
|
if (queue->queued) {
|
|
struct tiocb *prev = (struct tiocb *)
|
|
queue->iocbs[queue->queued - 1]->data;
|
|
prev->next = tiocb;
|
|
}
|
|
|
|
queue->iocbs[queue->queued++] = iocb;
|
|
}
|
|
|
|
static inline int
|
|
deferred_tiocbs(struct tqueue *queue)
|
|
{
|
|
return (queue->deferred.head != NULL);
|
|
}
|
|
|
|
static inline void
|
|
defer_tiocb(struct tqueue *queue, struct tiocb *tiocb)
|
|
{
|
|
struct tlist *list = &queue->deferred;
|
|
|
|
if (!list->head)
|
|
list->head = list->tail = tiocb;
|
|
else
|
|
list->tail = list->tail->next = tiocb;
|
|
|
|
queue->tiocbs_deferred++;
|
|
queue->deferrals++;
|
|
}
|
|
|
|
static inline void
|
|
queue_deferred_tiocb(struct tqueue *queue)
|
|
{
|
|
struct tlist *list = &queue->deferred;
|
|
|
|
if (list->head) {
|
|
struct tiocb *tiocb = list->head;
|
|
|
|
list->head = tiocb->next;
|
|
if (!list->head)
|
|
list->tail = NULL;
|
|
|
|
queue_tiocb(queue, tiocb);
|
|
queue->tiocbs_deferred--;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
queue_deferred_tiocbs(struct tqueue *queue)
|
|
{
|
|
while (!tapdisk_queue_full(queue) && deferred_tiocbs(queue))
|
|
queue_deferred_tiocb(queue);
|
|
}
|
|
|
|
/*
|
|
* td_complete may queue more tiocbs
|
|
*/
|
|
static void
|
|
complete_tiocb(struct tqueue *queue, struct tiocb *tiocb, unsigned long res)
|
|
{
|
|
int err;
|
|
struct iocb *iocb = &tiocb->iocb;
|
|
|
|
if (res == iocb->u.c.nbytes)
|
|
err = 0;
|
|
else if ((int)res < 0)
|
|
err = (int)res;
|
|
else
|
|
err = -EIO;
|
|
|
|
tiocb->cb(tiocb->arg, tiocb, err);
|
|
}
|
|
|
|
static int
|
|
cancel_tiocbs(struct tqueue *queue, int err)
|
|
{
|
|
int queued;
|
|
struct tiocb *tiocb;
|
|
|
|
if (!queue->queued)
|
|
return 0;
|
|
|
|
/*
|
|
* td_complete may queue more tiocbs, which
|
|
* will overwrite the contents of queue->iocbs.
|
|
* use a private linked list to keep track
|
|
* of the tiocbs we're cancelling.
|
|
*/
|
|
tiocb = queue->iocbs[0]->data;
|
|
queued = queue->queued;
|
|
queue->queued = 0;
|
|
|
|
for (; tiocb != NULL; tiocb = tiocb->next)
|
|
complete_tiocb(queue, tiocb, err);
|
|
|
|
return queued;
|
|
}
|
|
|
|
static int
|
|
fail_tiocbs(struct tqueue *queue, int succeeded, int total, int err)
|
|
{
|
|
ERR(err, "io_submit error: %d of %d failed",
|
|
total - succeeded, total);
|
|
|
|
/* take any non-submitted, merged iocbs
|
|
* off of the queue, split them, and fail them */
|
|
queue->queued = io_expand_iocbs(&queue->opioctx,
|
|
queue->iocbs, succeeded, total);
|
|
|
|
return cancel_tiocbs(queue, err);
|
|
}
|
|
|
|
/*
|
|
* rwio
|
|
*/
|
|
|
|
struct rwio {
|
|
struct io_event *aio_events;
|
|
};
|
|
|
|
static void
|
|
tapdisk_rwio_destroy(struct tqueue *queue)
|
|
{
|
|
struct rwio *rwio = queue->tio_data;
|
|
|
|
if (rwio->aio_events) {
|
|
free(rwio->aio_events);
|
|
rwio->aio_events = NULL;
|
|
}
|
|
}
|
|
|
|
static int
|
|
tapdisk_rwio_setup(struct tqueue *queue, int size)
|
|
{
|
|
struct rwio *rwio = queue->tio_data;
|
|
int err;
|
|
|
|
rwio->aio_events = calloc(size, sizeof(struct io_event));
|
|
if (!rwio->aio_events)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline ssize_t
|
|
tapdisk_rwio_rw(const struct iocb *iocb)
|
|
{
|
|
int fd = iocb->aio_fildes;
|
|
char *buf = iocb->u.c.buf;
|
|
long long off = iocb->u.c.offset;
|
|
size_t size = iocb->u.c.nbytes;
|
|
ssize_t (*func)(int, void *, size_t) =
|
|
(iocb->aio_lio_opcode == IO_CMD_PWRITE ? vwrite : read);
|
|
|
|
if (lseek(fd, off, SEEK_SET) == (off_t)-1)
|
|
return -errno;
|
|
|
|
if (atomicio(func, fd, buf, size) != size)
|
|
return -errno;
|
|
|
|
return size;
|
|
}
|
|
|
|
static int
|
|
tapdisk_rwio_submit(struct tqueue *queue)
|
|
{
|
|
struct rwio *rwio = queue->tio_data;
|
|
int i, merged, split;
|
|
struct iocb *iocb;
|
|
struct tiocb *tiocb;
|
|
struct io_event *ep;
|
|
|
|
if (!queue->queued)
|
|
return 0;
|
|
|
|
tapdisk_filter_iocbs(queue->filter, queue->iocbs, queue->queued);
|
|
merged = io_merge(&queue->opioctx, queue->iocbs, queue->queued);
|
|
|
|
queue->queued = 0;
|
|
|
|
for (i = 0; i < merged; i++) {
|
|
ep = rwio->aio_events + i;
|
|
iocb = queue->iocbs[i];
|
|
ep->obj = iocb;
|
|
ep->res = tapdisk_rwio_rw(iocb);
|
|
}
|
|
|
|
split = io_split(&queue->opioctx, rwio->aio_events, merged);
|
|
tapdisk_filter_events(queue->filter, rwio->aio_events, split);
|
|
|
|
for (i = split, ep = rwio->aio_events; i-- > 0; ep++) {
|
|
iocb = ep->obj;
|
|
tiocb = iocb->data;
|
|
complete_tiocb(queue, tiocb, ep->res);
|
|
}
|
|
|
|
queue_deferred_tiocbs(queue);
|
|
|
|
return split;
|
|
}
|
|
|
|
static const struct tio td_tio_rwio = {
|
|
.name = "rwio",
|
|
.data_size = 0,
|
|
.tio_setup = NULL,
|
|
.tio_destroy = NULL,
|
|
.tio_submit = tapdisk_rwio_submit
|
|
};
|
|
|
|
/*
|
|
* libaio
|
|
*/
|
|
|
|
struct lio {
|
|
io_context_t aio_ctx;
|
|
struct io_event *aio_events;
|
|
|
|
int event_fd;
|
|
int event_id;
|
|
|
|
int flags;
|
|
};
|
|
|
|
#define LIO_FLAG_EVENTFD (1<<0)
|
|
|
|
static int
|
|
tapdisk_lio_check_resfd(void)
|
|
{
|
|
return tapdisk_linux_version() >= KERNEL_VERSION(2, 6, 22);
|
|
}
|
|
|
|
static void
|
|
tapdisk_lio_destroy_aio(struct tqueue *queue)
|
|
{
|
|
struct lio *lio = queue->tio_data;
|
|
|
|
if (lio->event_fd >= 0) {
|
|
close(lio->event_fd);
|
|
lio->event_fd = -1;
|
|
}
|
|
|
|
if (lio->aio_ctx) {
|
|
io_destroy(lio->aio_ctx);
|
|
lio->aio_ctx = 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
__lio_setup_aio_poll(struct tqueue *queue, int qlen)
|
|
{
|
|
struct lio *lio = queue->tio_data;
|
|
int err, fd;
|
|
|
|
lio->aio_ctx = REQUEST_ASYNC_FD;
|
|
|
|
fd = io_setup(qlen, &lio->aio_ctx);
|
|
if (fd < 0) {
|
|
lio->aio_ctx = 0;
|
|
err = -errno;
|
|
|
|
if (err == -EINVAL)
|
|
goto fail_fd;
|
|
|
|
goto fail;
|
|
}
|
|
|
|
lio->event_fd = fd;
|
|
|
|
return 0;
|
|
|
|
fail_fd:
|
|
DPRINTF("Couldn't get fd for AIO poll support. This is probably "
|
|
"because your kernel does not have the aio-poll patch "
|
|
"applied.\n");
|
|
fail:
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
__lio_setup_aio_eventfd(struct tqueue *queue, int qlen)
|
|
{
|
|
struct lio *lio = queue->tio_data;
|
|
int err;
|
|
|
|
err = io_setup(qlen, &lio->aio_ctx);
|
|
if (err < 0) {
|
|
lio->aio_ctx = 0;
|
|
return err;
|
|
}
|
|
|
|
lio->event_fd = tapdisk_sys_eventfd(0);
|
|
if (lio->event_fd < 0)
|
|
return -errno;
|
|
|
|
lio->flags |= LIO_FLAG_EVENTFD;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
tapdisk_lio_setup_aio(struct tqueue *queue, int qlen)
|
|
{
|
|
struct lio *lio = queue->tio_data;
|
|
int err;
|
|
|
|
lio->aio_ctx = 0;
|
|
lio->event_fd = -1;
|
|
|
|
/*
|
|
* prefer the mainline eventfd(2) api, if available.
|
|
* if not, fall back to the poll fd patch.
|
|
*/
|
|
|
|
err = !tapdisk_lio_check_resfd();
|
|
if (!err)
|
|
err = __lio_setup_aio_eventfd(queue, qlen);
|
|
if (err)
|
|
err = __lio_setup_aio_poll(queue, qlen);
|
|
|
|
if (err == -EAGAIN)
|
|
goto fail_rsv;
|
|
fail:
|
|
return err;
|
|
|
|
fail_rsv:
|
|
DPRINTF("Couldn't setup AIO context. If you are trying to "
|
|
"concurrently use a large number of blktap-based disks, you may "
|
|
"need to increase the system-wide aio request limit. "
|
|
"(e.g. 'echo 1048576 > /proc/sys/fs/aio-max-nr')\n");
|
|
goto fail;
|
|
}
|
|
|
|
|
|
static void
|
|
tapdisk_lio_destroy(struct tqueue *queue)
|
|
{
|
|
struct lio *lio = queue->tio_data;
|
|
|
|
if (!lio)
|
|
return;
|
|
|
|
if (lio->event_id >= 0) {
|
|
tapdisk_server_unregister_event(lio->event_id);
|
|
lio->event_id = -1;
|
|
}
|
|
|
|
tapdisk_lio_destroy_aio(queue);
|
|
|
|
if (lio->aio_events) {
|
|
free(lio->aio_events);
|
|
lio->aio_events = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tapdisk_lio_set_eventfd(struct tqueue *queue, int n, struct iocb **iocbs)
|
|
{
|
|
struct lio *lio = queue->tio_data;
|
|
int i;
|
|
|
|
if (lio->flags & LIO_FLAG_EVENTFD)
|
|
for (i = 0; i < n; ++i)
|
|
__io_set_eventfd(iocbs[i], lio->event_fd);
|
|
}
|
|
|
|
static void
|
|
tapdisk_lio_ack_event(struct tqueue *queue)
|
|
{
|
|
struct lio *lio = queue->tio_data;
|
|
uint64_t val;
|
|
|
|
if (lio->flags & LIO_FLAG_EVENTFD)
|
|
read(lio->event_fd, &val, sizeof(val));
|
|
}
|
|
|
|
static void
|
|
tapdisk_lio_event(event_id_t id, char mode, void *private)
|
|
{
|
|
struct tqueue *queue = private;
|
|
struct lio *lio;
|
|
int i, ret, split;
|
|
struct iocb *iocb;
|
|
struct tiocb *tiocb;
|
|
struct io_event *ep;
|
|
|
|
tapdisk_lio_ack_event(queue);
|
|
|
|
lio = queue->tio_data;
|
|
ret = io_getevents(lio->aio_ctx, 0,
|
|
queue->size, lio->aio_events, NULL);
|
|
split = io_split(&queue->opioctx, lio->aio_events, ret);
|
|
tapdisk_filter_events(queue->filter, lio->aio_events, split);
|
|
|
|
DBG("events: %d, tiocbs: %d\n", ret, split);
|
|
|
|
queue->iocbs_pending -= ret;
|
|
queue->tiocbs_pending -= split;
|
|
|
|
for (i = split, ep = lio->aio_events; i-- > 0; ep++) {
|
|
iocb = ep->obj;
|
|
tiocb = iocb->data;
|
|
complete_tiocb(queue, tiocb, ep->res);
|
|
}
|
|
|
|
queue_deferred_tiocbs(queue);
|
|
}
|
|
|
|
static int
|
|
tapdisk_lio_setup(struct tqueue *queue, int qlen)
|
|
{
|
|
struct lio *lio = queue->tio_data;
|
|
size_t sz;
|
|
int err;
|
|
|
|
lio->event_id = -1;
|
|
|
|
err = tapdisk_lio_setup_aio(queue, qlen);
|
|
if (err)
|
|
goto fail;
|
|
|
|
lio->event_id =
|
|
tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
|
|
lio->event_fd, 0,
|
|
tapdisk_lio_event,
|
|
queue);
|
|
err = lio->event_id;
|
|
if (err < 0)
|
|
goto fail;
|
|
|
|
lio->aio_events = calloc(qlen, sizeof(struct io_event));
|
|
if (!lio->aio_events) {
|
|
err = -errno;
|
|
goto fail;
|
|
}
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
tapdisk_lio_destroy(queue);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
tapdisk_lio_submit(struct tqueue *queue)
|
|
{
|
|
struct lio *lio = queue->tio_data;
|
|
int merged, submitted, err = 0;
|
|
|
|
if (!queue->queued)
|
|
return 0;
|
|
|
|
tapdisk_filter_iocbs(queue->filter, queue->iocbs, queue->queued);
|
|
merged = io_merge(&queue->opioctx, queue->iocbs, queue->queued);
|
|
tapdisk_lio_set_eventfd(queue, merged, queue->iocbs);
|
|
submitted = io_submit(lio->aio_ctx, merged, queue->iocbs);
|
|
|
|
DBG("queued: %d, merged: %d, submitted: %d\n",
|
|
queue->queued, merged, submitted);
|
|
|
|
if (submitted < 0) {
|
|
err = submitted;
|
|
submitted = 0;
|
|
} else if (submitted < merged)
|
|
err = -EIO;
|
|
|
|
queue->iocbs_pending += submitted;
|
|
queue->tiocbs_pending += queue->queued;
|
|
queue->queued = 0;
|
|
|
|
if (err)
|
|
queue->tiocbs_pending -=
|
|
fail_tiocbs(queue, submitted, merged, err);
|
|
|
|
return submitted;
|
|
}
|
|
|
|
static const struct tio td_tio_lio = {
|
|
.name = "lio",
|
|
.data_size = sizeof(struct lio),
|
|
.tio_setup = tapdisk_lio_setup,
|
|
.tio_destroy = tapdisk_lio_destroy,
|
|
.tio_submit = tapdisk_lio_submit,
|
|
};
|
|
|
|
static void
|
|
tapdisk_queue_free_io(struct tqueue *queue)
|
|
{
|
|
if (queue->tio) {
|
|
if (queue->tio->tio_destroy)
|
|
queue->tio->tio_destroy(queue);
|
|
queue->tio = NULL;
|
|
}
|
|
|
|
if (queue->tio_data) {
|
|
free(queue->tio_data);
|
|
queue->tio_data = NULL;
|
|
}
|
|
}
|
|
|
|
static int
|
|
tapdisk_queue_init_io(struct tqueue *queue, int drv)
|
|
{
|
|
const struct tio *tio;
|
|
int err;
|
|
|
|
switch (drv) {
|
|
case TIO_DRV_LIO:
|
|
tio = &td_tio_lio;
|
|
break;
|
|
case TIO_DRV_RWIO:
|
|
tio = &td_tio_rwio;
|
|
break;
|
|
default:
|
|
err = -EINVAL;
|
|
goto fail;
|
|
}
|
|
|
|
queue->tio_data = calloc(1, tio->data_size);
|
|
if (!queue->tio_data) {
|
|
PERROR("malloc(%zu)", tio->data_size);
|
|
err = -errno;
|
|
goto fail;
|
|
}
|
|
|
|
queue->tio = tio;
|
|
|
|
if (tio->tio_setup) {
|
|
err = tio->tio_setup(queue, queue->size);
|
|
if (err)
|
|
goto fail;
|
|
}
|
|
|
|
DPRINTF("I/O queue driver: %s\n", tio->name);
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
tapdisk_queue_free_io(queue);
|
|
return err;
|
|
}
|
|
|
|
int
|
|
tapdisk_init_queue(struct tqueue *queue, int size,
|
|
int drv, struct tfilter *filter)
|
|
{
|
|
int i, err;
|
|
|
|
memset(queue, 0, sizeof(struct tqueue));
|
|
|
|
queue->size = size;
|
|
queue->filter = filter;
|
|
|
|
if (!size)
|
|
return 0;
|
|
|
|
err = tapdisk_queue_init_io(queue, drv);
|
|
if (err)
|
|
goto fail;
|
|
|
|
queue->iocbs = calloc(size, sizeof(struct iocb *));
|
|
if (!queue->iocbs) {
|
|
err = -errno;
|
|
goto fail;
|
|
}
|
|
|
|
err = opio_init(&queue->opioctx, size);
|
|
if (err)
|
|
goto fail;
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
tapdisk_free_queue(queue);
|
|
return err;
|
|
}
|
|
|
|
void
|
|
tapdisk_free_queue(struct tqueue *queue)
|
|
{
|
|
tapdisk_queue_free_io(queue);
|
|
|
|
free(queue->iocbs);
|
|
queue->iocbs = NULL;
|
|
|
|
opio_free(&queue->opioctx);
|
|
}
|
|
|
|
void
|
|
tapdisk_debug_queue(struct tqueue *queue)
|
|
{
|
|
struct tiocb *tiocb = queue->deferred.head;
|
|
|
|
WARN("TAPDISK QUEUE:\n");
|
|
WARN("size: %d, tio: %s, queued: %d, iocbs_pending: %d, "
|
|
"tiocbs_pending: %d, tiocbs_deferred: %d, deferrals: %"PRIx64"\n",
|
|
queue->size, queue->tio->name, queue->queued, queue->iocbs_pending,
|
|
queue->tiocbs_pending, queue->tiocbs_deferred, queue->deferrals);
|
|
|
|
if (tiocb) {
|
|
WARN("deferred:\n");
|
|
for (; tiocb != NULL; tiocb = tiocb->next) {
|
|
struct iocb *io = &tiocb->iocb;
|
|
WARN("%s of %lu bytes at %lld\n",
|
|
(io->aio_lio_opcode == IO_CMD_PWRITE ?
|
|
"write" : "read"),
|
|
io->u.c.nbytes, io->u.c.offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
tapdisk_prep_tiocb(struct tiocb *tiocb, int fd, int rw, char *buf, size_t size,
|
|
long long offset, td_queue_callback_t cb, void *arg)
|
|
{
|
|
struct iocb *iocb = &tiocb->iocb;
|
|
|
|
if (rw)
|
|
io_prep_pwrite(iocb, fd, buf, size, offset);
|
|
else
|
|
io_prep_pread(iocb, fd, buf, size, offset);
|
|
|
|
iocb->data = tiocb;
|
|
tiocb->cb = cb;
|
|
tiocb->arg = arg;
|
|
tiocb->next = NULL;
|
|
}
|
|
|
|
void
|
|
tapdisk_queue_tiocb(struct tqueue *queue, struct tiocb *tiocb)
|
|
{
|
|
if (!tapdisk_queue_full(queue))
|
|
queue_tiocb(queue, tiocb);
|
|
else
|
|
defer_tiocb(queue, tiocb);
|
|
}
|
|
|
|
|
|
/*
|
|
* fail_tiocbs may queue more tiocbs
|
|
*/
|
|
int
|
|
tapdisk_submit_tiocbs(struct tqueue *queue)
|
|
{
|
|
return queue->tio->tio_submit(queue);
|
|
}
|
|
|
|
int
|
|
tapdisk_submit_all_tiocbs(struct tqueue *queue)
|
|
{
|
|
int submitted = 0;
|
|
|
|
do {
|
|
submitted += tapdisk_submit_tiocbs(queue);
|
|
} while (!tapdisk_queue_empty(queue));
|
|
|
|
return submitted;
|
|
}
|
|
|
|
/*
|
|
* cancel_tiocbs may queue more tiocbs
|
|
*/
|
|
int
|
|
tapdisk_cancel_tiocbs(struct tqueue *queue)
|
|
{
|
|
return cancel_tiocbs(queue, -EIO);
|
|
}
|
|
|
|
int
|
|
tapdisk_cancel_all_tiocbs(struct tqueue *queue)
|
|
{
|
|
int cancelled = 0;
|
|
|
|
do {
|
|
cancelled += tapdisk_cancel_tiocbs(queue);
|
|
} while (!tapdisk_queue_empty(queue));
|
|
|
|
return cancelled;
|
|
}
|