Add block device tracing facility
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.
This commit is contained in:
parent
a9f89a7290
commit
fd4c2b74f3
20 changed files with 682 additions and 30 deletions
|
@ -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 \
|
||||
|
|
4
commands/btrace/Makefile
Normal file
4
commands/btrace/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
PROG= btrace
|
||||
MAN=
|
||||
|
||||
.include <bsd.prog.mk>
|
218
commands/btrace/btrace.c
Normal file
218
commands/btrace/btrace.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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 \
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) );
|
||||
|
|
40
common/include/minix/btrace.h
Normal file
40
common/include/minix/btrace.h
Normal file
|
@ -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 */
|
|
@ -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
|
||||
|
||||
|
|
15
common/include/sys/ioc_block.h
Normal file
15
common/include/sys/ioc_block.h
Normal file
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -40,10 +40,12 @@
|
|||
#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;
|
||||
}
|
||||
|
|
|
@ -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) );
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) );
|
||||
|
|
283
lib/libblockdriver/trace.c
Normal file
283
lib/libblockdriver/trace.c
Normal file
|
@ -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;
|
||||
}
|
11
lib/libblockdriver/trace.h
Normal file
11
lib/libblockdriver/trace.h
Normal file
|
@ -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 */
|
|
@ -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 \
|
||||
|
|
59
man/man8/btrace.8
Normal file
59
man/man8/btrace.8
Normal file
|
@ -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>
|
Loading…
Reference in a new issue