Branch data Line data Source code
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 : 617 : async_append_create(int fd)
55 : : {
56 : : struct async_append *ap;
57 : :
58 : 617 : ap = xmalloc(sizeof *ap);
59 : 617 : ap->fd = fd;
60 : 617 : ap->aiocbs = xmalloc(MAX_CBS * sizeof *ap->aiocbs);
61 : 617 : ap->aiocb_head = ap->aiocb_tail = 0;
62 : 617 : ap->buffer = xmalloc(BUFFER_SIZE);
63 : 617 : byteq_init(&ap->byteq, ap->buffer, BUFFER_SIZE);
64 : :
65 : 617 : return ap;
66 : : }
67 : :
68 : : void
69 : 6 : async_append_destroy(struct async_append *ap)
70 : : {
71 [ + + ]: 6 : if (ap) {
72 : 2 : async_append_flush(ap);
73 : 2 : free(ap->aiocbs);
74 : 2 : free(ap->buffer);
75 : 2 : free(ap);
76 : : }
77 : 6 : }
78 : :
79 : : static bool
80 : 86591 : async_append_is_full(const struct async_append *ap)
81 : : {
82 : 86591 : return (ap->aiocb_head - ap->aiocb_tail >= MAX_CBS
83 [ + + ][ + + ]: 86591 : || byteq_is_full(&ap->byteq));
84 : : }
85 : :
86 : : static bool
87 : 50223 : async_append_is_empty(const struct async_append *ap)
88 : : {
89 : 50223 : return byteq_is_empty(&ap->byteq);
90 : : }
91 : :
92 : : static void
93 : 2220 : async_append_wait(struct async_append *ap)
94 : : {
95 : 2220 : int n = 0;
96 : :
97 [ + + ]: 50219 : while (!async_append_is_empty(ap)) {
98 : 50034 : struct aiocb *aiocb = &ap->aiocbs[ap->aiocb_tail & (MAX_CBS - 1)];
99 : 50034 : int error = aio_error(aiocb);
100 : :
101 [ + + ]: 50034 : if (error == EINPROGRESS) {
102 : 4043 : const struct aiocb *p = aiocb;
103 [ + + ]: 4043 : if (n > 0) {
104 : 2035 : return;
105 : : }
106 : 2008 : aio_suspend(&p, 1, NULL);
107 : : } else {
108 : 45991 : ignore(aio_return(aiocb));
109 : 45991 : ap->aiocb_tail++;
110 : 45991 : byteq_advance_tail(&ap->byteq, aiocb->aio_nbytes);
111 : 45991 : n++;
112 : : }
113 : : }
114 : : }
115 : :
116 : : void
117 : 82154 : async_append_write(struct async_append *ap, const void *data_, size_t size)
118 : : {
119 : 82154 : const uint8_t *data = data_;
120 : :
121 [ + + ]: 166527 : while (size > 0) {
122 : : struct aiocb *aiocb;
123 : : size_t chunk_size;
124 : : void *chunk;
125 : :
126 [ + + ]: 86591 : while (async_append_is_full(ap)) {
127 : 2218 : async_append_wait(ap);
128 : : }
129 : :
130 : 84373 : chunk = byteq_head(&ap->byteq);
131 : 84373 : chunk_size = byteq_headroom(&ap->byteq);
132 [ + + ]: 84373 : if (chunk_size > size) {
133 : 82154 : chunk_size = size;
134 : : }
135 : 84373 : memcpy(chunk, data, chunk_size);
136 : :
137 : 84373 : aiocb = &ap->aiocbs[ap->aiocb_head & (MAX_CBS - 1)];
138 : 84373 : memset(aiocb, 0, sizeof *aiocb);
139 : 84373 : aiocb->aio_fildes = ap->fd;
140 : 84373 : aiocb->aio_offset = 0;
141 : 84373 : aiocb->aio_buf = chunk;
142 : 84373 : aiocb->aio_nbytes = chunk_size;
143 : 84373 : aiocb->aio_sigevent.sigev_notify = SIGEV_NONE;
144 [ - + ]: 84373 : if (aio_write(aiocb) == -1) {
145 : 0 : async_append_flush(ap);
146 : 0 : ignore(write(ap->fd, data, size));
147 : 0 : return;
148 : : }
149 : :
150 : 84373 : data += chunk_size;
151 : 84373 : size -= chunk_size;
152 : 84373 : byteq_advance_head(&ap->byteq, chunk_size);
153 : 84373 : ap->aiocb_head++;
154 : : }
155 : : }
156 : :
157 : : void
158 : 2 : async_append_flush(struct async_append *ap)
159 : : {
160 [ + + ]: 4 : while (!async_append_is_empty(ap)) {
161 : 2 : async_append_wait(ap);
162 : : }
163 : 2 : }
|