[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 05 of 21] blktap3/drivers: Introduce logging for the tapdisk
This patch copies logging functionality from blktap2, with changes coming from blktap2.5. Signed-off-by: Thanos Makatos <thanos.makatos@xxxxxxxxxx> diff --git a/tools/blktap2/drivers/log.h b/tools/blktap3/drivers/log.h copy from tools/blktap2/drivers/log.h copy to tools/blktap3/drivers/log.h --- a/tools/blktap2/drivers/log.h +++ b/tools/blktap3/drivers/log.h @@ -32,10 +32,7 @@ #define __LOG_H__ 1 #include <inttypes.h> - #include <xen/io/ring.h> -/* for wmb et al */ -#include <xenctrl.h> #define LOGCMD_SHMP "shmp" #define LOGCMD_PEEK "peek" diff --git a/tools/blktap2/drivers/tapdisk-client.c b/tools/blktap3/drivers/tapdisk-client.c copy from tools/blktap2/drivers/tapdisk-client.c copy to tools/blktap3/drivers/tapdisk-client.c --- a/tools/blktap2/drivers/tapdisk-client.c +++ b/tools/blktap3/drivers/tapdisk-client.c @@ -39,6 +39,7 @@ #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> +#include <xenctrl.h> #include "log.h" @@ -109,7 +110,7 @@ static int tdctl_open(const char* sockpa saddr.sun_family = AF_UNIX; memcpy(saddr.sun_path, sockpath, strlen(sockpath)); - if (connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { + if (connect(fd, &saddr, sizeof(saddr)) < 0) { BWPRINTF("error connecting to socket %s: %s", sockpath, strerror(errno)); close(fd); return -1; @@ -392,7 +393,7 @@ int await_responses(struct writelog* wl, BWPRINTF("EOF on control socket"); return -1; } else if (rc < sizeof(msg)) { - BWPRINTF("short reply (%d/%d bytes)", rc, (int) sizeof(msg)); + BWPRINTF("short reply (%d/%lu bytes)", rc, sizeof(msg)); return -1; } diff --git a/tools/blktap2/drivers/tapdisk-log.c b/tools/blktap3/drivers/tapdisk-log.c copy from tools/blktap2/drivers/tapdisk-log.c copy to tools/blktap3/drivers/tapdisk-log.c --- a/tools/blktap2/drivers/tapdisk-log.c +++ b/tools/blktap3/drivers/tapdisk-log.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, XenSource Inc. + * Copyright (c) 2008, 2009, XenSource Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,232 +26,241 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <stdio.h> -#include <fcntl.h> +#include <stdlib.h> #include <unistd.h> -#include <stdlib.h> -#include <string.h> +#include <errno.h> #include <stdarg.h> #include <syslog.h> -#include <inttypes.h> #include <sys/time.h> +#include <sys/stat.h> +#include <sys/types.h> #include "tapdisk-log.h" #include "tapdisk-utils.h" +#include "tapdisk-logfile.h" +#include "tapdisk-syslog.h" +#include "tapdisk-server.h" + +#define TLOG_LOGFILE_BUFSZ (16<<10) +#define TLOG_SYSLOG_BUFSZ (8<<10) #define MAX_ENTRY_LEN 512 -#define MAX_ERROR_MESSAGES 16 -struct error { - int cnt; - int err; - char *func; - char msg[MAX_ENTRY_LEN]; +struct tlog { + char *name; + td_logfile_t logfile; + int precious; + int level; + + char *ident; + td_syslog_t syslog; + unsigned long errors; }; -struct ehandle { - int cnt; - int dropped; - struct error errors[MAX_ERROR_MESSAGES]; -}; - -struct tlog { - char *p; - int size; - uint64_t cnt; - char *buf; - int level; - char *file; - int append; -}; - -static struct ehandle tapdisk_err; static struct tlog tapdisk_log; -void -open_tlog(char *file, size_t bytes, int level, int append) +static void tlog_logfile_vprint(const char *fmt, va_list ap) { - tapdisk_log.size = ((bytes + 511) & (~511)); + tapdisk_logfile_vprintf(&tapdisk_log.logfile, fmt, ap); +} - if (asprintf(&tapdisk_log.file, "%s.%d", file, getpid()) == -1) - return; +static void __printf(1, 2) tlog_logfile_print(const char *fmt, ...) +{ + va_list ap; - if (posix_memalign((void **)&tapdisk_log.buf, 512, tapdisk_log.size)) { - free(tapdisk_log.file); - tapdisk_log.buf = NULL; - return; + va_start(ap, fmt); + tlog_logfile_vprint(fmt, ap); + va_end(ap); } - memset(tapdisk_log.buf, 0, tapdisk_log.size); +#define tlog_info(_fmt, _args ...) \ + tlog_logfile_print("%s: "_fmt, tapdisk_log.ident, ##_args) - tapdisk_log.p = tapdisk_log.buf; - tapdisk_log.level = level; - tapdisk_log.append = append; +static void tlog_logfile_save(void) +{ + td_logfile_t *logfile = &tapdisk_log.logfile; + const char *name = tapdisk_log.name; + int err; + + tlog_info("saving log, %lu errors", tapdisk_log.errors); + + tapdisk_logfile_flush(logfile); + + err = tapdisk_logfile_rename(logfile, TLOG_DIR, name, ".log"); + + tlog_syslog(LOG_INFO, "logfile saved to %s: %d\n", logfile->path, err); +} + +static void tlog_logfile_close(void) +{ + td_logfile_t *logfile = &tapdisk_log.logfile; + int keep; + + keep = tapdisk_log.precious || tapdisk_log.errors; + + tlog_info("closing log, %lu errors", tapdisk_log.errors); + + if (keep) + tlog_logfile_save(); + + tapdisk_logfile_close(logfile); + + if (!keep) + tapdisk_logfile_unlink(logfile); +} + +static int tlog_logfile_open(const char *name, int level) +{ + td_logfile_t *logfile = &tapdisk_log.logfile; + int mode, err; + + err = mkdir(TLOG_DIR, 0755); + if (err) { + err = -errno; + if (err != -EEXIST) + goto fail; + } + + err = tapdisk_logfile_open(logfile, + TLOG_DIR, name, ".tmp", TLOG_LOGFILE_BUFSZ); + if (err) + goto fail; + + mode = (level == TLOG_DBG) ? _IOLBF : _IOFBF; + + err = tapdisk_logfile_setvbuf(logfile, mode); + if (err) + goto fail; + + tlog_info("log start, level %d", level); + + return 0; + + fail: + tlog_logfile_close(); + return err; +} + +static void tlog_syslog_close(void) +{ + td_syslog_t *syslog = &tapdisk_log.syslog; + + tapdisk_syslog_stats(syslog, LOG_INFO); + tapdisk_syslog_flush(syslog); + tapdisk_syslog_close(syslog); + } + +static int tlog_syslog_open(const char *ident __attribute__((unused)), + int facility) +{ + td_syslog_t *syslog = &tapdisk_log.syslog; + int err; + + err = tapdisk_syslog_open(syslog, + tapdisk_log.ident, facility, + TLOG_SYSLOG_BUFSZ); + return err; + } + +void tlog_vsyslog(int prio, const char *fmt, va_list ap) +{ + td_syslog_t *syslog = &tapdisk_log.syslog; + + tapdisk_vsyslog(syslog, prio, fmt, ap); +} + +void tlog_syslog(int prio, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + tlog_vsyslog(prio, fmt, ap); + va_end(ap); +} + +int +tlog_open(const char *name, int facility, int level) +{ + int err; + + DPRINTF("tapdisk-log: started, level %d\n", level); + + tapdisk_log.level = level; + tapdisk_log.name = strdup(name); + tapdisk_log.ident = tapdisk_syslog_ident(name); + + if (!tapdisk_log.name || !tapdisk_log.ident) { + err = -errno; + goto fail; + } + + err = tlog_logfile_open(tapdisk_log.name, level); + if (err) + goto fail; + + err = tlog_syslog_open(tapdisk_log.ident, facility); + if (err) + goto fail; + + return 0; + + fail: + tlog_close(); + return err; } void -close_tlog(void) +tlog_close(void) { - if (!tapdisk_log.buf) - return; + DPRINTF("tapdisk-log: closing after %lu errors\n", tapdisk_log.errors); - if (tapdisk_log.append) - tlog_flush(); + tlog_logfile_close(); + tlog_syslog_close(); - free(tapdisk_log.buf); - free(tapdisk_log.file); + free(tapdisk_log.ident); + tapdisk_log.ident = NULL; +} - memset(&tapdisk_log, 0, sizeof(struct tlog)); +void tlog_precious(void) +{ + if (!tapdisk_log.precious) + tlog_logfile_save(); + + tapdisk_log.precious = 1; } void -__tlog_write(int level, const char *func, const char *fmt, ...) +__tlog_write(int level, const char *fmt, ...) { - char *buf; - va_list ap; - struct timeval t; - int ret, len, avail; + va_list ap; - if (!tapdisk_log.buf) - return; - - if (level > tapdisk_log.level) - return; - - avail = tapdisk_log.size - (tapdisk_log.p - tapdisk_log.buf); - if (avail < MAX_ENTRY_LEN) { - if (tapdisk_log.append) - tlog_flush(); - tapdisk_log.p = tapdisk_log.buf; - } - - buf = tapdisk_log.p; - gettimeofday(&t, NULL); - len = snprintf(buf, MAX_ENTRY_LEN - 1, "%08"PRIu64":%010ld.%06lld:" - "%s ", tapdisk_log.cnt, - t.tv_sec, (unsigned long long)t.tv_usec, func); - - va_start(ap, fmt); - ret = vsnprintf(buf + len, MAX_ENTRY_LEN - (len + 1), fmt, ap); - va_end(ap); - - len = (ret < MAX_ENTRY_LEN - (len + 1) ? - len + ret : MAX_ENTRY_LEN - 1); - buf[len] = '\0'; - - tapdisk_log.cnt++; - tapdisk_log.p += len; + if (level <= tapdisk_log.level) { + va_start(ap, fmt); + tlog_logfile_vprint(fmt, ap); + va_end(ap); + } } -void -__tlog_error(int err, const char *func, const char *fmt, ...) +void __tlog_error(const char *fmt, ...) { - va_list ap; - int i, len, ret; - struct error *e; - struct timeval t; + va_list ap; - err = (err > 0 ? err : -err); + va_start(ap, fmt); + tlog_vsyslog(LOG_ERR, fmt, ap); + va_end(ap); - for (i = 0; i < tapdisk_err.cnt; i++) { - e = &tapdisk_err.errors[i]; - if (e->err == err && e->func == func) { - e->cnt++; - return; - } - } - - if (tapdisk_err.cnt >= MAX_ERROR_MESSAGES) { - tapdisk_err.dropped++; - return; - } - - gettimeofday(&t, NULL); - e = &tapdisk_err.errors[tapdisk_err.cnt]; - - len = snprintf(e->msg, MAX_ENTRY_LEN - 1, "%010ld.%06lld:%s ", - t.tv_sec, (unsigned long long)t.tv_usec, func); - - va_start(ap, fmt); - ret = vsnprintf(e->msg + len, MAX_ENTRY_LEN - (len + 1), fmt, ap); - va_end(ap); - - len = (ret < MAX_ENTRY_LEN - (len + 1) ? - len + ret : MAX_ENTRY_LEN - 1); - e->msg[len] = '\0'; - - e->cnt++; - e->err = err; - e->func = (char *)func; - tapdisk_err.cnt++; + tapdisk_log.errors++; } -void -tlog_print_errors(void) +void tapdisk_start_logging(const char *ident, const char *_facility) { - int i; - struct error *e; + int facility; - for (i = 0; i < tapdisk_err.cnt; i++) { - e = &tapdisk_err.errors[i]; - syslog(LOG_INFO, "TAPDISK ERROR: errno %d at %s (cnt = %d): " - "%s\n", e->err, e->func, e->cnt, e->msg); - } - - if (tapdisk_err.dropped) - syslog(LOG_INFO, "TAPDISK ERROR: %d other error messages " - "dropped\n", tapdisk_err.dropped); + facility = tapdisk_syslog_facility(_facility); + tapdisk_server_openlog(ident, LOG_CONS | LOG_ODELAY, facility); } -void -tlog_flush_errors(void) +void tapdisk_stop_logging(void) { - int i; - struct error *e; - - for (i = 0; i < tapdisk_err.cnt; i++) { - e = &tapdisk_err.errors[i]; - tlog_write(TLOG_WARN, "TAPDISK ERROR: errno %d at %s " - "(cnt = %d): %s\n", e->err, e->func, e->cnt, - e->msg); - } - - if (tapdisk_err.dropped) - tlog_write(TLOG_WARN, "TAPDISK ERROR: %d other error messages " - "dropped\n", tapdisk_err.dropped); + tapdisk_server_closelog(); } - -void -tlog_flush(void) -{ - int fd, flags; - size_t size, wsize; - - if (!tapdisk_log.buf) - return; - - flags = O_CREAT | O_WRONLY | O_DIRECT | O_NONBLOCK; - if (!tapdisk_log.append) - flags |= O_TRUNC; - - fd = open(tapdisk_log.file, flags, 0644); - if (fd == -1) - return; - - if (tapdisk_log.append) - if (lseek(fd, 0, SEEK_END) == (off_t)-1) - goto out; - - tlog_flush_errors(); - - size = tapdisk_log.p - tapdisk_log.buf; - wsize = ((size + 511) & (~511)); - - memset(tapdisk_log.buf + size, '\n', wsize - size); - write_exact(fd, tapdisk_log.buf, wsize); - - tapdisk_log.p = tapdisk_log.buf; - -out: - close(fd); -} diff --git a/tools/blktap3/drivers/tapdisk-logfile.c b/tools/blktap3/drivers/tapdisk-logfile.c new file mode 100644 --- /dev/null +++ b/tools/blktap3/drivers/tapdisk-logfile.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2009, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER + * 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 <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <time.h> +#include <stdarg.h> +#include <sys/time.h> +#include <sys/mman.h> + +#include "tapdisk-logfile.h" +#include "tapdisk-utils.h" + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +static inline size_t page_align(size_t size) +{ + size_t page_size = sysconf(_SC_PAGE_SIZE); + return (size + page_size - 1) & ~(page_size - 1); +} + +static void tapdisk_logfile_free_buffer(td_logfile_t * log) +{ + if (log->vbuf) { + munmap(log->vbuf, page_align(log->vbufsz)); + log->vbuf = NULL; + } +} + +static int tapdisk_logfile_init_buffer(td_logfile_t * log, size_t size) +{ + int prot, flags, err; + + if (!size) + return -EINVAL; + + prot = PROT_READ | PROT_WRITE; + flags = MAP_ANONYMOUS | MAP_PRIVATE; + + log->vbuf = mmap(NULL, page_align(size), prot, flags, -1, 0); + if (log->vbuf == MAP_FAILED) { + log->vbuf = NULL; + goto fail; + } + + err = mlock(log->vbuf, page_align(size)); + if (err) + goto fail; + + log->vbufsz = size; + + return 0; + + fail: + tapdisk_logfile_free_buffer(log); + err = -errno; + return err; +} + +int tapdisk_logfile_unlink(td_logfile_t * log) +{ + int err; + + err = unlink(log->path); + if (err) + err = -errno; + + return err; +} + +static int +__tapdisk_logfile_rename(td_logfile_t * log, const char *newpath) +{ + const size_t max = sizeof(log->path); + int err; + + if (!strcmp(log->path, newpath)) + return 0; + + if (strlen(newpath) > max) + return -ENAMETOOLONG; + + err = rename(log->path, newpath); + if (err) { + err = -errno; + return err; + } + + strncpy(log->path, newpath, max); + + return 0; +} + +static int +tapdisk_logfile_name(char *path, size_t size, + const char *dir, const char *ident, + const char *suffix) +{ + const size_t max = MIN(size, TD_LOGFILE_PATH_MAX); + return snprintf(path, max, "%s/%s.%d%s", dir, ident, getpid(), suffix); +} + +int +tapdisk_logfile_rename(td_logfile_t * log, + const char *dir, const char *ident, + const char *suffix) +{ + char newpath[TD_LOGFILE_PATH_MAX + 1]; + + tapdisk_logfile_name(newpath, sizeof(newpath), dir, ident, suffix); + + return __tapdisk_logfile_rename(log, newpath); +} + +void tapdisk_logfile_close(td_logfile_t * log) +{ + if (log->file) { + fclose(log->file); + log->file = NULL; + } + + tapdisk_logfile_free_buffer(log); +} + +int +tapdisk_logfile_open(td_logfile_t * log, + const char *dir, const char *ident, const char *ext, + size_t bufsz) +{ + int err; + + memset(log, 0, sizeof(log)); + + tapdisk_logfile_name(log->path, sizeof(log->path), dir, ident, ext); + + log->file = fopen(log->path, "w"); + if (!log->file) { + err = -errno; + goto fail; + } + + err = tapdisk_logfile_init_buffer(log, bufsz); + if (err) + goto fail; + + return 0; + + fail: + tapdisk_logfile_unlink(log); + tapdisk_logfile_close(log); + return err; +} + +int tapdisk_logfile_setvbuf(td_logfile_t * log, int mode) +{ + int err = 0; + + if (log->file) { + err = setvbuf(log->file, log->vbuf, mode, log->vbufsz); + if (err) + err = -errno; + } + + return err; +} + +ssize_t +tapdisk_logfile_vprintf(td_logfile_t * log, const char *fmt, va_list ap) +{ + char buf[1024]; + size_t size, n; + ssize_t len; + struct timeval tv; + + if (!log->file) + return -EBADF; + + gettimeofday(&tv, NULL); + + size = sizeof(buf); + len = 0; + + len += tapdisk_syslog_strftime(buf, size, &tv); + len += snprintf(buf + len, size - len, ": "); + len += tapdisk_syslog_strftv(buf + len, size - len, &tv); + len += snprintf(buf + len, size - len, " "); + len += vsnprintf(buf + len, size - len, fmt, ap); + + if (buf[len - 1] != '\n') + len += snprintf(buf + len, size - len, "\n"); + + n = fwrite(buf, len, 1, log->file); + if (n != len) + len = -ferror(log->file); + + return len; +} + +ssize_t tapdisk_logfile_printf(td_logfile_t * log, const char *fmt, ...) +{ + va_list ap; + int rv; + + va_start(ap, fmt); + rv = tapdisk_logfile_vprintf(log, fmt, ap); + va_end(ap); + + return rv; +} + +int tapdisk_logfile_flush(td_logfile_t * log) +{ + int rv = EOF; + + if (log->file) + rv = fflush(log->file); + + return rv; +} diff --git a/tools/blktap3/drivers/tapdisk-logfile.h b/tools/blktap3/drivers/tapdisk-logfile.h new file mode 100644 --- /dev/null +++ b/tools/blktap3/drivers/tapdisk-logfile.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2009, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER + * 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. + */ + +#ifndef __TAPDISK_LOGFILE_H__ +#define __TAPDISK_LOGFILE_H__ + +#include <stdio.h> + +typedef struct _td_logfile td_logfile_t; + +#define TD_LOGFILE_PATH_MAX 128UL + +struct _td_logfile { + char path[TD_LOGFILE_PATH_MAX]; + FILE *file; + char *vbuf; + size_t vbufsz; +}; + +int tapdisk_logfile_open(td_logfile_t *, + const char *dir, const char *ident, + const char *ext, size_t bufsz); + +ssize_t tapdisk_logfile_printf(td_logfile_t *, const char *fmt, ...); +ssize_t tapdisk_logfile_vprintf(td_logfile_t *, const char *fmt, + va_list ap); + +void tapdisk_logfile_close(td_logfile_t *); +int tapdisk_logfile_unlink(td_logfile_t *); +int tapdisk_logfile_rename(td_logfile_t *, + const char *dir, const char *ident, + const char *ext); + +int tapdisk_logfile_setvbuf(td_logfile_t * log, int mode); +int tapdisk_logfile_flush(td_logfile_t *); + +#endif /* __TAPDISK_LOGFILE_H__ */ diff --git a/tools/blktap3/drivers/tapdisk-loglimit.c b/tools/blktap3/drivers/tapdisk-loglimit.c new file mode 100644 --- /dev/null +++ b/tools/blktap3/drivers/tapdisk-loglimit.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2011, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER + * 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. + */ + +/* + * Simple log rate limiting. Allow for bursts, then drop messages + * until some interval expired. + */ + +#include <stdio.h> +#include "blktap3.h" +#include "tapdisk-loglimit.h" + +void tapdisk_loglimit_init(td_loglimit_t * rl, int burst, int interval) +{ + rl->burst = burst; + rl->interval = interval; + + rl->count = 0; + rl->dropped = 0; + + gettimeofday(&rl->ts, NULL); +} + +static void timeradd_ms(struct timeval *tv, long ms) +{ + tv->tv_usec += ms * 1000; + if (tv->tv_usec > 1000000) { + tv->tv_sec += tv->tv_usec / 1000000; + tv->tv_usec %= 1000000; + } +} + +static void +tapdisk_loglimit_update(td_loglimit_t * rl, struct timeval *now) +{ + struct timeval next = rl->ts; + + timeradd_ms(&next, rl->interval); + + if (timercmp(&next, now, <)) { + rl->count = 0; + rl->ts = *now; + } +} + +static void tapdisk_loglimit_update_now(td_loglimit_t * rl) +{ + struct timeval now; + + gettimeofday(&now, NULL); + + tapdisk_loglimit_update(rl, &now); +} + +int tapdisk_loglimit_pass(td_loglimit_t * rl) +{ + if (!rl->interval) + return 1; /* unlimited */ + + if (unlikely(rl->count >= rl->burst)) { + + tapdisk_loglimit_update_now(rl); + + if (rl->count >= rl->burst) { + rl->dropped++; + return 0; + } + } + + rl->count++; + return 1; +} diff --git a/tools/blktap3/drivers/tapdisk-loglimit.h b/tools/blktap3/drivers/tapdisk-loglimit.h new file mode 100644 --- /dev/null +++ b/tools/blktap3/drivers/tapdisk-loglimit.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER + * 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. + */ + +#ifndef __TAPDISK_LOGLIMIT_H__ +#define __TAPDISK_LOGLIMIT_H__ + +#include <sys/time.h> + +typedef struct td_loglimit td_loglimit_t; + +struct td_loglimit { + int burst; + int interval; + + int count; + int dropped; + + struct timeval ts; +}; + +void tapdisk_loglimit_init(td_loglimit_t * rl, int burst, int interval); + +int tapdisk_loglimit_pass(td_loglimit_t *); + +#endif /* __TAPDISK_LOGLIMIT_H__ */ diff --git a/tools/blktap3/drivers/tapdisk-syslog.c b/tools/blktap3/drivers/tapdisk-syslog.c new file mode 100644 --- /dev/null +++ b/tools/blktap3/drivers/tapdisk-syslog.c @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2009, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER + * 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. + */ + +/* + * A non-blocking, buffered BSD syslog client. + * + * http://www.ietf.org/rfc/rfc3164.txt (FIXME: Read this.) + */ + +#define _ISOC99_SOURCE +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <fcntl.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/param.h> + +#include "tapdisk-server.h" +#include "tapdisk-syslog.h" +#include "tapdisk-utils.h" + +static int tapdisk_syslog_sock_send(td_syslog_t * log, + const void *msg, size_t size); +static int tapdisk_syslog_sock_connect(td_syslog_t * log); + +static void tapdisk_syslog_sock_mask(td_syslog_t * log); +static void tapdisk_syslog_sock_unmask(td_syslog_t * log); + +static const struct sockaddr_un syslog_addr = { + .sun_family = AF_UNIX, + .sun_path = "/dev/log" +}; + +#define RING_PTR(_log, _idx) \ + (&(_log)->ring[(_idx) % (_log)->ringsz]) + +#define RING_FREE(_log) \ + ((_log)->ringsz - ((_log)->prod - (_log)->cons)) + +/* + * NB. Ring buffer. + * + * We allocate a number of pages as indicated by @bufsz during + * initialization. From that, 1K is reserved for message staging, the + * rest is cyclic ring space. + * + * All producer/consumer offsets wrap on size_t range, not buffer + * size. Hence the RING() macros. + */ + +static void __tapdisk_syslog_ring_init(td_syslog_t * log) +{ + log->buf = NULL; + log->bufsz = 0; + log->msg = NULL; + log->ring = NULL; + log->ringsz = 0; +} + +static inline size_t page_align(size_t size) +{ + size_t page_size = sysconf(_SC_PAGE_SIZE); + return (size + page_size - 1) & ~(page_size - 1); +} + +static void tapdisk_syslog_ring_uninit(td_syslog_t * log) +{ + if (log->buf) + munmap(log->buf, log->bufsz); + + __tapdisk_syslog_ring_init(log); +} + +static int tapdisk_syslog_ring_init(td_syslog_t * log, size_t size) +{ + int prot, flags, err; + + __tapdisk_syslog_ring_init(log); + + log->bufsz = page_align(size); + + prot = PROT_READ | PROT_WRITE; + flags = MAP_ANONYMOUS | MAP_PRIVATE; + + log->buf = mmap(NULL, log->bufsz, prot, flags, -1, 0); + if (log->buf == MAP_FAILED) { + log->buf = NULL; + err = -ENOMEM; + goto fail; + } + + err = mlock(log->buf, size); + if (err) { + err = -errno; + goto fail; + } + + log->msg = log->buf; + log->ring = log->buf + TD_SYSLOG_PACKET_MAX; + log->ringsz = size - TD_SYSLOG_PACKET_MAX; + + return 0; + + fail: + tapdisk_syslog_ring_uninit(log); + + return err; +} + +static int +tapdisk_syslog_ring_write_str(td_syslog_t * log, const char *msg, + size_t len) +{ + size_t size, prod, i; + + len = MIN(len, TD_SYSLOG_PACKET_MAX); + size = len + 1; + + if (size > RING_FREE(log)) + return -ENOBUFS; + + prod = log->prod; + + for (i = 0; i < len; ++i) { + char c; + + c = msg[i]; + if (c == 0) + break; + + *RING_PTR(log, prod) = c; + prod++; + } + + *RING_PTR(log, prod) = 0; + + log->prod = prod + 1; + + return 0; +} + +static ssize_t +tapdisk_syslog_ring_read_pkt(td_syslog_t * log, char *msg, size_t size) +{ + size_t cons; + ssize_t sz; + + size = MIN(size, TD_SYSLOG_PACKET_MAX); + + sz = 0; + cons = log->cons; + + while (sz < size) { + char c; + + if (cons == log->prod) + break; + + c = *RING_PTR(log, cons); + msg[sz++] = c; + cons++; + + if (c == 0) + break; + } + + return sz - 1; +} + +static int tapdisk_syslog_ring_dispatch_one(td_syslog_t * log) +{ + size_t len; + int err; + + len = tapdisk_syslog_ring_read_pkt(log, log->msg, + TD_SYSLOG_PACKET_MAX); + if (len == -1) + return -ENOMSG; + + err = tapdisk_syslog_sock_send(log, log->msg, len); + + if (err == -EAGAIN) + return err; + + if (err) + goto fail; + + done: + log->cons += len + 1; + return 0; + + fail: + log->stats.fails++; + goto done; +} + +static void tapdisk_syslog_ring_warning(td_syslog_t * log) +{ + int n, err; + + n = log->oom; + log->oom = 0; + + err = tapdisk_syslog(log, LOG_WARNING, + "tapdisk-syslog: %d messages dropped", n); + if (err) + log->oom = n; +} + +static void tapdisk_syslog_ring_dispatch(td_syslog_t * log) +{ + int err; + + do { + err = tapdisk_syslog_ring_dispatch_one(log); + } while (!err); + + if (log->oom) + tapdisk_syslog_ring_warning(log); +} + +static int +tapdisk_syslog_vsprintf(char *buf, size_t size, + int prio, const struct timeval *tv, + const char *ident, const char *fmt, va_list ap) +{ + char tsbuf[TD_SYSLOG_STRTIME_LEN + 1]; + size_t len; + + /* + * PKT := PRI HEADER MSG + * PRI := "<" {"0" .. "9"} ">" + * HEADER := TIMESTAMP HOSTNAME + * MSG := <TAG> <SEP> <CONTENT> + * SEP := ":" | " " | "[" + */ + + tapdisk_syslog_strftime(tsbuf, sizeof(tsbuf), tv); + + len = 0; + + /* NB. meant to work with c99 null buffers */ + + len += snprintf(buf ? buf + len : NULL, buf ? size - len : 0, + "<%d>%s %s: ", prio, tsbuf, ident); + + len += vsnprintf(buf ? buf + len : NULL, buf ? size - len : 0, + fmt, ap); + + return MIN(len, size); +} + +/* + * NB. Sockets. + * + * Syslog is based on a connectionless (DGRAM) unix transport. + * + * While it is reliable, we cannot block on syslogd because -- as with + * any IPC in tapdisk -- we could deadlock in page I/O writeback. + * Hence the syslog(3) avoidance on the datapath, which this code + * facilitates. + * + * This type of socket has a single (global) receive buffer on + * syslogd's end, but no send buffer at all. The does just that: + * headroom on the sender side. + * + * The transport is rather stateless, but we still need to connect() + * the socket, or select() will find no receive buffer to block + * on. While we never disconnect, connections are unreliable because + * syslog may shut down. + * + * Reconnection will be attempted with every user message submitted. + * Any send() or connect() failure other than EAGAIN discards the + * message. Also, the write event handler will go on to discard any + * remaining ring contents as well, once the socket is disconnected. + * + * In summary, no attempts to mask service blackouts in here. + */ + +int +tapdisk_vsyslog(td_syslog_t * log, int prio, const char *fmt, va_list ap) +{ + struct timeval now; + size_t len; + int err; + + gettimeofday(&now, NULL); + + len = tapdisk_syslog_vsprintf(log->msg, TD_SYSLOG_PACKET_MAX, + prio | log->facility, + &now, log->ident, fmt, ap); + + log->stats.count += 1; + log->stats.bytes += len; + + if (log->cons != log->prod) + goto busy; + + send: + err = tapdisk_syslog_sock_send(log, log->msg, len); + if (!err) + return 0; + + if (err == -ENOTCONN) { + err = tapdisk_syslog_sock_connect(log); + if (!err) + goto send; + } + + if (err != -EAGAIN) + goto fail; + + tapdisk_syslog_sock_unmask(log); + + busy: + if (log->oom) { + err = -ENOBUFS; + goto oom; + } + + err = tapdisk_syslog_ring_write_str(log, log->msg, len); + if (!err) + return 0; + + log->oom_tv = now; + + oom: + log->oom++; + log->stats.drops++; + return err; + + fail: + log->stats.fails++; + return err; +} + +int tapdisk_syslog(td_syslog_t * log, int prio, const char *fmt, ...) +{ + va_list ap; + int err; + + va_start(ap, fmt); + err = tapdisk_vsyslog(log, prio, fmt, ap); + va_end(ap); + + return err; +} + +static int +tapdisk_syslog_sock_send(td_syslog_t * log, const void *msg, size_t size) +{ + ssize_t n; + + log->stats.xmits++; + + n = send(log->sock, msg, size, MSG_DONTWAIT); + if (n < 0) + return -errno; + + return 0; +} + +static void +tapdisk_syslog_sock_event(event_id_t id __attribute__((unused)), + char mode __attribute__((unused)), void *private) +{ + td_syslog_t *log = private; + + tapdisk_syslog_ring_dispatch(log); + + if (log->cons == log->prod) + tapdisk_syslog_sock_mask(log); +} + +static void __tapdisk_syslog_sock_init(td_syslog_t * log) +{ + log->sock = -1; + log->event_id = -1; +} + +static void tapdisk_syslog_sock_close(td_syslog_t * log) +{ + if (log->sock >= 0) + close(log->sock); + + if (log->event_id >= 0) + tapdisk_server_unregister_event(log->event_id); + + __tapdisk_syslog_sock_init(log); +} + +static int tapdisk_syslog_sock_open(td_syslog_t * log) +{ + event_id_t id; + int s, err; + + __tapdisk_syslog_sock_init(log); + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + err = -errno; + goto fail; + } + + log->sock = s; + +#if 0 + err = fcntl(s, F_SETFL, O_NONBLOCK); + if (err < 0) { + err = -errno; + goto fail; + } +#endif + + id = tapdisk_server_register_event(SCHEDULER_POLL_WRITE_FD, + s, 0, + tapdisk_syslog_sock_event, log); + if (id < 0) { + err = id; + goto fail; + } + + log->event_id = id; + + tapdisk_syslog_sock_mask(log); + + return 0; + + fail: + tapdisk_syslog_sock_close(log); + return err; +} + +static int tapdisk_syslog_sock_connect(td_syslog_t * log) +{ + int err; + + err = connect(log->sock, &syslog_addr, sizeof(syslog_addr)); + if (err < 0) + err = -errno; + + return err; +} + +static void tapdisk_syslog_sock_mask(td_syslog_t * log) +{ + tapdisk_server_mask_event(log->event_id, 1); +} + +static void tapdisk_syslog_sock_unmask(td_syslog_t * log) +{ + tapdisk_server_mask_event(log->event_id, 0); +} + +void __tapdisk_syslog_init(td_syslog_t * log) +{ + memset(log, 0, sizeof(td_syslog_t)); + __tapdisk_syslog_sock_init(log); + __tapdisk_syslog_ring_init(log); +} + +void tapdisk_syslog_close(td_syslog_t * log) +{ + tapdisk_syslog_ring_uninit(log); + tapdisk_syslog_sock_close(log); + + if (log->ident) + free(log->ident); + + __tapdisk_syslog_init(log); +} + +int +tapdisk_syslog_open(td_syslog_t * log, const char *ident, int facility, + size_t bufsz) +{ + int err; + + __tapdisk_syslog_init(log); + + log->facility = facility; + log->ident = ident ? strndup(ident, TD_SYSLOG_IDENT_MAX) : NULL; + + err = tapdisk_syslog_sock_open(log); + if (err) + goto fail; + + err = tapdisk_syslog_ring_init(log, bufsz); + if (err) + goto fail; + + return 0; + + fail: + tapdisk_syslog_close(log); + + return err; +} + +void tapdisk_syslog_stats(td_syslog_t * log, int prio) +{ + struct _td_syslog_stats *s = &log->stats; + + tapdisk_syslog(log, prio, + "tapdisk-syslog: %llu messages, %llu bytes, " + "xmits: %llu, failed: %llu, dropped: %llu", + s->count, s->bytes, s->xmits, s->fails, s->drops); +} + +void tapdisk_syslog_flush(td_syslog_t * log) +{ + while (log->cons != log->prod) + tapdisk_server_iterate(); +} diff --git a/tools/blktap3/drivers/tapdisk-syslog.h b/tools/blktap3/drivers/tapdisk-syslog.h new file mode 100644 --- /dev/null +++ b/tools/blktap3/drivers/tapdisk-syslog.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2009, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER + * 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. + */ + +#ifndef __TAPDISK_SYSLOG_H__ +#define __TAPDISK_SYSLOG_H__ + +#include <syslog.h> +#include <stdarg.h> +#include "scheduler.h" + +typedef struct _td_syslog td_syslog_t; + +#define TD_SYSLOG_PACKET_MAX 1024 + +struct _td_syslog_stats { + unsigned long long count; + unsigned long long bytes; + unsigned long long xmits; + unsigned long long fails; + unsigned long long drops; +}; + +struct _td_syslog { + char *ident; + int facility; + + int sock; + event_id_t event_id; + + void *buf; + size_t bufsz; + + char *msg; + + char *ring; + size_t ringsz; + + size_t prod; + size_t cons; + + int oom; + struct timeval oom_tv; + + struct _td_syslog_stats stats; +}; + +int tapdisk_syslog_open(td_syslog_t *, + const char *ident, int facility, size_t bufsz); +void tapdisk_syslog_close(td_syslog_t *); +void tapdisk_syslog_flush(td_syslog_t *); +void tapdisk_syslog_stats(td_syslog_t *, int prio); + +int tapdisk_vsyslog(td_syslog_t *, int prio, const char *fmt, va_list ap); +int tapdisk_syslog(td_syslog_t *, int prio, const char *fmt, ...); + +#endif /* __TAPDISK_SYSLOG_H__ */ _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |