Whamcloud - gitweb
LU-8836 lprocfs: move lprocfs_stats_[un]lock to a source file
[fs/lustre-release.git] / lustre / obdclass / lprocfs_status.c
index 650fc05..53e46a6 100644 (file)
  *
  * You should have received a copy of the GNU General Public License
  * version 2 along with this program; If not, see
- * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
+ * http://www.gnu.org/licenses/gpl-2.0.html
  *
  * GPL HEADER END
  */
@@ -300,20 +296,28 @@ EXPORT_SYMBOL(lprocfs_uint_seq_show);
 int lprocfs_wr_uint(struct file *file, const char __user *buffer,
                     unsigned long count, void *data)
 {
-        unsigned *p = data;
-        char dummy[MAX_STRING_SIZE + 1], *end;
-        unsigned long tmp;
+       unsigned        *p = data;
+       char             dummy[MAX_STRING_SIZE + 1];
+       char            *end;
+       unsigned long    tmp;
 
-        dummy[MAX_STRING_SIZE] = '\0';
-       if (copy_from_user(dummy, buffer, MAX_STRING_SIZE))
-                return -EFAULT;
+       if (count >= sizeof(dummy))
+               return -EINVAL;
 
-        tmp = simple_strtoul(dummy, &end, 0);
-        if (dummy == end)
-                return -EINVAL;
+       if (count == 0)
+               return 0;
+
+       if (copy_from_user(dummy, buffer, count))
+               return -EFAULT;
 
-        *p = (unsigned int)tmp;
-        return count;
+       dummy[count] = 0;
+
+       tmp = simple_strtoul(dummy, &end, 0);
+       if (dummy == end)
+               return -EINVAL;
+
+       *p = (unsigned int)tmp;
+       return count;
 }
 EXPORT_SYMBOL(lprocfs_wr_uint);
 
@@ -335,7 +339,7 @@ EXPORT_SYMBOL(lprocfs_uint_seq_write);
 int lprocfs_u64_seq_show(struct seq_file *m, void *data)
 {
        LASSERT(data != NULL);
-       seq_printf(m, LPU64"\n", *(__u64 *)data);
+       seq_printf(m, "%llu\n", *(__u64 *)data);
        return 0;
 }
 EXPORT_SYMBOL(lprocfs_u64_seq_show);
@@ -416,7 +420,7 @@ int lprocfs_kbytestotal_seq_show(struct seq_file *m, void *data)
                while (blk_size >>= 1)
                        result <<= 1;
 
-               seq_printf(m, LPU64"\n", result);
+               seq_printf(m, "%llu\n", result);
        }
        return rc;
 }
@@ -436,7 +440,7 @@ int lprocfs_kbytesfree_seq_show(struct seq_file *m, void *data)
                while (blk_size >>= 1)
                        result <<= 1;
 
-               seq_printf(m, LPU64"\n", result);
+               seq_printf(m, "%llu\n", result);
        }
        return rc;
 }
@@ -456,7 +460,7 @@ int lprocfs_kbytesavail_seq_show(struct seq_file *m, void *data)
                while (blk_size >>= 1)
                        result <<= 1;
 
-               seq_printf(m, LPU64"\n", result);
+               seq_printf(m, "%llu\n", result);
        }
        return rc;
 }
@@ -470,7 +474,7 @@ int lprocfs_filestotal_seq_show(struct seq_file *m, void *data)
                            cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
                            OBD_STATFS_NODELAY);
        if (!rc)
-               seq_printf(m, LPU64"\n", osfs.os_files);
+               seq_printf(m, "%llu\n", osfs.os_files);
        return rc;
 }
 EXPORT_SYMBOL(lprocfs_filestotal_seq_show);
@@ -483,7 +487,7 @@ int lprocfs_filesfree_seq_show(struct seq_file *m, void *data)
                            cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
                            OBD_STATFS_NODELAY);
        if (!rc)
-               seq_printf(m, LPU64"\n", osfs.os_ffree);
+               seq_printf(m, "%llu\n", osfs.os_ffree);
        return rc;
 }
 EXPORT_SYMBOL(lprocfs_filesfree_seq_show);
@@ -528,6 +532,95 @@ int lprocfs_conn_uuid_seq_show(struct seq_file *m, void *data)
 EXPORT_SYMBOL(lprocfs_conn_uuid_seq_show);
 
 /** add up per-cpu counters */
+
+/**
+ * Lock statistics structure for access, possibly only on this CPU.
+ *
+ * The statistics struct may be allocated with per-CPU structures for
+ * efficient concurrent update (usually only on server-wide stats), or
+ * as a single global struct (e.g. for per-client or per-job statistics),
+ * so the required locking depends on the type of structure allocated.
+ *
+ * For per-CPU statistics, pin the thread to the current cpuid so that
+ * will only access the statistics for that CPU.  If the stats structure
+ * for the current CPU has not been allocated (or previously freed),
+ * allocate it now.  The per-CPU statistics do not need locking since
+ * the thread is pinned to the CPU during update.
+ *
+ * For global statistics, lock the stats structure to prevent concurrent update.
+ *
+ * \param[in] stats    statistics structure to lock
+ * \param[in] opc      type of operation:
+ *                     LPROCFS_GET_SMP_ID: "lock" and return current CPU index
+ *                             for incrementing statistics for that CPU
+ *                     LPROCFS_GET_NUM_CPU: "lock" and return number of used
+ *                             CPU indices to iterate over all indices
+ * \param[out] flags   CPU interrupt saved state for IRQ-safe locking
+ *
+ * \retval cpuid of current thread or number of allocated structs
+ * \retval negative on error (only for opc LPROCFS_GET_SMP_ID + per-CPU stats)
+ */
+int lprocfs_stats_lock(struct lprocfs_stats *stats,
+                      enum lprocfs_stats_lock_ops opc,
+                      unsigned long *flags)
+{
+       if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU) {
+               if (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE)
+                       spin_lock_irqsave(&stats->ls_lock, *flags);
+               else
+                       spin_lock(&stats->ls_lock);
+               return opc == LPROCFS_GET_NUM_CPU ? 1 : 0;
+       }
+
+       switch (opc) {
+       case LPROCFS_GET_SMP_ID: {
+               unsigned int cpuid = get_cpu();
+
+               if (unlikely(!stats->ls_percpu[cpuid])) {
+                       int rc = lprocfs_stats_alloc_one(stats, cpuid);
+
+                       if (rc < 0) {
+                               put_cpu();
+                               return rc;
+                       }
+               }
+               return cpuid;
+       }
+       case LPROCFS_GET_NUM_CPU:
+               return stats->ls_biggest_alloc_num;
+       default:
+               LBUG();
+       }
+}
+
+/**
+ * Unlock statistics structure after access.
+ *
+ * Unlock the lock acquired via lprocfs_stats_lock() for global statistics,
+ * or unpin this thread from the current cpuid for per-CPU statistics.
+ *
+ * This function must be called using the same arguments as used when calling
+ * lprocfs_stats_lock() so that the correct operation can be performed.
+ *
+ * \param[in] stats    statistics structure to unlock
+ * \param[in] opc      type of operation (current cpuid or number of structs)
+ * \param[in] flags    CPU interrupt saved state for IRQ-safe locking
+ */
+void lprocfs_stats_unlock(struct lprocfs_stats *stats,
+                         enum lprocfs_stats_lock_ops opc,
+                         unsigned long *flags)
+{
+       if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU) {
+               if (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE)
+                       spin_unlock_irqrestore(&stats->ls_lock, *flags);
+               else
+                       spin_unlock(&stats->ls_lock);
+       } else if (opc == LPROCFS_GET_SMP_ID) {
+               put_cpu();
+       }
+}
+
+/** add up per-cpu counters */
 void lprocfs_stats_collect(struct lprocfs_stats *stats, int idx,
                           struct lprocfs_counter *cnt)
 {
@@ -599,6 +692,7 @@ static void obd_import_flags2str(struct obd_import *imp, struct seq_file *m)
 #undef flag2str
 
 static const char *obd_connect_names[] = {
+       /* flags names  */
        "read_only",
        "lov_index",
        "connect_from_mds",
@@ -663,41 +757,81 @@ static const char *obd_connect_names[] = {
        "bulk_mbits",
        "compact_obdo",
        "second_flags",
+       /* flags2 names */
+       "file_secctx",
        NULL
 };
 
-static void obd_connect_seq_flags2str(struct seq_file *m, __u64 flags, char *sep)
+static void obd_connect_seq_flags2str(struct seq_file *m, __u64 flags,
+                                     __u64 flags2, const char *sep)
 {
        bool first = true;
-       __u64 mask = 1;
+       __u64 mask;
        int i;
 
-       for (i = 0; obd_connect_names[i] != NULL; i++, mask <<= 1) {
+       for (i = 0, mask = 1; i < 64; i++, mask <<= 1) {
                if (flags & mask) {
                        seq_printf(m, "%s%s",
                                   first ? "" : sep, obd_connect_names[i]);
                        first = false;
                }
        }
-       if (flags & ~(mask - 1))
-               seq_printf(m, "%sunknown_"LPX64,
+
+       if (flags & ~(mask - 1)) {
+               seq_printf(m, "%sunknown_%#llx",
                           first ? "" : sep, flags & ~(mask - 1));
+               first = false;
+       }
+
+       if (!(flags & OBD_CONNECT_FLAGS2) || flags2 == 0)
+               return;
+
+       for (i = 64, mask = 1; obd_connect_names[i] != NULL; i++, mask <<= 1) {
+               if (flags2 & mask) {
+                       seq_printf(m, "%s%s",
+                                  first ? "" : sep, obd_connect_names[i]);
+                       first = false;
+               }
+       }
+
+       if (flags2 & ~(mask - 1)) {
+               seq_printf(m, "%sunknown2_%#llx",
+                          first ? "" : sep, flags2 & ~(mask - 1));
+               first = false;
+       }
 }
 
-int obd_connect_flags2str(char *page, int count, __u64 flags, char *sep)
+int obd_connect_flags2str(char *page, int count, __u64 flags, __u64 flags2,
+                         const char *sep)
 {
-       __u64 mask = 1;
+       __u64 mask;
        int i, ret = 0;
 
-       for (i = 0; obd_connect_names[i] != NULL; i++, mask <<= 1) {
+       for (i = 0, mask = 1; i < 64; i++, mask <<= 1) {
                if (flags & mask)
                        ret += snprintf(page + ret, count - ret, "%s%s",
                                        ret ? sep : "", obd_connect_names[i]);
        }
+
        if (flags & ~(mask - 1))
                ret += snprintf(page + ret, count - ret,
-                               "%sunknown_"LPX64,
+                               "%sunknown_%#llx",
                                ret ? sep : "", flags & ~(mask - 1));
+
+       if (!(flags & OBD_CONNECT_FLAGS2) || flags2 == 0)
+               return ret;
+
+       for (i = 64, mask = 1; obd_connect_names[i] != NULL; i++, mask <<= 1) {
+               if (flags2 & mask)
+                       ret += snprintf(page + ret, count - ret, "%s%s",
+                                       ret ? sep : "", obd_connect_names[i]);
+       }
+
+       if (flags2 & ~(mask - 1))
+               ret += snprintf(page + ret, count - ret,
+                               "%sunknown2_%#llx",
+                               ret ? sep : "", flags2 & ~(mask - 1));
+
        return ret;
 }
 EXPORT_SYMBOL(obd_connect_flags2str);
@@ -711,7 +845,7 @@ static void obd_connect_data_seqprint(struct seq_file *m,
        flags = ocd->ocd_connect_flags;
 
        seq_printf(m, "    connect_data:\n"
-                  "       flags: "LPX64"\n"
+                  "       flags: %#llx\n"
                   "       instance: %u\n",
                   ocd->ocd_connect_flags,
                   ocd->ocd_instance);
@@ -730,7 +864,7 @@ static void obd_connect_data_seqprint(struct seq_file *m,
        if (flags & OBD_CONNECT_BRW_SIZE)
                seq_printf(m, "       max_brw_size: %d\n", ocd->ocd_brw_size);
        if (flags & OBD_CONNECT_IBITS)
-               seq_printf(m, "       ibits_known: "LPX64"\n",
+               seq_printf(m, "       ibits_known: %#llx\n",
                           ocd->ocd_ibits_known);
        if (flags & OBD_CONNECT_GRANT_PARAM)
                seq_printf(m, "       grant_block_size: %d\n"
@@ -742,7 +876,7 @@ static void obd_connect_data_seqprint(struct seq_file *m,
                           ocd->ocd_grant_max_blks << ocd->ocd_grant_blkbits,
                           ocd->ocd_grant_tax_kb << 10);
        if (flags & OBD_CONNECT_TRANSNO)
-               seq_printf(m, "       first_transno: "LPX64"\n",
+               seq_printf(m, "       first_transno: %#llx\n",
                           ocd->ocd_transno);
        if (flags & OBD_CONNECT_CKSUM)
                seq_printf(m, "       cksum_types: %#x\n",
@@ -750,7 +884,7 @@ static void obd_connect_data_seqprint(struct seq_file *m,
        if (flags & OBD_CONNECT_MAX_EASIZE)
                seq_printf(m, "       max_easize: %d\n", ocd->ocd_max_easize);
        if (flags & OBD_CONNECT_MAXBYTES)
-               seq_printf(m, "       max_object_bytes: "LPU64"\n",
+               seq_printf(m, "       max_object_bytes: %llu\n",
                           ocd->ocd_maxbytes);
        if (flags & OBD_CONNECT_MULTIMODRPCS)
                seq_printf(m, "       max_mod_rpcs: %hu\n",
@@ -784,6 +918,7 @@ int lprocfs_import_seq_show(struct seq_file *m, void *data)
                   obd2cli_tgt(obd),
                   ptlrpc_import_state_name(imp->imp_state));
        obd_connect_seq_flags2str(m, imp->imp_connect_data.ocd_connect_flags,
+                                 imp->imp_connect_data.ocd_connect_flags2,
                                  ", ");
        seq_printf(m, " ]\n");
        obd_connect_data_seqprint(m, ocd);
@@ -833,7 +968,7 @@ int lprocfs_import_seq_show(struct seq_file *m, void *data)
                   "       inflight: %u\n"
                   "       unregistering: %u\n"
                   "       timeouts: %u\n"
-                  "       avg_waittime: "LPU64" %s\n",
+                  "       avg_waittime: %llu %s\n",
                   atomic_read(&imp->imp_inflight),
                   atomic_read(&imp->imp_unregistering),
                   atomic_read(&imp->imp_timeouts),
@@ -853,9 +988,9 @@ int lprocfs_import_seq_show(struct seq_file *m, void *data)
                   at_get(&imp->imp_at.iat_net_latency));
 
        seq_printf(m, "    transactions:\n"
-                  "       last_replay: "LPU64"\n"
-                  "       peer_committed: "LPU64"\n"
-                  "       last_checked: "LPU64"\n",
+                  "       last_replay: %llu\n"
+                  "       peer_committed: %llu\n"
+                  "       last_checked: %llu\n",
                   imp->imp_last_replay_transno,
                   imp->imp_peer_committed_transno,
                   imp->imp_last_transno_checked);
@@ -871,7 +1006,7 @@ int lprocfs_import_seq_show(struct seq_file *m, void *data)
                        do_div(sum, ret.lc_count);
                        ret.lc_sum = sum;
                        seq_printf(m, "    %s_data_averages:\n"
-                                  "       bytes_per_rpc: "LPU64"\n",
+                                  "       bytes_per_rpc: %llu\n",
                                   rw ? "write" : "read",
                                   ret.lc_sum);
                }
@@ -884,7 +1019,7 @@ int lprocfs_import_seq_show(struct seq_file *m, void *data)
                        __u64 sum = ret.lc_sum;
                        do_div(sum, ret.lc_count);
                        ret.lc_sum = sum;
-                       seq_printf(m, "       %s_per_rpc: "LPU64"\n",
+                       seq_printf(m, "       %s_per_rpc: %llu\n",
                                   header->lc_units, ret.lc_sum);
                        j = (int)ret.lc_sum;
                        if (j > 0)
@@ -989,11 +1124,14 @@ int lprocfs_connect_flags_seq_show(struct seq_file *m, void *data)
 {
        struct obd_device *obd = data;
        __u64 flags;
+       __u64 flags2;
 
        LPROCFS_CLIMP_CHECK(obd);
        flags = obd->u.cli.cl_import->imp_connect_data.ocd_connect_flags;
-       seq_printf(m, "flags="LPX64"\n", flags);
-       obd_connect_seq_flags2str(m, flags, "\n");
+       flags2 = obd->u.cli.cl_import->imp_connect_data.ocd_connect_flags2;
+       seq_printf(m, "flags=%#llx\n", flags);
+       seq_printf(m, "flags2=%#llx\n", flags2);
+       obd_connect_seq_flags2str(m, flags, flags2, "\n");
        seq_printf(m, "\n");
        LPROCFS_CLIMP_EXIT(obd);
        return 0;
@@ -1074,7 +1212,6 @@ int lprocfs_stats_alloc_one(struct lprocfs_stats *stats, unsigned int cpuid)
        }
        return rc;
 }
-EXPORT_SYMBOL(lprocfs_stats_alloc_one);
 
 struct lprocfs_stats *lprocfs_alloc_stats(unsigned int num,
                                           enum lprocfs_stats_flags flags)
@@ -1159,6 +1296,32 @@ void lprocfs_free_stats(struct lprocfs_stats **statsh)
 }
 EXPORT_SYMBOL(lprocfs_free_stats);
 
+u64 lprocfs_stats_collector(struct lprocfs_stats *stats, int idx,
+                           enum lprocfs_fields_flags field)
+{
+       unsigned long flags = 0;
+       unsigned int num_cpu;
+       unsigned int i;
+       u64 ret = 0;
+
+       LASSERT(stats);
+
+       num_cpu = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU, &flags);
+       for (i = 0; i < num_cpu; i++) {
+               struct lprocfs_counter *cntr;
+
+               if (!stats->ls_percpu[i])
+                       continue;
+
+               cntr = lprocfs_stats_counter_get(stats, i, idx);
+               ret += lprocfs_read_helper(cntr, &stats->ls_cnt_header[idx],
+                                          stats->ls_flags, field);
+       }
+       lprocfs_stats_unlock(stats, LPROCFS_GET_NUM_CPU, &flags);
+       return ret;
+}
+EXPORT_SYMBOL(lprocfs_stats_collector);
+
 void lprocfs_clear_stats(struct lprocfs_stats *stats)
 {
        struct lprocfs_counter          *percpu_cntr;
@@ -1225,7 +1388,6 @@ static int lprocfs_stats_seq_show(struct seq_file *p, void *v)
        struct lprocfs_counter_header   *hdr;
        struct lprocfs_counter           ctr;
        int                              idx    = *(loff_t *)v;
-       int                              rc     = 0;
 
        if (idx == 0) {
                struct timeval now;
@@ -1233,34 +1395,25 @@ static int lprocfs_stats_seq_show(struct seq_file *p, void *v)
                do_gettimeofday(&now);
                seq_printf(p, "%-25s %lu.%lu secs.usecs\n",
                           "snapshot_time", now.tv_sec, now.tv_usec);
-               if (rc < 0)
-                       return rc;
        }
 
        hdr = &stats->ls_cnt_header[idx];
        lprocfs_stats_collect(stats, idx, &ctr);
 
        if (ctr.lc_count == 0)
-               goto out;
+               return 0;
 
-       seq_printf(p, "%-25s "LPD64" samples [%s]", hdr->lc_name,
+       seq_printf(p, "%-25s %lld samples [%s]", hdr->lc_name,
                   ctr.lc_count, hdr->lc_units);
-       if (rc < 0)
-               goto out;
 
        if ((hdr->lc_config & LPROCFS_CNTR_AVGMINMAX) && ctr.lc_count > 0) {
-               seq_printf(p, " "LPD64" "LPD64" "LPD64,
+               seq_printf(p, " %lld %lld %lld",
                           ctr.lc_min, ctr.lc_max, ctr.lc_sum);
-               if (rc < 0)
-                       goto out;
                if (hdr->lc_config & LPROCFS_CNTR_STDDEV)
-                       seq_printf(p, " "LPD64, ctr.lc_sumsquare);
-               if (rc < 0)
-                       goto out;
+                       seq_printf(p, " %llu", ctr.lc_sumsquare);
        }
        seq_putc(p, '\n');
-out:
-       return (rc < 0) ? rc : 0;
+       return 0;
 }
 
 static const struct seq_operations lprocfs_stats_seq_sops = {
@@ -1386,7 +1539,6 @@ void lprocfs_init_mps_stats(int num_private_stats, struct lprocfs_stats *stats)
         LPROCFS_MD_OP_INIT(num_private_stats, stats, set_lock_data);
         LPROCFS_MD_OP_INIT(num_private_stats, stats, lock_match);
         LPROCFS_MD_OP_INIT(num_private_stats, stats, cancel_unused);
-        LPROCFS_MD_OP_INIT(num_private_stats, stats, get_remote_perm);
         LPROCFS_MD_OP_INIT(num_private_stats, stats, intent_getattr_async);
         LPROCFS_MD_OP_INIT(num_private_stats, stats, revalidate_lock);
 }
@@ -1663,6 +1815,17 @@ static int preprocess_numeric_str(char *buffer, __u64 *mult, __u64 def_mult,
        start = buffer;
 
        while (*buffer) {
+               /* allow for a single new line before the null terminator */
+               if (*buffer == '\n') {
+                       *buffer = '\0';
+                       buffer++;
+
+                       if (*buffer)
+                               return -EINVAL;
+
+                       break;
+               }
+
                /* any chars after our unit indicates a malformed string */
                if (hit_unit)
                        return -EINVAL;
@@ -1879,12 +2042,14 @@ EXPORT_SYMBOL(lprocfs_str_to_s64);
 int lprocfs_str_with_units_to_s64(const char __user *buffer,
                                  unsigned long count, __s64 *val, char defunit)
 {
-       __u64 mult;
+       __u64 mult = 1;
        int rc;
 
-       rc = get_mult(defunit, &mult);
-       if (rc)
-               return rc;
+       if (defunit != '1') {
+               rc = get_mult(defunit, &mult);
+               if (rc)
+                       return rc;
+       }
 
        return str_to_s64_internal(buffer, count, val, mult, true);
 }