/src/openvswitch/lib/async-append-aio.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2013 Nicira, Inc. |
2 | | * |
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: |
6 | | * |
7 | | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | | * |
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. |
14 | | */ |
15 | | |
16 | | #include <config.h> |
17 | | |
18 | | /* This implementation of the async-append.h interface uses the POSIX |
19 | | * asynchronous I/O interface. */ |
20 | | |
21 | | #include "async-append.h" |
22 | | |
23 | | #include <aio.h> |
24 | | #include <errno.h> |
25 | | #include <stdlib.h> |
26 | | #include <unistd.h> |
27 | | |
28 | | #include "byteq.h" |
29 | | #include "ovs-thread.h" |
30 | | #include "util.h" |
31 | | |
32 | | /* Maximum number of bytes of buffered data. */ |
33 | | enum { BUFFER_SIZE = 65536 }; |
34 | | |
35 | | /* Maximum number of aiocbs to use. |
36 | | * |
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 |
39 | | * space. */ |
40 | | enum { MAX_CBS = ROUND_DOWN_POW2(BUFFER_SIZE / sizeof(struct aiocb)) }; |
41 | | BUILD_ASSERT_DECL(IS_POW2(MAX_CBS)); |
42 | | |
43 | | struct async_append { |
44 | | int fd; |
45 | | |
46 | | struct aiocb *aiocbs; |
47 | | unsigned int aiocb_head, aiocb_tail; |
48 | | |
49 | | uint8_t *buffer; |
50 | | struct byteq byteq; |
51 | | }; |
52 | | |
53 | | struct async_append * |
54 | | async_append_create(int fd) |
55 | 0 | { |
56 | 0 | struct async_append *ap; |
57 | |
|
58 | 0 | ap = xmalloc(sizeof *ap); |
59 | 0 | ap->fd = fd; |
60 | 0 | ap->aiocbs = xmalloc(MAX_CBS * sizeof *ap->aiocbs); |
61 | 0 | ap->aiocb_head = ap->aiocb_tail = 0; |
62 | 0 | ap->buffer = xmalloc(BUFFER_SIZE); |
63 | 0 | byteq_init(&ap->byteq, ap->buffer, BUFFER_SIZE); |
64 | |
|
65 | 0 | return ap; |
66 | 0 | } |
67 | | |
68 | | void |
69 | | async_append_destroy(struct async_append *ap) |
70 | 0 | { |
71 | 0 | if (ap) { |
72 | 0 | async_append_flush(ap); |
73 | 0 | free(ap->aiocbs); |
74 | 0 | free(ap->buffer); |
75 | 0 | free(ap); |
76 | 0 | } |
77 | 0 | } |
78 | | |
79 | | static bool |
80 | | async_append_is_full(const struct async_append *ap) |
81 | 0 | { |
82 | 0 | return (ap->aiocb_head - ap->aiocb_tail >= MAX_CBS |
83 | 0 | || byteq_is_full(&ap->byteq)); |
84 | 0 | } |
85 | | |
86 | | static bool |
87 | | async_append_is_empty(const struct async_append *ap) |
88 | 0 | { |
89 | 0 | return byteq_is_empty(&ap->byteq); |
90 | 0 | } |
91 | | |
92 | | static void |
93 | | async_append_wait(struct async_append *ap) |
94 | 0 | { |
95 | 0 | int n = 0; |
96 | |
|
97 | 0 | while (!async_append_is_empty(ap)) { |
98 | 0 | struct aiocb *aiocb = &ap->aiocbs[ap->aiocb_tail & (MAX_CBS - 1)]; |
99 | 0 | int error = aio_error(aiocb); |
100 | |
|
101 | 0 | if (error == EINPROGRESS) { |
102 | 0 | const struct aiocb *p = aiocb; |
103 | 0 | if (n > 0) { |
104 | 0 | return; |
105 | 0 | } |
106 | 0 | aio_suspend(&p, 1, NULL); |
107 | 0 | } else { |
108 | 0 | ignore(aio_return(aiocb)); |
109 | 0 | ap->aiocb_tail++; |
110 | 0 | byteq_advance_tail(&ap->byteq, aiocb->aio_nbytes); |
111 | 0 | n++; |
112 | 0 | } |
113 | 0 | } |
114 | 0 | } |
115 | | |
116 | | void |
117 | | async_append_write(struct async_append *ap, const void *data_, size_t size) |
118 | 0 | { |
119 | 0 | const uint8_t *data = data_; |
120 | |
|
121 | 0 | while (size > 0) { |
122 | 0 | struct aiocb *aiocb; |
123 | 0 | size_t chunk_size; |
124 | 0 | void *chunk; |
125 | |
|
126 | 0 | while (async_append_is_full(ap)) { |
127 | 0 | async_append_wait(ap); |
128 | 0 | } |
129 | |
|
130 | 0 | chunk = byteq_head(&ap->byteq); |
131 | 0 | chunk_size = byteq_headroom(&ap->byteq); |
132 | 0 | if (chunk_size > size) { |
133 | 0 | chunk_size = size; |
134 | 0 | } |
135 | 0 | memcpy(chunk, data, chunk_size); |
136 | |
|
137 | 0 | aiocb = &ap->aiocbs[ap->aiocb_head & (MAX_CBS - 1)]; |
138 | 0 | memset(aiocb, 0, sizeof *aiocb); |
139 | 0 | aiocb->aio_fildes = ap->fd; |
140 | 0 | aiocb->aio_offset = 0; |
141 | 0 | aiocb->aio_buf = chunk; |
142 | 0 | aiocb->aio_nbytes = chunk_size; |
143 | 0 | aiocb->aio_sigevent.sigev_notify = SIGEV_NONE; |
144 | 0 | if (aio_write(aiocb) == -1) { |
145 | 0 | async_append_flush(ap); |
146 | 0 | ignore(write(ap->fd, data, size)); |
147 | 0 | return; |
148 | 0 | } |
149 | | |
150 | 0 | data += chunk_size; |
151 | 0 | size -= chunk_size; |
152 | 0 | byteq_advance_head(&ap->byteq, chunk_size); |
153 | 0 | ap->aiocb_head++; |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | void |
158 | | async_append_flush(struct async_append *ap) |
159 | 0 | { |
160 | 0 | while (!async_append_is_empty(ap)) { |
161 | 0 | async_append_wait(ap); |
162 | 0 | } |
163 | 0 | } |