|
Packit |
d3489f |
/* SPDX-License-Identifier: MIT */
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* gcc -Wall -O2 -D_GNU_SOURCE -o ucontext-cp ucontext-cp.c -luring
|
|
Packit |
d3489f |
*/
|
|
Packit |
d3489f |
#define _POSIX_C_SOURCE 199309L
|
|
Packit |
d3489f |
#include <stdio.h>
|
|
Packit |
d3489f |
#include <fcntl.h>
|
|
Packit |
d3489f |
#include <string.h>
|
|
Packit |
d3489f |
#include <stdlib.h>
|
|
Packit |
d3489f |
#include <unistd.h>
|
|
Packit |
d3489f |
#include <assert.h>
|
|
Packit |
d3489f |
#include <errno.h>
|
|
Packit |
d3489f |
#include <ucontext.h>
|
|
Packit |
d3489f |
#include <signal.h>
|
|
Packit |
d3489f |
#include <inttypes.h>
|
|
Packit |
d3489f |
#include <sys/types.h>
|
|
Packit |
d3489f |
#include <sys/ioctl.h>
|
|
Packit |
d3489f |
#include <sys/timerfd.h>
|
|
Packit |
d3489f |
#include <sys/poll.h>
|
|
Packit |
d3489f |
#include "liburing.h"
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
#define QD 64
|
|
Packit |
d3489f |
#define BS 1024
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
#ifndef SIGSTKSZ
|
|
Packit |
d3489f |
#define SIGSTKSZ 8192
|
|
Packit |
d3489f |
#endif
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
typedef struct {
|
|
Packit |
d3489f |
struct io_uring *ring;
|
|
Packit |
d3489f |
unsigned char stack_buf[SIGSTKSZ];
|
|
Packit |
d3489f |
ucontext_t ctx_main, ctx_fnew;
|
|
Packit |
d3489f |
} async_context;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
typedef struct {
|
|
Packit |
d3489f |
async_context *pctx;
|
|
Packit |
d3489f |
int *psuccess;
|
|
Packit |
d3489f |
int *pfailure;
|
|
Packit |
d3489f |
int infd;
|
|
Packit |
d3489f |
int outfd;
|
|
Packit |
d3489f |
} arguments_bundle;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
#define DEFINE_AWAIT_OP(operation) \
|
|
Packit |
d3489f |
static ssize_t await_##operation( \
|
|
Packit |
d3489f |
async_context *pctx, \
|
|
Packit |
d3489f |
int fd, \
|
|
Packit |
d3489f |
const struct iovec *ioves, \
|
|
Packit |
d3489f |
unsigned int nr_vecs, \
|
|
Packit |
d3489f |
off_t offset) \
|
|
Packit |
d3489f |
{ \
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe = io_uring_get_sqe(pctx->ring); \
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe; \
|
|
Packit |
d3489f |
\
|
|
Packit |
d3489f |
if (!sqe) \
|
|
Packit |
d3489f |
return -1; \
|
|
Packit |
d3489f |
\
|
|
Packit |
d3489f |
io_uring_prep_##operation(sqe, fd, ioves, nr_vecs, offset); \
|
|
Packit |
d3489f |
io_uring_sqe_set_data(sqe, pctx); \
|
|
Packit |
d3489f |
swapcontext(&pctx->ctx_fnew, &pctx->ctx_main); \
|
|
Packit |
d3489f |
io_uring_peek_cqe(pctx->ring, &cqe); \
|
|
Packit |
d3489f |
assert(cqe); \
|
|
Packit |
d3489f |
io_uring_cqe_seen(pctx->ring, cqe); \
|
|
Packit |
d3489f |
\
|
|
Packit |
d3489f |
return cqe->res; \
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
DEFINE_AWAIT_OP(readv)
|
|
Packit |
d3489f |
DEFINE_AWAIT_OP(writev)
|
|
Packit |
d3489f |
#undef DEFINE_AWAIT_OP
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int await_poll(async_context *pctx, int fd, short poll_mask) {
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe = io_uring_get_sqe(pctx->ring);
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
if (!sqe)
|
|
Packit |
d3489f |
return -1;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_uring_prep_poll_add(sqe, fd, poll_mask);
|
|
Packit |
d3489f |
io_uring_sqe_set_data(sqe, pctx);
|
|
Packit |
d3489f |
swapcontext(&pctx->ctx_fnew, &pctx->ctx_main);
|
|
Packit |
d3489f |
io_uring_peek_cqe(pctx->ring, &cqe);
|
|
Packit |
d3489f |
assert(cqe);
|
|
Packit |
d3489f |
io_uring_cqe_seen(pctx->ring, cqe);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return cqe->res;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int await_delay(async_context *pctx, time_t seconds) {
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe = io_uring_get_sqe(pctx->ring);
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
struct __kernel_timespec ts = {
|
|
Packit |
d3489f |
.tv_sec = seconds,
|
|
Packit |
d3489f |
.tv_nsec = 0
|
|
Packit |
d3489f |
};
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (!sqe)
|
|
Packit |
d3489f |
return -1;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
|
Packit |
d3489f |
io_uring_sqe_set_data(sqe, pctx);
|
|
Packit |
d3489f |
swapcontext(&pctx->ctx_fnew, &pctx->ctx_main);
|
|
Packit |
d3489f |
io_uring_peek_cqe(pctx->ring, &cqe);
|
|
Packit |
d3489f |
assert(cqe);
|
|
Packit |
d3489f |
io_uring_cqe_seen(pctx->ring, cqe);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int setup_context(async_context *pctx, struct io_uring *ring)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
pctx->ring = ring;
|
|
Packit |
d3489f |
ret = getcontext(&pctx->ctx_fnew);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
perror("getcontext");
|
|
Packit |
d3489f |
return -1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
pctx->ctx_fnew.uc_stack.ss_sp = &pctx->stack_buf;
|
|
Packit |
d3489f |
pctx->ctx_fnew.uc_stack.ss_size = sizeof(pctx->stack_buf);
|
|
Packit |
d3489f |
pctx->ctx_fnew.uc_link = &pctx->ctx_main;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int copy_file(async_context *pctx, int infd, int outfd, struct iovec* piov)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
off_t offset = 0;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (;;) {
|
|
Packit |
d3489f |
ssize_t bytes_read;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
printf("%d->%d: readv %ld bytes from %ld\n", infd, outfd, (long) piov->iov_len, (long) offset);
|
|
Packit |
d3489f |
if ((bytes_read = await_readv(pctx, infd, piov, 1, offset)) < 0) {
|
|
Packit |
d3489f |
perror("await_readv");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
if (bytes_read == 0)
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
piov->iov_len = bytes_read;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
printf("%d->%d: writev %ld bytes from %ld\n", infd, outfd, (long) piov->iov_len, (long) offset);
|
|
Packit |
d3489f |
if (await_writev(pctx, outfd, piov, 1, offset) != bytes_read) {
|
|
Packit |
d3489f |
perror("await_writev");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
if (bytes_read < BS)
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
offset += bytes_read;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
printf("%d->%d: wait %ds\n", infd, outfd, 1);
|
|
Packit |
d3489f |
await_delay(pctx, 1);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static void copy_file_wrapper(arguments_bundle *pbundle)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct iovec iov = {
|
|
Packit |
d3489f |
.iov_base = malloc(BS),
|
|
Packit |
d3489f |
.iov_len = BS,
|
|
Packit |
d3489f |
};
|
|
Packit |
d3489f |
async_context *pctx = pbundle->pctx;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int ret = copy_file(pctx, pbundle->infd, pbundle->outfd, &iov;;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
printf("%d->%d: done with ret code %d\n", pbundle->infd, pbundle->outfd, ret);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (ret == 0) {
|
|
Packit |
d3489f |
++*pbundle->psuccess;
|
|
Packit |
d3489f |
} else {
|
|
Packit |
d3489f |
++*pbundle->pfailure;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
free(iov.iov_base);
|
|
Packit |
d3489f |
close(pbundle->infd);
|
|
Packit |
d3489f |
close(pbundle->outfd);
|
|
Packit |
d3489f |
free(pbundle->pctx);
|
|
Packit |
d3489f |
free(pbundle);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
swapcontext(&pctx->ctx_fnew, &pctx->ctx_main);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int main(int argc, char *argv[])
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring ring;
|
|
Packit |
d3489f |
int i, req_count, ret;
|
|
Packit |
d3489f |
int success = 0, failure = 0;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (argc < 3) {
|
|
Packit |
d3489f |
fprintf(stderr, "%s: infile1 outfile1 [infile2 outfile2 [...]]\n", argv[0]);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_queue_init(QD, &ring, 0);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
fprintf(stderr, "queue_init: %s\n", strerror(-ret));
|
|
Packit |
d3489f |
return -1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
req_count = (argc - 1) / 2;
|
|
Packit |
d3489f |
printf("copying %d files...\n", req_count);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 1; i < argc; i += 2) {
|
|
Packit |
d3489f |
int infd, outfd;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
async_context *pctx = malloc(sizeof(*pctx));
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (!pctx || setup_context(pctx, &ring))
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
infd = open(argv[i], O_RDONLY);
|
|
Packit |
d3489f |
if (infd < 0) {
|
|
Packit |
d3489f |
perror("open infile");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
outfd = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
Packit |
d3489f |
if (outfd < 0) {
|
|
Packit |
d3489f |
perror("open outfile");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
arguments_bundle *pbundle = malloc(sizeof(*pbundle));
|
|
Packit |
d3489f |
pbundle->pctx = pctx;
|
|
Packit |
d3489f |
pbundle->psuccess = &succes;;
|
|
Packit |
d3489f |
pbundle->pfailure = &failure;
|
|
Packit |
d3489f |
pbundle->infd = infd;
|
|
Packit |
d3489f |
pbundle->outfd = outfd;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
makecontext(&pctx->ctx_fnew, (void (*)(void)) copy_file_wrapper, 1, pbundle);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (swapcontext(&pctx->ctx_main, &pctx->ctx_fnew)) {
|
|
Packit |
d3489f |
perror("swapcontext");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* event loop */
|
|
Packit |
d3489f |
while (success + failure < req_count) {
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
/* usually be timed waiting */
|
|
Packit |
d3489f |
ret = io_uring_submit_and_wait(&ring, 1);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
fprintf(stderr, "submit_and_wait: %s\n", strerror(-ret));
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_wait_cqe(&ring, &cqe);
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
fprintf(stderr, "wait_cqe: %s\n", strerror(-ret));
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
async_context *pctx = io_uring_cqe_get_data(cqe);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (swapcontext(&pctx->ctx_main, &pctx->ctx_fnew)) {
|
|
Packit |
d3489f |
perror("swapcontext");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_uring_queue_exit(&ring);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
printf("finished with %d success(es) and %d failure(s)\n", success, failure);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return failure > 0;
|
|
Packit |
d3489f |
}
|