]> Zhao Yanbai Git Server - minix.git/commitdiff
Add block device tracing facility
authorDavid van Moolenbroek <david@minix3.org>
Thu, 24 Nov 2011 13:50:13 +0000 (14:50 +0100)
committerDavid van Moolenbroek <david@minix3.org>
Fri, 25 Nov 2011 12:47:21 +0000 (13:47 +0100)
The implementation is in libblockdriver, and works transparently for
all block drivers. The new btrace(8) tool can be used to control block
tracing; see ``man btrace'' for details.

20 files changed:
commands/Makefile
commands/btrace/Makefile [new file with mode: 0644]
commands/btrace/btrace.c [new file with mode: 0644]
common/include/Makefile.inc
common/include/minix/blockdriver.h
common/include/minix/blockdriver_mt.h
common/include/minix/btrace.h [new file with mode: 0644]
common/include/sys/Makefile.inc
common/include/sys/ioc_block.h [new file with mode: 0644]
lib/libblockdriver/Makefile
lib/libblockdriver/driver.c
lib/libblockdriver/driver.h
lib/libblockdriver/driver_mt.c
lib/libblockdriver/driver_st.c
lib/libblockdriver/mq.c
lib/libblockdriver/mq.h
lib/libblockdriver/trace.c [new file with mode: 0644]
lib/libblockdriver/trace.h [new file with mode: 0644]
man/man8/Makefile
man/man8/btrace.8 [new file with mode: 0644]

index 99c1586f710ea77cff0407b90fa5b9799f8c5a69..f3e377ac5e2e3fe2358e3b9c26c40596b219ef08 100644 (file)
@@ -4,7 +4,7 @@
 
 SUBDIR=        aal add_route arp ash at autil awk \
        backup badblocks banner basename binpackage \
-       binpackages cal calendar \
+       binpackages btrace cal calendar \
        cat cawf cd  cdprobe checkhier chmem \
        chmod chown chroot ci cksum cleantmp clear cmp co \
        comm compress cp crc cron crontab cut date \
diff --git a/commands/btrace/Makefile b/commands/btrace/Makefile
new file mode 100644 (file)
index 0000000..7daeefe
--- /dev/null
@@ -0,0 +1,4 @@
+PROG=  btrace
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/commands/btrace/btrace.c b/commands/btrace/btrace.c
new file mode 100644 (file)
index 0000000..716313c
--- /dev/null
@@ -0,0 +1,218 @@
+/* Block trace command line tool */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <minix/types.h>
+#include <minix/btrace.h>
+#include <sys/ioc_block.h>
+
+static void usage(char *name)
+{
+  printf("usage:\n"
+    "%s start <device> <nr_entries>\n"
+    "%s stop <device> <file>\n"
+    "%s reset <device>\n"
+    "%s dump <file>\n",
+    name, name, name, name);
+
+  exit(EXIT_FAILURE);
+}
+
+static void btrace_start(char *device, int nr_entries)
+{
+  int r, ctl, devfd;
+  size_t size;
+
+  if ((devfd = open(device, O_RDONLY)) < 0) {
+       perror("device open");
+       exit(EXIT_FAILURE);
+  }
+
+  size = nr_entries;
+  if ((r = ioctl(devfd, BIOCTRACEBUF, &size)) < 0) {
+       perror("ioctl(BIOCTRACEBUF)");
+       exit(EXIT_FAILURE);
+  }
+
+  ctl = BTCTL_START;
+  if ((r = ioctl(devfd, BIOCTRACECTL, &ctl)) < 0) {
+       perror("ioctl(BIOCTRACECTL)");
+
+       size = 0;
+       ioctl(devfd, BIOCTRACEBUF, &size);
+
+       exit(EXIT_FAILURE);
+  }
+
+  close(devfd);
+}
+
+static void btrace_stop(char *device, char *file)
+{
+  btrace_entry buf[BTBUF_SIZE];
+  int r, ctl, devfd, outfd;
+  size_t size;
+
+  if ((devfd = open(device, O_RDONLY)) < 0) {
+       perror("device open");
+       exit(EXIT_FAILURE);
+  }
+
+  if ((outfd = open(file, O_CREAT|O_TRUNC|O_WRONLY, 0600)) < 0) {
+       perror("file open");
+       exit(EXIT_FAILURE);
+  }
+
+  ctl = BTCTL_STOP;
+  if ((r = ioctl(devfd, BIOCTRACECTL, &ctl)) < 0) {
+       perror("ioctl(BIOCTRACECTL)");
+       exit(EXIT_FAILURE);
+  }
+
+  for (;;) {
+       if ((r = ioctl(devfd, BIOCTRACEGET, buf)) < 0) {
+               perror("ioctl(BIOCTRACEGET)");
+               break;
+       }
+
+       if (r == 0) break;
+
+       size = r * sizeof(buf[0]);
+       if ((r = write(outfd, (char *) buf, size)) != size) {
+               if (r < 0) perror("write");
+               else fputs("short write\n", stderr);
+       }
+  }
+
+  close(outfd);
+
+  size = 0;
+  if ((r = ioctl(devfd, BIOCTRACEBUF, &size)) < 0) {
+       perror("ioctl(BIOCTRACEBUF)");
+       exit(EXIT_FAILURE);
+  }
+
+  close(devfd);
+}
+
+static void btrace_reset(char *device)
+{
+  size_t size;
+  int r, ctl, devfd;
+
+  if ((devfd = open(device, O_RDONLY)) < 0) {
+       perror("device open");
+       exit(EXIT_FAILURE);
+  }
+
+  ctl = BTCTL_STOP;
+  (void) ioctl(devfd, BIOCTRACECTL, &ctl);
+
+  size = 0;
+  if ((r = ioctl(devfd, BIOCTRACEBUF, &size)) < 0) {
+       perror("ioctl(BIOCTRACEBUF)");
+       exit(EXIT_FAILURE);
+  }
+
+  close(devfd);
+}
+
+static void dump_entry(btrace_entry *entry)
+{
+  switch (entry->request) {
+  case BTREQ_OPEN: printf("OPEN"); break;
+  case BTREQ_CLOSE: printf("CLOSE"); break;
+  case BTREQ_READ: printf("READ"); break;
+  case BTREQ_WRITE: printf("WRITE"); break;
+  case BTREQ_GATHER: printf("GATHER"); break;
+  case BTREQ_SCATTER: printf("SCATTER"); break;
+  case BTREQ_IOCTL: printf("IOCTL"); break;
+  }
+
+  printf(" request\n");
+
+  switch (entry->request) {
+  case BTREQ_OPEN:
+       printf("- access:\t%lx\n", entry->size);
+       break;
+  case BTREQ_READ:
+  case BTREQ_WRITE:
+  case BTREQ_GATHER:
+  case BTREQ_SCATTER:
+       printf("- position:\t%08lx%08lx\n",
+               ex64hi(entry->position), ex64lo(entry->position));
+       printf("- size:\t\t%u\n", entry->size);
+       printf("- flags:\t%x\n", entry->flags);
+       break;
+  case BTREQ_IOCTL:
+       printf("- request:\t%08x\n", entry->size);
+       break;
+  }
+
+  printf("- start:\t%u us\n", entry->start_time);
+  printf("- finish:\t%u us\n", entry->finish_time);
+  if (entry->result == BTRES_INPROGRESS)
+       printf("- result:\t(in progress)\n");
+  else
+       printf("- result:\t%d\n", entry->result);
+  printf("\n");
+}
+
+static void btrace_dump(char *file)
+{
+  btrace_entry buf[BTBUF_SIZE];
+  int i, r, infd;
+
+  if ((infd = open(file, O_RDONLY)) < 0) {
+       perror("open");
+       exit(EXIT_FAILURE);
+  }
+
+  for (;;) {
+       if ((r = read(infd, (char *) buf, sizeof(buf))) <= 0)
+               break;
+
+       r /= sizeof(buf[0]);
+
+       for (i = 0; i < r; i++)
+               dump_entry(&buf[i]);
+  }
+
+  if (r < 0) perror("read");
+
+  close(infd);
+}
+
+int main(int argc, char **argv)
+{
+  int num;
+  char *name = argv[0];
+
+  if (argc < 3) usage(name);
+
+  if (!strcmp(argv[1], "start")) {
+       if (argc < 4) usage(name);
+
+       num = atoi(argv[3]);
+
+       if (num <= 0) usage(name);
+
+       btrace_start(argv[2], num);
+  }
+  else if (!strcmp(argv[1], "stop")) {
+       if (argc < 4) usage(name);
+
+       btrace_stop(argv[2], argv[3]);
+  }
+  else if (!strcmp(argv[1], "reset")) {
+       btrace_reset(argv[2]);
+  }
+  else if (!strcmp(argv[1], "dump")) {
+       btrace_dump(argv[2]);
+  }
+  else usage(name);
+
+  return EXIT_SUCCESS;
+}
index 81be73c6a3093b5c04774a7ef5521953e13acd97..1b38a2783db34de1847fa6d908c86f24c716dcb3 100644 (file)
@@ -6,6 +6,7 @@ INCS+=  env.h fetch.h hgfs.h lib.h libutil.h timers.h
 
 INCS+=         minix/acpi.h minix/ansi.h minix/audio_fw.h minix/bitmap.h \
        minix/bdev.h minix/blockdriver.h minix/blockdriver_mt.h \
+       minix/btrace.h \
        minix/callnr.h minix/chardriver.h minix/com.h minix/compiler.h \
        minix/config.h minix/const.h minix/cpufeature.h minix/crtso.h \
        minix/debug.h minix/devio.h minix/devman.h minix/dmap.h \
index 2d212ec083915d6ed6c32f2a21cd2e1f4914d4a0..efd5261a5b447dda7f80c1b4256914c9d8c304d1 100644 (file)
@@ -27,7 +27,7 @@ struct blockdriver {
  */
 _PROTOTYPE( void blockdriver_announce, (void) );
 
-#ifndef _DRIVER_MT_API
+#ifndef _BLOCKDRIVER_MT_API
 /* Additional functions for the singlethreaded version. These allow the driver
  * to either use the stock driver_task(), or implement its own message loop.
  * To avoid accidents, these functions are not exposed when minix/driver_mt.h
@@ -39,7 +39,7 @@ _PROTOTYPE( void blockdriver_process, (struct blockdriver *dp, message *m_ptr,
 _PROTOTYPE( void blockdriver_terminate, (void) );
 _PROTOTYPE( void blockdriver_task, (struct blockdriver *bdp) );
 _PROTOTYPE( int blockdriver_mq_queue, (message *m_ptr, int status) );
-#endif /* !_DRIVER_MT_API */
+#endif /* !_BLOCKDRIVER_MT_API */
 
 /* Parameters for the disk drive. */
 #define SECTOR_SIZE      512   /* physical sector size in bytes */
index 143660f4631d2ea9d05d387adcd13a137a47867d..05b523d5c50279ae90e33cdd0310beaaa223fd93 100644 (file)
@@ -1,11 +1,11 @@
 #ifndef _MINIX_BLOCKDRIVER_MT_H
 #define _MINIX_BLOCKDRIVER_MT_H
 
-#define DRIVER_MT_API 1                /* do not expose the singlethreaded API */
+#define BLOCKDRIVER_MT_API 1   /* do not expose the singlethreaded API */
 #include <minix/blockdriver.h>
 
 /* The maximum number of worker threads. */
-#define DRIVER_MT_MAX_WORKERS  32
+#define BLOCKDRIVER_MT_MAX_WORKERS     32
 
 _PROTOTYPE( void blockdriver_mt_task, (struct blockdriver *driver_tab) );
 _PROTOTYPE( void blockdriver_mt_sleep, (void) );
diff --git a/common/include/minix/btrace.h b/common/include/minix/btrace.h
new file mode 100644 (file)
index 0000000..d97b7d9
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _MINIX_BTRACE_H
+#define _MINIX_BTRACE_H
+
+/* Control directives. */
+enum {
+  BTCTL_START,
+  BTCTL_STOP
+};
+
+/* Request codes. */
+enum {
+  BTREQ_OPEN,
+  BTREQ_CLOSE,
+  BTREQ_READ,
+  BTREQ_WRITE,
+  BTREQ_GATHER,
+  BTREQ_SCATTER,
+  BTREQ_IOCTL
+};
+
+/* Special result codes. */
+#define BTRES_INPROGRESS       (-997)
+
+/* Block trace entry. */
+typedef struct {
+  u32_t request;               /* request code; one of BTR_xxx */
+  u32_t size;                  /* request size, ioctl request, or access */
+  u64_t position;              /* starting disk position */
+  u32_t flags;                 /* transfer flags */
+  i32_t result;                        /* request result; OK, bytes, or error */
+  u32_t start_time;            /* request service start time (us) */
+  u32_t finish_time;           /* request service completion time (us) */
+} btrace_entry;                        /* (32 bytes) */
+
+/* This is the number of btrace_entry structures copied out at once using the
+ * BIOCTRACEGET ioctl call.
+ */
+#define BTBUF_SIZE     1024
+
+#endif /* _MINIX_BTRACE_H */
index f8e4c932b7ce851f35a563c5b42816403d61acde..2f62740f1c22742c3a7268cfb3cd3a1be0a8854a 100644 (file)
@@ -3,7 +3,7 @@
 .PATH: ${MINIXSRCDIR}/common/include/sys
 
 INCS+=         elf32.h elf64.h elf_common.h elf_generic.h \
-       ioc_file.h ioc_tape.h ioc_disk.h \
+       ioc_block.h ioc_file.h ioc_tape.h ioc_disk.h \
        ioc_memory.h ioc_sound.h ioc_tty.h \
        kbdio.h mtio.h svrctl.h video.h vm.h procfs.h elf_core.h exec_elf.h
 
diff --git a/common/include/sys/ioc_block.h b/common/include/sys/ioc_block.h
new file mode 100644 (file)
index 0000000..663a306
--- /dev/null
@@ -0,0 +1,15 @@
+/*     sys/ioc_block.h - Block ioctl() command codes.
+ *
+ */
+
+#ifndef _S_I_BLOCK_H
+#define _S_I_BLOCK_H
+
+#include <minix/ioctl.h>
+#include <minix/btrace.h>
+
+#define BIOCTRACEBUF   _IOW('b', 1, size_t)
+#define BIOCTRACECTL   _IOW('b', 2, int)
+#define BIOCTRACEGET   _IOR_BIG(3, btrace_entry[BTBUF_SIZE])
+
+#endif /* _S_I_BLOCK_H */
index 0c153d21872e9ed478f58221eab4b1cd4def3c3f..abd061095dadc4861dd321cb857d2d1738496fba 100644 (file)
@@ -3,7 +3,7 @@
 
 LIB=   blockdriver
 
-SRCS=  driver.c drvlib.c driver_st.c driver_mt.c mq.c event.c
+SRCS=  driver.c drvlib.c driver_st.c driver_mt.c mq.c event.c trace.c
 
 .if ${USE_STATECTL} != "no"
 CPPFLAGS+= -DUSE_STATECTL
index f63e9cbc137bc472b55278a0a128e720f0a59706..e7940cc4249844f75734d9db9fce05ab27e2233f 100644 (file)
 #include <minix/drivers.h>
 #include <minix/blockdriver.h>
 #include <minix/ds.h>
+#include <sys/ioc_block.h>
 #include <sys/ioc_disk.h>
 
 #include "driver.h"
 #include "mq.h"
+#include "trace.h"
 
 /* Management data for opened devices. */
 PRIVATE int open_devs[MAX_NR_OPEN_DEVICES];
@@ -207,7 +209,7 @@ PRIVATE int do_rdwt(struct blockdriver *bdp, message *mp)
 /*===========================================================================*
  *                             do_vrdwt                                     *
  *===========================================================================*/
-PRIVATE int do_vrdwt(struct blockdriver *bdp, message *mp)
+PRIVATE int do_vrdwt(struct blockdriver *bdp, message *mp, thread_id_t id)
 {
 /* Carry out an device read or write to/from a vector of buffers. */
   iovec_t iovec[NR_IOREQS];
@@ -226,12 +228,14 @@ PRIVATE int do_vrdwt(struct blockdriver *bdp, message *mp)
        return EINVAL;
   }
 
-  /* Check for overflow condition. */
+  /* Check for overflow condition, and update the size for block tracing. */
   for (i = size = 0; i < nr_req; i++) {
        if ((ssize_t) (size + iovec[i].iov_size) < size) return EINVAL;
        size += iovec[i].iov_size;
   }
 
+  trace_setsize(id, size);
+
   /* Transfer bytes from/to the device. */
   do_write = (mp->m_type == BDEV_SCATTER);
   position = make64(mp->BDEV_POS_LO, mp->BDEV_POS_HI);
@@ -249,7 +253,10 @@ PRIVATE int do_vrdwt(struct blockdriver *bdp, message *mp)
 PRIVATE int do_ioctl(struct blockdriver *bdp, message *mp)
 {
 /* Carry out an I/O control request. For now, we handle setting/getting
- * partitions here, and let the driver handle any other requests.
+ * partitions here, forward block trace control requests to the tracing module,
+ * and let the driver handle any other requests. In the future, a stricter
+ * separation between block and disk I/O controls may become desirable, but we
+ * do not have any non-"disk" block drivers at this time.
  */
   struct device *dv;
   struct partition entry;
@@ -263,6 +270,14 @@ PRIVATE int do_ioctl(struct blockdriver *bdp, message *mp)
   grant = mp->BDEV_GRANT;
 
   switch (request) {
+  case BIOCTRACEBUF:
+  case BIOCTRACECTL:
+  case BIOCTRACEGET:
+       /* Block trace control. */
+       r = trace_ctl(minor, request, mp->m_source, grant);
+
+       break;
+
   case DIOCSETP:
        /* Copy just this one partition table entry. */
        r = sys_safecopyfrom(mp->m_source, grant, 0, (vir_bytes) &entry,
@@ -337,7 +352,8 @@ PUBLIC void blockdriver_handle_notify(struct blockdriver *bdp, message *m_ptr)
 /*===========================================================================*
  *                             blockdriver_handle_request                   *
  *===========================================================================*/
-PUBLIC int blockdriver_handle_request(struct blockdriver *bdp, message *m_ptr)
+PUBLIC int blockdriver_handle_request(struct blockdriver *bdp, message *m_ptr,
+       thread_id_t id)
 {
 /* Call the appropiate driver function, based on the type of request. Return
  * the result code that is to be sent back to the caller, or EDONTREPLY if no
@@ -358,6 +374,8 @@ PUBLIC int blockdriver_handle_request(struct blockdriver *bdp, message *m_ptr)
        set_open_dev(m_ptr->BDEV_MINOR);
   }
 
+  trace_start(id, m_ptr);
+
   /* Call the appropriate function(s) for this request. */
   switch (m_ptr->m_type) {
   case BDEV_OPEN:      r = do_open(bdp, m_ptr);        break;
@@ -365,7 +383,7 @@ PUBLIC int blockdriver_handle_request(struct blockdriver *bdp, message *m_ptr)
   case BDEV_READ:
   case BDEV_WRITE:     r = do_rdwt(bdp, m_ptr);        break;
   case BDEV_GATHER:
-  case BDEV_SCATTER:   r = do_vrdwt(bdp, m_ptr);       break;
+  case BDEV_SCATTER:   r = do_vrdwt(bdp, m_ptr, id);   break;
   case BDEV_IOCTL:     r = do_ioctl(bdp, m_ptr);       break;
   default:
        if (bdp->bdr_other)
@@ -378,5 +396,7 @@ PUBLIC int blockdriver_handle_request(struct blockdriver *bdp, message *m_ptr)
   if (bdp->bdr_cleanup != NULL)
        (*bdp->bdr_cleanup)();
 
+  trace_finish(id, r);
+
   return r;
 }
index 4e1299e3dceb39f9e321c2b14666933ca1c78ece..5e51933ad6457ffaefe5f62e8d4a35e8588426a5 100644 (file)
@@ -1,10 +1,13 @@
 #ifndef _BLOCKDRIVER_DRIVER_H
 #define _BLOCKDRIVER_DRIVER_H
 
+#define SINGLE_THREAD  (0)                             /* single-thread ID */
+#define MAIN_THREAD    (BLOCKDRIVER_MT_MAX_WORKERS)    /* main thread ID */
+
 _PROTOTYPE( void blockdriver_handle_notify, (struct blockdriver *bdp,
        message *m_ptr) );
 _PROTOTYPE( int blockdriver_handle_request, (struct blockdriver *bdp,
-       message *m_ptr) );
+       message *m_ptr, thread_id_t thread) );
 _PROTOTYPE( void blockdriver_reply, (message *m_ptr, int ipc_status,
        int reply) );
 
index fd7cc0215a5b00915ee1bf5437a7ef80e7c2b056..7780c192f493e98140471698719e3467e1992e65 100644 (file)
@@ -40,9 +40,9 @@ PRIVATE int running = FALSE;
 
 PRIVATE mthread_key_t worker_key;
 
-PRIVATE worker_t worker[DRIVER_MT_MAX_WORKERS];
+PRIVATE worker_t worker[BLOCKDRIVER_MT_MAX_WORKERS];
 
-PRIVATE worker_t *exited[DRIVER_MT_MAX_WORKERS];
+PRIVATE worker_t *exited[BLOCKDRIVER_MT_MAX_WORKERS];
 PRIVATE int num_exited = 0;
 
 /*===========================================================================*
@@ -126,7 +126,7 @@ PRIVATE void *worker_thread(void *param)
        wp->state = STATE_RUNNING;
 
        /* Handle the request, and send a reply. */
-       r = blockdriver_handle_request(bdtab, &m);
+       r = blockdriver_handle_request(bdtab, &m, wp->id);
 
        blockdriver_reply(&m, ipc_status, r);
   }
@@ -223,7 +223,7 @@ PRIVATE void master_handle_request(message *m_ptr, int ipc_status)
    */
   if (!IS_BDEV_RQ(m_ptr->m_type)) {
        /* Process as 'other' message. */
-       r = blockdriver_handle_request(bdtab, m_ptr);
+       r = blockdriver_handle_request(bdtab, m_ptr, MAIN_THREAD);
 
        blockdriver_reply(m_ptr, ipc_status, r);
 
@@ -240,7 +240,7 @@ PRIVATE void master_handle_request(message *m_ptr, int ipc_status)
   }
 
   /* Start the thread if it is not already running. */
-  assert(thread_id >= 0 && thread_id < DRIVER_MT_MAX_WORKERS);
+  assert(thread_id >= 0 && thread_id < BLOCKDRIVER_MT_MAX_WORKERS);
 
   wp = &worker[thread_id];
 
@@ -269,7 +269,7 @@ PRIVATE void master_init(struct blockdriver *bdp)
 
   bdtab = bdp;
 
-  for (i = 0; i < DRIVER_MT_MAX_WORKERS; i++)
+  for (i = 0; i < BLOCKDRIVER_MT_MAX_WORKERS; i++)
        worker[i].state = STATE_DEAD;
 
   /* Initialize a per-thread key, where each worker thread stores its own
@@ -368,7 +368,7 @@ PUBLIC void blockdriver_mt_wakeup(thread_id_t id)
  */
   worker_t *wp;
 
-  assert(id >= 0 && id < DRIVER_MT_MAX_WORKERS);
+  assert(id >= 0 && id < BLOCKDRIVER_MT_MAX_WORKERS);
 
   wp = &worker[id];
 
index b3b225b66340afa5d54bd2ab676a0dd6f537bcc0..e703e7131c0989c6b44907b02bb6771810b1e6da 100644 (file)
@@ -27,7 +27,7 @@ PUBLIC int blockdriver_receive_mq(message *m_ptr, int *status_ptr)
 /* receive() interface for drivers with message queueing. */
 
   /* Any queued messages? */
-  if (mq_dequeue(MQ_SINGLE, m_ptr, status_ptr))
+  if (mq_dequeue(SINGLE_THREAD, m_ptr, status_ptr))
        return OK;
 
   /* Fall back to standard receive() interface otherwise. */
@@ -81,7 +81,7 @@ PUBLIC void blockdriver_process(struct blockdriver *bdp, message *m_ptr,
 
        /* Do not reply to notifications. */
   } else {
-       r = blockdriver_handle_request(bdp, m_ptr);
+       r = blockdriver_handle_request(bdp, m_ptr, SINGLE_THREAD);
 
        blockdriver_reply(m_ptr, ipc_status, r);
   }
@@ -94,5 +94,5 @@ PUBLIC int blockdriver_mq_queue(message *m, int status)
 {
 /* Queue a message for later processing. */
 
-  return mq_enqueue(MQ_SINGLE, m, status);
+  return mq_enqueue(SINGLE_THREAD, m, status);
 }
index 602fa3b1096f290756dc27d3105e90ea0ba0d707..2263563585e7dbb4e634a8540363b31006d3f33e 100644 (file)
@@ -22,7 +22,7 @@ struct mq_cell {
 
 PRIVATE struct mq_cell pool[MQ_SIZE];
 
-PRIVATE STAILQ_HEAD(queue, mq_cell) queue[DRIVER_MT_MAX_WORKERS];
+PRIVATE STAILQ_HEAD(queue, mq_cell) queue[BLOCKDRIVER_MT_MAX_WORKERS];
 PRIVATE STAILQ_HEAD(free_list, mq_cell) free_list;
 
 /*===========================================================================*
@@ -36,7 +36,7 @@ PUBLIC void mq_init(void)
 
   STAILQ_INIT(&free_list);
 
-  for (i = 0; i < DRIVER_MT_MAX_WORKERS; i++)
+  for (i = 0; i < BLOCKDRIVER_MT_MAX_WORKERS; i++)
        STAILQ_INIT(&queue[i]);
 
   for (i = 0; i < MQ_SIZE; i++)
@@ -54,7 +54,7 @@ PUBLIC int mq_enqueue(thread_id_t thread_id, const message *mess,
  */
   struct mq_cell *cell;
 
-  assert(thread_id >= 0 && thread_id < DRIVER_MT_MAX_WORKERS);
+  assert(thread_id >= 0 && thread_id < BLOCKDRIVER_MT_MAX_WORKERS);
 
   if (STAILQ_EMPTY(&free_list))
        return FALSE;
@@ -80,7 +80,7 @@ PUBLIC int mq_dequeue(thread_id_t thread_id, message *mess, int *ipc_status)
  */
   struct mq_cell *cell;
 
-  assert(thread_id >= 0 && thread_id < DRIVER_MT_MAX_WORKERS);
+  assert(thread_id >= 0 && thread_id < BLOCKDRIVER_MT_MAX_WORKERS);
 
   if (STAILQ_EMPTY(&queue[thread_id]))
        return FALSE;
index 7d772e49e4af144f57d5e7afa93605818b436505..427d3df05f66b4f06d066b281054f06df1a5b72e 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef _BLOCKDRIVER_MQ_H
 #define _BLOCKDRIVER_MQ_H
 
-#define MQ_SINGLE      0       /* thread ID for single-threading */
-
 _PROTOTYPE( void mq_init, (void) );
 _PROTOTYPE( int mq_enqueue, (thread_id_t thread_id, const message *mess,
        int ipc_status) );
diff --git a/lib/libblockdriver/trace.c b/lib/libblockdriver/trace.c
new file mode 100644 (file)
index 0000000..61ed3fe
--- /dev/null
@@ -0,0 +1,283 @@
+/* This file implements block level tracing support. */
+
+#include <minix/drivers.h>
+#include <minix/blockdriver_mt.h>
+#include <minix/btrace.h>
+#include <sys/ioc_block.h>
+#include <minix/minlib.h>
+#include <assert.h>
+
+#include "trace.h"
+
+#define NO_TRACEDEV            ((dev_t) -1)
+#define NO_TIME                        ((u32_t) -1)
+
+PRIVATE int trace_enabled      = FALSE;
+PRIVATE dev_t trace_dev                = NO_TRACEDEV;
+PRIVATE btrace_entry *trace_buf        = NULL;
+PRIVATE size_t trace_size      = 0;
+PRIVATE size_t trace_pos;
+PRIVATE size_t trace_next;
+PRIVATE u64_t trace_tsc;
+
+/* Pointers to in-progress trace entries for each thread (all worker threads,
+ * plus one for the main thread). Each pointer is set to NULL whenever no
+ * operation is currently being traced for that thread, for whatever reason.
+ */
+PRIVATE btrace_entry *trace_ptr[BLOCKDRIVER_MT_MAX_WORKERS + 1] = { NULL };
+
+/*===========================================================================*
+ *                             trace_gettime                                *
+ *===========================================================================*/
+PRIVATE u32_t trace_gettime(void)
+{
+/* Return the current time, in microseconds since the start of the trace.
+ */
+  u64_t tsc;
+
+  assert(trace_enabled);
+
+  read_tsc_64(&tsc);
+
+  tsc = sub64(tsc, trace_tsc);
+
+  return tsc_64_to_micros(tsc);
+}
+
+/*===========================================================================*
+ *                             trace_ctl                                    *
+ *===========================================================================*/
+PUBLIC int trace_ctl(dev_t minor, unsigned int request, endpoint_t endpt,
+       cp_grant_id_t grant)
+{
+/* Process a block trace control request.
+ */
+  size_t size;
+  int r, ctl, entries;
+
+  switch (request) {
+  case BIOCTRACEBUF:
+       /* The size cannot be changed when tracing is enabled. */
+       if (trace_enabled) return EBUSY;
+
+       /* Copy in the requested size. */
+       if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &size,
+               sizeof(size), D)) != OK)
+               return r;
+
+       if (size >= INT_MAX / sizeof(btrace_entry)) return EINVAL;
+
+       /* The size can only be set or reset, not changed. */
+       if (size > 0 && trace_size > 0) return EBUSY;
+
+       /* Allocate or free a buffer for tracing data. For future multi-device
+        * tracing support, the buffer is associated with a minor device.
+        */
+       if (size == 0) {
+               if (trace_dev == NO_TRACEDEV) return OK;
+
+               if (trace_dev != minor) return EINVAL;
+
+               free(trace_buf);
+
+               trace_dev = NO_TRACEDEV;
+       } else {
+               if ((trace_buf = malloc(size * sizeof(btrace_entry))) == NULL)
+                       return errno;
+
+               trace_dev = minor;
+       }
+
+       trace_size = size;
+       trace_pos = 0;
+       trace_next = 0;
+
+       return OK;
+
+  case BIOCTRACECTL:
+       /* We can only start/stop tracing if the given device has a trace
+        * buffer associated with it.
+        */
+       if (trace_dev != minor) return EINVAL;
+
+       /* Copy in the request code. */
+       if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &ctl,
+               sizeof(ctl), D)) != OK)
+               return r;
+
+       /* Start or stop tracing. */
+       switch (ctl) {
+       case BTCTL_START:
+               if (trace_enabled) return EBUSY;
+
+               read_tsc_64(&trace_tsc);
+
+               trace_enabled = TRUE;
+
+               break;
+
+       case BTCTL_STOP:
+               if (!trace_enabled) return EINVAL;
+
+               trace_enabled = FALSE;
+
+               /* Cancel all ongoing trace operations. */
+               memset(trace_ptr, 0, sizeof(trace_ptr));
+
+               break;
+
+       default:
+               return EINVAL;
+       }
+
+       return OK;
+
+  case BIOCTRACEGET:
+       /* We can only retrieve tracing entries if the given device has a trace
+        * buffer associated with it.
+        */
+       if (trace_dev != minor) return EINVAL;
+
+       if (trace_enabled) return EBUSY;
+
+       /* How much can we copy out? */
+       entries = MIN(trace_pos - trace_next,
+               _MINIX_IOCTL_SIZE_BIG(request) / sizeof(btrace_entry));
+
+       if (entries == 0)
+               return 0;
+
+       if ((r = sys_safecopyto(endpt, grant, 0,
+               (vir_bytes) &trace_buf[trace_next],
+               entries * sizeof(btrace_entry), D)) != OK)
+               return r;
+
+       trace_next += entries;
+
+       return entries;
+  }
+
+  return EINVAL;
+}
+
+/*===========================================================================*
+ *                             trace_start                                  *
+ *===========================================================================*/
+PUBLIC void trace_start(thread_id_t id, message *m_ptr)
+{
+/* Start creating a trace entry.
+ */
+  btrace_entry *entry;
+  int req;
+  u64_t pos;
+  size_t size;
+  int flags;
+
+  if (!trace_enabled || trace_dev != m_ptr->BDEV_MINOR) return;
+
+  assert(id >= 0 && id < BLOCKDRIVER_MT_MAX_WORKERS + 1);
+
+  if (trace_pos == trace_size)
+       return;
+
+  switch (m_ptr->m_type) {
+  case BDEV_OPEN:      req = BTREQ_OPEN;       break;
+  case BDEV_CLOSE:     req = BTREQ_CLOSE;      break;
+  case BDEV_READ:      req = BTREQ_READ;       break;
+  case BDEV_WRITE:     req = BTREQ_WRITE;      break;
+  case BDEV_GATHER:    req = BTREQ_GATHER;     break;
+  case BDEV_SCATTER:   req = BTREQ_SCATTER;    break;
+  case BDEV_IOCTL:     req = BTREQ_IOCTL;      break;
+  default:             return;
+  }
+
+  switch (m_ptr->m_type) {
+  case BDEV_OPEN:
+  case BDEV_CLOSE:
+       pos = cvu64(0);
+       size = m_ptr->BDEV_ACCESS;
+       flags = 0;
+
+       break;
+
+  case BDEV_READ:
+  case BDEV_WRITE:
+  case BDEV_GATHER:
+  case BDEV_SCATTER:
+       pos = make64(m_ptr->BDEV_POS_LO, m_ptr->BDEV_POS_HI);
+       size = m_ptr->BDEV_COUNT;
+       flags = m_ptr->BDEV_FLAGS;
+
+       break;
+
+  case BDEV_IOCTL:
+       pos = cvu64(0);
+       size = m_ptr->BDEV_REQUEST;
+       flags = 0;
+
+       /* Do not log trace control requests. */
+       switch (size) {
+       case BIOCTRACEBUF:
+       case BIOCTRACECTL:
+       case BIOCTRACEGET:
+               return;
+       }
+
+       break;
+
+  default:
+       /* Do not log any other messages. */
+       return;
+  }
+
+  entry = &trace_buf[trace_pos];
+  entry->request = req;
+  entry->size = size;
+  entry->position = pos;
+  entry->flags = flags;
+  entry->result = BTRES_INPROGRESS;
+  entry->start_time = trace_gettime();
+  entry->finish_time = NO_TIME;
+
+  trace_ptr[id] = entry;
+  trace_pos++;
+}
+
+/*===========================================================================*
+ *                             trace_setsize                                *
+ *===========================================================================*/
+PUBLIC void trace_setsize(thread_id_t id, size_t size)
+{
+/* Set the current trace entry's actual (byte) size, for vector requests.
+ */
+  btrace_entry *entry;
+
+  if (!trace_enabled) return;
+
+  assert(id >= 0 && id < BLOCKDRIVER_MT_MAX_WORKERS + 1);
+
+  if ((entry = trace_ptr[id]) == NULL) return;
+
+  entry->size = size;
+}
+
+/*===========================================================================*
+ *                             trace_finish                                 *
+ *===========================================================================*/
+PUBLIC void trace_finish(thread_id_t id, int result)
+{
+/* Finish a trace entry.
+ */
+  btrace_entry *entry;
+
+  if (!trace_enabled) return;
+
+  assert(id >= 0 && id < BLOCKDRIVER_MT_MAX_WORKERS + 1);
+
+  if ((entry = trace_ptr[id]) == NULL) return;
+
+  entry->result = result;
+  entry->finish_time = trace_gettime();
+
+  trace_ptr[id] = NULL;
+}
diff --git a/lib/libblockdriver/trace.h b/lib/libblockdriver/trace.h
new file mode 100644 (file)
index 0000000..041a954
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _BLOCKDRIVER_TRACE_H
+#define _BLOCKDRIVER_TRACE_H
+
+_PROTOTYPE( int trace_ctl, (dev_t minor, unsigned int request,
+       endpoint_t endpt, cp_grant_id_t grant));
+
+_PROTOTYPE( void trace_start, (thread_id_t thread_id, message *m_ptr));
+_PROTOTYPE( void trace_setsize, (thread_id_t thread_id, size_t size));
+_PROTOTYPE( void trace_finish, (thread_id_t thread_id, int r));
+
+#endif /* _BLOCKDRIVER_TRACE_H */
index 8dde810a4115c8931e85607845a1f30c68e3f4d7..1a26413a3b2c5f2d8659bf280eafddd4e57e9acd 100644 (file)
@@ -1,4 +1,4 @@
-MAN=   add_route.8 backup.8 badblocks.8 boot.8 \
+MAN=   add_route.8 backup.8 badblocks.8 boot.8 btrace.8 \
        cdprobe.8 checkhier.8 chown.8 cleantmp.8 config.8 cron.8 \
        dhcpd.8 diskctl.8 dosminix.8 elvprsv.8 fdisk.8 fingerd.8 ftpd.8 \
        getty.8 halt.8 hgfs.8 httpd.8 ifconfig.8 inet.8 init.8 \
diff --git a/man/man8/btrace.8 b/man/man8/btrace.8
new file mode 100644 (file)
index 0000000..77c4052
--- /dev/null
@@ -0,0 +1,59 @@
+.TH BTRACE 8
+.SH NAME
+btrace \- block-level tracing interface
+.SH SYNOPSIS
+\fBbtrace\fR \fBstart\fR \fIdevice\fR \fIentries\fR
+.PP
+\fBbtrace\fR \fBstop\fR \fIdevice\fR \fIfile\fR
+.PP
+\fBbtrace\fR \fBreset\fR \fIdevice\fR
+.PP
+\fBbtrace\fR \fBdump\fR \fIfile\fR
+.SH DESCRIPTION
+The \fBbtrace\fR tool is the user interface to MINIX3's block-level tracing
+facility. It allows one to start, stop, and reset tracing, and dump a trace
+output file in a somewhat human-readable format.
+.SH COMMANDS
+.TP 10
+\fBstart\fR
+This command starts tracing all block requests on the given \fIdevice\fR. Each
+block request takes up one entry in the log. The \fIentries\fR parameter
+specifies the allocation side of the log in the driver process, in number of
+(32-byte) entries. Once the log is full, no more entries will be added.
+.TP 10
+\fBstop\fR
+This command stops tracing on the given \fIdevice\fR, and dumps the resulting
+log to the given output \fIfile\fR.
+.TP 10
+\fBreset\fR
+This command stops tracing on the given \fIdevice\fR and resets the state of
+the block tracer. This should be useful only in emergency situations.
+.TP 10
+\fBdump\fR
+Dump the contents of a log file generated earlier with \fBbtrace stop\fR, in
+human-readable format. Heavy users of the block tracing facility will probably
+want to write their own tools for parsing and visualizing dump files.
+.SH LIMITATIONS
+Only one block device can be traced per driver at once. It is therefore also
+not possible to trace a device and all its partitions at the same time. The
+tracing facility has been designed for tracing activity of a single file
+system, in which case these limitations are not important.
+.PP
+The log will always start with a \fIclose\fR operation on the device, since
+\fBbtrace\fR closes the file descriptor used to instruct the driver to start
+tracing. Similarly, for logs that have not already filled up during tracing,
+the last entry will be a \fBbtrace\fR-triggered \fIopen\fR operation.
+.SH EXAMPLES
+.TP 35
+.B btrace start /dev/c2d0 10240
+# Start a block trace on c2d0.
+.TP 35
+.B btrace stop /dev/c2d0 outfile
+# Stop the block trace on c2d0.
+.TP 35
+.B btrace dump outfile
+# View the output of the trace.
+.SH "SEE ALSO"
+.BR ioctl (2).
+.SH AUTHOR
+David van Moolenbroek <david@minix3.org>