Whamcloud - gitweb
- merge b_md into b_intent
authorpschwan <pschwan>
Fri, 31 Jan 2003 21:16:03 +0000 (21:16 +0000)
committerpschwan <pschwan>
Fri, 31 Jan 2003 21:16:03 +0000 (21:16 +0000)
- rename invalidate-show.diff, at andreas's request

lustre/tests/recovery-small.sh [new file with mode: 0755]
lustre/utils/obdio.c [new file with mode: 0644]
lustre/utils/obdstat.c [new file with mode: 0644]

diff --git a/lustre/tests/recovery-small.sh b/lustre/tests/recovery-small.sh
new file mode 100755 (executable)
index 0000000..b57b169
--- /dev/null
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+set -ex
+
+LUSTRE=${LUSTRE:-`dirname $0`/..}
+PATH=$PATH:$LUSTRE/utils:$LUSTRE/tests
+
+. $LUSTRE/../ltest/functional/llite/common/common.sh
+
+PDSH='pdsh -S -w'
+
+# XXX I wish all this stuff was in some default-config.sh somewhere
+MDSNODE=${MDSNODE:-dev2}
+OSTNODE=${OSTNODE:-dev3}
+CLIENT=${CLIENTNODE:-dev4}
+NETWORKTYPE=${NETWORKTYPE:-tcp}
+MOUNTPT=${MOUNTPT:-/mnt/lustre}
+CONFIG=recovery-small.xml
+MDSDEV=/tmp/mds
+OSTDEV=/tmp/ost
+MDSSIZE=100000
+OSTSIZE=100000
+
+do_mds() {
+    $PDSH $MDSNODE "PATH=\$PATH:$LUSTRE/utils:$LUSTRE/tests; cd $PWD; $@"
+}
+
+do_client() {
+    $PDSH $CLIENT "PATH=\$PATH:$LUSTRE/utils:$LUSTRE/tests; cd $PWD; $@"
+}
+
+do_ost() {
+    $PDSH $OSTNODE "PATH=\$PATH:$LUSTRE/utils:$LUSTRE/tests; cd $PWD; $@"
+}
+
+drop_request() {
+    do_mds "echo 0x121 > /proc/sys/lustre/fail_loc"
+    do_client "$1"
+    do_mds "echo 0 > /proc/sys/lustre/fail_loc"
+}
+
+drop_reply() {
+    do_mds "echo 0x120 > /proc/sys/lustre/fail_loc"
+    do_client "$@"
+    do_mds "echo 0 > /proc/sys/lustre/fail_loc"
+}
+
+make_config() {
+    rm -f $CONFIG
+    for NODE in $CLIENT $MDSNODE $OSTNODE; do
+       lmc -m $CONFIG --add net --node $NODE --nid `h2$NETWORKTYPE $NODE` \
+           --nettype $NETWORKTYPE || exit 4
+    done
+    lmc -m $CONFIG --add mds --node $MDSNODE --mds mds1 --dev $MDSDEV \
+        --size $MDSSIZE || exit 5
+    lmc -m $CONFIG --add ost --node $OSTNODE --obd obd1 --dev $OSTDEV \
+        --size $OSTSIZE || exit 6
+    lmc -m $CONFIG --add mtpt --node $CLIENT --path $MOUNTPT --mds mds1 \
+        --obd obd1 || exit 7
+}
+
+start_mds() {
+    do_mds "lconf $@ $CONFIG"
+}
+
+shutdown_mds() {
+    do_mds "lconf $@ --cleanup $CONFIG"
+}
+
+start_ost() {
+    do_ost "lconf $@ $CONFIG"
+}
+
+shutdown_ost() {
+    do_ost "lconf $@ --cleanup $CONFIG"
+}
+
+mount_client() {
+    do_client "lconf $@ $CONFIG"
+}
+
+unmount_client() {
+    do_client "lconf $@ --cleanup $CONFIG"
+}
+
+setup() {
+    make_config
+    start_mds --reformat
+    start_ost --reformat
+    # XXX we should write our own upcall, when we move this somewhere better.
+    mount_client --timeout=10 \
+        --recovery_upcall=$PWD/../../ltest/functional/llite/09/client-upcall.sh
+}
+
+cleanup() {
+    unmount_client || true
+    shutdown_mds || true
+    shutdown_ost || true
+}
+
+replay() {
+    if [ $# -gt 1 ]; then
+        do_client "$1"
+        shift
+    fi
+    do_mds "sync"
+    do_mds 'echo -e "device \$mds1\\nprobe\\nnotransno\\nreadonly" | lctl'
+    do_client "$1" &
+    shutdown_mds -f
+    start_mds
+    wait
+}
+
+if [ ! -z "$ONLY" ]; then
+    eval "$ONLY"
+    exit $?
+fi
+
+setup
+drop_request "mcreate /mnt/lustre/1"
+drop_reply "mcreate /mnt/lustre/2"
+replay "mcreate /mnt/lustre/3"
+cleanup
diff --git a/lustre/utils/obdio.c b/lustre/utils/obdio.c
new file mode 100644 (file)
index 0000000..f79d21d
--- /dev/null
@@ -0,0 +1,474 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ *  Copyright (C) 2002 Cluster File Systems, Inc.
+ *   Author: Eric Barton <eeb@clusterfs.com>
+ *
+ *   This file is part of Lustre, http://www.lustre.org.
+ *
+ *   Lustre is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2 of the GNU General Public
+ *   License as published by the Free Software Foundation.
+ *
+ *   Lustre is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with Lustre; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <signal.h>
+
+#include <linux/lustre_lib.h>
+#include <linux/lustre_idl.h>
+#include <linux/lustre_dlm.h>
+#include <linux/obd_lov.h>      /* for IOC_LOV_SET_OSC_ACTIVE */
+#include <linux/obd.h>          /* for struct lov_stripe_md */
+#include <linux/obd_class.h>
+#include <linux/lustre_build_version.h>
+
+#include <unistd.h>
+#include <sys/un.h>
+#include <time.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <string.h>
+
+#include <asm/page.h>           /* needed for PAGE_SIZE - rread */
+
+#define __KERNEL__
+#include <linux/list.h>
+#undef __KERNEL__
+
+#include "obdctl.h"
+
+struct obdio_conn {
+        int                   oc_fd;
+        uint64_t               oc_conn_addr;
+        uint64_t               oc_conn_cookie;
+        struct obd_ioctl_data  oc_data;
+        char                   oc_buffer[8192];
+};
+
+char *
+obdio_alloc_aligned_buffer (char **spacep, int size) 
+{
+        int   pagesize = getpagesize();
+        char *space = malloc (size + pagesize - 1);
+        
+        *spacep = space;
+        if (space == NULL)
+                return (NULL);
+        
+        return ((char *)(((unsigned long)space + pagesize - 1) & ~(pagesize - 1)));
+}
+
+void
+obdio_iocinit (struct obdio_conn *conn)
+{
+        memset (&conn->oc_data, 0, sizeof (conn->oc_data));
+        conn->oc_data.ioc_version = OBD_IOCTL_VERSION;
+        conn->oc_data.ioc_addr = conn->oc_conn_addr;
+        conn->oc_data.ioc_cookie = conn->oc_conn_cookie;
+        conn->oc_data.ioc_len = sizeof (conn->oc_data);
+}
+
+int
+obdio_ioctl (struct obdio_conn *conn, int cmd) 
+{
+        char *buf = conn->oc_buffer;
+        int   rc;
+        int   rc2;
+        
+        rc = obd_ioctl_pack (&conn->oc_data, &buf, sizeof (conn->oc_buffer));
+        if (rc != 0) {
+                fprintf (stderr, "obd_ioctl_pack: %d (%s)\n", 
+                         rc, strerror (errno));
+                abort ();
+        }
+        
+        rc = ioctl (conn->oc_fd, cmd, buf);
+        if (rc != 0)
+                return (rc);
+        
+        rc2 = obd_ioctl_unpack (&conn->oc_data, buf, sizeof (conn->oc_buffer));
+        if (rc2 != 0) {
+                fprintf (stderr, "obd_ioctl_unpack: %d (%s)\n",
+                         rc2, strerror (errno));
+                abort ();
+        }
+        
+        return (rc);
+}
+
+struct obdio_conn *
+obdio_connect (int device)
+{
+        struct obdio_conn  *conn;
+        int                 rc;
+
+        conn = malloc (sizeof (*conn));
+        if (conn == NULL) {
+                fprintf (stderr, "obdio_connect: no memory\n");
+                return (NULL);
+        }
+        memset (conn, 0, sizeof (*conn));
+        
+       conn->oc_fd = open ("/dev/obd", O_RDWR);
+       if (conn->oc_fd < 0) {
+                fprintf (stderr, "Can't open /dev/obd: %s\n",
+                         strerror (errno));
+                goto failed;
+        }
+
+        obdio_iocinit (conn);
+        conn->oc_data.ioc_dev = device;
+        rc = obdio_ioctl (conn, OBD_IOC_DEVICE);
+        if (rc != 0) {
+                fprintf (stderr, "Can't set device %d: %s\n",
+                         device, strerror (errno));
+                goto failed;
+        }
+        
+        obdio_iocinit (conn);
+        rc = obdio_ioctl (conn, OBD_IOC_CONNECT);
+        if (rc != 0) {
+                fprintf (stderr, "Can't connect to device %d: %s\n",
+                         device, strerror (errno));
+                goto failed;
+        }
+        
+        conn->oc_conn_addr = conn->oc_data.ioc_addr;
+        conn->oc_conn_cookie = conn->oc_data.ioc_cookie;
+        return (conn);
+        
+ failed:
+        free (conn);
+        return (NULL);
+}
+
+void
+obdio_disconnect (struct obdio_conn *conn) 
+{
+        close (conn->oc_fd);
+        /* obdclass will automatically close on last ref */
+        free (conn);
+}
+
+int
+obdio_open (struct obdio_conn *conn, uint64_t oid, struct lustre_handle *fh) 
+{
+        int    rc;
+        
+        obdio_iocinit (conn);
+        
+        conn->oc_data.ioc_obdo1.o_id = oid;
+        conn->oc_data.ioc_obdo1.o_mode = S_IFREG;
+        conn->oc_data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE | OBD_MD_FLMODE;
+        
+        rc = obdio_ioctl (conn, OBD_IOC_OPEN);
+        
+        if (rc == 0)
+                obd_oa2handle (fh, &conn->oc_data.ioc_obdo1);
+
+        return (rc);
+}
+
+int
+obdio_close (struct obdio_conn *conn, uint64_t oid, struct lustre_handle *fh) 
+{
+        obdio_iocinit (conn);
+        
+
+        conn->oc_data.ioc_obdo1.o_id = oid;
+        conn->oc_data.ioc_obdo1.o_mode = S_IFREG;
+        obd_handle2oa (&conn->oc_data.ioc_obdo1, fh);
+        conn->oc_data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE | 
+                                          OBD_MD_FLMODE | OBD_MD_FLHANDLE;
+        
+        return (obdio_ioctl (conn, OBD_IOC_CLOSE));
+}
+
+int
+obdio_pread (struct obdio_conn *conn, uint64_t oid, 
+             char *buffer, uint32_t count, uint64_t offset) 
+{
+        obdio_iocinit (conn);
+        
+        conn->oc_data.ioc_obdo1.o_id = oid;
+        conn->oc_data.ioc_obdo1.o_mode = S_IFREG;
+        conn->oc_data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE | OBD_MD_FLMODE;
+
+        conn->oc_data.ioc_pbuf2 = buffer;
+        conn->oc_data.ioc_plen2 = count;
+        conn->oc_data.ioc_count = count;
+        conn->oc_data.ioc_offset = offset;
+
+        return (obdio_ioctl (conn, OBD_IOC_BRW_READ));
+}
+
+int
+obdio_pwrite (struct obdio_conn *conn, uint64_t oid, 
+              char *buffer, uint32_t count, uint64_t offset) 
+{
+        obdio_iocinit (conn);
+        
+        conn->oc_data.ioc_obdo1.o_id = oid;
+        conn->oc_data.ioc_obdo1.o_mode = S_IFREG;
+        conn->oc_data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE | OBD_MD_FLMODE;
+
+        conn->oc_data.ioc_pbuf2 = buffer;
+        conn->oc_data.ioc_plen2 = count;
+        conn->oc_data.ioc_count = count;
+        conn->oc_data.ioc_offset = offset;
+
+        return (obdio_ioctl (conn, OBD_IOC_BRW_WRITE));
+}
+
+void
+obdio_test_extent (struct obdio_conn *conn, uint32_t myid, int reps,
+                   uint64_t oid, uint64_t offset, uint32_t size)
+{
+        char     *space;
+        char     *buffer = obdio_alloc_aligned_buffer (&space, size);
+        uint32_t *ibuf;
+        int       i;
+        int       j;
+        int       rc;
+        
+        if (buffer == NULL) {
+                fprintf (stderr, "Can't allocate buffer size %d\n", size);
+                abort ();
+        }
+
+        for (i = 0; i < reps; i++) {
+                ibuf = (uint32_t *) buffer;
+                for (j = 0; j < size / (4 * sizeof (*ibuf)); j++) {
+                        ibuf[0] = myid;
+                        ibuf[1] = i;
+                        ibuf[2] = j;
+                        ibuf[3] = myid;
+                        ibuf += 4;
+                }
+                
+                rc = obdio_pwrite (conn, oid, buffer, size, offset);
+                if (rc != 0) {
+                        fprintf (stderr, "Error writing "LPX64" @ "LPU64" for %u: %s\n",
+                                 oid, offset, size, strerror (errno));
+                        abort ();
+                }
+                
+                memset (buffer, 0xbb, size);
+                
+                rc = obdio_pread (conn, oid, buffer, size, offset);
+                if (rc != 0) {
+                        fprintf (stderr, "Error reading "LPX64" @ "LPU64" for %u: %s\n",
+                                 oid, offset, size, strerror (errno));
+                        abort ();
+                }
+                
+                ibuf = (uint32_t *) buffer;
+                for (j = 0; j < size / (4 * sizeof (*ibuf)); j++) {
+                        if (ibuf[0] != myid ||
+                            ibuf[1] != i ||
+                            ibuf[2] != j ||
+                            ibuf[3] != myid) {
+                                fprintf (stderr, "Error checking "LPX64" @ "LPU64" for %u, chunk %d: %s\n",
+                                         oid, offset, size, j, strerror (errno));
+                                fprintf (stderr, "Expected [%x,%x,%x,%x], got [%x,%x,%x,%x]\n",
+                                         myid, i, j, myid, ibuf[0], ibuf[1], ibuf[2], ibuf[3]);
+                                abort ();
+                        }
+                        ibuf += 4;
+                }
+        }
+}
+
+int
+parse_kmg (uint64_t *valp, char *str)
+{
+        uint64_t        val;
+        char            mod[32];
+
+        switch (sscanf (str, LPU64"%1[gGmMkK]", &val, mod))
+        {
+        default:
+                return (-1);
+
+        case 1:
+                *valp = val;
+                return (0);
+
+        case 2:
+                switch (*mod)
+                {
+                case 'g':
+                case 'G':
+                        *valp = val << 30;
+                        return (0);
+
+                case 'm':
+                case 'M':
+                        *valp = val << 20;
+                        return (0);
+
+                case 'k':
+                case 'K':
+                        *valp = val << 10;
+                        return (0);
+
+                default:
+                        *valp = val;
+                        return (0);
+                }
+        }
+}
+
+void
+usage (char *cmdname, int help) 
+{
+        char *name = strrchr (cmdname, '/');
+        
+        if (name == NULL)
+                name = cmdname;
+        
+        fprintf (help ? stdout : stderr,
+                 "usage: %s -d device -s size -o offset [-i id][-n reps] oid\n",
+                 name);
+}
+
+int
+main (int argc, char **argv) 
+{
+        struct lustre_handle fh;
+        uint32_t           myid = getpid ();
+        uint64_t           oid;
+        uint64_t           base_offset = 0;
+        int                set_base = 0;
+        uint32_t           size = 0;
+        int                set_size = 0;
+        int                device = 0;
+        int                set_device = 0;
+        int                reps = 1;
+        char              *end;
+        struct obdio_conn *conn;
+        uint64_t           val;
+        int                rc;
+        int                c;
+
+        while ((c = getopt (argc, argv, "hi:s:b:d:n:")) != -1)
+                switch (c) {
+                case 'h':
+                        usage (argv[0], 1);
+                        return (0);
+                        
+                case 'i':
+                        myid = strtol (optarg, &end, 0);
+                        if (end == optarg || *end != 0) {
+                                fprintf (stderr, "Can't parse id %s\n",
+                                         optarg);
+                                return (1);
+                        }
+                        myid = (uint32_t)val;
+                        break;
+                        
+                case 's':
+                        if (parse_kmg (&val, optarg) != 0) {
+                                fprintf (stderr, "Can't parse size %s\n",
+                                         optarg);
+                                return (1);
+                        }
+                        size = (uint32_t)val;
+                        set_size++;
+                        break;
+                        
+                case 'b':
+                        if (parse_kmg (&val, optarg) != 0) {
+                                fprintf (stderr, "Can't parse base offset %s\n",
+                                         optarg);
+                                return (1);
+                        }
+                        base_offset = val;
+                        set_base++;
+                        break;
+
+                case 'd':
+                        device = strtol (optarg, &end, 0);
+                        if (end == optarg || *end != 0) {
+                                fprintf (stderr, "Can't parse device %s\n",
+                                         optarg);
+                                return (1);
+                        }
+                        set_device++;
+                        break;
+                case 'n':
+                        if (parse_kmg (&val, optarg) != 0) {
+                                fprintf (stderr, "Can't parse reps %s\n",
+                                         optarg);
+                                return (1);
+                        }
+                        reps = (int)val;
+                        break;
+                        
+                default:
+                        usage (argv[0], 0);
+                        return (1);
+        }
+
+        if (!set_size ||
+            !set_base ||
+            !set_device ||
+            optind == argc) {
+                fprintf (stderr, "No %s specified\n",
+                         !set_size ? "size" :
+                         !set_base ? "base offset" :
+                         !set_device ? "device" : "object id");
+                return (1);
+        }
+        
+        oid = strtoull (argv[optind], &end, 0);
+        if (end == argv[optind] || *end != 0) {
+                fprintf (stderr, "Can't parse object id %s\n",
+                         argv[optind]);
+                return (1);
+        }
+        
+        conn = obdio_connect (device);
+        if (conn == NULL)
+                return (1);
+        
+        rc = obdio_open (conn, oid, &fh);
+        if (rc != 0) {
+                fprintf (stderr, "Failed to open object "LPX64": %s\n",
+                         oid, strerror (errno));
+                return (1);
+        }
+        
+        obdio_test_extent (conn, myid, reps, oid, base_offset, size);
+        
+        rc = obdio_close (conn, oid, &fh);
+        if (rc != 0) {
+                fprintf (stderr, "Error closing object "LPX64": %s\n",
+                         oid, strerror (errno));
+                return (1);
+        }
+
+        obdio_disconnect (conn);
+        return (0);
+}
+
+
diff --git a/lustre/utils/obdstat.c b/lustre/utils/obdstat.c
new file mode 100644 (file)
index 0000000..b66fcc3
--- /dev/null
@@ -0,0 +1,189 @@
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+struct one_stat {
+       char       *name;
+        int         fd;
+       long long   current;
+       long long   delta;
+};
+
+struct one_stat *read_bytes;
+struct one_stat *read_reqs;
+struct one_stat *write_bytes;
+struct one_stat *write_reqs;
+struct one_stat *getattr_reqs;
+struct one_stat *setattr_reqs;
+struct one_stat *create_reqs;
+struct one_stat *destroy_reqs;
+struct one_stat *statfs_reqs;
+struct one_stat *open_reqs;
+struct one_stat *close_reqs;
+struct one_stat *punch_reqs;
+
+struct one_stat *
+init_one_stat (char *basename, char *name) 
+{
+       char             fname[1024];
+       struct one_stat *stat = (struct one_stat *)malloc (sizeof (*stat));
+       
+       if (stat == NULL) {
+               fprintf (stderr, "Can't allocate stat %s: %s\n", 
+                        name, strerror (errno));
+               abort ();
+       }
+
+       snprintf (fname, sizeof (fname), "%s/%s", basename, name);
+
+       memset (stat, 0, sizeof (*stat));
+       stat->name = name;
+
+       stat->fd = open (fname, O_RDONLY);
+       if (stat->fd < 0 ) {
+               fprintf (stderr, "Can't open stat %s: %s\n", 
+                        fname, strerror (errno));
+               abort ();
+       }
+
+       return (stat);
+}
+
+void
+update_one_stat (struct one_stat *stat) 
+{
+        static char buffer[1024];
+       long long prev = stat->current;
+       int  nob;
+
+       lseek (stat->fd, 0, SEEK_SET);
+       nob = read (stat->fd, buffer, sizeof (buffer) - 1);
+       if (nob < 0) {
+               fprintf (stderr, "Can't read stat %s: %s\n",
+                        stat->name, strerror (errno));
+               abort ();
+       }
+       
+       buffer[nob] = 0;
+       if (sscanf (buffer, "%Ld", &stat->current) != 1) {
+               fprintf (stderr, "Can't parse stat %s: %s\n",
+                        stat->name, strerror (errno));
+               abort ();
+       }
+
+       stat->delta = stat->current - prev;
+}
+
+double
+timenow ()
+{
+       struct timeval tv;
+   
+       gettimeofday (&tv, NULL);
+       return (tv.tv_sec + tv.tv_usec / 1000000.0);
+}
+
+void
+do_stat (void)
+{
+       static double last = 0.0;
+       double now;
+       double t;
+   
+       now = timenow();
+
+       update_one_stat (read_bytes);
+       update_one_stat (read_reqs);
+       update_one_stat (write_bytes);
+       update_one_stat (write_reqs);
+       update_one_stat (getattr_reqs);
+       update_one_stat (setattr_reqs);
+       update_one_stat (open_reqs);
+       update_one_stat (close_reqs);
+       update_one_stat (create_reqs);
+       update_one_stat (destroy_reqs);
+       update_one_stat (statfs_reqs);
+       update_one_stat (punch_reqs);
+       
+       if (last == 0.0) {
+               printf ("R %Ld/%Ld W %Ld/%Ld attr %Ld/%Ld open %Ld/%Ld create %Ld/%Ld stat %Ld punch %Ld\n",
+                       read_bytes->current, read_reqs->current,
+                       write_bytes->current, write_reqs->current,
+                       getattr_reqs->current, setattr_reqs->current,
+                       open_reqs->current, close_reqs->current,
+                       create_reqs->current, destroy_reqs->current,
+                       statfs_reqs->current, punch_reqs->current);
+       } else {
+               t = now - last;
+
+               printf ("R %7Ld (%6d/s %7.2fMb/s) W %7Ld (%6d/s %7.2fMb/s)",
+                       read_reqs->delta, (int)(read_reqs->delta / t),
+                       read_bytes->delta / ((1<<20) * t),
+                       write_reqs->delta, (int)(write_reqs->delta / t),
+                       write_bytes->delta / ((1<<20) * t));
+               
+               if (getattr_reqs->delta != 0)
+                       printf (" ga:%Ld", getattr_reqs->delta);
+               
+               if (setattr_reqs->delta != 0)
+                       printf (" sa:%Ld", setattr_reqs->delta);
+
+               if (open_reqs->delta != 0)
+                       printf (" op:%Ld", open_reqs->delta);
+               
+               if (close_reqs->delta != 0)
+                       printf (" cl:%Ld", close_reqs->delta);
+
+               if (create_reqs->delta != 0)
+                       printf (" cx:%Ld", create_reqs->delta);
+               
+               if (destroy_reqs->delta != 0)
+                       printf (" dx:%Ld", destroy_reqs->delta);
+
+               if (statfs_reqs->delta != 0)
+                       printf (" st:%Ld", statfs_reqs->delta);
+               
+               if (punch_reqs->delta != 0)
+                       printf (" pu:%Ld", punch_reqs->delta);
+               
+               printf ("\n");
+       }
+
+       last = timenow();
+}
+
+int main (int argc, char **argv)
+{
+       char *basedir = "/proc/sys/obdfilter";
+       int  interval = 0;
+   
+       if (argc > 1)
+               interval = atoi (argv[1]);
+
+       read_bytes = init_one_stat (basedir, "read_bytes");
+       read_reqs = init_one_stat (basedir, "read_reqs");
+       write_bytes = init_one_stat (basedir, "write_bytes");
+       write_reqs = init_one_stat (basedir, "write_reqs");
+       getattr_reqs = init_one_stat (basedir, "getattr_reqs");
+       setattr_reqs = init_one_stat (basedir, "setattr_reqs");
+       create_reqs = init_one_stat (basedir, "create_reqs");
+       destroy_reqs = init_one_stat (basedir, "destroy_reqs");
+       statfs_reqs = init_one_stat (basedir, "statfs_reqs");
+       open_reqs = init_one_stat (basedir, "open_reqs");
+       close_reqs = init_one_stat (basedir, "close_reqs");
+       punch_reqs = init_one_stat (basedir, "punch_reqs");
+
+       do_stat ();
+
+       if (interval == 0)
+               return (0);
+   
+       for (;;) {
+               sleep (interval);
+               do_stat ();
+       }
+}