ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
cache_bench_host.c
Go to the documentation of this file.
1
13#include "cache_bench_host.h"
14
15#include <errno.h>
16#include <fcntl.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <unistd.h>
23
24#include "ra8_attributes.h"
25
26typedef enum : uint8_t {
29
30typedef enum : uint16_t {
33
53static cb_io_status_t
54internal_sink_write(void* ctx, const uint8_t* data, size_t length, size_t* out_written)
55{
56 if ((ctx == nullptr) || (out_written == nullptr)) {
57 return k_cb_io_fault;
58 }
59 const int fd = *(const int*)ctx;
60 for (uint8_t attempt = 0U; attempt < (uint8_t)k_cb_host_retry_limit; ++attempt) {
61 const ssize_t result = write(fd, data, length);
62 if (result >= 0) {
63 *out_written = (size_t)result;
64 return k_cb_io_ok;
65 }
66 if (errno != EINTR) {
67 break;
68 }
69 }
70 *out_written = 0U;
71 return k_cb_io_fault;
72}
73
75{
76 static int s_output_fd = STDOUT_FILENO;
77 static int s_error_fd = STDERR_FILENO;
78 if (output != nullptr) {
79 *output = (cb_sink_t){.write = internal_sink_write, .ctx = &s_output_fd};
80 }
81 if (error != nullptr) {
82 *error = (cb_sink_t){.write = internal_sink_write, .ctx = &s_error_fd};
83 }
84}
85
107static cb_io_status_t
108internal_source_read(void* ctx, uint64_t offset, uint8_t* dst, size_t capacity, size_t* out_read)
109{
110 cb_host_source_t* source = (cb_host_source_t*)ctx;
111 if ((source == nullptr) || (dst == nullptr) || (out_read == nullptr) || (offset > source->size)) {
112 return k_cb_io_fault;
113 }
114 struct stat info = {};
115 if ((fstat(source->fd, &info) != 0) || (info.st_size < 0) ||
116 ((uint64_t)info.st_size != source->size)) {
117 return k_cb_io_mutated;
118 }
119 for (uint8_t attempt = 0U; attempt < (uint8_t)k_cb_host_retry_limit; ++attempt) {
120 const ssize_t result = pread(source->fd, dst, capacity, (off_t)offset);
121 if (result >= 0) {
122 *out_read = (size_t)result;
123 return k_cb_io_ok;
124 }
125 if (errno != EINTR) {
126 break;
127 }
128 }
129 *out_read = 0U;
130 return k_cb_io_fault;
131}
132
134{
135 if ((path == nullptr) || (binding == nullptr) || (source == nullptr)) {
136 return k_cb_io_fault;
137 }
138 *binding = (cb_host_source_t){.fd = -1};
139 const int fd = open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
140 if (fd < 0) {
141 return k_cb_io_fault;
142 }
143 struct stat info = {};
144 if ((fstat(fd, &info) != 0) || !S_ISREG(info.st_mode) || (info.st_size < 0)) {
145 (void)close(fd);
146 return k_cb_io_fault;
147 }
148 *binding = (cb_host_source_t){.fd = fd, .size = (uint64_t)info.st_size};
149 *source = (cb_source_t){.read = internal_source_read, .ctx = binding, .size = binding->size};
150 return k_cb_io_ok;
151}
152
154{
155 if ((binding == nullptr) || (binding->fd < 0)) {
156 return k_cb_io_fault;
157 }
158 const int result = close(binding->fd);
159 binding->fd = -1;
160 return (result == 0) ? k_cb_io_ok : k_cb_io_fault;
161}
162
182static cb_io_status_t
183internal_split_path(const char* path, char* parent, size_t parent_capacity, const char** base)
184{
185 const char* slash = strrchr(path, '/');
186 *base = (slash == nullptr) ? path : slash + 1;
187 const size_t parent_length = (slash == nullptr) ? 1U : (size_t)(slash - path);
188 if ((**base == '\0') || (parent_length >= parent_capacity)) {
189 return k_cb_io_capacity;
190 }
191 size_t output_length = parent_length;
192 if (slash == nullptr) {
193 parent[0] = '.';
194 } else if (parent_length == 0U) {
195 parent[0] = '/';
196 output_length = 1U;
197 } else {
198 memcpy(parent, path, parent_length);
199 }
200 parent[output_length] = '\0';
201 return k_cb_io_ok;
202}
203
226static cb_io_status_t
227internal_create_temp_exclusive(cb_host_output_t* binding, const char* parent, const char* base)
228{
229 for (uint8_t attempt = 0U; attempt < (uint8_t)k_cb_host_retry_limit; ++attempt) {
230 const int length = snprintf(binding->temporary,
231 sizeof(binding->temporary),
232 "%s/.%s.cache-bench.%ld.%u",
233 parent,
234 base,
235 (long)getpid(),
236 (unsigned int)attempt);
237 if ((length < 0) || ((size_t)length >= sizeof(binding->temporary))) {
238 return k_cb_io_capacity;
239 }
240 binding->fd = open(binding->temporary,
241 O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC,
242 (mode_t)k_cb_host_create_mode);
243 if (binding->fd >= 0) {
244 binding->transactional = true;
245 return k_cb_io_ok;
246 }
247 if (errno != EEXIST) {
248 break;
249 }
250 }
251 return k_cb_io_fault;
252}
253
255{
256 if ((binding == nullptr) || (sink == nullptr)) {
257 return k_cb_io_fault;
258 }
259 *binding = (cb_host_output_t){.fd = -1, .dir_fd = -1};
260 if (path == nullptr) {
261 binding->fd = STDOUT_FILENO;
262 *sink = (cb_sink_t){.write = internal_sink_write, .ctx = &binding->fd};
263 return k_cb_io_ok;
264 }
265 const size_t path_length = strlen(path);
266 if (path_length >= sizeof(binding->destination)) {
267 return k_cb_io_capacity;
268 }
269 memcpy(binding->destination, path, path_length + 1U);
270 char parent[k_cb_host_path_capacity];
271 const char* base = nullptr;
272 cb_io_status_t status = internal_split_path(path, parent, sizeof(parent), &base);
273 if (status != k_cb_io_ok) {
274 return status;
275 }
276 binding->dir_fd = open(parent, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
277 if (binding->dir_fd < 0) {
278 return k_cb_io_fault;
279 }
280 status = internal_create_temp_exclusive(binding, parent, base);
281 if (status == k_cb_io_ok) {
282 *sink = (cb_sink_t){.write = internal_sink_write, .ctx = &binding->fd};
283 return k_cb_io_ok;
284 }
285 cb_host_output_abort(binding);
286 return status;
287}
288
290{
291 if ((binding == nullptr) || (binding->fd < 0)) {
292 return k_cb_io_fault;
293 }
294 if (!binding->transactional) {
295 return k_cb_io_ok;
296 }
297 bool ok = fsync(binding->fd) == 0;
298 ok = (close(binding->fd) == 0) && ok;
299 binding->fd = -1;
300 if (ok) {
301 ok = rename(binding->temporary, binding->destination) == 0;
302 }
303 if (ok) {
304 ok = fsync(binding->dir_fd) == 0;
305 }
306 const bool dir_ok = close(binding->dir_fd) == 0;
307 binding->dir_fd = -1;
308 if (!ok) {
309 (void)unlink(binding->temporary);
310 }
311 binding->transactional = false;
312 return (ok && dir_ok) ? k_cb_io_ok : k_cb_io_fault;
313}
314
316{
317 if (binding == nullptr) {
318 return;
319 }
320 if (binding->fd >= 0) {
321 (void)close(binding->fd);
322 binding->fd = -1;
323 }
324 if (binding->transactional) {
325 (void)unlink(binding->temporary);
326 }
327 if (binding->dir_fd >= 0) {
328 (void)close(binding->dir_fd);
329 binding->dir_fd = -1;
330 }
331 binding->transactional = false;
332}
333
354static cb_io_status_t
355internal_scratch_transfer(int fd, bool write_mode, uint64_t offset, void* data, size_t length)
356{
357 size_t complete = 0U;
358 uint32_t attempts = 0U;
359 while (complete < length) {
360 const ssize_t result =
361 write_mode
362 ? pwrite(fd,
363 &((const uint8_t*)data)[complete],
364 length - complete,
365 (off_t)(offset + complete))
366 : pread(fd, &((uint8_t*)data)[complete], length - complete, (off_t)(offset + complete));
367 if (result > 0) {
368 complete += (size_t)result;
369 attempts = 0U;
370 continue;
371 }
372 if ((result < 0) && (errno == EINTR) && (attempts < (uint32_t)k_cb_host_retry_limit)) {
373 attempts++;
374 continue;
375 }
376 return k_cb_io_fault;
377 }
378 return k_cb_io_ok;
379}
380
399static cb_io_status_t internal_scratch_read(void* ctx, uint64_t offset, void* data, size_t length)
400{
401 return internal_scratch_transfer(((cb_host_scratch_t*)ctx)->fd, false, offset, data, length);
402}
403
422static cb_io_status_t internal_scratch_write(void* ctx, uint64_t offset, void* data, size_t length)
423{
424 return internal_scratch_transfer(((cb_host_scratch_t*)ctx)->fd, true, offset, data, length);
425}
426
428{
429 if ((binding == nullptr) || (scratch == nullptr)) {
430 return k_cb_io_fault;
431 }
432 char path[] = "/tmp/ra8-cache-bench.XXXXXX";
433 binding->fd = mkstemp(path);
434 if (binding->fd < 0) {
435 return k_cb_io_fault;
436 }
437 if (unlink(path) != 0) {
438 (void)close(binding->fd);
439 binding->fd = -1;
440 return k_cb_io_fault;
441 }
442 *scratch =
443 (cb_scratch_t){.read = internal_scratch_read, .write = internal_scratch_write, .ctx = binding};
444 return k_cb_io_ok;
445}
446
448{
449 if ((binding == nullptr) || (binding->fd < 0)) {
450 return k_cb_io_fault;
451 }
452 const int result = close(binding->fd);
453 binding->fd = -1;
454 return (result == 0) ? k_cb_io_ok : k_cb_io_fault;
455}
cb_io_status_t cb_host_source_open(const char *path, cb_host_source_t *binding, cb_source_t *source)
Open a captured-trace path and publish its source seam.
static cb_io_status_t internal_split_path(const char *path, char *parent, size_t parent_capacity, const char **base)
Split an output path into parent bytes and a borrowed leaf pointer.
cb_host_retry_t
@ k_cb_host_retry_limit
Bounded EINTR/temp-name retry count.
cb_host_mode_t
@ k_cb_host_create_mode
Owner-only scratch/output permissions.
cb_io_status_t cb_host_scratch_close(cb_host_scratch_t *binding)
Close the scratch transaction.
cb_io_status_t cb_host_scratch_open(cb_host_scratch_t *binding, cb_scratch_t *scratch)
Create an unlinked raw-descriptor scratch transaction.
void cb_host_output_abort(cb_host_output_t *binding)
Abandon a temporary sibling while preserving the destination.
static cb_io_status_t internal_scratch_write(void *ctx, uint64_t offset, void *data, size_t length)
Write an exact region through a bound host scratch transaction.
void cb_host_standard_sinks(cb_sink_t *output, cb_sink_t *error)
Bind the process standard-output and standard-error descriptor sinks.
cb_io_status_t cb_host_output_open(const char *path, cb_host_output_t *binding, cb_sink_t *sink)
Open a sibling output transaction, or bind descriptor one for NULL.
static cb_io_status_t internal_sink_write(void *ctx, const uint8_t *data, size_t length, size_t *out_written)
Write one bounded fragment to a borrowed host descriptor.
cb_io_status_t cb_host_output_commit(cb_host_output_t *binding)
Flush and atomically publish a transactional output.
cb_io_status_t cb_host_source_close(cb_host_source_t *binding)
Close a source binding.
static cb_io_status_t internal_scratch_read(void *ctx, uint64_t offset, void *data, size_t length)
Read an exact region through a bound host scratch transaction.
static cb_io_status_t internal_source_read(void *ctx, uint64_t offset, uint8_t *dst, size_t capacity, size_t *out_read)
Read a bounded fragment from a snapshotted host source.
static cb_io_status_t internal_create_temp_exclusive(cb_host_output_t *binding, const char *parent, const char *base)
Create a uniquely-named exclusive temp file beside the destination.
static cb_io_status_t internal_scratch_transfer(int fd, bool write_mode, uint64_t offset, void *data, size_t length)
Complete one positional scratch transfer with bounded interruption retries.
POSIX raw-descriptor composition bindings for cache_bench.
@ k_cb_host_path_capacity
Bounded host path copy.
cb_io_status_t
Status returned by tool-local injected I/O callbacks.
@ k_cb_io_ok
Operation completed.
@ k_cb_io_fault
Backing device or sink failed.
@ k_cb_io_mutated
A replay source changed between passes.
@ k_cb_io_capacity
Caller-owned storage was too small.
#define O_DIRECTORY
No-op directory-open fallback for hosts lacking the flag.
#define O_NOFOLLOW
Zero fallback paired with explicit no-follow metadata validation.
#define O_CLOEXEC
Zero fallback when the host lacks close-on-exec open flags.
-proof
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
char * strrchr(const char *s, int c)
Locate last occurrence of character in string.
Output transaction or borrowed standard descriptor.
bool transactional
True when temporary sibling is live.
int dir_fd
Parent descriptor for durable rename.
char temporary[k_cb_host_path_capacity]
Sibling temporary path.
int fd
Writable descriptor.
char destination[k_cb_host_path_capacity]
Final path.
Unlinked raw-descriptor scratch binding.
int fd
Open temporary descriptor.
Borrowed raw descriptor source binding.
int fd
Open read-only descriptor.
uint64_t size
Size snapshot.
Injected bounded random-access scratch transaction.
Injected output sink.
Immutable injected byte source with a snapshotted length.