ggatel(8) extension for binding multiple files
Julian Hsiao
julian at hsiao.email
Thu Jul 7 01:29:59 UTC 2016
Here's an updated patch, with some minor refactors, and addresses some
known issues:
> - I use alloca(3) instead of malloc(3) in map_bundle() because using the
> latter causes incorrect behavior somehow. It's probably buffer
> overruns and / or UBs somewhere in my code.
Fixed: I was using realloc(3) incorrectly elsewhere.
> - Both ggatel(8) and md(4) implement BIO_DELETE by zeroing the requested
> range [...] I didn't implement it.
This is now implemented. By default BIO_DELETE will write zeros, and this
can be disabled with the -n option.
Incidentally, while testing, I found out that ggatel(8)'s BIO_DELETE code
is actually broken. It works in my extension, but this patch doesn't fix
the original code. I've filed a bug report[0] outlining the issue.
Lastly, I've added a license block so the few people who might find this
feature useful are free to use it.
Julian Hsiao
[0] https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=210864
Index: sbin/ggate/ggatel/Makefile
===================================================================
diff --git a/stable/10/sbin/ggate/ggatel/Makefile
b/stable/10/sbin/ggate/ggatel/Makefile
--- a/stable/10/sbin/ggate/ggatel/Makefile (revision 302332)
+++ b/stable/10/sbin/ggate/ggatel/Makefile (working copy)
@@ -4,7 +4,7 @@
PROG= ggatel
MAN= ggatel.8
-SRCS= ggatel.c ggate.c
+SRCS= ggatel.c ggatel2.c ggate.c
CFLAGS+= -DLIBGEOM
CFLAGS+= -I${.CURDIR}/../shared
Index: sbin/ggate/ggatel/ggatel.c
===================================================================
diff --git a/stable/10/sbin/ggate/ggatel/ggatel.c
b/stable/10/sbin/ggate/ggatel/ggatel.c
--- a/stable/10/sbin/ggate/ggatel/ggatel.c (revision 302332)
+++ b/stable/10/sbin/ggate/ggatel/ggatel.c (working copy)
@@ -46,6 +46,10 @@
#include <geom/gate/g_gate.h>
#include "ggate.h"
+int check_divs(const char *const, unsigned int *const, size_t *const,
+ size_t *const);
+void g_gatel_serve_bundle(const int , const unsigned int, const size_t,
+ const size_t, const int, const int, const unsigned int);
static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
@@ -55,12 +59,13 @@
static int force = 0;
static unsigned sectorsize = 0;
static unsigned timeout = G_GATE_TIMEOUT;
+static unsigned delete_zero = 1;
static void
usage(void)
{
- fprintf(stderr, "usage: %s create [-v] [-o <ro|wo|rw>] "
+ fprintf(stderr, "usage: %s create [-v] [-n] [-o <ro|wo|rw>] "
"[-s sectorsize] [-t timeout] [-u unit] <path>\n", getprogname());
fprintf(stderr, " %s rescue [-v] [-o <ro|wo|rw>] <-u unit> "
"<path>\n", getprogname());
@@ -149,6 +154,11 @@
}
break;
case BIO_DELETE:
+ if (!delete_zero) {
+ error = EOPNOTSUPP;
+ break;
+ }
+ // FIXME: Bug 210864
case BIO_WRITE:
if (pwrite(fd, ggio.gctl_data, ggio.gctl_length,
ggio.gctl_offset) == -1) {
@@ -168,17 +178,39 @@
g_gatel_create(void)
{
struct g_gate_ctl_create ggioc;
- int fd;
+ int fd, isdir = -1;
+ size_t div_size, num_divs;
fd = open(path, g_gate_openflags(flags) | O_DIRECT | O_FSYNC);
- if (fd == -1)
- err(EXIT_FAILURE, "Cannot open %s", path);
+ if (fd == -1) {
+ if (errno == EISDIR) {
+ isdir = 1;
+ } else {
+ err(EXIT_FAILURE, "Cannot open %s", path);
+ }
+ } else {
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ err(EXIT_FAILURE, "stat(%s) failed", path);
+ }
+ isdir = S_ISDIR(sb.st_mode);
+ }
+ assert(isdir != -1);
+
memset(&ggioc, 0, sizeof(ggioc));
ggioc.gctl_version = G_GATE_VERSION;
ggioc.gctl_unit = unit;
- ggioc.gctl_mediasize = g_gate_mediasize(fd);
- if (sectorsize == 0)
- sectorsize = g_gate_sectorsize(fd);
+ if (isdir) {
+ if (fd != -1 && close(fd) == -1) {
+ err(EXIT_FAILURE, "close(%s) failed", path);
+ }
+ fd = check_divs(path, §orsize, &div_size, &num_divs);
+ ggioc.gctl_mediasize = (off_t) div_size * num_divs;
+ } else {
+ ggioc.gctl_mediasize = g_gate_mediasize(fd);
+ if (sectorsize == 0)
+ sectorsize = g_gate_sectorsize(fd);
+ }
ggioc.gctl_sectorsize = sectorsize;
ggioc.gctl_timeout = timeout;
ggioc.gctl_flags = flags;
@@ -188,7 +220,12 @@
if (unit == -1)
printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
unit = ggioc.gctl_unit;
- g_gatel_serve(fd);
+ if (isdir) {
+ g_gatel_serve_bundle(fd, sectorsize, div_size, num_divs, unit,
+ g_gate_openflags(flags), delete_zero);
+ } else {
+ g_gatel_serve(fd);
+ }
}
static void
@@ -230,7 +267,7 @@
for (;;) {
int ch;
- ch = getopt(argc, argv, "fo:s:t:u:v");
+ ch = getopt(argc, argv, "fo:s:t:u:vn");
if (ch == -1)
break;
switch (ch) {
@@ -280,6 +317,11 @@
usage();
g_gate_verbose++;
break;
+ case 'n':
+ if (action != CREATE)
+ usage();
+ delete_zero = 0;
+ break;
default:
usage();
}
Index: sbin/ggate/ggatel/ggatel2.c
===================================================================
diff --git a/stable/10/sbin/ggate/ggatel/ggatel2.c
b/stable/10/sbin/ggate/ggatel/ggatel2.c
new file mode 10644
--- /dev/null (nonexistent)
+++ b/stable/10/sbin/ggate/ggatel/ggatel2.c (working copy)
@@ -0,0 +1,724 @@
+/*
+Copyright (c) 2016, Julian Hsiao <julian at hsiao.email>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <math.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <assert.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <err.h>
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/bio.h>
+#include <sys/disk.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/syslog.h>
+
+#include <geom/gate/g_gate.h>
+#include "ggate.h"
+
+/* ======== */
+
+/*
+ Doesn't work with 3.4.1; clang-devel is currently 3.9.0, and I couldn't be
+ bothered to find out which version support was first added.
+*/
+#if defined(__clang__) && \
+ (__clang_major__ > 3 || \
+ (__clang_major__ == 3 && __clang_minor__ >= 9))
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Weverything"
+#endif
+
+/* ======== */
+
+int check_divs(const char *const, unsigned int *const, size_t *const,
+ size_t *const);
+void g_gatel_serve_bundle(const int , const unsigned int, const size_t,
+ const size_t, const int, const int, const unsigned int);
+
+/* ======== */
+
+static void
+g_gate_verbose_log(const int v, const int p, const char *const m, ...)
+{
+ if (g_gate_verbose >= v) {
+ va_list ap;
+ va_start(ap, m);
+ g_gate_vlog(p, m, ap);
+ va_end(ap);
+ }
+}
+
+#ifdef NDEBUG
+__attribute__((unused))
+#endif
+static inline bool
+mul_overflow(const size_t a, const size_t b)
+{
+ return(a != 0 && (a * b) / a != b);
+}
+
+static unsigned int
+MINDIV_SIZE(void)
+{
+ static unsigned int ps;
+ if (ps == 0) {
+ const long ps2 = sysconf(_SC_PAGESIZE);
+ static_assert(sizeof(size_t) >= sizeof(long), "");
+ assert(ps2 > 0);
+ assert(ps2 <= UINT_MAX);
+ ps = (unsigned int) ps2;
+ }
+ return(ps);
+}
+
+static size_t
+DIV_NAME_BUFSIZE(void)
+{
+ static size_t dnbs;
+ if (dnbs == 0) {
+ dnbs = (size_t) (ceil(log(SIZE_MAX) / log(16)));
+ ++dnbs;
+ dnbs *= sizeof(char);
+
+ }
+ return(dnbs);
+}
+
+/* ======== */
+
+static void
+numtohexstr(const size_t num, char *const buf, const size_t buflen)
+{
+#ifdef NDEBUG
+ __attribute__((unused))
+#endif
+ const int r = snprintf(buf, buflen, "%zx", num);
+ assert(r > 0);
+ assert((unsigned int) r < buflen);
+}
+
+int
+check_divs(const char *const bundle, unsigned int *const blk_size,
+ size_t *const div_size, size_t *const num_divs)
+{
+ assert(blk_size != NULL);
+ assert(div_size != NULL);
+ assert(num_divs != NULL);
+
+ int dfd;
+ if ((dfd = open(bundle, O_RDONLY | O_DIRECTORY | O_CLOEXEC)) == -1) {
+ err(5, "open(%s) failed", bundle);
+ }
+ char *buf = malloc(DIV_NAME_BUFSIZE());
+ if (buf == NULL) {
+ err(5, "malloc(DIV_NAME_BUFSIZE) failed");
+ }
+
+ for (*num_divs = 0; *num_divs < SIZE_MAX; ++(*num_divs)) {
+ int fd;
+ struct stat sb = { .st_dev = 0 };
+
+ numtohexstr(*num_divs, buf, DIV_NAME_BUFSIZE());
+ if ((fd = openat(dfd, buf, O_RDONLY | O_CLOEXEC)) == -1) {
+ if (errno == ENOENT) {
+ break;
+ }
+ err(5, "open(%s/%s) failed", bundle, buf);
+ }
+ if (fstat(fd, &sb) == -1) {
+ err(5, "fstat(%s/%s) failed", bundle, buf);
+ }
+
+ if (S_ISCHR(sb.st_mode)) {
+ if (ioctl(fd, DIOCGMEDIASIZE, &sb.st_size) == -1) {
+ err(5, "ioctl(%s/%s, DIOCGMEDIASIZE) failed", bundle, buf);
+ }
+
+ unsigned int bs;
+ if (ioctl(fd, DIOCGSECTORSIZE, &bs) == -1) {
+ err(5, "ioctl(%s/%s, DIOCGSECTORSIZE) failed", bundle, buf);
+ }
+ if (*blk_size == 0) {
+ *blk_size = bs;
+ } else if (*blk_size != bs) {
+ errx(5, "sector size of %s/%s (%u bytes) is not the same as "
+ "requested size or that of other divs (%u bytes).",
+ bundle, buf, bs, *blk_size);
+ }
+ } else if (!S_ISREG(sb.st_mode)) {
+ errx(5, "%s/%s must be a file or character device.", bundle, buf);
+ }
+
+ if (close(fd) == -1) {
+ err(5, "close(%s/%s) failed", bundle, buf);
+ }
+
+ assert(sb.st_size > 0);
+ static_assert(sizeof(size_t) >= sizeof(sb.st_size), "");
+ const size_t st_size = (size_t) sb.st_size;
+
+ if (st_size < MINDIV_SIZE()) {
+ errx(5, "size of %s/%s is less than %u bytes.",
+ bundle, buf, MINDIV_SIZE());
+ }
+ if (st_size % MINDIV_SIZE() != 0) {
+ errx(5, "size of %s/%s is not a multiple of %u.",
+ bundle, buf, MINDIV_SIZE());
+ }
+
+ if (*num_divs == 0) {
+ *div_size = st_size;
+ } else if (st_size != *div_size) {
+ errx(5, "%s/%s is not the same size as other divs (%zu bytes).",
+ bundle, buf, *div_size);
+ }
+ }
+
+ if (*num_divs == 0) {
+ errx(5, "No divs found in %s.", bundle);
+ }
+
+ *blk_size = (*blk_size == 0) ? MINDIV_SIZE() : *blk_size;
+ if (*blk_size < MINDIV_SIZE()) {
+ errx(5, "sector size must be at least %u bytes.", MINDIV_SIZE());
+ }
+ if (*blk_size % MINDIV_SIZE() != 0) {
+ errx(5, "sector size must be a multiple of %u bytes.", MINDIV_SIZE());
+ }
+ if (*blk_size > *div_size) {
+ errx(5, "sector size cannot be greater than div size (%zu bytes).",
+ *div_size);
+ }
+
+ struct g_gate_ctl_create ggcc = { .gctl_mediasize = 0 };
+ static_assert(sizeof(long) == sizeof(ggcc.gctl_mediasize), "");
+ assert(!mul_overflow(*div_size, *num_divs));
+ assert(*div_size * *num_divs <= LONG_MAX);
+ static_assert(sizeof(unsigned int) == sizeof(ggcc.gctl_sectorsize), "");
+ g_gate_verbose_log(1, LOG_DEBUG, "blk_size = %u", *blk_size);
+ g_gate_verbose_log(1, LOG_DEBUG, "div_size = %zu", *div_size);
+ g_gate_verbose_log(1, LOG_DEBUG, "num_divs = %zu", *num_divs);
+
+ free(buf);
+ return(dfd);
+}
+
+static void
+map_fd(void *const addr, const int fd, const int prot,
+#ifdef NDEBUG
+ __attribute__((unused))
+#endif
+ const size_t div_size)
+{
+ assert(div_size != 0);
+
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ err(1, "fstat() failed");
+ }
+
+ assert(sb.st_size > 0);
+ static_assert(sizeof(size_t) >= sizeof(sb.st_size), "");
+ const size_t st_size = (size_t) sb.st_size;
+ assert(st_size == div_size);
+ assert(st_size % MINDIV_SIZE() == 0);
+
+ void *m;
+ const int flags = MAP_SHARED | MAP_FIXED | MAP_NOCORE/* | MAP_NOSYNC*/;
+ if ((m = mmap(addr, st_size, prot, flags, fd, 0)) == MAP_FAILED) {
+ err(1, "mmap() failed");
+ }
+}
+
+static void
+map_bundle(const uintptr_t base, const uintptr_t addr, const size_t div_size,
+ const int bundlefd, const int open_flags)
+{
+ assert(base % MINDIV_SIZE() == 0);
+ assert(addr % MINDIV_SIZE() == 0);
+ assert(addr >= base);
+ assert((addr - base) % MINDIV_SIZE() == 0);
+
+ int divfd;
+
+ static char *div;
+ if (div == NULL) {
+ div = malloc(DIV_NAME_BUFSIZE());
+ assert(div != NULL);
+ }
+ numtohexstr((addr - base) / div_size, div, DIV_NAME_BUFSIZE());
+
+ g_gate_verbose_log(3, LOG_DEBUG,
+ "-> [ 0x%09" PRIxPTR ", 0x%09" PRIxPTR " ): 0x%lx; %s",
+ addr, addr + div_size, div_size, div);
+
+ if ((divfd = openat(bundlefd, div, open_flags | O_CLOEXEC)) == -1) {
+ err(6, "open(%s) failed", div);
+ }
+
+ int prot;
+ switch (open_flags & O_ACCMODE) {
+ case O_RDWR:
+ prot = PROT_READ | PROT_WRITE;
+ break;
+ case O_RDONLY:
+ prot = PROT_READ;
+ break;
+ case O_WRONLY:
+ prot = PROT_WRITE;
+ break;
+ default:
+ errx(6, "unknown open() flags: %d", open_flags);
+ break;
+ }
+ map_fd((void *) addr, divfd, prot, div_size);
+ if (close(divfd) == -1) {
+ err(6, "close(%s) failed", div);
+ }
+}
+
+static void *
+resv_vaddr(void *const addr, const size_t len)
+{
+ void *p;
+ const int prot = PROT_NONE;
+ const int flags = MAP_PRIVATE | MAP_ANON |
+ ((addr == NULL) ? 0 : MAP_FIXED);
+ if ((p = mmap(addr, len, prot, flags, -1, 0)) == MAP_FAILED) {
+ err(2, "mmap() failed");
+ }
+
+ return(p);
+}
+
+/* ======== */
+
+static sigjmp_buf memfault_env;
+static siginfo_t memfault_info;
+
+__attribute__((noreturn)) static void
+memfault_hdl(int sig, siginfo_t *info, __attribute__((unused)) void *uap)
+{
+ memfault_info = *info;
+ siglongjmp(memfault_env, sig);
+}
+
+static void
+install_memfault_hdl()
+{
+ struct sigaction a = {
+ .sa_sigaction = memfault_hdl,
+ .sa_flags = SA_SIGINFO
+ };
+ if (sigaction(SIGSEGV, &a, NULL) == -1) {
+ err(3, "sigaction(SIGSEGV) failed");
+ }
+ struct sigaction b = {
+ .sa_sigaction = memfault_hdl,
+ .sa_flags = SA_SIGINFO
+ };
+ if (sigaction(SIGBUS, &b, NULL) == -1) {
+ err(3, "sigaction(SIGBUS) failed");
+ }
+
+ // Use uncatchable signal as memfault_hdl installed flag
+ memfault_info.si_signo = SIGKILL;
+}
+
+static void
+check_expected_memfault(const void *const as, const void *const ae)
+{
+ if (memfault_info.si_signo == SIGKILL) {
+ errx(4, "memfault_info not initialized!");
+ } else if (memfault_info.si_signo != SIGSEGV &&
+ memfault_info.si_signo != SIGBUS) {
+ errx(4, "unexpected %s", strsignal(memfault_info.si_signo));
+ } else if (
+ !((as <= memfault_info.si_addr) && (memfault_info.si_addr < ae))
+ ) {
+ errx(4, "unexpected address %p", memfault_info.si_addr);
+ }
+}
+
+/* ======== */
+
+static void
+msync2(const void *const a, const size_t n, const int f)
+{
+ const uintptr_t b = (uintptr_t) a;
+ const size_t m = b % MINDIV_SIZE();
+ const uintptr_t c = b - m;
+ const size_t nn = n + m;
+
+ g_gate_verbose_log(3, LOG_DEBUG,
+ " msync(0x%09" PRIxPTR ", 0x%08lx)", c, nn);
+
+ if (nn > 0 && msync((void *) c, nn, f) == -1) {
+ err(8, "msync() failed");
+ }
+}
+
+__attribute__((unused)) static void *
+memcpy_msync(void *const d, const void *const s, const size_t n)
+{
+ const uintptr_t dd = (uintptr_t) d;
+ g_gate_verbose_log(3, LOG_DEBUG,
+ "memcpy(0x%09" PRIxPTR ", ..., 0x%08lx)", dd, n);
+
+ memcpy(d, s, n);
+ msync2(d, n, MS_SYNC);
+ return(d);
+}
+
+/* ======== */
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+struct bundle_spec {
+ size_t resv;
+ uintptr_t as;
+ uintptr_t ae;
+
+ int bundlefd;
+ size_t div_size;
+ size_t num_divs;
+ unsigned int blk_size;
+
+ int open_flags;
+ bool bio_delete;
+};
+#pragma clang diagnostic pop
+
+/* Too lazy to do LRU */
+#define mapped_addrs_size 100
+static void *mapped_addrs[mapped_addrs_size];
+
+static void
+update_mapped_addrs(const size_t div_size, void *const a)
+{
+ assert(mapped_addrs_size <= UINT_MAX);
+
+ const size_t i = arc4random_uniform((unsigned int) mapped_addrs_size);
+ if (mapped_addrs[i] != NULL) {
+ resv_vaddr(mapped_addrs[i], div_size);
+
+ const uintptr_t mai = (uintptr_t) mapped_addrs[i];
+ g_gate_verbose_log(3, LOG_DEBUG,
+ "<- [ 0x%09" PRIxPTR ", 0x%09" PRIxPTR " ): 0x%lx",
+ mai, mai + div_size, div_size);
+ }
+ mapped_addrs[i] = a;
+}
+
+static void
+do_read(const struct bundle_spec *const bspec,
+ struct g_gate_ctl_io *const ggio)
+{
+ static_assert(sizeof(ggio->gctl_length) <= sizeof(size_t), "");
+ static_assert(sizeof(ggio->gctl_offset) <= sizeof(uintptr_t), "");
+ assert(ggio->gctl_length >= 0);
+ assert(ggio->gctl_offset >= 0);
+
+ assert(memfault_info.si_signo == SIGKILL);
+
+ assert(!mul_overflow(bspec->div_size, mapped_addrs_size));
+ const size_t max_len = bspec->div_size * mapped_addrs_size;
+ if ((size_t) ggio->gctl_length > max_len) {
+ ggio->gctl_error = ENOMEM;
+ return;
+ }
+
+ const uintptr_t a = bspec->as + (uintptr_t) ggio->gctl_offset;
+ assert(a >= bspec->as);
+ assert(a < bspec->ae);
+ const uintptr_t b = a + (uintptr_t) ggio->gctl_length;
+ assert(b <= bspec->ae);
+ const size_t m = ((size_t) ggio->gctl_offset) % bspec->div_size;
+
+ for (;;) {
+ volatile uintptr_t c = (volatile uintptr_t) NULL;
+ if (sigsetjmp(memfault_env, 1) == 0) {
+ for (c = a - m; c < b; c += bspec->div_size) {
+ static_assert(CHAR_BIT == 8, "");
+ volatile unsigned char *d1 = (volatile unsigned char *) c;
+ __attribute__((unused)) volatile unsigned char d2 = *d1;
+ }
+ break;
+ } else {
+ check_expected_memfault((void *) bspec->as, (void *) bspec->ae);
+ const uintptr_t si_addr = (uintptr_t) memfault_info.si_addr;
+ memfault_info.si_signo = SIGKILL;
+
+ assert((void *) c != NULL);
+ assert(si_addr == c);
+
+ // UB??
+ assert(bspec->as <= si_addr);
+ const size_t n = (si_addr - bspec->as) % bspec->div_size;
+ const uintptr_t d = si_addr - n;
+ assert(d <= si_addr);
+ assert(si_addr < d + 2 * bspec->div_size);
+
+ assert(d >= bspec->as);
+ assert(d < bspec->ae);
+ assert((d - bspec->as) % bspec->div_size == 0);
+
+ update_mapped_addrs(bspec->div_size, (void *) d);
+
+ assert(d % bspec->blk_size == 0);
+ map_bundle(bspec->as, d, bspec->div_size, bspec->bundlefd,
+ bspec->open_flags);
+ }
+ }
+
+ g_gate_verbose_log(2, LOG_DEBUG, "do_read(0x%lx, 0x%lx)",
+ ggio->gctl_offset, ggio->gctl_length);
+ ggio->gctl_data = (void *) a;
+ ggio->gctl_error = 0;
+}
+
+static void
+do_write(const struct bundle_spec *const bspec,
+ struct g_gate_ctl_io *const ggio, const bool zero)
+{
+ static void *zs_;
+ if (zero && zs_ == NULL) {
+ zs_ = calloc(1, bspec->blk_size);
+ assert(zs_ != NULL);
+ }
+ const void *const zeros = zs_;
+
+ static_assert(sizeof(ggio->gctl_length) <= sizeof(size_t), "");
+ static_assert(sizeof(ggio->gctl_offset) <= sizeof(uintptr_t), "");
+ assert(ggio->gctl_length >= 0);
+ assert(ggio->gctl_offset >= 0);
+
+ assert(memfault_info.si_signo == SIGKILL);
+ assert(((size_t) ggio->gctl_length) % bspec->blk_size == 0);
+
+ uintptr_t d = bspec->as + (uintptr_t) ggio->gctl_offset;
+ assert(d >= bspec->as);
+ assert(d < bspec->ae);
+ uintptr_t s = (uintptr_t) ggio->gctl_data;
+ size_t len = (size_t) ggio->gctl_length;
+
+ for (;;) {
+ if (sigsetjmp(memfault_env, 1) == 0) {
+ g_gate_verbose_log(3, LOG_DEBUG,
+ "memcpy(0x%09" PRIxPTR ", ..., 0x%08lx)", d, len);
+ if (zero) {
+ assert(zeros != NULL);
+ for (size_t i = 0; i < len; i += bspec->blk_size) {
+ memcpy((void *) (d + i), zeros, bspec->blk_size);
+ }
+ } else {
+ memcpy((void *) d, (void *) s, len);
+ }
+ break;
+ } else {
+ check_expected_memfault((void *) bspec->as, (void *) bspec->ae);
+ const uintptr_t si_addr = (uintptr_t) memfault_info.si_addr;
+ memfault_info.si_signo = SIGKILL;
+
+ // UB??
+ assert(bspec->as <= si_addr);
+ const size_t m = (si_addr - bspec->as) % bspec->div_size;
+ const uintptr_t a = si_addr - m;
+ assert(a <= si_addr);
+ assert(si_addr < a + 2 * bspec->div_size);
+
+ update_mapped_addrs(bspec->div_size, (void *) a);
+
+ assert(a % bspec->blk_size == 0);
+ map_bundle(bspec->as, a, bspec->div_size, bspec->bundlefd,
+ bspec->open_flags);
+
+ // More UB??
+ assert(d <= si_addr);
+ assert(len >= si_addr - d);
+ len = len - (si_addr - d);
+ s = s + (si_addr - d);
+ d = si_addr;
+ }
+ }
+
+ g_gate_verbose_log(2, LOG_DEBUG, "do_write(0x%lx, 0x%lx)",
+ ggio->gctl_offset, ggio->gctl_length);
+ ggio->gctl_error = 0;
+}
+
+static void
+do_delete(const struct bundle_spec *const bspec,
+ struct g_gate_ctl_io *const ggio)
+{
+ if (bspec->bio_delete) {
+ g_gate_verbose_log(2, LOG_DEBUG, "do_delete() => do_write()");
+ do_write(bspec, ggio, true);
+ } else {
+ g_gate_verbose_log(2, LOG_DEBUG, "do_delete() => EOPNOTSUPP");
+ ggio->gctl_error = EOPNOTSUPP;
+ }
+}
+
+static void
+do_flush(const struct bundle_spec *const bspec,
+ __attribute__((unused)) const struct g_gate_ctl_io *const ggio)
+{
+ if (g_gate_verbose >= 4) {
+ for (size_t i = 0; i < mapped_addrs_size; ++i) {
+ const void *ma = mapped_addrs[i];
+ if (ma != NULL) {
+ g_gate_verbose_log(4, LOG_DEBUG,
+ "0x%09" PRIxPTR, (uintptr_t) ma);
+ }
+ }
+ }
+
+ g_gate_verbose_log(2, LOG_DEBUG, "do_flush()");
+ msync2((void *) bspec->as, bspec->resv, MS_SYNC);
+}
+
+__attribute__((noreturn)) void
+g_gatel_serve_bundle(const int dfd_, const unsigned int ss_, const size_t ds_,
+ const size_t nd_, const int unit, const int fs_, const unsigned int dz_)
+{
+ freopen("/dev/null", "r", stdin);
+ if (g_gate_verbose == 0) {
+ if (daemon(0, 0) == -1) {
+ g_gate_destroy(unit, 1);
+ err(EXIT_FAILURE, "Cannot daemonize");
+ }
+ freopen("/dev/null", "w", stdout);
+ freopen("/dev/null", "w", stderr);
+ }
+ g_gate_verbose_log(1, LOG_DEBUG, "Worker created: %u.", getpid());
+
+ assert(dfd_ != -1);
+ assert(!mul_overflow(ds_, nd_));
+ struct bundle_spec bspec = {
+ .resv = ds_ * nd_,
+ .bundlefd = dfd_,
+ .div_size = ds_,
+ .num_divs = nd_,
+ .blk_size = ss_,
+ .open_flags = fs_,
+ .bio_delete = (bool) dz_
+ };
+ bspec.as = (uintptr_t) (resv_vaddr(NULL, bspec.resv));
+ bspec.ae = bspec.as + bspec.resv;
+
+ g_gate_verbose_log(1, LOG_DEBUG,
+ "[ 0x%09" PRIxPTR ", 0x%09" PRIxPTR " ): 0x%lx",
+ bspec.as, bspec.ae, bspec.resv);
+
+ assert(bspec.blk_size > 0);
+ static_assert(sizeof(bspec.blk_size) <= sizeof(size_t), "");
+ void *ggio_data_buf;
+ size_t ggio_data_buf_len = (size_t) bspec.blk_size;
+ if ((ggio_data_buf = malloc(ggio_data_buf_len)) == NULL) {
+ err(EXIT_FAILURE, "malloc() failed");
+ }
+
+ install_memfault_hdl();
+
+ for (;;) {
+ struct g_gate_ctl_io ggio = {
+ .gctl_version = G_GATE_VERSION,
+ .gctl_unit = unit,
+ .gctl_data = (void *) ggio_data_buf,
+ .gctl_length = (off_t) ggio_data_buf_len
+ };
+
+ g_gate_ioctl(G_GATE_CMD_START, &ggio);
+
+ switch (ggio.gctl_error) {
+ case 0:
+ break;
+ case ECANCELED:
+ g_gate_close_device();
+ if (close(bspec.bundlefd) == -1) {
+ err(EXIT_FAILURE, "close() failed");
+ }
+ do_flush(&bspec, &ggio);
+ free(ggio_data_buf);
+ g_gate_verbose_log(1, LOG_DEBUG, "Finished.");
+ exit(EXIT_SUCCESS);
+ case ENOMEM:
+ assert(ggio.gctl_cmd == BIO_WRITE
+ || (ggio.gctl_cmd == BIO_DELETE && bspec.bio_delete));
+ assert(ggio.gctl_length > 0);
+ static_assert(sizeof(ggio.gctl_length) <=
+ sizeof(ggio_data_buf_len), "");
+ ggio_data_buf_len = (size_t) ggio.gctl_length;
+ ggio_data_buf = realloc(ggio_data_buf, ggio_data_buf_len);
+ if (ggio_data_buf == NULL) {
+ err(EXIT_FAILURE, "realloc() failed");
+ }
+ continue;
+ case ENXIO:
+ default:
+ g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME,
+ strerror(ggio.gctl_error));
+ }
+
+ switch(ggio.gctl_cmd) {
+ case BIO_READ:
+ do_read(&bspec, &ggio);
+ break;
+ case BIO_WRITE:
+ do_write(&bspec, &ggio, false);
+ break;
+ case BIO_DELETE:
+ do_delete(&bspec, &ggio);
+ break;
+ case BIO_FLUSH:
+ do_flush(&bspec, &ggio);
+ break;
+ default:
+ g_gate_verbose_log(1, LOG_DEBUG, "unsupported: %d", ggio.gctl_cmd);
+ ggio.gctl_error = EOPNOTSUPP;
+ break;
+ }
+
+ g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
+ g_gate_verbose_log(3, LOG_DEBUG, "========");
+ }
+}
Property changes on: stable/10/sbin/ggate/ggatel/ggatel2.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
More information about the freebsd-hackers
mailing list