예제 #1
0
파일: diskio.c 프로젝트: gdahlm/sanlock
static struct aicb *find_callback_slot(struct task *task)
{
	struct timespec ts;
	struct io_event event;
	int cleared = 0;
	int rv;
	int i;

 find:
	for (i = 0; i < task->cb_size; i++) {
		if (task->callbacks[i].used)
			continue;
		return &task->callbacks[i];
	}

	if (cleared++)
		return NULL;

	memset(&ts, 0, sizeof(struct timespec));
	ts.tv_sec = task->io_timeout_seconds;
 retry:
	memset(&event, 0, sizeof(event));

	rv = io_getevents(task->aio_ctx, 1, 1, &event, &ts);
	if (rv == -EINTR)
		goto retry;
	if (rv < 0)
		return NULL;
	if (rv == 1) {
		struct iocb *ev_iocb = event.obj;
		struct aicb *ev_aicb = container_of(ev_iocb, struct aicb, iocb);

		log_taske(task, "aio collect %p:%p:%p result %ld:%ld old free",
			  ev_aicb, ev_iocb, ev_aicb->buf, event.res, event.res2);
		ev_aicb->used = 0;
		free(ev_aicb->buf);
		ev_aicb->buf = NULL;
		goto find;
	}
	return NULL;
}
예제 #2
0
파일: diskio.c 프로젝트: gdahlm/sanlock
int read_iobuf_reap(int fd, uint64_t offset, char *iobuf, int iobuf_len, struct task *task)
{
	struct timespec ts;
	struct aicb *aicb;
	struct iocb *iocb;
	struct io_event event;
	int rv;

	aicb = task->read_iobuf_timeout_aicb;
	iocb = &aicb->iocb;

	if (!aicb->used)
		return -EINVAL;
	if (iocb->aio_fildes != fd)
		return -EINVAL;
	if (iocb->u.c.buf != iobuf)
		return -EINVAL;
	if (iocb->u.c.nbytes != iobuf_len)
		return -EINVAL;
	if (iocb->u.c.offset != offset)
		return -EINVAL;
	if (iocb->aio_lio_opcode != IO_CMD_PREAD)
		return -EINVAL;

	memset(&ts, 0, sizeof(struct timespec));
	ts.tv_nsec = 500000000; /* half a second */
 retry:
	memset(&event, 0, sizeof(event));

	rv = io_getevents(task->aio_ctx, 1, 1, &event, &ts);
	if (rv == -EINTR)
		goto retry;
	if (rv < 0) {
		log_taske(task, "aio getevent %p:%p:%p rv %d r",
			  aicb, iocb, iobuf, rv);
		goto out;
	}
	if (rv == 1) {
		struct iocb *ev_iocb = event.obj;
		struct aicb *ev_aicb = container_of(ev_iocb, struct aicb, iocb);

		ev_aicb->used = 0;

		if (ev_iocb != iocb) {
			log_taske(task, "aio collect %p:%p:%p result %ld:%ld other free r",
				  ev_aicb, ev_iocb, ev_aicb->buf, event.res, event.res2);
			free(ev_aicb->buf);
			ev_aicb->buf = NULL;
			goto retry;
		}
		if ((int)event.res < 0) {
			log_taske(task, "aio collect %p:%p:%p result %ld:%ld match res r",
				  ev_aicb, ev_iocb, ev_aicb->buf, event.res, event.res2);
			rv = event.res;
			goto out;
		}
		if (event.res != iobuf_len) {
			log_taske(task, "aio collect %p:%p:%p result %ld:%ld match len %d r",
				  ev_aicb, ev_iocb, ev_aicb->buf, event.res, event.res2, iobuf_len);
			rv = -EMSGSIZE;
			goto out;
		}

		log_taske(task, "aio collect %p:%p:%p result %ld:%ld match reap",
			  ev_aicb, ev_iocb, ev_aicb->buf, event.res, event.res2);

		rv = 0;
		goto out;
	}

	/* timed out again */
	rv = SANLK_AIO_TIMEOUT;
 out:
	return rv;
}
예제 #3
0
파일: diskio.c 프로젝트: gdahlm/sanlock
static int do_linux_aio(int fd, uint64_t offset, char *buf, int len,
			struct task *task, int cmd)
{
	struct timespec ts;
	struct aicb *aicb;
	struct iocb *iocb;
	struct io_event event;
	int rv;

	/* I expect this pre-emptively catches the io_submit EAGAIN case */

	aicb = find_callback_slot(task);
	if (!aicb)
		return -ENOENT;

	iocb = &aicb->iocb;

	memset(iocb, 0, sizeof(struct iocb));
	iocb->aio_fildes = fd;
	iocb->aio_lio_opcode = cmd;
	iocb->u.c.buf = buf;
	iocb->u.c.nbytes = len;
	iocb->u.c.offset = offset;

	rv = io_submit(task->aio_ctx, 1, &iocb);
	if (rv < 0) {
		log_taske(task, "aio submit %p:%p:%p rv %d fd %d cmd %d",
			  aicb, iocb, buf, rv, fd, cmd);
		goto out;
	}

	task->io_count++;

	/* don't reuse aicb->iocb or free the buf until we reap the event */
	aicb->used = 1;
	aicb->buf = buf;

	memset(&ts, 0, sizeof(struct timespec));
	ts.tv_sec = task->io_timeout_seconds;
 retry:
	memset(&event, 0, sizeof(event));

	rv = io_getevents(task->aio_ctx, 1, 1, &event, &ts);
	if (rv == -EINTR)
		goto retry;
	if (rv < 0) {
		log_taske(task, "aio getevent %p:%p:%p rv %d",
			  aicb, iocb, buf, rv);
		goto out;
	}
	if (rv == 1) {
		struct iocb *ev_iocb = event.obj;
		struct aicb *ev_aicb = container_of(ev_iocb, struct aicb, iocb);

		ev_aicb->used = 0;

		if (ev_iocb != iocb) {
			log_taske(task, "aio collect %p:%p:%p result %ld:%ld other free",
				  ev_aicb, ev_iocb, ev_aicb->buf, event.res, event.res2);
			free(ev_aicb->buf);
			ev_aicb->buf = NULL;
			goto retry;
		}
		if ((int)event.res < 0) {
			log_taske(task, "aio collect %p:%p:%p result %ld:%ld match res",
				  ev_aicb, ev_iocb, ev_aicb->buf, event.res, event.res2);
			rv = event.res;
			goto out;
		}
		if (event.res != len) {
			log_taske(task, "aio collect %p:%p:%p result %ld:%ld match len %d",
				  ev_aicb, ev_iocb, ev_aicb->buf, event.res, event.res2, len);
			rv = -EMSGSIZE;
			goto out;
		}

		/* standard success case */
		rv = 0;
		goto out;
	}

	/* Timed out waiting for result.  If cancel fails, we could try retry
	   io_getevents indefinately, but that removes the whole point of using
	   aio, which is the timeout.  So, we need to be prepared to reap the
	   event the next time we call io_getevents for a different i/o.  We
	   can't reuse the iocb for this timed out io until we get an event for
	   it because we need to compare the iocb to event.obj to distinguish
	   events for separate submissions.

	   <phro> dct: io_cancel doesn't work, in general.  you are very
	   likely going to get -EINVAL from that call */

	task->to_count++;

	log_taske(task, "aio timeout %p:%p:%p sec %d to_count %d",
		  aicb, iocb, buf, task->io_timeout_seconds, task->to_count);

	rv = io_cancel(task->aio_ctx, iocb, &event);
	if (!rv) {
		aicb->used = 0;
		rv = -ECANCELED;
	} else {
		/* aicb->used and aicb->buf both remain set */
		rv = SANLK_AIO_TIMEOUT;

		if (cmd == IO_CMD_PREAD)
			task->read_iobuf_timeout_aicb = aicb;
	}
 out:
	return rv;
}
예제 #4
0
void close_task_aio(struct task *task)
{
	struct timespec ts;
	struct io_event event;
	uint64_t last_warn;
	int rv, i, used, warn;

	if (!task->use_aio)
		goto skip_aio;

	memset(&ts, 0, sizeof(struct timespec));
	ts.tv_sec = task->io_timeout_seconds;

	last_warn = time(NULL);

	/* wait for all outstanding aio to complete before
	   destroying aio context, freeing iocb and buffers */

	while (1) {
		warn = 0;

		if (time(NULL) - last_warn >= task->io_timeout_seconds) {
			last_warn = time(NULL);
			warn = 1;
		}

		used = 0;

		for (i = 0; i < task->cb_size; i++) {
			if (!task->callbacks[i].used)
				continue;
			used++;

			if (!warn)
				continue;
			log_taske(task, "close_task_aio %d %p busy",
				  i, &task->callbacks[i]);
		}

		if (!used)
			break;

		memset(&event, 0, sizeof(event));

		rv = io_getevents(task->aio_ctx, 1, 1, &event, &ts);
		if (rv == -EINTR)
			continue;
		if (rv < 0)
			break;
		if (rv == 1) {
			struct iocb *ev_iocb = event.obj;
			struct aicb *ev_aicb = container_of(ev_iocb, struct aicb, iocb);

			if (ev_aicb->buf == task->iobuf)
				task->iobuf = NULL;

			log_taske(task, "aio collect %p:%p:%p result %ld:%ld close free",
				  ev_aicb, ev_iocb, ev_aicb->buf, event.res, event.res2);

			ev_aicb->used = 0;
			free(ev_aicb->buf);
			ev_aicb->buf = NULL;
		}
	}
	io_destroy(task->aio_ctx);

	if (task->iobuf)
		free(task->iobuf);

 skip_aio:
	if (task->callbacks)
		free(task->callbacks);
	task->callbacks = NULL;
}