]> granicus.if.org Git - sysstat/blobdiff - rd_stats.c
sar/sadc: Add stable identifier support for disks statistics
[sysstat] / rd_stats.c
index d5e9ca35b72987820a3051b45ce41eae8dc0d0fd..2569fae28e8c91f9d8fd71e820e4bed36c4ffdc8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * rd_stats.c: Read system statistics
- * (C) 1999-2016 by Sebastien GODARD (sysstat <at> orange.fr)
+ * (C) 1999-2019 by Sebastien GODARD (sysstat <at> orange.fr)
  *
  ***************************************************************************
  * This program is free software; you can redistribute it and/or modify it *
@@ -32,7 +32,6 @@
 
 #include "common.h"
 #include "rd_stats.h"
-#include "ioconf.h"
 
 #ifdef USE_NLS
 #include <locale.h>
 
 /*
  ***************************************************************************
- * Read CPU statistics and machine uptime.
+ * Read CPU statistics.
+ * Remember that this function is used by several sysstat commands!
  *
  * IN:
- * @st_cpu     Structure where stats will be saved.
- * @nbr                Total number of CPU (including cpu "all").
+ * @st_cpu     Buffer where structures containing stats will be saved.
+ * @nr_alloc   Total number of structures allocated. Value is >= 1.
  *
  * OUT:
- * @st_cpu     Structure with statistics.
- * @uptime     Machine uptime multiplied by the number of processors.
- * @uptime0    Machine uptime. Filled only if previously set to zero.
+ * @st_cpu     Buffer with statistics.
+ *
+ * RETURNS:
+ * Highest CPU number(*) for which statistics have been read.
+ * 1 means CPU "all", 2 means CPU 0, 3 means CPU 1, etc.
+ * Or -1 if the buffer was too small and needs to be reallocated.
+ *
+ * (*)This doesn't account for all processors in the machine in the case
+ * where some CPU are offline and located at the end of the list.
  ***************************************************************************
  */
-void read_stat_cpu(struct stats_cpu *st_cpu, int nbr,
-                  unsigned long long *uptime, unsigned long long *uptime0)
+__nr_t read_stat_cpu(struct stats_cpu *st_cpu, __nr_t nr_alloc)
 {
        FILE *fp;
        struct stats_cpu *st_cpu_i;
        struct stats_cpu sc;
        char line[8192];
-       int proc_nb;
+       int proc_nr;
+       __nr_t cpu_read = 0;
 
        if ((fp = fopen(STAT, "r")) == NULL) {
                fprintf(stderr, _("Cannot open %s: %s\n"), STAT, strerror(errno));
@@ -97,93 +103,82 @@ void read_stat_cpu(struct stats_cpu *st_cpu, int nbr,
                               &st_cpu->cpu_guest,
                               &st_cpu->cpu_guest_nice);
 
-                       /*
-                        * Compute the uptime of the system in jiffies (1/100ths of a second
-                        * if HZ=100).
-                        * Machine uptime is multiplied by the number of processors here.
-                        *
-                        * NB: Don't add cpu_guest/cpu_guest_nice because cpu_user/cpu_nice
-                        * already include them.
-                        */
-                       *uptime = st_cpu->cpu_user + st_cpu->cpu_nice    +
-                               st_cpu->cpu_sys    + st_cpu->cpu_idle    +
-                               st_cpu->cpu_iowait + st_cpu->cpu_hardirq +
-                               st_cpu->cpu_steal  + st_cpu->cpu_softirq;
+                       if (!cpu_read) {
+                               cpu_read = 1;
+                       }
+
+                       if (nr_alloc == 1)
+                               /* We just want to read stats for CPU "all" */
+                               break;
                }
 
                else if (!strncmp(line, "cpu", 3)) {
-                       if (nbr > 1) {
-                               /* All the fields don't necessarily exist */
-                               memset(&sc, 0, STATS_CPU_SIZE);
-                               /*
-                                * Read the number of jiffies spent in the different modes
-                                * (user, nice, etc) for current proc.
-                                * This is done only on SMP machines.
-                                */
-                               sscanf(line + 3, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
-                                      &proc_nb,
-                                      &sc.cpu_user,
-                                      &sc.cpu_nice,
-                                      &sc.cpu_sys,
-                                      &sc.cpu_idle,
-                                      &sc.cpu_iowait,
-                                      &sc.cpu_hardirq,
-                                      &sc.cpu_softirq,
-                                      &sc.cpu_steal,
-                                      &sc.cpu_guest,
-                                      &sc.cpu_guest_nice);
-
-                               if (proc_nb < (nbr - 1)) {
-                                       st_cpu_i = st_cpu + proc_nb + 1;
-                                       *st_cpu_i = sc;
-                               }
-                               /*
-                                * else additional CPUs have been dynamically registered
-                                * in /proc/stat.
-                                */
+                       /* All the fields don't necessarily exist */
+                       memset(&sc, 0, STATS_CPU_SIZE);
+                       /*
+                        * Read the number of jiffies spent in the different modes
+                        * (user, nice, etc) for current proc.
+                        * This is done only on SMP machines.
+                        */
+                       sscanf(line + 3, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
+                              &proc_nr,
+                              &sc.cpu_user,
+                              &sc.cpu_nice,
+                              &sc.cpu_sys,
+                              &sc.cpu_idle,
+                              &sc.cpu_iowait,
+                              &sc.cpu_hardirq,
+                              &sc.cpu_softirq,
+                              &sc.cpu_steal,
+                              &sc.cpu_guest,
+                              &sc.cpu_guest_nice);
+
+                       if (proc_nr + 2 > nr_alloc) {
+                               cpu_read = -1;
+                               break;
+                       }
 
-                               if (!proc_nb && !*uptime0) {
-                                       /*
-                                        * Compute uptime reduced to one proc using proc#0.
-                                        * Done if /proc/uptime was unavailable.
-                                        *
-                                        * NB: Don't add cpu_guest/cpu_guest_nice because cpu_user/cpu_nice
-                                        * already include them.
-                                        */
-                                       *uptime0 = sc.cpu_user + sc.cpu_nice  +
-                                               sc.cpu_sys     + sc.cpu_idle  +
-                                               sc.cpu_iowait  + sc.cpu_steal +
-                                               sc.cpu_hardirq + sc.cpu_softirq;
-                               }
+                       st_cpu_i = st_cpu + proc_nr + 1;
+                       *st_cpu_i = sc;
+
+                       if (proc_nr + 2 > cpu_read) {
+                               cpu_read = proc_nr + 2;
                        }
                }
        }
 
        fclose(fp);
+       return cpu_read;
 }
 
 /*
  ***************************************************************************
  * Read interrupts statistics from /proc/stat.
+ * Remember that this function is used by several sysstat commands!
  *
  * IN:
  * @st_irq     Structure where stats will be saved.
- * @nbr                Number of interrupts to read, including the total number
- *             of interrupts.
+ * @nr_alloc   Number of structures allocated. Value is >= 1.
  *
  * OUT:
  * @st_irq     Structure with statistics.
+ *
+ * RETURNS:
+ * Number of interrupts read, or -1 if the buffer was too small and
+ * needs to be reallocated.
  ***************************************************************************
  */
-void read_stat_irq(struct stats_irq *st_irq, int nbr)
+__nr_t read_stat_irq(struct stats_irq *st_irq, __nr_t nr_alloc)
 {
        FILE *fp;
        struct stats_irq *st_irq_i;
        char line[8192];
        int i, pos;
+       unsigned long long irq_nr;
+       __nr_t irq_read = 0;
 
        if ((fp = fopen(STAT, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -192,15 +187,34 @@ void read_stat_irq(struct stats_irq *st_irq, int nbr)
                        sscanf(line + 5, "%llu", &st_irq->irq_nr);
                        pos = strcspn(line + 5, " ") + 5;
 
-                       for (i = 1; i < nbr; i++) {
-                               st_irq_i = st_irq + i;
-                               sscanf(line + pos, " %llu", &st_irq_i->irq_nr);
-                               pos += strcspn(line + pos + 1, " ") + 1;
+                       irq_read++;
+                       if (nr_alloc == 1)
+                               /* We just want to read the total number of interrupts */
+                               break;
+
+                       do {
+                               i = sscanf(line + pos, " %llu", &irq_nr);
+                               if (i < 1)
+                                       break;
+
+                               if (irq_read + 1 > nr_alloc) {
+                                       irq_read = -1;
+                                       break;
+                               }
+                               st_irq_i = st_irq + irq_read++;
+                               st_irq_i->irq_nr = irq_nr;
+
+                               i = strcspn(line + pos + 1, " ");
+                               pos += i + 1;
                        }
+                       while ((i > 0) && (pos < (sizeof(line) - 1)));
+
+                       break;
                }
        }
 
        fclose(fp);
+       return irq_read;
 }
 
 /*
@@ -212,85 +226,93 @@ void read_stat_irq(struct stats_irq *st_irq, int nbr)
  *
  * OUT:
  * @st_memory  Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_meminfo(struct stats_memory *st_memory)
+__nr_t read_meminfo(struct stats_memory *st_memory)
 {
        FILE *fp;
        char line[128];
 
        if ((fp = fopen(MEMINFO, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
                if (!strncmp(line, "MemTotal:", 9)) {
                        /* Read the total amount of memory in kB */
-                       sscanf(line + 9, "%lu", &st_memory->tlmkb);
+                       sscanf(line + 9, "%llu", &st_memory->tlmkb);
                }
                else if (!strncmp(line, "MemFree:", 8)) {
                        /* Read the amount of free memory in kB */
-                       sscanf(line + 8, "%lu", &st_memory->frmkb);
+                       sscanf(line + 8, "%llu", &st_memory->frmkb);
+               }
+               else if (!strncmp(line, "MemAvailable:", 13)) {
+                       /* Read the amount of available memory in kB */
+                       sscanf(line + 13, "%llu", &st_memory->availablekb);
                }
                else if (!strncmp(line, "Buffers:", 8)) {
                        /* Read the amount of buffered memory in kB */
-                       sscanf(line + 8, "%lu", &st_memory->bufkb);
+                       sscanf(line + 8, "%llu", &st_memory->bufkb);
                }
                else if (!strncmp(line, "Cached:", 7)) {
                        /* Read the amount of cached memory in kB */
-                       sscanf(line + 7, "%lu", &st_memory->camkb);
+                       sscanf(line + 7, "%llu", &st_memory->camkb);
                }
                else if (!strncmp(line, "SwapCached:", 11)) {
                        /* Read the amount of cached swap in kB */
-                       sscanf(line + 11, "%lu", &st_memory->caskb);
+                       sscanf(line + 11, "%llu", &st_memory->caskb);
                }
                else if (!strncmp(line, "Active:", 7)) {
                        /* Read the amount of active memory in kB */
-                       sscanf(line + 7, "%lu", &st_memory->activekb);
+                       sscanf(line + 7, "%llu", &st_memory->activekb);
                }
                else if (!strncmp(line, "Inactive:", 9)) {
                        /* Read the amount of inactive memory in kB */
-                       sscanf(line + 9, "%lu", &st_memory->inactkb);
+                       sscanf(line + 9, "%llu", &st_memory->inactkb);
                }
                else if (!strncmp(line, "SwapTotal:", 10)) {
                        /* Read the total amount of swap memory in kB */
-                       sscanf(line + 10, "%lu", &st_memory->tlskb);
+                       sscanf(line + 10, "%llu", &st_memory->tlskb);
                }
                else if (!strncmp(line, "SwapFree:", 9)) {
                        /* Read the amount of free swap memory in kB */
-                       sscanf(line + 9, "%lu", &st_memory->frskb);
+                       sscanf(line + 9, "%llu", &st_memory->frskb);
                }
                else if (!strncmp(line, "Dirty:", 6)) {
                        /* Read the amount of dirty memory in kB */
-                       sscanf(line + 6, "%lu", &st_memory->dirtykb);
+                       sscanf(line + 6, "%llu", &st_memory->dirtykb);
                }
                else if (!strncmp(line, "Committed_AS:", 13)) {
                        /* Read the amount of commited memory in kB */
-                       sscanf(line + 13, "%lu", &st_memory->comkb);
+                       sscanf(line + 13, "%llu", &st_memory->comkb);
                }
                else if (!strncmp(line, "AnonPages:", 10)) {
                        /* Read the amount of pages mapped into userspace page tables in kB */
-                       sscanf(line + 10, "%lu", &st_memory->anonpgkb);
+                       sscanf(line + 10, "%llu", &st_memory->anonpgkb);
                }
                else if (!strncmp(line, "Slab:", 5)) {
                        /* Read the amount of in-kernel data structures cache in kB */
-                       sscanf(line + 5, "%lu", &st_memory->slabkb);
+                       sscanf(line + 5, "%llu", &st_memory->slabkb);
                }
                else if (!strncmp(line, "KernelStack:", 12)) {
                        /* Read the kernel stack utilization in kB */
-                       sscanf(line + 12, "%lu", &st_memory->kstackkb);
+                       sscanf(line + 12, "%llu", &st_memory->kstackkb);
                }
                else if (!strncmp(line, "PageTables:", 11)) {
                        /* Read the amount of memory dedicated to the lowest level of page tables in kB */
-                       sscanf(line + 11, "%lu", &st_memory->pgtblkb);
+                       sscanf(line + 11, "%llu", &st_memory->pgtblkb);
                }
                else if (!strncmp(line, "VmallocUsed:", 12)) {
                        /* Read the amount of vmalloc area which is used in kB */
-                       sscanf(line + 12, "%lu", &st_memory->vmusedkb);
+                       sscanf(line + 12, "%llu", &st_memory->vmusedkb);
                }
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -298,29 +320,137 @@ void read_meminfo(struct stats_memory *st_memory)
  * Read machine uptime, independently of the number of processors.
  *
  * OUT:
- * @uptime     Uptime value in jiffies.
+ * @uptime     Uptime value in hundredths of a second.
  ***************************************************************************
  */
 void read_uptime(unsigned long long *uptime)
 {
-       FILE *fp;
+       FILE *fp = NULL;
        char line[128];
        unsigned long up_sec, up_cent;
+       int err = FALSE;
 
-       if ((fp = fopen(UPTIME, "r")) == NULL)
-               return;
+       if ((fp = fopen(UPTIME, "r")) == NULL) {
+               err = TRUE;
+       }
+       else if (fgets(line, sizeof(line), fp) == NULL) {
+               err = TRUE;
+       }
+       else if (sscanf(line, "%lu.%lu", &up_sec, &up_cent) == 2) {
+               *uptime = (unsigned long long) up_sec * 100 +
+                         (unsigned long long) up_cent;
+       }
+       else {
+               err = TRUE;
+       }
 
-       if (fgets(line, sizeof(line), fp) == NULL) {
+       if (fp != NULL) {
                fclose(fp);
-               return;
        }
+       if (err) {
+               fprintf(stderr, _("Cannot read %s\n"), UPTIME);
+               exit(2);
+       }
+}
 
-       sscanf(line, "%lu.%lu", &up_sec, &up_cent);
-       *uptime = (unsigned long long) up_sec * HZ +
-                 (unsigned long long) up_cent * HZ / 100;
+/*
+ ***************************************************************************
+ * Compute "extended" device statistics (service time, etc.).
+ *
+ * IN:
+ * @sdc                Structure with current device statistics.
+ * @sdp                Structure with previous device statistics.
+ * @itv                Interval of time in 1/100th of a second.
+ *
+ * OUT:
+ * @xds                Structure with extended statistics.
+ ***************************************************************************
+*/
+void compute_ext_disk_stats(struct stats_disk *sdc, struct stats_disk *sdp,
+                           unsigned long long itv, struct ext_disk_stats *xds)
+{
+       xds->util  = S_VALUE(sdp->tot_ticks, sdc->tot_ticks, itv);
+       /*
+        * Kernel gives ticks already in milliseconds for all platforms
+        * => no need for further scaling.
+        */
+       xds->await = (sdc->nr_ios - sdp->nr_ios) ?
+               ((sdc->rd_ticks - sdp->rd_ticks) + (sdc->wr_ticks - sdp->wr_ticks) + (sdc->dc_ticks - sdp->dc_ticks)) /
+               ((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0;
+       xds->arqsz = (sdc->nr_ios - sdp->nr_ios) ?
+               ((sdc->rd_sect - sdp->rd_sect) + (sdc->wr_sect - sdp->wr_sect) + (sdc->dc_sect - sdp->dc_sect)) /
+               ((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0;
+}
 
-       fclose(fp);
+/*
+ ***************************************************************************
+ * Since ticks may vary slightly from CPU to CPU, we'll want
+ * to recalculate itv based on this CPU's tick count, rather
+ * than that reported by the "cpu" line. Otherwise we
+ * occasionally end up with slightly skewed figures, with
+ * the skew being greater as the time interval grows shorter.
+ *
+ * IN:
+ * @scc        Current sample statistics for current CPU.
+ * @scp        Previous sample statistics for current CPU.
+ *
+ * RETURNS:
+ * Interval of time based on current CPU, expressed in jiffies.
+ ***************************************************************************
+ */
+unsigned long long get_per_cpu_interval(struct stats_cpu *scc,
+                                       struct stats_cpu *scp)
+{
+       unsigned long long ishift = 0LL;
 
+       if ((scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest)) {
+               /*
+                * Sometimes the nr of jiffies spent in guest mode given by the guest
+                * counter in /proc/stat is slightly higher than that included in
+                * the user counter. Update the interval value accordingly.
+                */
+               ishift += (scp->cpu_user - scp->cpu_guest) -
+                         (scc->cpu_user - scc->cpu_guest);
+       }
+       if ((scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice)) {
+               /*
+                * Idem for nr of jiffies spent in guest_nice mode.
+                */
+               ishift += (scp->cpu_nice - scp->cpu_guest_nice) -
+                         (scc->cpu_nice - scc->cpu_guest_nice);
+       }
+
+       /*
+        * Workaround for CPU coming back online: With recent kernels
+        * some fields (user, nice, system) restart from their previous value,
+        * whereas others (idle, iowait) restart from zero.
+        * For the latter we need to set their previous value to zero to
+        * avoid getting an interval value < 0.
+        * (I don't know how the other fields like hardirq, steal... behave).
+        * Don't assume the CPU has come back from offline state if previous
+        * value was greater than ULLONG_MAX - 0x7ffff (the counter probably
+        * overflew).
+        */
+       if ((scc->cpu_idle < scp->cpu_idle) && (scp->cpu_idle < (ULLONG_MAX - 0x7ffff))) {
+               scp->cpu_idle = 0;
+       }
+       if ((scc->cpu_iowait < scp->cpu_iowait) && (scp->cpu_iowait < (ULLONG_MAX - 0x7ffff))) {
+               scp->cpu_iowait = 0;
+       }
+
+       /*
+        * Don't take cpu_guest and cpu_guest_nice into account
+        * because cpu_user and cpu_nice already include them.
+        */
+       return ((scc->cpu_user    + scc->cpu_nice   +
+                scc->cpu_sys     + scc->cpu_iowait +
+                scc->cpu_idle    + scc->cpu_steal  +
+                scc->cpu_hardirq + scc->cpu_softirq) -
+               (scp->cpu_user    + scp->cpu_nice   +
+                scp->cpu_sys     + scp->cpu_iowait +
+                scp->cpu_idle    + scp->cpu_steal  +
+                scp->cpu_hardirq + scp->cpu_softirq) +
+                ishift);
 }
 
 #ifdef SOURCE_SADC
@@ -372,15 +502,18 @@ void oct2chr(char *str)
  *
  * OUT:
  * @st_pcsw    Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_stat_pcsw(struct stats_pcsw *st_pcsw)
+__nr_t read_stat_pcsw(struct stats_pcsw *st_pcsw)
 {
        FILE *fp;
        char line[8192];
 
        if ((fp = fopen(STAT, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -396,6 +529,7 @@ void read_stat_pcsw(struct stats_pcsw *st_pcsw)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -407,20 +541,23 @@ void read_stat_pcsw(struct stats_pcsw *st_pcsw)
  *
  * OUT:
  * @st_queue   Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_loadavg(struct stats_queue *st_queue)
+__nr_t read_loadavg(struct stats_queue *st_queue)
 {
        FILE *fp;
        char line[8192];
-       int load_tmp[3];
+       unsigned int load_tmp[3];
        int rc;
 
        if ((fp = fopen(LOADAVG, "r")) == NULL)
-               return;
+               return 0;
 
        /* Read load averages and queue length */
-       rc = fscanf(fp, "%d.%u %d.%u %d.%u %lu/%u %*d\n",
+       rc = fscanf(fp, "%u.%u %u.%u %u.%u %llu/%llu %*d\n",
                    &load_tmp[0], &st_queue->load_avg_1,
                    &load_tmp[1], &st_queue->load_avg_5,
                    &load_tmp[2], &st_queue->load_avg_15,
@@ -430,7 +567,7 @@ void read_loadavg(struct stats_queue *st_queue)
        fclose(fp);
 
        if (rc < 8)
-               return;
+               return 0;
 
        st_queue->load_avg_1  += load_tmp[0] * 100;
        st_queue->load_avg_5  += load_tmp[1] * 100;
@@ -443,18 +580,19 @@ void read_loadavg(struct stats_queue *st_queue)
 
        /* Read nr of tasks blocked from /proc/stat */
        if ((fp = fopen(STAT, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
                if (!strncmp(line, "procs_blocked ", 14)) {
                        /* Read number of processes blocked */
-                       sscanf(line + 14, "%lu", &st_queue->procs_blocked);
+                       sscanf(line + 14, "%llu", &st_queue->procs_blocked);
                        break;
                }
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -466,15 +604,18 @@ void read_loadavg(struct stats_queue *st_queue)
  *
  * OUT:
  * @st_swap    Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_vmstat_swap(struct stats_swap *st_swap)
+__nr_t read_vmstat_swap(struct stats_swap *st_swap)
 {
        FILE *fp;
        char line[128];
 
        if ((fp = fopen(VMSTAT, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -489,6 +630,7 @@ void read_vmstat_swap(struct stats_swap *st_swap)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -500,16 +642,19 @@ void read_vmstat_swap(struct stats_swap *st_swap)
  *
  * OUT:
  * @st_paging  Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_vmstat_paging(struct stats_paging *st_paging)
+__nr_t read_vmstat_paging(struct stats_paging *st_paging)
 {
        FILE *fp;
        char line[128];
        unsigned long pgtmp;
 
        if ((fp = fopen(VMSTAT, "r")) == NULL)
-               return;
+               return 0;
 
        st_paging->pgsteal = 0;
        st_paging->pgscan_kswapd = st_paging->pgscan_direct = 0;
@@ -541,12 +686,12 @@ void read_vmstat_paging(struct stats_paging *st_paging)
                        sscanf(strchr(line, ' '), "%lu", &pgtmp);
                        st_paging->pgsteal += pgtmp;
                }
-               else if (!strncmp(line, "pgscan_kswapd_", 14)) {
+               else if (!strncmp(line, "pgscan_kswapd", 13)) {
                        /* Read number of pages scanned by the kswapd daemon */
                        sscanf(strchr(line, ' '), "%lu", &pgtmp);
                        st_paging->pgscan_kswapd += pgtmp;
                }
-               else if (!strncmp(line, "pgscan_direct_", 14)) {
+               else if (!strncmp(line, "pgscan_direct", 13)) {
                        /* Read number of pages scanned directly */
                        sscanf(strchr(line, ' '), "%lu", &pgtmp);
                        st_paging->pgscan_direct += pgtmp;
@@ -554,6 +699,7 @@ void read_vmstat_paging(struct stats_paging *st_paging)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -565,40 +711,59 @@ void read_vmstat_paging(struct stats_paging *st_paging)
  *
  * OUT:
  * @st_io      Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_diskstats_io(struct stats_io *st_io)
+__nr_t read_diskstats_io(struct stats_io *st_io)
 {
        FILE *fp;
-       char line[256];
+       char line[1024];
        char dev_name[MAX_NAME_LEN];
        unsigned int major, minor;
-       unsigned long rd_ios, wr_ios, rd_sec, wr_sec;
+       unsigned long rd_ios, wr_ios, dc_ios;
+       unsigned long rd_sec, wr_sec, dc_sec;
 
        if ((fp = fopen(DISKSTATS, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
-               if (sscanf(line, "%u %u %s %lu %*u %lu %*u %lu %*u %lu",
+               /* Discard I/O stats may be not available */
+               dc_ios = dc_sec = 0;
+
+               if (sscanf(line,
+                          "%u %u %s "
+                          "%lu %*u %lu %*u "
+                          "%lu %*u %lu %*u "
+                          "%*u %*u %*u "
+                          "%lu %*u %lu",
                           &major, &minor, dev_name,
-                          &rd_ios, &rd_sec, &wr_ios, &wr_sec) == 7) {
+                          &rd_ios, &rd_sec,
+                          &wr_ios, &wr_sec,
+                          &dc_ios, &dc_sec) >= 7) {
 
                        if (is_device(dev_name, IGNORE_VIRTUAL_DEVICES)) {
                                /*
                                 * OK: It's a (real) device and not a partition.
                                 * Note: Structure should have been initialized first!
                                 */
-                               st_io->dk_drive      += rd_ios + wr_ios;
+                               st_io->dk_drive      += (unsigned long long) rd_ios +
+                                                       (unsigned long long) wr_ios +
+                                                       (unsigned long long) dc_ios;
                                st_io->dk_drive_rio  += rd_ios;
                                st_io->dk_drive_rblk += rd_sec;
                                st_io->dk_drive_wio  += wr_ios;
                                st_io->dk_drive_wblk += wr_sec;
+                               st_io->dk_drive_dio  += dc_ios;
+                               st_io->dk_drive_dblk += dc_sec;
                        }
                }
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -607,54 +772,89 @@ void read_diskstats_io(struct stats_io *st_io)
  *
  * IN:
  * @st_disk    Structure where stats will be saved.
- * @nbr                Maximum number of block devices.
+ * @nr_alloc   Total number of structures allocated. Value is >= 1.
  * @read_part  True if disks *and* partitions should be read; False if only
  *             disks are read.
  *
  * OUT:
  * @st_disk    Structure with statistics.
+ *
+ * RETURNS:
+ * Number of block devices read, or -1 if the buffer was too small and
+ * needs to be reallocated.
  ***************************************************************************
  */
-void read_diskstats_disk(struct stats_disk *st_disk, int nbr, int read_part)
+__nr_t read_diskstats_disk(struct stats_disk *st_disk, __nr_t nr_alloc,
+                          int read_part)
 {
        FILE *fp;
-       char line[256];
+       char line[1024];
        char dev_name[MAX_NAME_LEN];
-       int dsk = 0;
        struct stats_disk *st_disk_i;
-       unsigned int major, minor, rd_ticks, wr_ticks, tot_ticks, rq_ticks;
-       unsigned long rd_ios, wr_ios, rd_sec, wr_sec;
+       unsigned int major, minor, rd_ticks, wr_ticks, dc_ticks, tot_ticks, rq_ticks, part_nr;
+       unsigned long rd_ios, wr_ios, dc_ios, rd_sec, wr_sec, dc_sec;
+       unsigned long long wwn[2];
+       __nr_t dsk_read = 0;
 
        if ((fp = fopen(DISKSTATS, "r")) == NULL)
-               return;
+               return 0;
+
+       while (fgets(line, sizeof(line), fp) != NULL) {
 
-       while ((fgets(line, sizeof(line), fp) != NULL) && (dsk < nbr)) {
+               /* Discard I/O stats may be not available */
+               dc_ios = dc_sec = dc_ticks = 0;
 
-               if (sscanf(line, "%u %u %s %lu %*u %lu %u %lu %*u %lu"
-                          " %u %*u %u %u",
+               if (sscanf(line,
+                          "%u %u %s "
+                          "%lu %*u %lu %u "
+                          "%lu %*u %lu %u "
+                          "%*u %u %u "
+                          "%lu %*u %lu %u",
                           &major, &minor, dev_name,
-                          &rd_ios, &rd_sec, &rd_ticks, &wr_ios, &wr_sec, &wr_ticks,
-                          &tot_ticks, &rq_ticks) == 11) {
+                          &rd_ios, &rd_sec, &rd_ticks,
+                          &wr_ios, &wr_sec, &wr_ticks,
+                          &tot_ticks, &rq_ticks,
+                          &dc_ios, &dc_sec, &dc_ticks) >= 11) {
 
-                       if (!rd_ios && !wr_ios)
+                       if (!rd_ios && !wr_ios && !dc_ios)
                                /* Unused device: Ignore it */
                                continue;
                        if (read_part || is_device(dev_name, ACCEPT_VIRTUAL_DEVICES)) {
-                               st_disk_i = st_disk + dsk++;
+
+                               if (dsk_read + 1 > nr_alloc) {
+                                       dsk_read = -1;
+                                       break;
+                               }
+
+                               st_disk_i = st_disk + dsk_read++;
                                st_disk_i->major     = major;
                                st_disk_i->minor     = minor;
-                               st_disk_i->nr_ios    = rd_ios + wr_ios;
+                               st_disk_i->nr_ios    = (unsigned long long) rd_ios +
+                                                      (unsigned long long) wr_ios +
+                                                      (unsigned long long) dc_ios;
                                st_disk_i->rd_sect   = rd_sec;
                                st_disk_i->wr_sect   = wr_sec;
+                               st_disk_i->dc_sect   = dc_sec;
                                st_disk_i->rd_ticks  = rd_ticks;
                                st_disk_i->wr_ticks  = wr_ticks;
+                               st_disk_i->dc_ticks  = dc_ticks;
                                st_disk_i->tot_ticks = tot_ticks;
                                st_disk_i->rq_ticks  = rq_ticks;
+
+                               if (get_wwnid_from_pretty(dev_name, wwn, &part_nr) < 0) {
+                                       st_disk_i->wwn[0] = 0ULL;
+                               }
+                               else {
+                                       st_disk_i->wwn[0] = wwn[0];
+                                       st_disk_i->wwn[1] = wwn[1];
+                                       st_disk_i->part_nr = part_nr;
+                               }
                        }
                }
        }
 
        fclose(fp);
+       return dsk_read;
 }
 
 /*
@@ -663,33 +863,39 @@ void read_diskstats_disk(struct stats_disk *st_disk, int nbr, int read_part)
  *
  * IN:
  * @st_serial  Structure where stats will be saved.
- * @nbr                Maximum number of serial lines.
+ * @nr_alloc   Total number of structures allocated. Value is >= 1.
  *
  * OUT:
  * @st_serial  Structure with statistics.
+ *
+ * RETURNS:
+ * Number of serial lines read, or -1 if the buffer was too small and
+ * needs to be reallocated.
  ***************************************************************************
  */
-void read_tty_driver_serial(struct stats_serial *st_serial, int nbr)
+__nr_t read_tty_driver_serial(struct stats_serial *st_serial, __nr_t nr_alloc)
 {
        FILE *fp;
        struct stats_serial *st_serial_i;
-       int sl = 0;
        char line[256];
        char *p;
+       __nr_t sl_read = 0;
 
        if ((fp = fopen(SERIAL, "r")) == NULL)
-               return;
+               return 0;
 
-       while ((fgets(line, sizeof(line), fp) != NULL) && (sl < nbr)) {
+       while (fgets(line, sizeof(line), fp) != NULL ) {
 
                if ((p = strstr(line, "tx:")) != NULL) {
-                       st_serial_i = st_serial + sl;
+
+                       if (sl_read + 1 > nr_alloc) {
+                               sl_read = -1;
+                               break;
+                       }
+
+                       st_serial_i = st_serial + sl_read++;
+                       /* Read serial line number */
                        sscanf(line, "%u", &st_serial_i->line);
-                       /*
-                        * A value of 0 means an unused structure.
-                        * So increment it to make sure it is not zero.
-                        */
-                       (st_serial_i->line)++;
                        /*
                         * Read the number of chars transmitted and received by
                         * current serial line.
@@ -710,12 +916,11 @@ void read_tty_driver_serial(struct stats_serial *st_serial, int nbr)
                        if ((p = strstr(line, "oe:")) != NULL) {
                                sscanf(p + 3, "%u", &st_serial_i->overrun);
                        }
-
-                       sl++;
                }
        }
 
        fclose(fp);
+       return sl_read;
 }
 
 /*
@@ -727,17 +932,20 @@ void read_tty_driver_serial(struct stats_serial *st_serial, int nbr)
  *
  * OUT:
  * @st_ktables Structure with statistics.
+ *
+ * RETURNS:
+ * 1 (always success).
  ***************************************************************************
  */
-void read_kernel_tables(struct stats_ktables *st_ktables)
+__nr_t read_kernel_tables(struct stats_ktables *st_ktables)
 {
        FILE *fp;
-       unsigned int parm;
+       unsigned long long parm;
        int rc = 0;
 
        /* Open /proc/sys/fs/dentry-state file */
        if ((fp = fopen(FDENTRY_STATE, "r")) != NULL) {
-               rc = fscanf(fp, "%*d %u",
+               rc = fscanf(fp, "%*d %llu",
                            &st_ktables->dentry_stat);
                fclose(fp);
                if (rc == 0) {
@@ -747,7 +955,7 @@ void read_kernel_tables(struct stats_ktables *st_ktables)
 
        /* Open /proc/sys/fs/file-nr file */
        if ((fp = fopen(FFILE_NR, "r")) != NULL) {
-               rc = fscanf(fp, "%u %u",
+               rc = fscanf(fp, "%llu %llu",
                            &st_ktables->file_used, &parm);
                fclose(fp);
                /*
@@ -764,7 +972,7 @@ void read_kernel_tables(struct stats_ktables *st_ktables)
 
        /* Open /proc/sys/fs/inode-state file */
        if ((fp = fopen(FINODE_STATE, "r")) != NULL) {
-               rc = fscanf(fp, "%u %u",
+               rc = fscanf(fp, "%llu %llu",
                            &st_ktables->inode_used, &parm);
                fclose(fp);
                /*
@@ -781,13 +989,15 @@ void read_kernel_tables(struct stats_ktables *st_ktables)
 
        /* Open /proc/sys/kernel/pty/nr file */
        if ((fp = fopen(PTY_NR, "r")) != NULL) {
-               rc = fscanf(fp, "%u",
+               rc = fscanf(fp, "%llu",
                            &st_ktables->pty_nr);
                fclose(fp);
                if (rc == 0) {
                        st_ktables->pty_nr = 0;
                }
        }
+
+       return 1;
 }
 
 /*
@@ -796,32 +1006,39 @@ void read_kernel_tables(struct stats_ktables *st_ktables)
  *
  * IN:
  * @st_net_dev Structure where stats will be saved.
- * @nbr                Maximum number of network interfaces.
+ * @nr_alloc   Total number of structures allocated. Value is >= 1.
  *
  * OUT:
  * @st_net_dev Structure with statistics.
  *
  * RETURNS:
- * Number of interfaces for which stats have been read.
+ * Number of interfaces read, or -1 if the buffer was too small and
+ * needs to be reallocated.
  ***************************************************************************
  */
-int read_net_dev(struct stats_net_dev *st_net_dev, int nbr)
+__nr_t read_net_dev(struct stats_net_dev *st_net_dev, __nr_t nr_alloc)
 {
        FILE *fp;
        struct stats_net_dev *st_net_dev_i;
        char line[256];
        char iface[MAX_IFACE_LEN];
-       int dev = 0;
+       __nr_t dev_read = 0;
        int pos;
 
        if ((fp = fopen(NET_DEV, "r")) == NULL)
                return 0;
 
-       while ((fgets(line, sizeof(line), fp) != NULL) && (dev < nbr)) {
+       while (fgets(line, sizeof(line), fp) != NULL) {
 
                pos = strcspn(line, ":");
                if (pos < strlen(line)) {
-                       st_net_dev_i = st_net_dev + dev;
+
+                       if (dev_read + 1 > nr_alloc) {
+                               dev_read = -1;
+                               break;
+                       }
+
+                       st_net_dev_i = st_net_dev + dev_read++;
                        strncpy(iface, line, MINIMUM(pos, MAX_IFACE_LEN - 1));
                        iface[MINIMUM(pos, MAX_IFACE_LEN - 1)] = '\0';
                        sscanf(iface, "%s", st_net_dev_i->interface); /* Skip heading spaces */
@@ -834,13 +1051,11 @@ int read_net_dev(struct stats_net_dev *st_net_dev, int nbr)
                               &st_net_dev_i->tx_bytes,
                               &st_net_dev_i->tx_packets,
                               &st_net_dev_i->tx_compressed);
-                       dev++;
                }
        }
 
        fclose(fp);
-
-       return dev;
+       return dev_read;
 }
 
 /*
@@ -849,7 +1064,7 @@ int read_net_dev(struct stats_net_dev *st_net_dev, int nbr)
  *
  * IN:
  * @st_net_dev Structure where stats will be saved.
- * @nbr                Real number of network interfaces available.
+ * @nbr                Number of network interfaces to read.
  *
  * OUT:
  * @st_net_dev Structure with statistics.
@@ -914,29 +1129,39 @@ void read_if_info(struct stats_net_dev *st_net_dev, int nbr)
  *
  * IN:
  * @st_net_edev        Structure where stats will be saved.
- * @nbr                Maximum number of network interfaces.
+ * @nr_alloc   Total number of structures allocated. Value is >= 1.
  *
  * OUT:
  * @st_net_edev        Structure with statistics.
+ *
+ * RETURNS:
+ * Number of interfaces read, or -1 if the buffer was too small and
+ * needs to be reallocated.
  ***************************************************************************
  */
-void read_net_edev(struct stats_net_edev *st_net_edev, int nbr)
+__nr_t read_net_edev(struct stats_net_edev *st_net_edev, __nr_t nr_alloc)
 {
        FILE *fp;
        struct stats_net_edev *st_net_edev_i;
        static char line[256];
        char iface[MAX_IFACE_LEN];
-       int dev = 0;
+       __nr_t dev_read = 0;
        int pos;
 
        if ((fp = fopen(NET_DEV, "r")) == NULL)
-               return;
+               return 0;
 
-       while ((fgets(line, sizeof(line), fp) != NULL) && (dev < nbr)) {
+       while (fgets(line, sizeof(line), fp) != NULL) {
 
                pos = strcspn(line, ":");
                if (pos < strlen(line)) {
-                       st_net_edev_i = st_net_edev + dev;
+
+                       if (dev_read + 1 > nr_alloc) {
+                               dev_read = -1;
+                               break;
+                       }
+
+                       st_net_edev_i = st_net_edev + dev_read++;
                        strncpy(iface, line, MINIMUM(pos, MAX_IFACE_LEN - 1));
                        iface[MINIMUM(pos, MAX_IFACE_LEN - 1)] = '\0';
                        sscanf(iface, "%s", st_net_edev_i->interface); /* Skip heading spaces */
@@ -951,11 +1176,11 @@ void read_net_edev(struct stats_net_edev *st_net_edev, int nbr)
                               &st_net_edev_i->tx_fifo_errors,
                               &st_net_edev_i->collisions,
                               &st_net_edev_i->tx_carrier_errors);
-                       dev++;
                }
        }
 
        fclose(fp);
+       return dev_read;
 }
 
 /*
@@ -967,16 +1192,19 @@ void read_net_edev(struct stats_net_edev *st_net_edev, int nbr)
  *
  * OUT:
  * @st_net_nfs Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_nfs(struct stats_net_nfs *st_net_nfs)
+__nr_t read_net_nfs(struct stats_net_nfs *st_net_nfs)
 {
        FILE *fp;
        char line[256];
        unsigned int getattcnt = 0, accesscnt = 0, readcnt = 0, writecnt = 0;
 
        if ((fp = fopen(NET_RPC_NFS, "r")) == NULL)
-               return;
+               return 0;
 
        memset(st_net_nfs, 0, STATS_NET_NFS_SIZE);
 
@@ -1008,6 +1236,7 @@ void read_net_nfs(struct stats_net_nfs *st_net_nfs)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1019,16 +1248,19 @@ void read_net_nfs(struct stats_net_nfs *st_net_nfs)
  *
  * OUT:
  * @st_net_nfsd        Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_nfsd(struct stats_net_nfsd *st_net_nfsd)
+__nr_t read_net_nfsd(struct stats_net_nfsd *st_net_nfsd)
 {
        FILE *fp;
        char line[256];
        unsigned int getattcnt = 0, accesscnt = 0, readcnt = 0, writecnt = 0;
 
        if ((fp = fopen(NET_RPC_NFSD, "r")) == NULL)
-               return;
+               return 0;
 
        memset(st_net_nfsd, 0, STATS_NET_NFSD_SIZE);
 
@@ -1072,6 +1304,7 @@ void read_net_nfsd(struct stats_net_nfsd *st_net_nfsd)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1083,16 +1316,19 @@ void read_net_nfsd(struct stats_net_nfsd *st_net_nfsd)
  *
  * OUT:
  * @st_net_sock        Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_sock(struct stats_net_sock *st_net_sock)
+__nr_t read_net_sock(struct stats_net_sock *st_net_sock)
 {
        FILE *fp;
        char line[96];
        char *p;
 
        if ((fp = fopen(NET_SOCKSTAT, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1122,6 +1358,7 @@ void read_net_sock(struct stats_net_sock *st_net_sock)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1133,16 +1370,19 @@ void read_net_sock(struct stats_net_sock *st_net_sock)
  *
  * OUT:
  * @st_net_ip  Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_ip(struct stats_net_ip *st_net_ip)
+__nr_t read_net_ip(struct stats_net_ip *st_net_ip)
 {
        FILE *fp;
        char line[1024];
        int sw = FALSE;
 
        if ((fp = fopen(NET_SNMP, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1168,6 +1408,7 @@ void read_net_ip(struct stats_net_ip *st_net_ip)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1179,16 +1420,19 @@ void read_net_ip(struct stats_net_ip *st_net_ip)
  *
  * OUT:
  * @st_net_eip Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_eip(struct stats_net_eip *st_net_eip)
+__nr_t read_net_eip(struct stats_net_eip *st_net_eip)
 {
        FILE *fp;
        char line[1024];
        int sw = FALSE;
 
        if ((fp = fopen(NET_SNMP, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1214,6 +1458,7 @@ void read_net_eip(struct stats_net_eip *st_net_eip)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1225,9 +1470,12 @@ void read_net_eip(struct stats_net_eip *st_net_eip)
  *
  * OUT:
  * @st_net_icmp        Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_icmp(struct stats_net_icmp *st_net_icmp)
+__nr_t read_net_icmp(struct stats_net_icmp *st_net_icmp)
 {
        FILE *fp;
        char line[1024];
@@ -1235,7 +1483,7 @@ void read_net_icmp(struct stats_net_icmp *st_net_icmp)
        int sw = FALSE;
 
        if ((fp = fopen(NET_SNMP, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1286,6 +1534,7 @@ void read_net_icmp(struct stats_net_icmp *st_net_icmp)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1297,23 +1546,26 @@ void read_net_icmp(struct stats_net_icmp *st_net_icmp)
  *
  * OUT:
  * @st_net_eicmp       Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_eicmp(struct stats_net_eicmp *st_net_eicmp)
+__nr_t read_net_eicmp(struct stats_net_eicmp *st_net_eicmp)
 {
        FILE *fp;
        char line[1024];
+       static char format[256] = "";
        int sw = FALSE;
 
        if ((fp = fopen(NET_SNMP, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
                if (!strncmp(line, "Icmp:", 5)) {
                        if (sw) {
-                               sscanf(line + 5, "%*u %lu %lu %lu %lu %lu %lu %*u %*u "
-                                      "%*u %*u %*u %*u %*u %lu %lu %lu %lu %lu %lu",
+                               sscanf(line + 5, format,
                                       &st_net_eicmp->InErrors,
                                       &st_net_eicmp->InDestUnreachs,
                                       &st_net_eicmp->InTimeExcds,
@@ -1330,12 +1582,32 @@ void read_net_eicmp(struct stats_net_eicmp *st_net_eicmp)
                                break;
                        }
                        else {
+                               if (!strlen(format)) {
+                                       if (strstr(line, "InCsumErrors")) {
+                                               /*
+                                                * New format: InCsumErrors field exists at position #3.
+                                                * Capture: 2,4,5,6,7,8,16,17,18,19,20,21
+                                                */
+                                               strcpy(format, "%*u %lu %*u %lu %lu %lu %lu %lu %*u %*u "
+                                                              "%*u %*u %*u %*u %*u %lu %lu %lu %lu %lu %lu");
+                                       }
+                                       else {
+                                               /*
+                                                * Old format: InCsumErrors field doesn't exist.
+                                                * Capture: 2,3,4,5,6,7,15,16,17,18,19,20
+                                                */
+                                               strcpy(format, "%*u %lu %lu %lu %lu %lu %lu %*u %*u "
+                                                              "%*u %*u %*u %*u %*u %lu %lu %lu %lu %lu %lu");
+
+                                       }
+                               }
                                sw = TRUE;
                        }
                }
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1347,16 +1619,19 @@ void read_net_eicmp(struct stats_net_eicmp *st_net_eicmp)
  *
  * OUT:
  * @st_net_tcp Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_tcp(struct stats_net_tcp *st_net_tcp)
+__nr_t read_net_tcp(struct stats_net_tcp *st_net_tcp)
 {
        FILE *fp;
        char line[1024];
        int sw = FALSE;
 
        if ((fp = fopen(NET_SNMP, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1378,6 +1653,7 @@ void read_net_tcp(struct stats_net_tcp *st_net_tcp)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1389,16 +1665,19 @@ void read_net_tcp(struct stats_net_tcp *st_net_tcp)
  *
  * OUT:
  * @st_net_etcp        Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_etcp(struct stats_net_etcp *st_net_etcp)
+__nr_t read_net_etcp(struct stats_net_etcp *st_net_etcp)
 {
        FILE *fp;
        char line[1024];
        int sw = FALSE;
 
        if ((fp = fopen(NET_SNMP, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1421,6 +1700,7 @@ void read_net_etcp(struct stats_net_etcp *st_net_etcp)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1432,16 +1712,19 @@ void read_net_etcp(struct stats_net_etcp *st_net_etcp)
  *
  * OUT:
  * @st_net_udp Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_udp(struct stats_net_udp *st_net_udp)
+__nr_t read_net_udp(struct stats_net_udp *st_net_udp)
 {
        FILE *fp;
        char line[1024];
        int sw = FALSE;
 
        if ((fp = fopen(NET_SNMP, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1462,6 +1745,7 @@ void read_net_udp(struct stats_net_udp *st_net_udp)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1473,15 +1757,18 @@ void read_net_udp(struct stats_net_udp *st_net_udp)
  *
  * OUT:
  * @st_net_sock6       Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_sock6(struct stats_net_sock6 *st_net_sock6)
+__nr_t read_net_sock6(struct stats_net_sock6 *st_net_sock6)
 {
        FILE *fp;
        char line[96];
 
        if ((fp = fopen(NET_SOCKSTAT6, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1504,6 +1791,7 @@ void read_net_sock6(struct stats_net_sock6 *st_net_sock6)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1515,15 +1803,18 @@ void read_net_sock6(struct stats_net_sock6 *st_net_sock6)
  *
  * OUT:
  * @st_net_ip6 Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_ip6(struct stats_net_ip6 *st_net_ip6)
+__nr_t read_net_ip6(struct stats_net_ip6 *st_net_ip6)
 {
        FILE *fp;
        char line[128];
 
        if ((fp = fopen(NET_SNMP6, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1560,6 +1851,7 @@ void read_net_ip6(struct stats_net_ip6 *st_net_ip6)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1571,15 +1863,18 @@ void read_net_ip6(struct stats_net_ip6 *st_net_ip6)
  *
  * OUT:
  * @st_net_eip6        Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_eip6(struct stats_net_eip6 *st_net_eip6)
+__nr_t read_net_eip6(struct stats_net_eip6 *st_net_eip6)
 {
        FILE *fp;
        char line[128];
 
        if ((fp = fopen(NET_SNMP6, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1619,6 +1914,7 @@ void read_net_eip6(struct stats_net_eip6 *st_net_eip6)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1630,15 +1926,18 @@ void read_net_eip6(struct stats_net_eip6 *st_net_eip6)
  *
  * OUT:
  * @st_net_icmp6       Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_icmp6(struct stats_net_icmp6 *st_net_icmp6)
+__nr_t read_net_icmp6(struct stats_net_icmp6 *st_net_icmp6)
 {
        FILE *fp;
        char line[128];
 
        if ((fp = fopen(NET_SNMP6, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1696,6 +1995,7 @@ void read_net_icmp6(struct stats_net_icmp6 *st_net_icmp6)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1707,15 +2007,18 @@ void read_net_icmp6(struct stats_net_icmp6 *st_net_icmp6)
  *
  * OUT:
  * @st_net_eicmp6      Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_eicmp6(struct stats_net_eicmp6 *st_net_eicmp6)
+__nr_t read_net_eicmp6(struct stats_net_eicmp6 *st_net_eicmp6)
 {
        FILE *fp;
        char line[128];
 
        if ((fp = fopen(NET_SNMP6, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1755,6 +2058,7 @@ void read_net_eicmp6(struct stats_net_eicmp6 *st_net_eicmp6)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1766,15 +2070,18 @@ void read_net_eicmp6(struct stats_net_eicmp6 *st_net_eicmp6)
  *
  * OUT:
  * @st_net_udp6        Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_net_udp6(struct stats_net_udp6 *st_net_udp6)
+__nr_t read_net_udp6(struct stats_net_udp6 *st_net_udp6)
 {
        FILE *fp;
        char line[128];
 
        if ((fp = fopen(NET_SNMP6, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1793,6 +2100,7 @@ void read_net_udp6(struct stats_net_udp6 *st_net_udp6)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*
@@ -1801,52 +2109,57 @@ void read_net_udp6(struct stats_net_udp6 *st_net_udp6)
  *
  * IN:
  * @st_pwr_cpufreq     Structure where stats will be saved.
- * @nbr                        Total number of CPU (including cpu "all").
+ * @nr_alloc           Total number of structures allocated. Value is >= 1.
  *
  * OUT:
  * @st_pwr_cpufreq     Structure with statistics.
+ *
+ * RETURNS:
+ * Highest CPU number for which statistics have been read.
+ * 1 means CPU "all", 2 means CPU 0, 3 means CPU 1, etc.
+ * Or -1 if the buffer was too small and needs to be reallocated.
  ***************************************************************************
  */
-void read_cpuinfo(struct stats_pwr_cpufreq *st_pwr_cpufreq, int nbr)
+__nr_t read_cpuinfo(struct stats_pwr_cpufreq *st_pwr_cpufreq, __nr_t nr_alloc)
 {
        FILE *fp;
        struct stats_pwr_cpufreq *st_pwr_cpufreq_i;
        char line[1024];
        int nr = 0;
-       unsigned int proc_nb = 0, ifreq, dfreq;
+       __nr_t cpu_read = 1;    /* For CPU "all" */
+       unsigned int proc_nr = 0, ifreq, dfreq;
 
        if ((fp = fopen(CPUINFO, "r")) == NULL)
-               return;
+               return 0;
 
        st_pwr_cpufreq->cpufreq = 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
                if (!strncmp(line, "processor\t", 10)) {
-                       sscanf(strchr(line, ':') + 1, "%u", &proc_nb);
+                       sscanf(strchr(line, ':') + 1, "%u", &proc_nr);
+
+                       if (proc_nr + 2 > nr_alloc) {
+                               cpu_read = -1;
+                               break;
+                       }
                }
 
-               else if (!strncmp(line, "cpu MHz\t", 8)) {
+               /* Entry in /proc/cpuinfo is different between Intel and Power architectures */
+               else if (!strncmp(line, "cpu MHz\t", 8) ||
+                        !strncmp(line, "clock\t", 6)) {
                        sscanf(strchr(line, ':') + 1, "%u.%u", &ifreq, &dfreq);
 
-                       if (proc_nb < (nbr - 1)) {
-                               /* Save current CPU frequency */
-                               st_pwr_cpufreq_i = st_pwr_cpufreq + proc_nb + 1;
-                               st_pwr_cpufreq_i->cpufreq = ifreq * 100 + dfreq / 10;
+                       /* Save current CPU frequency */
+                       st_pwr_cpufreq_i = st_pwr_cpufreq + proc_nr + 1;
+                       st_pwr_cpufreq_i->cpufreq = ifreq * 100 + dfreq / 10;
 
-                               /* Also save it to compute an average CPU frequency */
-                               st_pwr_cpufreq->cpufreq += st_pwr_cpufreq_i->cpufreq;
-                               nr++;
-                       }
-                       else if (!proc_nb && (nbr == 1)) {
-                               /*
-                                * We are reading freq for "Processor 0" and we have a machine
-                                * with only one processor and not an SMP kernel, with /sys not mounted
-                                * (the nr of proc has been counted using /proc/stat and there was
-                                * only one line with global CPU stats here).
-                                * This is a very specific case, I must admit...
-                                */
-                               st_pwr_cpufreq->cpufreq = ifreq * 100 + dfreq / 10;
+                       /* Also save it to compute an average CPU frequency */
+                       st_pwr_cpufreq->cpufreq += st_pwr_cpufreq_i->cpufreq;
+                       nr++;
+
+                       if (proc_nr + 2 > cpu_read) {
+                               cpu_read = proc_nr + 2;
                        }
                }
        }
@@ -1857,6 +2170,7 @@ void read_cpuinfo(struct stats_pwr_cpufreq *st_pwr_cpufreq, int nbr)
                /* Compute average CPU frequency for this machine */
                st_pwr_cpufreq->cpufreq /= nr;
        }
+       return cpu_read;
 }
 
 /*
@@ -1868,26 +2182,37 @@ void read_cpuinfo(struct stats_pwr_cpufreq *st_pwr_cpufreq, int nbr)
  *
  * OUT:
  * @st_huge    Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_meminfo_huge(struct stats_huge *st_huge)
+__nr_t read_meminfo_huge(struct stats_huge *st_huge)
 {
        FILE *fp;
        char line[128];
        unsigned long szhkb = 0;
 
        if ((fp = fopen(MEMINFO, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
                if (!strncmp(line, "HugePages_Total:", 16)) {
                        /* Read the total number of huge pages */
-                       sscanf(line + 16, "%lu", &st_huge->tlhkb);
+                       sscanf(line + 16, "%llu", &st_huge->tlhkb);
                }
                else if (!strncmp(line, "HugePages_Free:", 15)) {
                        /* Read the number of free huge pages */
-                       sscanf(line + 15, "%lu", &st_huge->frhkb);
+                       sscanf(line + 15, "%llu", &st_huge->frhkb);
+               }
+               else if (!strncmp(line, "HugePages_Rsvd:", 15)) {
+                       /* Read the number of reserved huge pages */
+                       sscanf(line + 15, "%llu", &st_huge->rsvdhkb);
+               }
+               else if (!strncmp(line, "HugePages_Surp:", 15)) {
+                       /* Read the number of surplus huge pages */
+                       sscanf(line + 15, "%llu", &st_huge->surphkb);
                }
                else if (!strncmp(line, "Hugepagesize:", 13)) {
                        /* Read the default size of a huge page in kB */
@@ -1900,6 +2225,10 @@ void read_meminfo_huge(struct stats_huge *st_huge)
        /* We want huge pages stats in kB and not expressed in a number of pages */
        st_huge->tlhkb *= szhkb;
        st_huge->frhkb *= szhkb;
+       st_huge->rsvdhkb *= szhkb;
+       st_huge->surphkb *= szhkb;
+
+       return 1;
 }
 
 /*
@@ -1913,9 +2242,12 @@ void read_meminfo_huge(struct stats_huge *st_huge)
  *
  * OUT:
  * @st_pwr_wghfreq     Structure with statistics.
+ *
+ * RETURNS:
+ * 1 on success, 0 otherwise.
  ***************************************************************************
  */
-void read_time_in_state(struct stats_pwr_wghfreq *st_pwr_wghfreq, int cpu_nr, int nbr)
+int read_time_in_state(struct stats_pwr_wghfreq *st_pwr_wghfreq, int cpu_nr, int nbr)
 {
        FILE *fp;
        struct stats_pwr_wghfreq *st_pwr_wghfreq_j;
@@ -1928,7 +2260,7 @@ void read_time_in_state(struct stats_pwr_wghfreq *st_pwr_wghfreq, int cpu_nr, in
        snprintf(filename, MAX_PF_NAME, "%s/cpu%d/%s",
                 SYSFS_DEVCPU, cpu_nr, SYSFS_TIME_IN_STATE);
        if ((fp = fopen(filename, "r")) == NULL)
-               return;
+               return 0;
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
@@ -1944,6 +2276,67 @@ void read_time_in_state(struct stats_pwr_wghfreq *st_pwr_wghfreq, int cpu_nr, in
        }
 
        fclose(fp);
+       return 1;
+}
+
+/*
+ ***************************************************************************
+ * Read weighted CPU frequency statistics.
+ *
+ * IN:
+ * @st_pwr_wghfreq     Structure where stats will be saved.
+ * @nr_alloc           Total number of structures allocated. Value is >= 0.
+ * @nr2                        Number of sub-items allocated per structure.
+ *
+ * OUT:
+ * @st_pwr_wghfreq     Structure with statistics.
+ *
+ * RETURNS:
+ * Number of CPU for which statistics have been read.
+ * 1 means CPU "all", 2 means CPU "all" and 0, etc.
+ * Or -1 if the buffer was to small and needs to be reallocated.
+ ***************************************************************************
+ */
+__nr_t read_cpu_wghfreq(struct stats_pwr_wghfreq *st_pwr_wghfreq, __nr_t nr_alloc,
+                       __nr_t nr2)
+{
+       __nr_t cpu_read = 0;
+       int j;
+       struct stats_pwr_wghfreq *st_pwr_wghfreq_i, *st_pwr_wghfreq_j, *st_pwr_wghfreq_all_j;
+
+       do {
+               if (cpu_read + 2 > nr_alloc)
+                       return -1;
+
+               /* Read current CPU time-in-state data */
+               st_pwr_wghfreq_i = st_pwr_wghfreq + (cpu_read + 1) * nr2;
+               if (!read_time_in_state(st_pwr_wghfreq_i, cpu_read, nr2))
+                       break;
+
+               /* Also save data for CPU 'all' */
+               for (j = 0; j < nr2; j++) {
+                       st_pwr_wghfreq_j     = st_pwr_wghfreq_i + j;    /* CPU #cpu, state #j */
+                       st_pwr_wghfreq_all_j = st_pwr_wghfreq   + j;    /* CPU #all, state #j */
+                       if (!cpu_read) {
+                               /* Assume that possible frequencies are the same for all CPUs */
+                               st_pwr_wghfreq_all_j->freq = st_pwr_wghfreq_j->freq;
+                       }
+                       st_pwr_wghfreq_all_j->time_in_state += st_pwr_wghfreq_j->time_in_state;
+               }
+               cpu_read++;
+       }
+       while (1);
+
+       if (cpu_read > 0) {
+               for (j = 0; j < nr2; j++) {
+                       st_pwr_wghfreq_all_j = st_pwr_wghfreq + j;      /* CPU #all, state #j */
+                       st_pwr_wghfreq_all_j->time_in_state /= cpu_read;
+               }
+
+               return cpu_read + 1; /* For CPU "all" */
+       }
+
+       return 0;
 }
 
 /*
@@ -2039,40 +2432,46 @@ void read_usb_stats(struct stats_pwr_usb *st_pwr_usb, char *usb_device)
  *
  * IN:
  * @st_pwr_usb         Structure where stats will be saved.
- * @nbr                        Total number of USB devices.
+ * @nr_alloc           Total number of structures allocated. Value is >= 0.
  *
  * OUT:
  * @st_pwr_usb         Structure with statistics.
+ *
+ * RETURNS:
+ * Number of USB devices read, or -1 if the buffer was too small and
+ * needs to be reallocated.
  ***************************************************************************
  */
-void read_bus_usb_dev(struct stats_pwr_usb *st_pwr_usb, int nbr)
+__nr_t read_bus_usb_dev(struct stats_pwr_usb *st_pwr_usb, __nr_t nr_alloc)
 {
        DIR *dir;
        struct dirent *drd;
-       struct stats_pwr_usb *st_pwr_usb_j;
-       int j = 0;
+       struct stats_pwr_usb *st_pwr_usb_i;
+       __nr_t usb_read = 0;
 
        /* Open relevant /sys directory */
        if ((dir = opendir(SYSFS_USBDEV)) == NULL)
-               return;
+               return 0;
 
        /* Get current file entry */
        while ((drd = readdir(dir)) != NULL) {
 
                if (isdigit(drd->d_name[0]) && !strchr(drd->d_name, ':')) {
-                       if (j < nbr) {
-                               /* Read current USB device data */
-                               st_pwr_usb_j = st_pwr_usb + j;
-                               read_usb_stats(st_pwr_usb_j, drd->d_name);
-                               j++;
-                       }
-                       else
+
+                       if (usb_read + 1 > nr_alloc) {
+                               usb_read = -1;
                                break;
+                       }
+
+                       /* Read current USB device data */
+                       st_pwr_usb_i = st_pwr_usb + usb_read++;
+                       read_usb_stats(st_pwr_usb_i, drd->d_name);
                }
        }
 
        /* Close directory */
        closedir(dir);
+       return usb_read;
 }
 
 /*
@@ -2081,25 +2480,58 @@ void read_bus_usb_dev(struct stats_pwr_usb *st_pwr_usb, int nbr)
  *
  * IN:
  * @st_filesystem      Structure where stats will be saved.
- * @nbr                        Total number of filesystems.
+ * @nr_alloc           Total number of structures allocated. Value is >= 0.
  *
  * OUT:
  * @st_filesystem      Structure with statistics.
+ *
+ * RETURNS:
+ * Number of filesystems read, or -1 if the buffer was too small and
+ * needs to be reallocated.
  ***************************************************************************
  */
-void read_filesystem(struct stats_filesystem *st_filesystem, int nbr)
+__nr_t read_filesystem(struct stats_filesystem *st_filesystem, __nr_t nr_alloc)
 {
        FILE *fp;
-       char line[512], fs_name[128], mountp[256];
-       int fs = 0;
+       char line[512], fs_name[MAX_FS_LEN], mountp[256], type[128];
+       int skip = 0, skip_next = 0;
+       char *pos = 0, *pos2 = 0;
+       __nr_t fs_read = 0;
        struct stats_filesystem *st_filesystem_i;
        struct statvfs buf;
 
        if ((fp = fopen(MTAB, "r")) == NULL)
-               return;
+               return 0;
+
+       while (fgets(line, sizeof(line), fp) != NULL) {
+               /*
+                * Ignore line if the preceding line did not contain '\n'.
+                * (Some very long lines may be found for instance when
+                * overlay2 filesystem with docker is used).
+                */
+               skip = skip_next;
+               skip_next = (strchr(line, '\n') == NULL);
+               if (skip)
+                       continue;
 
-       while ((fgets(line, sizeof(line), fp) != NULL) && (fs < nbr)) {
                if (line[0] == '/') {
+                       /* Find field separator position */
+                       pos = strchr(line, ' ');
+                       if (pos == NULL)
+                               continue;
+
+                       /*
+                        * Find second field separator position,
+                        * read filesystem type,
+                        * if filesystem type is autofs, skip it
+                       */
+                       pos2 = strchr(pos + 1, ' ');
+                       if (pos2 == NULL)
+                               continue;
+
+                       sscanf(pos2 + 1, "%127s", type);
+                       if(strcmp(type, "autofs") == 0)
+                               continue;
 
                        /* Read current filesystem name */
                        sscanf(line, "%127s", fs_name);
@@ -2112,7 +2544,7 @@ void read_filesystem(struct stats_filesystem *st_filesystem, int nbr)
                         * from the fs name. This will result in a bogus name
                         * and following statvfs() function will always fail.
                         */
-                       sscanf(strchr(line, ' ') + 1, "%255s", mountp);
+                       sscanf(pos + 1, "%255s", mountp);
 
                        /* Replace octal codes */
                        oct2chr(mountp);
@@ -2121,15 +2553,20 @@ void read_filesystem(struct stats_filesystem *st_filesystem, int nbr)
                         * It's important to have read the whole mount point name
                         * for statvfs() to work properly (see above).
                         */
-                       if ((statvfs(mountp, &buf) < 0) || (!buf.f_blocks))
+                       if ((__statvfs(mountp, &buf) < 0) || (!buf.f_blocks))
                                continue;
 
-                       st_filesystem_i = st_filesystem + fs++;
-                       st_filesystem_i->f_blocks = buf.f_blocks * buf.f_frsize;
-                       st_filesystem_i->f_bfree  = buf.f_bfree * buf.f_frsize;
-                       st_filesystem_i->f_bavail = buf.f_bavail * buf.f_frsize;
-                       st_filesystem_i->f_files  = buf.f_files;
-                       st_filesystem_i->f_ffree  = buf.f_ffree;
+                       if (fs_read + 1 > nr_alloc) {
+                               fs_read = -1;
+                               break;
+                       }
+
+                       st_filesystem_i = st_filesystem + fs_read++;
+                       st_filesystem_i->f_blocks = (unsigned long long) buf.f_blocks * (unsigned long long) buf.f_frsize;
+                       st_filesystem_i->f_bfree  = (unsigned long long) buf.f_bfree * (unsigned long long) buf.f_frsize;
+                       st_filesystem_i->f_bavail = (unsigned long long) buf.f_bavail * (unsigned long long) buf.f_frsize;
+                       st_filesystem_i->f_files  = (unsigned long long) buf.f_files;
+                       st_filesystem_i->f_ffree  = (unsigned long long) buf.f_ffree;
                        strncpy(st_filesystem_i->fs_name, fs_name, MAX_FS_LEN);
                        st_filesystem_i->fs_name[MAX_FS_LEN - 1] = '\0';
                        strncpy(st_filesystem_i->mountp, mountp, MAX_FS_LEN);
@@ -2138,6 +2575,7 @@ void read_filesystem(struct stats_filesystem *st_filesystem, int nbr)
        }
 
        fclose(fp);
+       return fs_read;
 }
 
 /*
@@ -2146,36 +2584,45 @@ void read_filesystem(struct stats_filesystem *st_filesystem, int nbr)
  *
  * IN:
  * @st_fc      Structure where stats will be saved.
- * @nbr                Total number of HBAs.
+ * @nr_alloc   Total number of structures allocated. Value is >= 0.
  *
  * OUT:
  * @st_fc      Structure with statistics.
+ *
+ * RETURNS:
+ * Number of FC hosts read, or -1 if the buffer was too small and needs to
+ * be reallocated.
  ***************************************************************************
  */
-void read_fchost(struct stats_fchost *st_fc, int nbr)
+__nr_t read_fchost(struct stats_fchost *st_fc, __nr_t nr_alloc)
 {
        DIR *dir;
        FILE *fp;
        struct dirent *drd;
        struct stats_fchost *st_fc_i;
-       int fch = 0;
+       __nr_t fch_read = 0;
        char fcstat_filename[MAX_PF_NAME];
        char line[256];
        unsigned long rx_frames, tx_frames, rx_words, tx_words;
 
        /* Each host, if present, will have its own hostX entry within SYSFS_FCHOST */
-       if ((dir = opendir(SYSFS_FCHOST)) == NULL)
-               return; /* No FC hosts */
+       if ((dir = __opendir(SYSFS_FCHOST)) == NULL)
+               return 0; /* No FC hosts */
 
        /*
         * Read each of the counters via sysfs, where they are
         * returned as hex values (e.g. 0x72400).
         */
-       while (((drd = readdir(dir)) != NULL) && (fch < nbr)) {
+       while ((drd = __readdir(dir)) != NULL) {
                rx_frames = tx_frames = rx_words = tx_words = 0;
 
                if (!strncmp(drd->d_name, "host", 4)) {
 
+                       if (fch_read + 1 > nr_alloc) {
+                               fch_read = -1;
+                               break;
+                       }
+
                        snprintf(fcstat_filename, MAX_PF_NAME, FC_RX_FRAMES,
                                 SYSFS_FCHOST, drd->d_name);
                        if ((fp = fopen(fcstat_filename, "r"))) {
@@ -2212,17 +2659,18 @@ void read_fchost(struct stats_fchost *st_fc, int nbr)
                                fclose(fp);
                        }
 
-                       st_fc_i = st_fc + fch++;
+                       st_fc_i = st_fc + fch_read++;
                        st_fc_i->f_rxframes = rx_frames;
                        st_fc_i->f_txframes = tx_frames;
                        st_fc_i->f_rxwords  = rx_words;
                        st_fc_i->f_txwords  = tx_words;
-                       strncpy(st_fc_i->fchost_name, drd->d_name, MAX_FCH_LEN);
+                       memcpy(st_fc_i->fchost_name, drd->d_name, MAX_FCH_LEN);
                        st_fc_i->fchost_name[MAX_FCH_LEN - 1] = '\0';
                }
-
        }
-       closedir(dir);
+
+       __closedir(dir);
+       return fch_read;
 }
 
 /*
@@ -2231,25 +2679,38 @@ void read_fchost(struct stats_fchost *st_fc, int nbr)
  *
  * IN:
  * @st_softnet Structure where stats will be saved.
- * @nbr                Total number of CPU (including cpu "all").
+ * @nr_alloc   Total number of structures allocated. Value is >= 0.
+ * @online_cpu_bitmap
+ *             Bitmap listing online CPU.
  *
  * OUT:
  * @st_softnet Structure with statistics.
+ *
+ * RETURNS:
+ * 1 if stats have been sucessfully read, or 0 otherwise.
  ***************************************************************************
  */
-void read_softnet(struct stats_softnet *st_softnet, int nbr)
+int read_softnet(struct stats_softnet *st_softnet, __nr_t nr_alloc,
+                 unsigned char online_cpu_bitmap[])
 {
        FILE *fp;
        struct stats_softnet *st_softnet_i;
        char line[1024];
-       unsigned int proc_nb = 0;
+       int cpu;
 
+       /* Open /proc/net/softnet_stat file */
        if ((fp = fopen(NET_SOFTNET, "r")) == NULL)
-               return;
+               return 0;
+
+       for (cpu = 1; cpu < nr_alloc; cpu++) {
+               if (!(online_cpu_bitmap[(cpu - 1) >> 3] & (1 << ((cpu - 1) & 0x07))))
+                       /* CPU is offline */
+                       continue;
 
-       while ((fgets(line, sizeof(line), fp) != NULL) && (proc_nb < (nbr - 1))) {
+               if (fgets(line, sizeof(line), fp) == NULL)
+                       break;
 
-               st_softnet_i = st_softnet + proc_nb++;
+               st_softnet_i = st_softnet + cpu;
                sscanf(line, "%x %x %x %*x %*x %*x %*x %*x %*x %x %x",
                       &st_softnet_i->processed,
                       &st_softnet_i->dropped,
@@ -2259,6 +2720,7 @@ void read_softnet(struct stats_softnet *st_softnet, int nbr)
        }
 
        fclose(fp);
+       return 1;
 }
 
 /*------------------ END: FUNCTIONS USED BY SADC ONLY ---------------------*/