From owner-freebsd-hackers@freebsd.org Thu Jul 7 01:29:59 2016 Return-Path: Delivered-To: freebsd-hackers@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 19032B21ACA for ; Thu, 7 Jul 2016 01:29:59 +0000 (UTC) (envelope-from freebsd-hackers@m.gmane.org) Received: from plane.gmane.org (plane.gmane.org [80.91.229.3]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id B88A2118A for ; Thu, 7 Jul 2016 01:29:58 +0000 (UTC) (envelope-from freebsd-hackers@m.gmane.org) Received: from list by plane.gmane.org with local (Exim 4.69) (envelope-from ) id 1bKy8S-00021F-GS for freebsd-hackers@freebsd.org; Thu, 07 Jul 2016 03:29:48 +0200 Received: from ip184-189-249-34.sb.sd.cox.net ([184.189.249.34]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Thu, 07 Jul 2016 03:29:48 +0200 Received: from julian by ip184-189-249-34.sb.sd.cox.net with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Thu, 07 Jul 2016 03:29:48 +0200 X-Injected-Via-Gmane: http://gmane.org/ To: freebsd-hackers@freebsd.org From: Julian Hsiao Subject: Re: ggatel(8) extension for binding multiple files Date: Thu, 7 Jul 2016 01:29:34 +0000 (UTC) Lines: 913 Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Complaints-To: usenet@ger.gmane.org X-Gmane-NNTP-Posting-Host: sea.gmane.org User-Agent: Loom/3.14 (http://gmane.org/) X-Loom-IP: 184.189.249.34 (Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:48.0) Gecko/20100101 Firefox/48.0) X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 07 Jul 2016 01:29:59 -0000 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 #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 ] " + fprintf(stderr, "usage: %s create [-v] [-n] [-o ] " "[-s sectorsize] [-t timeout] [-u unit] \n", getprogname()); fprintf(stderr, " %s rescue [-v] [-o ] <-u unit> " "\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 +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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