-
Notifications
You must be signed in to change notification settings - Fork 405
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
examples/reg-wait: add registered wait example
Sometimes a bit of code is more useful than a man page. Add a basic example of how to use registered waits, using two indices. One is just a basic timeout wait, the other one uses min_wait_usec as well. Signed-off-by: Jens Axboe <[email protected]>
- Loading branch information
Showing
2 changed files
with
140 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
/* SPDX-License-Identifier: MIT */ | ||
/* | ||
* Sample program that shows how to use registered waits. | ||
* | ||
* (C) 2024 Jens Axboe <[email protected]> | ||
*/ | ||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <assert.h> | ||
#include <liburing.h> | ||
|
||
static unsigned long long mtime_since(const struct timeval *s, const struct timeval *e) | ||
{ | ||
long long sec, usec; | ||
|
||
sec = e->tv_sec - s->tv_sec; | ||
usec = (e->tv_usec - s->tv_usec); | ||
if (sec > 0 && usec < 0) { | ||
sec--; | ||
usec += 1000000; | ||
} | ||
|
||
sec *= 1000; | ||
usec /= 1000; | ||
return sec + usec; | ||
} | ||
|
||
static unsigned long long mtime_since_now(struct timeval *tv) | ||
{ | ||
struct timeval end; | ||
|
||
gettimeofday(&end, NULL); | ||
return mtime_since(tv, &end); | ||
} | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
struct io_uring_reg_wait *reg; | ||
struct io_uring_sqe *sqe; | ||
struct io_uring_cqe *cqe[2]; | ||
struct io_uring ring; | ||
char b1[8], b2[8]; | ||
unsigned long msec; | ||
struct timeval tv; | ||
int ret, fds[2]; | ||
|
||
if (pipe(fds) < 0) { | ||
perror("pipe"); | ||
return 1; | ||
} | ||
|
||
ret = io_uring_queue_init(8, &ring, 0); | ||
if (ret) { | ||
fprintf(stderr, "Queue init: %d\n", ret); | ||
return 1; | ||
} | ||
|
||
/* | ||
* Setup wait region. We'll use 32 here, but 64 is probably a more | ||
* logical value, as it'll pin a page regardless of size. 64 is the | ||
* max value on a 4k page size architecture. | ||
*/ | ||
reg = io_uring_setup_reg_wait(&ring, 32, &ret); | ||
if (!reg) { | ||
if (ret == -EINVAL) { | ||
fprintf(stderr, "Kernel doesn't support registered waits\n"); | ||
return 1; | ||
} | ||
fprintf(stderr, "Registered wait: %d\n", ret); | ||
return 1; | ||
} | ||
|
||
/* | ||
* Setup two distinct wait regions. Index 0 will be a 1 second wait, | ||
* and region 2 is a short wait using min_wait_usec as well. Neither | ||
* of these use a signal mask, but sigmask/sigmask_sz can be set as | ||
* well for that. | ||
*/ | ||
reg[0].ts.tv_sec = 1; | ||
reg[0].ts.tv_nsec = 0; | ||
reg[0].flags = IORING_REG_WAIT_TS; | ||
|
||
reg[1].ts.tv_sec = 0; | ||
reg[1].ts.tv_nsec = 100000000LL; | ||
reg[1].min_wait_usec = 10000; | ||
reg[1].flags = IORING_REG_WAIT_TS; | ||
|
||
/* | ||
* No pending completions. Wait with region 0, which should time | ||
* out after 1 second. | ||
*/ | ||
gettimeofday(&tv, NULL); | ||
ret = io_uring_submit_and_wait_reg(&ring, cqe, 1, 0); | ||
if (ret == -EINVAL) { | ||
fprintf(stderr, "Kernel doesn't support registered waits\n"); | ||
return 1; | ||
} else if (ret != -ETIME) { | ||
fprintf(stderr, "Wait should've timed out... %d\n", ret); | ||
return 1; | ||
} | ||
msec = mtime_since_now(&tv); | ||
if (msec < 900 || msec > 1100) { | ||
fprintf(stderr, "Wait took an unexpected amount of time: %lu\n", | ||
msec); | ||
return 1; | ||
} | ||
|
||
/* | ||
* Now prepare two pipe reads. We'll trigger one completion quickly, | ||
* but the other one will never happen. Use min_wait_usec timeout | ||
* to abort after 10 msec of time, where the overall timeout is | ||
* otherwise 100 msec. Since we're waiting on two events, the min | ||
* timeout ends up aborting us. | ||
*/ | ||
sqe = io_uring_get_sqe(&ring); | ||
io_uring_prep_read(sqe, fds[0], b1, sizeof(b1), 0); | ||
sqe = io_uring_get_sqe(&ring); | ||
io_uring_prep_read(sqe, fds[0], b2, sizeof(b2), 0); | ||
|
||
/* trigger one read */ | ||
write(fds[1], "Hello", 5); | ||
|
||
gettimeofday(&tv, NULL); | ||
ret = io_uring_submit_and_wait_reg(&ring, cqe, 2, 1); | ||
msec = mtime_since_now(&tv); | ||
if (ret != 2) { | ||
fprintf(stderr, "Should have submitted 2: %d\n", ret); | ||
return 1; | ||
} | ||
if (msec < 8 || msec > 12) { | ||
fprintf(stderr, "min_wait_usec should take ~10 msec: %lu\n", msec); | ||
return 1; | ||
} | ||
|
||
return 0; | ||
} |