1c9775ce7SJeremy Kerr /**
2c9775ce7SJeremy Kerr * Copyright © 2017 IBM Corporation
3c9775ce7SJeremy Kerr *
4c9775ce7SJeremy Kerr * Licensed under the Apache License, Version 2.0 (the "License");
5c9775ce7SJeremy Kerr * you may not use this file except in compliance with the License.
6c9775ce7SJeremy Kerr * You may obtain a copy of the License at
7c9775ce7SJeremy Kerr *
8c9775ce7SJeremy Kerr * http://www.apache.org/licenses/LICENSE-2.0
9c9775ce7SJeremy Kerr *
10c9775ce7SJeremy Kerr * Unless required by applicable law or agreed to in writing, software
11c9775ce7SJeremy Kerr * distributed under the License is distributed on an "AS IS" BASIS,
12c9775ce7SJeremy Kerr * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c9775ce7SJeremy Kerr * See the License for the specific language governing permissions and
14c9775ce7SJeremy Kerr * limitations under the License.
15c9775ce7SJeremy Kerr */
16c9775ce7SJeremy Kerr
17c9775ce7SJeremy Kerr #include <assert.h>
18c9775ce7SJeremy Kerr #include <stdint.h>
19c9775ce7SJeremy Kerr #include <stdlib.h>
20c9775ce7SJeremy Kerr #include <string.h>
21c9775ce7SJeremy Kerr
22c9775ce7SJeremy Kerr #include "console-server.h"
23c9775ce7SJeremy Kerr
min(size_t a,size_t b)2415ceb3e3SAndrew Jeffery static inline size_t min(size_t a, size_t b)
2515ceb3e3SAndrew Jeffery {
2615ceb3e3SAndrew Jeffery return a < b ? a : b;
2715ceb3e3SAndrew Jeffery }
28c9775ce7SJeremy Kerr
ringbuffer_init(size_t size)29c9775ce7SJeremy Kerr struct ringbuffer *ringbuffer_init(size_t size)
30c9775ce7SJeremy Kerr {
31c9775ce7SJeremy Kerr struct ringbuffer *rb;
32c9775ce7SJeremy Kerr
33c9775ce7SJeremy Kerr rb = malloc(sizeof(*rb) + size);
342834c5b1SAndrew Jeffery if (!rb) {
35c9775ce7SJeremy Kerr return NULL;
362834c5b1SAndrew Jeffery }
37c9775ce7SJeremy Kerr
38c9775ce7SJeremy Kerr memset(rb, 0, sizeof(*rb));
39c9775ce7SJeremy Kerr rb->size = size;
40c9775ce7SJeremy Kerr rb->buf = (void *)(rb + 1);
41c9775ce7SJeremy Kerr
42c9775ce7SJeremy Kerr return rb;
43c9775ce7SJeremy Kerr }
44c9775ce7SJeremy Kerr
ringbuffer_fini(struct ringbuffer * rb)45c9775ce7SJeremy Kerr void ringbuffer_fini(struct ringbuffer *rb)
46c9775ce7SJeremy Kerr {
472834c5b1SAndrew Jeffery while (rb->n_consumers) {
48c9775ce7SJeremy Kerr ringbuffer_consumer_unregister(rb->consumers[0]);
492834c5b1SAndrew Jeffery }
50c9775ce7SJeremy Kerr free(rb);
51c9775ce7SJeremy Kerr }
52c9775ce7SJeremy Kerr
53a72711afSAndrew Jeffery struct ringbuffer_consumer *
ringbuffer_consumer_register(struct ringbuffer * rb,ringbuffer_poll_fn_t fn,void * data)54a72711afSAndrew Jeffery ringbuffer_consumer_register(struct ringbuffer *rb, ringbuffer_poll_fn_t fn,
55a72711afSAndrew Jeffery void *data)
56c9775ce7SJeremy Kerr {
57c9775ce7SJeremy Kerr struct ringbuffer_consumer *rbc;
58c9775ce7SJeremy Kerr int n;
59c9775ce7SJeremy Kerr
60c9775ce7SJeremy Kerr rbc = malloc(sizeof(*rbc));
61c9775ce7SJeremy Kerr rbc->rb = rb;
62c9775ce7SJeremy Kerr rbc->poll_fn = fn;
63c9775ce7SJeremy Kerr rbc->poll_data = data;
64c9775ce7SJeremy Kerr rbc->pos = rb->tail;
65c9775ce7SJeremy Kerr
66c9775ce7SJeremy Kerr n = rb->n_consumers++;
6791b52175SAndrew Jeffery /*
6891b52175SAndrew Jeffery * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
6991b52175SAndrew Jeffery * pointer type.
7091b52175SAndrew Jeffery */
7191b52175SAndrew Jeffery /* NOLINTBEGIN(bugprone-sizeof-expression) */
7291b52175SAndrew Jeffery rb->consumers = reallocarray(rb->consumers, rb->n_consumers,
7391b52175SAndrew Jeffery sizeof(*rb->consumers));
7491b52175SAndrew Jeffery /* NOLINTEND(bugprone-sizeof-expression) */
75c9775ce7SJeremy Kerr rb->consumers[n] = rbc;
76c9775ce7SJeremy Kerr
77c9775ce7SJeremy Kerr return rbc;
78c9775ce7SJeremy Kerr }
79c9775ce7SJeremy Kerr
ringbuffer_consumer_unregister(struct ringbuffer_consumer * rbc)80c9775ce7SJeremy Kerr void ringbuffer_consumer_unregister(struct ringbuffer_consumer *rbc)
81c9775ce7SJeremy Kerr {
82c9775ce7SJeremy Kerr struct ringbuffer *rb = rbc->rb;
83c9775ce7SJeremy Kerr int i;
84c9775ce7SJeremy Kerr
852834c5b1SAndrew Jeffery for (i = 0; i < rb->n_consumers; i++) {
862834c5b1SAndrew Jeffery if (rb->consumers[i] == rbc) {
87c9775ce7SJeremy Kerr break;
882834c5b1SAndrew Jeffery }
892834c5b1SAndrew Jeffery }
90c9775ce7SJeremy Kerr
91c9775ce7SJeremy Kerr assert(i < rb->n_consumers);
92c9775ce7SJeremy Kerr
93c9775ce7SJeremy Kerr rb->n_consumers--;
94c9775ce7SJeremy Kerr
9591b52175SAndrew Jeffery /*
9691b52175SAndrew Jeffery * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
9791b52175SAndrew Jeffery * pointer type.
9891b52175SAndrew Jeffery */
99c9775ce7SJeremy Kerr memmove(&rb->consumers[i], &rb->consumers[i + 1],
100*2f1abc37SJohn Wang /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
101c9775ce7SJeremy Kerr sizeof(*rb->consumers) * (rb->n_consumers - i));
102c9775ce7SJeremy Kerr
103*2f1abc37SJohn Wang if (rb->n_consumers == 0) {
104*2f1abc37SJohn Wang free(rb->consumers);
105*2f1abc37SJohn Wang rb->consumers = NULL;
106*2f1abc37SJohn Wang } else {
107*2f1abc37SJohn Wang rb->consumers = reallocarray(
108*2f1abc37SJohn Wang rb->consumers, rb->n_consumers,
109*2f1abc37SJohn Wang /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
11091b52175SAndrew Jeffery sizeof(*rb->consumers));
111*2f1abc37SJohn Wang }
112c9775ce7SJeremy Kerr
113c9775ce7SJeremy Kerr free(rbc);
114c9775ce7SJeremy Kerr }
115c9775ce7SJeremy Kerr
ringbuffer_len(struct ringbuffer_consumer * rbc)1161cecc5deSJohnathan Mantey size_t ringbuffer_len(struct ringbuffer_consumer *rbc)
117c9775ce7SJeremy Kerr {
1182834c5b1SAndrew Jeffery if (rbc->pos <= rbc->rb->tail) {
119c9775ce7SJeremy Kerr return rbc->rb->tail - rbc->pos;
120c9775ce7SJeremy Kerr }
1210b7b0477SAndrew Jeffery return rbc->rb->tail + rbc->rb->size - rbc->pos;
1222834c5b1SAndrew Jeffery }
123c9775ce7SJeremy Kerr
ringbuffer_space(struct ringbuffer_consumer * rbc)124c9775ce7SJeremy Kerr static size_t ringbuffer_space(struct ringbuffer_consumer *rbc)
125c9775ce7SJeremy Kerr {
126c9775ce7SJeremy Kerr return rbc->rb->size - ringbuffer_len(rbc) - 1;
127c9775ce7SJeremy Kerr }
128c9775ce7SJeremy Kerr
ringbuffer_consumer_ensure_space(struct ringbuffer_consumer * rbc,size_t len)129a72711afSAndrew Jeffery static int ringbuffer_consumer_ensure_space(struct ringbuffer_consumer *rbc,
130a72711afSAndrew Jeffery size_t len)
131c9775ce7SJeremy Kerr {
132c9775ce7SJeremy Kerr enum ringbuffer_poll_ret prc;
1335c359cc6SAndrew Jeffery size_t force_len;
134c9775ce7SJeremy Kerr
1352834c5b1SAndrew Jeffery if (ringbuffer_space(rbc) >= len) {
136c9775ce7SJeremy Kerr return 0;
1372834c5b1SAndrew Jeffery }
138c9775ce7SJeremy Kerr
139c9775ce7SJeremy Kerr force_len = len - ringbuffer_space(rbc);
140c9775ce7SJeremy Kerr
141c9775ce7SJeremy Kerr prc = rbc->poll_fn(rbc->poll_data, force_len);
1422834c5b1SAndrew Jeffery if (prc != RINGBUFFER_POLL_OK) {
143c9775ce7SJeremy Kerr return -1;
1442834c5b1SAndrew Jeffery }
145c9775ce7SJeremy Kerr
146c9775ce7SJeremy Kerr return 0;
147c9775ce7SJeremy Kerr }
148c9775ce7SJeremy Kerr
ringbuffer_queue(struct ringbuffer * rb,uint8_t * data,size_t len)149c9775ce7SJeremy Kerr int ringbuffer_queue(struct ringbuffer *rb, uint8_t *data, size_t len)
150c9775ce7SJeremy Kerr {
151c9775ce7SJeremy Kerr struct ringbuffer_consumer *rbc;
152c9775ce7SJeremy Kerr size_t wlen;
153b70f8713SAndrew Jeffery int i;
154b70f8713SAndrew Jeffery int rc;
155c9775ce7SJeremy Kerr
1562834c5b1SAndrew Jeffery if (len >= rb->size) {
157c9775ce7SJeremy Kerr return -1;
1582834c5b1SAndrew Jeffery }
159c9775ce7SJeremy Kerr
1602834c5b1SAndrew Jeffery if (len == 0) {
1611cecc5deSJohnathan Mantey return 0;
1622834c5b1SAndrew Jeffery }
1631cecc5deSJohnathan Mantey
164c9775ce7SJeremy Kerr /* Ensure there is at least len bytes of space available.
165c9775ce7SJeremy Kerr *
166c9775ce7SJeremy Kerr * If a client doesn't have sufficient space, perform a blocking write
167c9775ce7SJeremy Kerr * (by calling ->poll_fn with force_len) to create it.
168c9775ce7SJeremy Kerr */
169c9775ce7SJeremy Kerr for (i = 0; i < rb->n_consumers; i++) {
170c9775ce7SJeremy Kerr rbc = rb->consumers[i];
171c9775ce7SJeremy Kerr
172c9775ce7SJeremy Kerr rc = ringbuffer_consumer_ensure_space(rbc, len);
173c9775ce7SJeremy Kerr if (rc) {
174c9775ce7SJeremy Kerr ringbuffer_consumer_unregister(rbc);
175c9775ce7SJeremy Kerr i--;
176c9775ce7SJeremy Kerr continue;
177c9775ce7SJeremy Kerr }
178c9775ce7SJeremy Kerr
179c9775ce7SJeremy Kerr assert(ringbuffer_space(rbc) >= len);
180c9775ce7SJeremy Kerr }
181c9775ce7SJeremy Kerr
182c9775ce7SJeremy Kerr /* Now that we know we have enough space, add new data to tail */
183c9775ce7SJeremy Kerr wlen = min(len, rb->size - rb->tail);
184c9775ce7SJeremy Kerr memcpy(rb->buf + rb->tail, data, wlen);
185c9775ce7SJeremy Kerr rb->tail = (rb->tail + wlen) % rb->size;
186c9775ce7SJeremy Kerr len -= wlen;
187c9775ce7SJeremy Kerr data += wlen;
188c9775ce7SJeremy Kerr
189c9775ce7SJeremy Kerr memcpy(rb->buf, data, len);
190c9775ce7SJeremy Kerr rb->tail += len;
191c9775ce7SJeremy Kerr
192c9775ce7SJeremy Kerr /* Inform consumers of new data in non-blocking mode, by calling
193c9775ce7SJeremy Kerr * ->poll_fn with 0 force_len */
194c9775ce7SJeremy Kerr for (i = 0; i < rb->n_consumers; i++) {
195c9775ce7SJeremy Kerr enum ringbuffer_poll_ret prc;
196c9775ce7SJeremy Kerr
197c9775ce7SJeremy Kerr rbc = rb->consumers[i];
198c9775ce7SJeremy Kerr prc = rbc->poll_fn(rbc->poll_data, 0);
199c9775ce7SJeremy Kerr if (prc == RINGBUFFER_POLL_REMOVE) {
200c9775ce7SJeremy Kerr ringbuffer_consumer_unregister(rbc);
201c9775ce7SJeremy Kerr i--;
202c9775ce7SJeremy Kerr }
203c9775ce7SJeremy Kerr }
204c9775ce7SJeremy Kerr
205c9775ce7SJeremy Kerr return 0;
206c9775ce7SJeremy Kerr }
207c9775ce7SJeremy Kerr
ringbuffer_dequeue_peek(struct ringbuffer_consumer * rbc,size_t offset,uint8_t ** data)208c9775ce7SJeremy Kerr size_t ringbuffer_dequeue_peek(struct ringbuffer_consumer *rbc, size_t offset,
209c9775ce7SJeremy Kerr uint8_t **data)
210c9775ce7SJeremy Kerr {
211c9775ce7SJeremy Kerr struct ringbuffer *rb = rbc->rb;
212c9775ce7SJeremy Kerr size_t pos;
213c9775ce7SJeremy Kerr size_t len;
214c9775ce7SJeremy Kerr
2152834c5b1SAndrew Jeffery if (offset >= ringbuffer_len(rbc)) {
216c9775ce7SJeremy Kerr return 0;
2172834c5b1SAndrew Jeffery }
218c9775ce7SJeremy Kerr
219c9775ce7SJeremy Kerr pos = (rbc->pos + offset) % rb->size;
2202834c5b1SAndrew Jeffery if (pos <= rb->tail) {
221c9775ce7SJeremy Kerr len = rb->tail - pos;
2222834c5b1SAndrew Jeffery } else {
223c9775ce7SJeremy Kerr len = rb->size - pos;
2242834c5b1SAndrew Jeffery }
225c9775ce7SJeremy Kerr
226c9775ce7SJeremy Kerr *data = rb->buf + pos;
227c9775ce7SJeremy Kerr return len;
228c9775ce7SJeremy Kerr }
229c9775ce7SJeremy Kerr
ringbuffer_dequeue_commit(struct ringbuffer_consumer * rbc,size_t len)230c9775ce7SJeremy Kerr int ringbuffer_dequeue_commit(struct ringbuffer_consumer *rbc, size_t len)
231c9775ce7SJeremy Kerr {
232c9775ce7SJeremy Kerr assert(len <= ringbuffer_len(rbc));
233c9775ce7SJeremy Kerr rbc->pos = (rbc->pos + len) % rbc->rb->size;
234c9775ce7SJeremy Kerr return 0;
235c9775ce7SJeremy Kerr }
236