1
/* Copyright (c) 2013 Nicira, Inc.
3
* Licensed under the Apache License, Version 2.0 (the "License");
4
* you may not use this file except in compliance with the License.
5
* You may obtain a copy of the License at:
7
* http://www.apache.org/licenses/LICENSE-2.0
9
* Unless required by applicable law or agreed to in writing, software
10
* distributed under the License is distributed on an "AS IS" BASIS,
11
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
* See the License for the specific language governing permissions and
13
* limitations under the License.
18
/* This implementation of the async-append.h interface uses the POSIX
19
* asynchronous I/O interface. */
21
#include "async-append.h"
29
#include "ovs-thread.h"
32
/* Maximum number of bytes of buffered data. */
33
enum { BUFFER_SIZE = 65536 };
35
/* Maximum number of aiocbs to use.
37
* aiocbs are big (144 bytes with glibc 2.11 on i386) so we try to allow for a
38
* reasonable number by basing the number we allocate on the amount of buffer
40
enum { MAX_CBS = ROUND_DOWN_POW2(BUFFER_SIZE / sizeof(struct aiocb)) };
41
BUILD_ASSERT_DECL(IS_POW2(MAX_CBS));
47
unsigned int aiocb_head, aiocb_tail;
53
static bool async_append_enabled;
56
async_append_enable(void)
58
assert_single_threaded();
59
forbid_forking("async i/o enabled");
60
async_append_enabled = true;
64
async_append_create(int fd)
66
struct async_append *ap;
68
ap = xmalloc(sizeof *ap);
70
ap->aiocbs = xmalloc(MAX_CBS * sizeof *ap->aiocbs);
71
ap->aiocb_head = ap->aiocb_tail = 0;
72
ap->buffer = xmalloc(BUFFER_SIZE);
73
byteq_init(&ap->byteq, ap->buffer, BUFFER_SIZE);
79
async_append_destroy(struct async_append *ap)
82
async_append_flush(ap);
90
async_append_is_full(const struct async_append *ap)
92
return (ap->aiocb_head - ap->aiocb_tail >= MAX_CBS
93
|| byteq_is_full(&ap->byteq));
97
async_append_is_empty(const struct async_append *ap)
99
return byteq_is_empty(&ap->byteq);
103
async_append_wait(struct async_append *ap)
107
while (!async_append_is_empty(ap)) {
108
struct aiocb *aiocb = &ap->aiocbs[ap->aiocb_tail & (MAX_CBS - 1)];
109
int error = aio_error(aiocb);
111
if (error == EINPROGRESS) {
112
const struct aiocb *p = aiocb;
116
aio_suspend(&p, 1, NULL);
118
ignore(aio_return(aiocb));
120
byteq_advance_tail(&ap->byteq, aiocb->aio_nbytes);
127
async_append_write(struct async_append *ap, const void *data_, size_t size)
129
const uint8_t *data = data_;
131
if (!async_append_enabled) {
132
ignore(write(ap->fd, data, size));
141
while (async_append_is_full(ap)) {
142
async_append_wait(ap);
145
chunk = byteq_head(&ap->byteq);
146
chunk_size = byteq_headroom(&ap->byteq);
147
if (chunk_size > size) {
150
memcpy(chunk, data, chunk_size);
152
aiocb = &ap->aiocbs[ap->aiocb_head & (MAX_CBS - 1)];
153
memset(aiocb, 0, sizeof *aiocb);
154
aiocb->aio_fildes = ap->fd;
155
aiocb->aio_offset = 0;
156
aiocb->aio_buf = chunk;
157
aiocb->aio_nbytes = chunk_size;
158
aiocb->aio_sigevent.sigev_notify = SIGEV_NONE;
159
if (aio_write(aiocb) == -1) {
160
async_append_flush(ap);
161
ignore(write(ap->fd, data, size));
167
byteq_advance_head(&ap->byteq, chunk_size);
173
async_append_flush(struct async_append *ap)
175
while (!async_append_is_empty(ap)) {
176
async_append_wait(ap);