Print this page
NEX-5175 want SMB statistics separately for reads, writes, other
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-5197 smbstat copying out wrong stats for per-share or per-client
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-4811 SMB needs to export a header for kstats
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Jeffry Molanus <jeffry.molanus@nexenta.com>
NEX-4313 want iops, bandwidth, and latency kstats for smb
Portions contributed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
5606 support build with binutils 2.25
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Marcel Telka <marcel.telka@nexenta.com>
Reviewed by: Piotr Jasiukajtis <estibi@me.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
NEX-3273 smbstat delays its output when redirected to a file
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
NEX-2705 smbstat output may lie about sat and show only zero for writes/s
SMB-78 smbstat should show SMB2 activity
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-65 SMB server in non-global zones (data structure changes)
Many things move to the smb_server_t object, and
many functions gain an sv arg (which server).

@@ -19,11 +19,11 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * smbstat: Server Message Block File System statistics
  *

@@ -101,10 +101,12 @@
 #include <stropts.h>
 #include <math.h>
 #include <umem.h>
 #include <locale.h>
 #include <smbsrv/smb_kstat.h>
+#include <smbsrv/smb.h>
+#include <smbsrv/smb2.h>
 
 #if !defined(TEXT_DOMAIN)
 #define TEXT_DOMAIN "SYS_TEST"
 #endif /* TEXT_DOMAIN */
 

@@ -142,10 +144,16 @@
         "\n%30s code   %%   rbytes/s   tbytes/s     req/s     rt-mean"  \
         "   rt-stddev\n"
 #define SMBSRV_REQUESTS_FORMAT  \
         "%30s  %02X   %3.0f  %1.3e  %1.3e  %1.3e  %1.3e  %1.3e\n"
 
+#define SMBSRV_CLNT_SHARE_BANNER        \
+        "%30s  rbytes/s   tbytes/s     req/s     rt-mean   rt-stddev\n"
+#define SMBSRV_CLNT_SHARE_FORMAT        \
+        "%30s  %1.3e  %1.3e  %1.3e  %1.3e  %1.3e\n"
+
+
 typedef enum {
         CPU_TICKS_IDLE = 0,
         CPU_TICKS_USER,
         CPU_TICKS_KERNEL,
         CPU_TICKS_SENTINEL

@@ -165,10 +173,16 @@
 typedef struct smbstat_wrk_snapshot {
         uint64_t        ws_maxthreads;
         uint64_t        ws_bnalloc;
 } smbstat_wrk_snapshot_t;
 
+/* Per-Client or Per-Share statistics. */
+typedef struct smbstat_clsh_snapshot {
+        hrtime_t        cs_snaptime;
+        smbsrv_clsh_kstats_t    cs_data;
+} smbstat_clsh_snapshot_t;
+
 typedef struct smbstat_req_info {
         char            ri_name[KSTAT_STRLEN];
         int             ri_opcode;
         double          ri_pct;
         double          ri_tbs;

@@ -212,12 +226,16 @@
         boolean_t       si_sat;
         double          si_ticks[CPU_TICKS_SENTINEL];
         /*
          * Latency & Throughput per request
          */
-        smbstat_req_info_t      si_reqs1[SMB_COM_NUM];
-        smbstat_req_info_t      si_reqs2[SMB2__NCMDS];
+        smbstat_req_info_t      si_reqs1[SMBSRV_KS_NREQS1];
+        smbstat_req_info_t      si_reqs2[SMBSRV_KS_NREQS2];
+        /*
+         * Latency & Throughput on specified client or share
+         */
+        smbstat_req_info_t      si_clsh[SMBSRV_CLSH__NREQ];
 } smbstat_srv_info_t;
 
 static void smbstat_init(void);
 static void smbstat_fini(void);
 static void smbstat_kstat_snapshot(void);

@@ -226,10 +244,11 @@
 
 static void smbstat_print_counters(void);
 static void smbstat_print_throughput(void);
 static void smbstat_print_utilization(void);
 static void smbstat_print_requests(void);
+static void smbstat_print_client_or_share(void);
 
 static void smbstat_cpu_init(void);
 static void smbstat_cpu_fini(void);
 static smbstat_cpu_snapshot_t *smbstat_cpu_current_snapshot(void);
 static smbstat_cpu_snapshot_t *smbstat_cpu_previous_snapshot(void);

@@ -240,10 +259,17 @@
 static void smbstat_wrk_fini(void);
 static void smbstat_wrk_snapshot(void);
 static void smbstat_wrk_process(void);
 static smbstat_wrk_snapshot_t *smbstat_wrk_current_snapshot(void);
 
+static void smbstat_clsh_init(void);
+static void smbstat_clsh_fini(void);
+static void smbstat_clsh_snapshot(void);
+static void smbstat_clsh_process(void);
+static smbstat_clsh_snapshot_t *smbstat_clsh_current_snapshot(void);
+static smbstat_clsh_snapshot_t *smbstat_clsh_previous_snapshot(void);
+
 static void smbstat_srv_init(void);
 static void smbstat_srv_fini(void);
 static void smbstat_srv_snapshot(void);
 static void smbstat_srv_process(void);
 static void smbstat_srv_process_counters(smbstat_srv_snapshot_t *);

@@ -284,23 +310,28 @@
 static boolean_t        smbstat_opt_n = B_FALSE;        /* by name */
 static boolean_t        smbstat_opt_u = B_FALSE;        /* utilization */
 static boolean_t        smbstat_opt_t = B_FALSE;        /* throughput */
 static boolean_t        smbstat_opt_r = B_FALSE;        /* requests */
 static boolean_t        smbstat_opt_z = B_FALSE;        /* non-zero requests */
+static boolean_t        smbstat_opt_C = B_FALSE;        /* per-client stats */
+static boolean_t        smbstat_opt_S = B_FALSE;        /* per-share stats */
+static char             *smbstat_opt_clsh = NULL;       /* -C or -S arg. */
 
 static uint_t           smbstat_interval = 0;
 static long             smbstat_nrcpus = 0;
 static kstat_ctl_t      *smbstat_ksc = NULL;
 static kstat_t          *smbstat_srv_ksp = NULL;
 static kstat_t          *smbstat_wrk_ksp = NULL;
+static kstat_t          *smbstat_clsh_ksp = NULL;
 static struct winsize   smbstat_ws;
 static uint16_t         smbstat_rows = 0;
 
 static int smbstat_snapshot_idx = 0;
 static smbstat_cpu_snapshot_t *smbstat_cpu_snapshots[SMBSTAT_SNAPSHOT_COUNT];
 static smbstat_srv_snapshot_t smbstat_srv_snapshots[SMBSTAT_SNAPSHOT_COUNT];
 static smbstat_wrk_snapshot_t smbstat_wrk_snapshots[SMBSTAT_SNAPSHOT_COUNT];
+static smbstat_clsh_snapshot_t smbstat_clsh_snapshots[SMBSTAT_SNAPSHOT_COUNT];
 static smbstat_srv_info_t smbstat_srv_info;
 
 /*
  * main
  */

@@ -317,11 +348,11 @@
                     gettext("%s: Trusted Extensions not supported.\n"),
                     argv[0]);
                 return (1);
         }
 
-        while ((c = getopt(argc, argv, "achnrtuz")) != EOF) {
+        while ((c = getopt(argc, argv, "achnrtuzC:S:")) != EOF) {
                 switch (c) {
                 case 'a':
                         smbstat_opt_a = B_TRUE;
                         break;
                 case 'n':

@@ -340,10 +371,20 @@
                         smbstat_opt_t = B_TRUE;
                         break;
                 case 'z':
                         smbstat_opt_z = B_TRUE;
                         break;
+                case 'C': /* per-client stats. */
+                        smbstat_opt_S = B_FALSE;
+                        smbstat_opt_C = B_TRUE;
+                        smbstat_opt_clsh = optarg;
+                        break;
+                case 'S': /* per-share stats. */
+                        smbstat_opt_C = B_FALSE;
+                        smbstat_opt_S = B_TRUE;
+                        smbstat_opt_clsh = optarg;
+                        break;
                 case 'h':
                         smbstat_usage(stdout, 0);
                 default:
                         smbstat_usage(stderr, 1);
                 }

@@ -350,11 +391,13 @@
         }
 
         if (!smbstat_opt_u &&
             !smbstat_opt_c &&
             !smbstat_opt_r &&
-            !smbstat_opt_t) {
+            !smbstat_opt_t &&
+            !smbstat_opt_C &&
+            !smbstat_opt_S) {
                 /* Default options when none is specified. */
                 smbstat_opt_u = B_TRUE;
                 smbstat_opt_t = B_TRUE;
         }
 

@@ -367,10 +410,11 @@
         if ((argc - optind) > 1)
                 smbstat_usage(stderr, 1);
 
         (void) atexit(smbstat_fini);
         smbstat_init();
+        smbstat_req_order();
         for (;;) {
                 smbstat_kstat_snapshot();
                 smbstat_kstat_process();
                 smbstat_kstat_print();
                 if (smbstat_interval == 0)

@@ -393,11 +437,11 @@
                 smbstat_fail(1, gettext("kstat_open(): can't open /dev/kstat"));
 
         smbstat_cpu_init();
         smbstat_srv_init();
         smbstat_wrk_init();
-        smbstat_req_order();
+        smbstat_clsh_init();
 }
 
 /*
  * smbstat_fini
  *

@@ -404,10 +448,11 @@
  * Releases the resources smbstat_init() allocated.
  */
 static void
 smbstat_fini(void)
 {
+        smbstat_clsh_fini();
         smbstat_wrk_fini();
         smbstat_srv_fini();
         smbstat_cpu_fini();
         (void) kstat_close(smbstat_ksc);
 }

@@ -421,10 +466,11 @@
 smbstat_kstat_snapshot(void)
 {
         smbstat_cpu_snapshot();
         smbstat_srv_snapshot();
         smbstat_wrk_snapshot();
+        smbstat_clsh_snapshot();
 }
 
 /*
  * smbstat_kstat_process
  */

@@ -432,10 +478,11 @@
 smbstat_kstat_process(void)
 {
         smbstat_cpu_process();
         smbstat_srv_process();
         smbstat_wrk_process();
+        smbstat_clsh_process();
 }
 
 /*
  * smbstat_kstat_print
  *

@@ -447,10 +494,11 @@
         smbstat_termio_init();
         smbstat_print_counters();
         smbstat_print_throughput();
         smbstat_print_utilization();
         smbstat_print_requests();
+        smbstat_print_client_or_share();
         (void) fflush(stdout);
 }
 
 /*
  * smbstat_print_counters

@@ -489,11 +537,12 @@
 {
         if (!smbstat_opt_t)
                 return;
 
         if (smbstat_opt_u || smbstat_opt_r || smbstat_opt_c ||
-            (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) {
+            smbstat_opt_C || smbstat_opt_S || (smbstat_rows == 0) ||
+            (smbstat_rows >= smbstat_ws.ws_row)) {
                 (void) printf(SMBSRV_THROUGHPUT_BANNER);
                 smbstat_rows = 1;
         }
         (void) printf(SMBSRV_THROUGHPUT_FORMAT,
             smbstat_zero(smbstat_srv_info.si_rbs),

@@ -555,11 +604,11 @@
                 return;
 
         (void) printf(SMBSRV_REQUESTS_BANNER, "       ");
 
         prq = smbstat_srv_info.si_reqs1;
-        for (i = 0; i < SMB_COM_NUM; i++) {
+        for (i = 0; i < SMBSRV_KS_NREQS1; i++) {
                 if (!smbstat_opt_a &&
                     strncmp(prq[i].ri_name, "Invalid", sizeof ("Invalid")) == 0)
                         continue;
 
                 if (!smbstat_opt_z || (prq[i].ri_pct != 0)) {

@@ -574,11 +623,11 @@
                             prq[i].ri_stddev);
                 }
         }
 
         prq = smbstat_srv_info.si_reqs2;
-        for (i = 0; i < SMB2__NCMDS; i++) {
+        for (i = 0; i < SMBSRV_KS_NREQS2; i++) {
                 if (!smbstat_opt_a && i == SMB2_INVALID_CMD)
                         continue;
 
                 if (!smbstat_opt_z || (prq[i].ri_pct != 0)) {
                         (void) printf(SMBSRV_REQUESTS_FORMAT,

@@ -592,10 +641,35 @@
                             prq[i].ri_stddev);
                 }
         }
 }
 
+static void
+smbstat_print_client_or_share(void)
+{
+        smbstat_req_info_t      *prq;
+        int     idx;
+
+        if (smbstat_opt_clsh == NULL)
+                return;
+
+        (void) printf(SMBSRV_CLNT_SHARE_BANNER, "       ");
+
+        prq = &smbstat_srv_info.si_clsh[0];
+
+        for (idx = 0; idx < SMBSRV_CLSH__NREQ; idx++, prq++) {
+        (void) printf(SMBSRV_CLNT_SHARE_FORMAT,
+            prq->ri_name,
+            smbstat_zero(prq->ri_rbs),
+            smbstat_zero(prq->ri_tbs),
+            smbstat_zero(prq->ri_rqs),
+            prq->ri_mean,
+            prq->ri_stddev);
+        }
+}
+
+
 /*
  * smbstat_cpu_init
  */
 static void
 smbstat_cpu_init(void)

@@ -790,17 +864,140 @@
 {
         return (&smbstat_wrk_snapshots[smbstat_snapshot_idx]);
 }
 
 /*
+ * smbstat_clsh_init
+ *
+ * Lookup the kstat for the client or share specified.
+ * The names for these look like:
+ *      session (a.k.a. client)
+ *              cl/$IPADDR  and instance ss->s_kid
+ *      share
+ *              sh/sharename    (always instance 0)
+ *
+ * These will look like: smbsrv:0:sh/myshare
+ * or smbsrv:N:cl/10.10.0.2
+ */
+static void
+smbstat_clsh_init(void)
+{
+        static const char       *kr_names[] = SMBSRV_CLSH__NAMES;
+        char    ks_name[KSTAT_STRLEN];
+        char    *prefix = "";
+        smbstat_req_info_t *info;
+        int     instance = -1;
+        int     i;
+
+        if (smbstat_opt_clsh == NULL)
+                return;
+
+        /*
+         * Currently we don't take an instance so this will return data
+         * from the first or the only client from the IP address specified.
+         */
+        if (smbstat_opt_C)
+                prefix = "cl";
+        if (smbstat_opt_S)
+                prefix = "sh";
+        (void) snprintf(ks_name, sizeof (ks_name),
+            "%s/%s", prefix, smbstat_opt_clsh);
+
+        smbstat_clsh_ksp = kstat_lookup(smbstat_ksc, SMBSRV_KSTAT_MODULE,
+            instance, ks_name);
+        if (smbstat_clsh_ksp == NULL) {
+                smbstat_fail(1, gettext("cannot retrieve smbsrv %s kstat\n"),
+                    ks_name);
+        }
+
+        info = smbstat_srv_info.si_clsh;
+        for (i = 0; i < SMBSRV_CLSH__NREQ; i++) {
+                (void) strlcpy(info[i].ri_name, kr_names[i],
+                    sizeof (info[i].ri_name));
+        }
+}
+
+static void
+smbstat_clsh_fini(void)
+{
+        smbstat_clsh_ksp = NULL;
+}
+
+/*
+ * smbstat_clsh_snapshot
+ */
+static void
+smbstat_clsh_snapshot(void)
+{
+        smbstat_clsh_snapshot_t *curr;
+
+        if (smbstat_clsh_ksp == NULL)
+                return;
+
+        curr = smbstat_clsh_current_snapshot();
+
+        if ((kstat_read(smbstat_ksc, smbstat_clsh_ksp, NULL) == -1) ||
+            (smbstat_clsh_ksp->ks_data_size != sizeof (curr->cs_data)))
+                smbstat_fail(1, gettext("kstat_read('%s') failed"),
+                    smbstat_clsh_ksp->ks_name);
+
+        curr->cs_snaptime = smbstat_clsh_ksp->ks_snaptime;
+
+        bcopy(smbstat_clsh_ksp->ks_data, &curr->cs_data,
+            sizeof (curr->cs_data));
+}
+
+/*
+ * smbstat_clsh_process
+ */
+static void
+smbstat_clsh_process(void)
+{
+        smbstat_clsh_snapshot_t *curr, *prev;
+        boolean_t               firstcall;
+        int     idx;
+
+        curr = smbstat_clsh_current_snapshot();
+        prev = smbstat_clsh_previous_snapshot();
+        firstcall = (prev->cs_snaptime == 0);
+
+
+        for (idx = 0; idx < SMBSRV_CLSH__NREQ; idx++) {
+                smbstat_srv_process_one_req(
+                    &smbstat_srv_info.si_clsh[idx],
+                    &curr->cs_data.ks_clsh[idx],
+                    &prev->cs_data.ks_clsh[idx],
+                    firstcall);
+        }
+}
+
+/*
+ * smbstat_clsh_current_snapshot
+ */
+static smbstat_clsh_snapshot_t *
+smbstat_clsh_current_snapshot(void)
+{
+        return (&smbstat_clsh_snapshots[smbstat_snapshot_idx]);
+}
+
+static smbstat_clsh_snapshot_t *
+smbstat_clsh_previous_snapshot(void)
+{
+        int     idx;
+
+        idx = (smbstat_snapshot_idx - 1) & SMBSTAT_SNAPSHOT_MASK;
+        return (&smbstat_clsh_snapshots[idx]);
+}
+
+/*
  * smbstat_srv_init
  */
 static void
 smbstat_srv_init(void)
 {
         smbstat_srv_ksp = kstat_lookup(smbstat_ksc, SMBSRV_KSTAT_MODULE,
-            getzoneid(), SMBSRV_KSTAT_STATISTICS);
+            -1, SMBSRV_KSTAT_STATISTICS);
         if (smbstat_srv_ksp == NULL)
                 smbstat_fail(1, gettext("cannot retrieve smbsrv kstat\n"));
 }
 
 /*

@@ -1030,20 +1227,20 @@
         smb_kstat_req_t         *curr_req;
         smb_kstat_req_t         *prev_req;
         int                     i, idx;
         boolean_t       firstcall = (prev->ss_snaptime == 0);
 
-        for (i = 0; i < SMB_COM_NUM; i++) {
+        for (i = 0; i < SMBSRV_KS_NREQS1; i++) {
                 info = &smbstat_srv_info.si_reqs1[i];
                 idx = info[i].ri_opcode & 0xFF;
                 curr_req = &curr->ss_data.ks_reqs1[idx];
                 prev_req = &prev->ss_data.ks_reqs1[idx];
                 smbstat_srv_process_one_req(
                     info, curr_req, prev_req, firstcall);
         }
 
-        for (i = 0; i < SMB2__NCMDS; i++) {
+        for (i = 0; i < SMBSRV_KS_NREQS2; i++) {
                 info = &smbstat_srv_info.si_reqs2[i];
                 curr_req = &curr->ss_data.ks_reqs2[i];
                 prev_req = &prev->ss_data.ks_reqs2[i];
                 smbstat_srv_process_one_req(
                     info, curr_req, prev_req, firstcall);

@@ -1097,11 +1294,10 @@
         }
         info->ri_stddev /= NANOSEC;
         info->ri_mean /= NANOSEC;
 }
 
-
 /*
  * smbstat_srv_current_snapshot
  *
  * Returns the current snapshot.
  */

@@ -1276,28 +1472,28 @@
         smbstat_srv_snapshot();
         ss = smbstat_srv_current_snapshot();
 
         reqs = ss->ss_data.ks_reqs1;
         info = smbstat_srv_info.si_reqs1;
-        for (i = 0; i < SMB_COM_NUM; i++) {
+        for (i = 0; i < SMBSRV_KS_NREQS1; i++) {
                 (void) strlcpy(info[i].ri_name, reqs[i].kr_name,
                     sizeof (reqs[i].kr_name));
                 info[i].ri_opcode = i;
         }
         if (smbstat_opt_n)
-                qsort(info, SMB_COM_NUM, sizeof (smbstat_req_info_t),
+                qsort(info, SMBSRV_KS_NREQS1, sizeof (smbstat_req_info_t),
                     smbstat_req_cmp_name);
 
         reqs = ss->ss_data.ks_reqs2;
         info = smbstat_srv_info.si_reqs2;
-        for (i = 0; i < SMB2__NCMDS; i++) {
+        for (i = 0; i < SMBSRV_KS_NREQS2; i++) {
                 (void) strlcpy(info[i].ri_name, reqs[i].kr_name,
                     sizeof (reqs[i].kr_name));
                 info[i].ri_opcode = i;
         }
         if (smbstat_opt_n)
-                qsort(info, SMB2__NCMDS, sizeof (smbstat_req_info_t),
+                qsort(info, SMBSRV_KS_NREQS2, sizeof (smbstat_req_info_t),
                     smbstat_req_cmp_name);
 }
 
 /*
  * Return the number of ticks delta between two hrtime_t