xref: /openbmc/obmc-console/ringbuffer.c (revision 2f1abc37384d6a5fda99f89b20ca0c11db940663)
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