2019-03-06 19:03:50 +03:00
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/mman.h>
# include <unistd.h>
# include <errno.h>
# include <string.h>
# include "liburing.h"
# include "barrier.h"
2019-05-22 17:59:12 +03:00
static int __io_uring_get_cqe ( struct io_uring * ring ,
struct io_uring_cqe * * cqe_ptr , int wait )
2019-03-06 19:03:50 +03:00
{
struct io_uring_cq * cq = & ring - > cq ;
const unsigned mask = * cq - > kring_mask ;
unsigned head ;
int ret ;
* cqe_ptr = NULL ;
head = * cq - > khead ;
do {
/*
* It ' s necessary to use a read_barrier ( ) before reading
* the CQ tail , since the kernel updates it locklessly . The
* kernel has the matching store barrier for the update . The
* kernel also ensures that previous stores to CQEs are ordered
* with the tail update .
*/
read_barrier ( ) ;
if ( head ! = * cq - > ktail ) {
* cqe_ptr = & cq - > cqes [ head & mask ] ;
break ;
}
if ( ! wait )
break ;
ret = io_uring_enter ( ring - > ring_fd , 0 , 1 ,
IORING_ENTER_GETEVENTS , NULL ) ;
if ( ret < 0 )
return - errno ;
} while ( 1 ) ;
return 0 ;
}
/*
2019-05-22 17:59:12 +03:00
* Return an IO completion , if one is readily available . Returns 0 with
* cqe_ptr filled in on success , - errno on failure .
2019-03-06 19:03:50 +03:00
*/
2019-05-22 17:59:12 +03:00
int io_uring_peek_cqe ( struct io_uring * ring , struct io_uring_cqe * * cqe_ptr )
2019-03-06 19:03:50 +03:00
{
2019-05-22 17:59:12 +03:00
return __io_uring_get_cqe ( ring , cqe_ptr , 0 ) ;
2019-03-06 19:03:50 +03:00
}
/*
2019-05-22 17:59:12 +03:00
* Return an IO completion , waiting for it if necessary . Returns 0 with
* cqe_ptr filled in on success , - errno on failure .
2019-03-06 19:03:50 +03:00
*/
2019-05-22 17:59:12 +03:00
int io_uring_wait_cqe ( struct io_uring * ring , struct io_uring_cqe * * cqe_ptr )
2019-03-06 19:03:50 +03:00
{
2019-05-22 17:59:12 +03:00
return __io_uring_get_cqe ( ring , cqe_ptr , 1 ) ;
2019-03-06 19:03:50 +03:00
}
/*
* Submit sqes acquired from io_uring_get_sqe ( ) to the kernel .
*
* Returns number of sqes submitted
*/
int io_uring_submit ( struct io_uring * ring )
{
struct io_uring_sq * sq = & ring - > sq ;
const unsigned mask = * sq - > kring_mask ;
2019-05-22 17:59:12 +03:00
unsigned ktail , ktail_next , submitted , to_submit ;
2019-03-06 19:03:50 +03:00
int ret ;
/*
* If we have pending IO in the kring , submit it first . We need a
* read barrier here to match the kernels store barrier when updating
* the SQ head .
*/
read_barrier ( ) ;
if ( * sq - > khead ! = * sq - > ktail ) {
submitted = * sq - > kring_entries ;
goto submit ;
}
if ( sq - > sqe_head = = sq - > sqe_tail )
return 0 ;
/*
* Fill in sqes that we have queued up , adding them to the kernel ring
*/
submitted = 0 ;
ktail = ktail_next = * sq - > ktail ;
2019-05-22 17:59:12 +03:00
to_submit = sq - > sqe_tail - sq - > sqe_head ;
while ( to_submit - - ) {
2019-03-06 19:03:50 +03:00
ktail_next + + ;
read_barrier ( ) ;
sq - > array [ ktail & mask ] = sq - > sqe_head & mask ;
ktail = ktail_next ;
sq - > sqe_head + + ;
submitted + + ;
}
if ( ! submitted )
return 0 ;
if ( * sq - > ktail ! = ktail ) {
/*
* First write barrier ensures that the SQE stores are updated
* with the tail update . This is needed so that the kernel
* will never see a tail update without the preceeding sQE
* stores being done .
*/
write_barrier ( ) ;
* sq - > ktail = ktail ;
/*
* The kernel has the matching read barrier for reading the
* SQ tail .
*/
write_barrier ( ) ;
}
submit :
ret = io_uring_enter ( ring - > ring_fd , submitted , 0 ,
IORING_ENTER_GETEVENTS , NULL ) ;
if ( ret < 0 )
return - errno ;
2019-05-22 17:59:12 +03:00
return ret ;
2019-03-06 19:03:50 +03:00
}
/*
* Return an sqe to fill . Application must later call io_uring_submit ( )
* when it ' s ready to tell the kernel about it . The caller may call this
* function multiple times before calling io_uring_submit ( ) .
*
* Returns a vacant sqe , or NULL if we ' re full .
*/
struct io_uring_sqe * io_uring_get_sqe ( struct io_uring * ring )
{
struct io_uring_sq * sq = & ring - > sq ;
unsigned next = sq - > sqe_tail + 1 ;
struct io_uring_sqe * sqe ;
/*
* All sqes are used
*/
if ( next - sq - > sqe_head > * sq - > kring_entries )
return NULL ;
sqe = & sq - > sqes [ sq - > sqe_tail & * sq - > kring_mask ] ;
sq - > sqe_tail = next ;
return sqe ;
}