Whamcloud - gitweb
LU-13601 llite: avoid needless large stats alloc 01/40901/13
authorAndreas Dilger <adilger@whamcloud.com>
Tue, 8 Dec 2020 06:54:58 +0000 (23:54 -0700)
committerOleg Drokin <green@whamcloud.com>
Fri, 3 Dec 2021 19:54:28 +0000 (19:54 +0000)
Allocate the ll_rw_extents_info (5896 bytes), ll_rw_offset_info
(6400 bytes), and ll_rw_process_info (640 bytes) structs only
when these stats are enabled, which is very rarely.

Signed-off-by: Andreas Dilger <adilger@whamcloud.com>
Change-Id: I59bbfce8d7f2422d810617d5fa712a67333ebbe5
Signed-off-by: Alexander Zarochentsev <alexander.zarochentsev@hpe.com>
Reviewed-on: https://review.whamcloud.com/40901
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Bobi Jam <bobijam@hotmail.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/llite/llite_internal.h
lustre/llite/llite_lib.c
lustre/llite/lproc_llite.c

index 7d27214..3f06e46 100644 (file)
@@ -703,15 +703,15 @@ struct ll_sb_info {
        struct cl_device         *ll_cl;
 
        /* Statistics */
-       struct ll_rw_extents_info ll_rw_extents_info;
+       struct ll_rw_extents_info *ll_rw_extents_info;
        int                       ll_extent_process_count;
        unsigned int              ll_offset_process_count;
+       struct ll_rw_process_info *ll_rw_process_info;
+       struct ll_rw_process_info *ll_rw_offset_info;
        ktime_t                   ll_process_stats_init;
-       struct ll_rw_process_info ll_rw_process_info[LL_PROCESS_HIST_MAX];
-       struct ll_rw_process_info ll_rw_offset_info[LL_OFFSET_HIST_MAX];
        unsigned int              ll_rw_offset_entry_count;
        int                       ll_stats_track_id;
-       enum stats_track_type     ll_stats_track_type;
+       enum stats_track_type     ll_stats_track_type;
        int                       ll_rw_stats_on;
 
        /* metadata stat-ahead */
@@ -984,6 +984,7 @@ void cl_put_grouplock(struct ll_grouplock *lg);
 int ll_debugfs_register_super(struct super_block *sb, const char *name);
 void ll_debugfs_unregister_super(struct super_block *sb);
 void ll_stats_ops_tally(struct ll_sb_info *sbi, int op, long count);
+void ll_free_rw_stats_info(struct ll_sb_info *sbi);
 
 enum {
        LPROC_LL_READ_BYTES,
index 87ccf1b..a60739e 100644 (file)
@@ -92,7 +92,6 @@ static struct ll_sb_info *ll_init_sbi(void)
        unsigned long lru_page_max;
        struct sysinfo si;
        int rc;
-       int i;
 
        ENTRY;
 
@@ -171,13 +170,6 @@ static struct ll_sb_info *ll_init_sbi(void)
 #endif
        set_bit(LL_SBI_LAZYSTATFS, sbi->ll_flags);
 
-        for (i = 0; i <= LL_PROCESS_HIST_MAX; i++) {
-               spin_lock_init(&sbi->ll_rw_extents_info.pp_extents[i].
-                              pp_r_hist.oh_lock);
-               spin_lock_init(&sbi->ll_rw_extents_info.pp_extents[i].
-                              pp_w_hist.oh_lock);
-        }
-
        /* metadata statahead is enabled by default */
        sbi->ll_sa_running_max = LL_SA_RUNNING_DEF;
        sbi->ll_sa_max = LL_SA_RPC_DEF;
@@ -261,6 +253,7 @@ static void ll_free_sbi(struct super_block *sb)
                                sizeof(struct ll_foreign_symlink_upcall_item));
                        sbi->ll_foreign_symlink_upcall_items = NULL;
                }
+               ll_free_rw_stats_info(sbi);
                pcc_super_fini(&sbi->ll_pcc_super);
                OBD_FREE(sbi, sizeof(*sbi));
        }
index 4b4d270..29493ab 100644 (file)
@@ -1912,25 +1912,26 @@ void ll_debugfs_unregister_super(struct super_block *sb)
 }
 #undef MAX_STRING_SIZE
 
-static void ll_display_extents_info(struct ll_rw_extents_info *io_extents,
-                                   struct seq_file *seq, int which)
-{
-        unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
-        unsigned long start, end, r, w;
-        char *unitp = "KMGTPEZY";
-        int i, units = 10;
-        struct per_process_info *pp_info = &io_extents->pp_extents[which];
-
-        read_cum = 0;
-        write_cum = 0;
-        start = 0;
-
-        for(i = 0; i < LL_HIST_MAX; i++) {
-                read_tot += pp_info->pp_r_hist.oh_buckets[i];
-                write_tot += pp_info->pp_w_hist.oh_buckets[i];
-        }
+static void ll_display_extents_info(struct ll_rw_extents_info *rw_extents,
+                                   struct seq_file *seq, int which)
+{
+       unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
+       unsigned long start, end, r, w;
+       char *unitp = "KMGTPEZY";
+       int i, units = 10;
+       struct per_process_info *pp_info;
+
+       pp_info = &rw_extents->pp_extents[which];
+       read_cum = 0;
+       write_cum = 0;
+       start = 0;
+
+       for (i = 0; i < LL_HIST_MAX; i++) {
+               read_tot += pp_info->pp_r_hist.oh_buckets[i];
+               write_tot += pp_info->pp_w_hist.oh_buckets[i];
+       }
 
-       for(i = 0; i < LL_HIST_MAX; i++) {
+       for (i = 0; i < LL_HIST_MAX; i++) {
                r = pp_info->pp_r_hist.oh_buckets[i];
                w = pp_info->pp_w_hist.oh_buckets[i];
                read_cum += r;
@@ -1955,38 +1956,107 @@ static void ll_display_extents_info(struct ll_rw_extents_info *io_extents,
 static int ll_rw_extents_stats_pp_seq_show(struct seq_file *seq, void *v)
 {
        struct ll_sb_info *sbi = seq->private;
-       struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
+       struct ll_rw_extents_info *rw_extents = sbi->ll_rw_extents_info;
        int k;
 
-       if (!sbi->ll_rw_stats_on) {
+       if (!sbi->ll_rw_stats_on || !rw_extents) {
                seq_puts(seq, "disabled\n write anything to this file to activate, then '0' or 'disable' to deactivate\n");
                return 0;
        }
-       lprocfs_stats_header(seq, ktime_get(), io_extents->pp_init, 25, ":", 1);
+
+       spin_lock(&sbi->ll_pp_extent_lock);
+       lprocfs_stats_header(seq, ktime_get(), rw_extents->pp_init, 25, ":", 1);
        seq_printf(seq, "%15s %19s       | %20s\n", " ", "read", "write");
        seq_printf(seq, "%13s   %14s %4s %4s  | %14s %4s %4s\n",
-                  "extents", "calls", "%", "cum%",
-                  "calls", "%", "cum%");
-       spin_lock(&sbi->ll_pp_extent_lock);
+                  "extents", "calls", "%", "cum%", "calls", "%", "cum%");
+
        for (k = 0; k < LL_PROCESS_HIST_MAX; k++) {
-               if (io_extents->pp_extents[k].pid != 0) {
+               if (rw_extents->pp_extents[k].pid != 0) {
                        seq_printf(seq, "\nPID: %d\n",
-                                  io_extents->pp_extents[k].pid);
-                       ll_display_extents_info(io_extents, seq, k);
+                                  rw_extents->pp_extents[k].pid);
+                       ll_display_extents_info(rw_extents, seq, k);
                }
        }
        spin_unlock(&sbi->ll_pp_extent_lock);
        return 0;
 }
 
+static int alloc_rw_stats_info(struct ll_sb_info *sbi)
+{
+       struct ll_rw_extents_info *rw_extents;
+       struct ll_rw_process_info *offset;
+       struct ll_rw_process_info *process;
+       int i, rc = 0;
+
+       OBD_ALLOC(rw_extents, sizeof(*rw_extents));
+       if (!rw_extents)
+               return -ENOMEM;
+
+       for (i = 0; i <= LL_PROCESS_HIST_MAX; i++) {
+               spin_lock_init(&rw_extents->pp_extents[i].pp_r_hist.oh_lock);
+               spin_lock_init(&rw_extents->pp_extents[i].pp_w_hist.oh_lock);
+       }
+
+       spin_lock(&sbi->ll_pp_extent_lock);
+       if (!sbi->ll_rw_extents_info)
+               sbi->ll_rw_extents_info = rw_extents;
+       spin_unlock(&sbi->ll_pp_extent_lock);
+       /* another writer allocated the struct before we got the lock */
+       if (sbi->ll_rw_extents_info != rw_extents)
+               OBD_FREE(rw_extents, sizeof(*rw_extents));
+
+       OBD_ALLOC(process, sizeof(*process) * LL_PROCESS_HIST_MAX);
+       if (!process)
+               GOTO(out, rc = -ENOMEM);
+       OBD_ALLOC(offset, sizeof(*offset) * LL_OFFSET_HIST_MAX);
+       if (!offset)
+               GOTO(out_free, rc = -ENOMEM);
+
+       spin_lock(&sbi->ll_process_lock);
+       if (!sbi->ll_rw_process_info)
+               sbi->ll_rw_process_info = process;
+       if (!sbi->ll_rw_offset_info)
+               sbi->ll_rw_offset_info = offset;
+       spin_unlock(&sbi->ll_process_lock);
+
+       /* another writer allocated the structs before we got the lock */
+       if (sbi->ll_rw_offset_info != offset)
+               OBD_FREE(offset, sizeof(*offset) * LL_OFFSET_HIST_MAX);
+       if (sbi->ll_rw_process_info != process) {
+out_free:
+               OBD_FREE(process, sizeof(*process) * LL_PROCESS_HIST_MAX);
+       }
+
+out:
+       return rc;
+}
+
+void ll_free_rw_stats_info(struct ll_sb_info *sbi)
+{
+       if (sbi->ll_rw_extents_info) {
+               OBD_FREE(sbi->ll_rw_extents_info,
+                        sizeof(*sbi->ll_rw_extents_info));
+               sbi->ll_rw_extents_info = NULL;
+       }
+       if (sbi->ll_rw_offset_info) {
+               OBD_FREE(sbi->ll_rw_offset_info,
+                        sizeof(*sbi->ll_rw_offset_info) * LL_OFFSET_HIST_MAX);
+               sbi->ll_rw_offset_info = NULL;
+       }
+       if (sbi->ll_rw_process_info) {
+               OBD_FREE(sbi->ll_rw_process_info,
+                       sizeof(*sbi->ll_rw_process_info) * LL_PROCESS_HIST_MAX);
+               sbi->ll_rw_process_info = NULL;
+       }
+}
+
 static ssize_t ll_rw_extents_stats_pp_seq_write(struct file *file,
                                                const char __user *buf,
-                                               size_t len,
-                                               loff_t *off)
+                                               size_t len, loff_t *off)
 {
        struct seq_file *seq = file->private_data;
        struct ll_sb_info *sbi = seq->private;
-       struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
+       struct ll_rw_extents_info *rw_extents;
        int i;
        __s64 value;
 
@@ -1995,19 +2065,31 @@ static ssize_t ll_rw_extents_stats_pp_seq_write(struct file *file,
 
        value = ll_stats_pid_write(buf, len);
 
-       if (value == 0)
+       if (value == 0) {
                sbi->ll_rw_stats_on = 0;
-       else
+       } else {
+               if (!sbi->ll_rw_extents_info) {
+                       int rc = alloc_rw_stats_info(sbi);
+
+                       if (rc)
+                               return rc;
+               }
                sbi->ll_rw_stats_on = 1;
+       }
+
 
        spin_lock(&sbi->ll_pp_extent_lock);
-       io_extents->pp_init = ktime_get();
-       for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
-               io_extents->pp_extents[i].pid = 0;
-               lprocfs_oh_clear(&io_extents->pp_extents[i].pp_r_hist);
-               lprocfs_oh_clear(&io_extents->pp_extents[i].pp_w_hist);
+       rw_extents = sbi->ll_rw_extents_info;
+       if (rw_extents) {
+               rw_extents->pp_init = ktime_get();
+               for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
+                       rw_extents->pp_extents[i].pid = 0;
+                       lprocfs_oh_clear(&rw_extents->pp_extents[i].pp_r_hist);
+                       lprocfs_oh_clear(&rw_extents->pp_extents[i].pp_w_hist);
+               }
        }
        spin_unlock(&sbi->ll_pp_extent_lock);
+
        return len;
 }
 
@@ -2016,21 +2098,22 @@ LDEBUGFS_SEQ_FOPS(ll_rw_extents_stats_pp);
 static int ll_rw_extents_stats_seq_show(struct seq_file *seq, void *v)
 {
        struct ll_sb_info *sbi = seq->private;
-       struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
+       struct ll_rw_extents_info *rw_extents = sbi->ll_rw_extents_info;
 
-       if (!sbi->ll_rw_stats_on) {
+       if (!sbi->ll_rw_stats_on || !rw_extents) {
                seq_puts(seq, "disabled\n write anything to this file to activate, then '0' or 'disable' to deactivate\n");
                return 0;
        }
 
-       lprocfs_stats_header(seq, ktime_get(), io_extents->pp_init, 25, ":", 1);
+       spin_lock(&sbi->ll_lock);
+       lprocfs_stats_header(seq, ktime_get(), rw_extents->pp_init, 25, ":", 1);
 
        seq_printf(seq, "%15s %19s       | %20s\n", " ", "read", "write");
        seq_printf(seq, "%13s   %14s %4s %4s  | %14s %4s %4s\n",
                   "extents", "calls", "%", "cum%",
                   "calls", "%", "cum%");
-       spin_lock(&sbi->ll_lock);
-       ll_display_extents_info(io_extents, seq, LL_PROCESS_HIST_MAX);
+
+       ll_display_extents_info(rw_extents, seq, LL_PROCESS_HIST_MAX);
        spin_unlock(&sbi->ll_lock);
 
        return 0;
@@ -2042,7 +2125,7 @@ static ssize_t ll_rw_extents_stats_seq_write(struct file *file,
 {
        struct seq_file *seq = file->private_data;
        struct ll_sb_info *sbi = seq->private;
-       struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
+       struct ll_rw_extents_info *rw_extents;
        int i;
        __s64 value;
 
@@ -2051,17 +2134,27 @@ static ssize_t ll_rw_extents_stats_seq_write(struct file *file,
 
        value = ll_stats_pid_write(buf, len);
 
-       if (value == 0)
+       if (value == 0) {
                sbi->ll_rw_stats_on = 0;
-       else
+       } else {
+               if (!sbi->ll_rw_extents_info) {
+                       int rc = alloc_rw_stats_info(sbi);
+
+                       if (rc)
+                               return rc;
+               }
                sbi->ll_rw_stats_on = 1;
+       }
 
        spin_lock(&sbi->ll_pp_extent_lock);
-       io_extents->pp_init = ktime_get();
-       for (i = 0; i <= LL_PROCESS_HIST_MAX; i++) {
-               io_extents->pp_extents[i].pid = 0;
-               lprocfs_oh_clear(&io_extents->pp_extents[i].pp_r_hist);
-               lprocfs_oh_clear(&io_extents->pp_extents[i].pp_w_hist);
+       rw_extents = sbi->ll_rw_extents_info;
+       if (rw_extents) {
+               rw_extents->pp_init = ktime_get();
+               for (i = 0; i <= LL_PROCESS_HIST_MAX; i++) {
+                       rw_extents->pp_extents[i].pid = 0;
+                       lprocfs_oh_clear(&rw_extents->pp_extents[i].pp_r_hist);
+                       lprocfs_oh_clear(&rw_extents->pp_extents[i].pp_w_hist);
+               }
        }
        spin_unlock(&sbi->ll_pp_extent_lock);
 
@@ -2071,65 +2164,73 @@ static ssize_t ll_rw_extents_stats_seq_write(struct file *file,
 LDEBUGFS_SEQ_FOPS(ll_rw_extents_stats);
 
 void ll_rw_stats_tally(struct ll_sb_info *sbi, pid_t pid,
-                       struct ll_file_data *file, loff_t pos,
-                       size_t count, int rw)
+                      struct ll_file_data *file, loff_t pos,
+                      size_t count, int rw)
 {
-        int i, cur = -1;
-        struct ll_rw_process_info *process;
-        struct ll_rw_process_info *offset;
-        int *off_count = &sbi->ll_rw_offset_entry_count;
-        int *process_count = &sbi->ll_offset_process_count;
-        struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
+       int i, cur = -1;
+       struct ll_rw_process_info *process;
+       struct ll_rw_process_info *offset;
+       int *off_count = &sbi->ll_rw_offset_entry_count;
+       int *process_count = &sbi->ll_offset_process_count;
+       struct ll_rw_extents_info *rw_extents;
 
-        if(!sbi->ll_rw_stats_on)
-                return;
-        process = sbi->ll_rw_process_info;
-        offset = sbi->ll_rw_offset_info;
+       if (!sbi->ll_rw_stats_on)
+               return;
 
        spin_lock(&sbi->ll_pp_extent_lock);
-        /* Extent statistics */
-        for(i = 0; i < LL_PROCESS_HIST_MAX; i++) {
-                if(io_extents->pp_extents[i].pid == pid) {
-                        cur = i;
-                        break;
-                }
-        }
-
-        if (cur == -1) {
-                /* new process */
-                sbi->ll_extent_process_count =
-                        (sbi->ll_extent_process_count + 1) % LL_PROCESS_HIST_MAX;
-                cur = sbi->ll_extent_process_count;
-                io_extents->pp_extents[cur].pid = pid;
-                lprocfs_oh_clear(&io_extents->pp_extents[cur].pp_r_hist);
-                lprocfs_oh_clear(&io_extents->pp_extents[cur].pp_w_hist);
-        }
+       rw_extents = sbi->ll_rw_extents_info;
+       if (!rw_extents) {
+               spin_unlock(&sbi->ll_pp_extent_lock);
+               return;
+       }
+
+       /* Extent statistics */
+       for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
+               if (rw_extents->pp_extents[i].pid == pid) {
+                       cur = i;
+                       break;
+               }
+       }
+
+       if (cur == -1) {
+               /* new process */
+               sbi->ll_extent_process_count =
+                       (sbi->ll_extent_process_count + 1) % LL_PROCESS_HIST_MAX;
+               cur = sbi->ll_extent_process_count;
+               rw_extents->pp_extents[cur].pid = pid;
+               lprocfs_oh_clear(&rw_extents->pp_extents[cur].pp_r_hist);
+               lprocfs_oh_clear(&rw_extents->pp_extents[cur].pp_w_hist);
+       }
 
        for (i = 0; (count >= 1 << (LL_HIST_START + i)) &&
             (i < (LL_HIST_MAX - 1)); i++);
        if (rw == 0) {
-               io_extents->pp_extents[cur].pp_r_hist.oh_buckets[i]++;
-               io_extents->pp_extents[LL_PROCESS_HIST_MAX].pp_r_hist.oh_buckets[i]++;
+               rw_extents->pp_extents[cur].pp_r_hist.oh_buckets[i]++;
+               rw_extents->pp_extents[LL_PROCESS_HIST_MAX].pp_r_hist.oh_buckets[i]++;
        } else {
-               io_extents->pp_extents[cur].pp_w_hist.oh_buckets[i]++;
-               io_extents->pp_extents[LL_PROCESS_HIST_MAX].pp_w_hist.oh_buckets[i]++;
+               rw_extents->pp_extents[cur].pp_w_hist.oh_buckets[i]++;
+               rw_extents->pp_extents[LL_PROCESS_HIST_MAX].pp_w_hist.oh_buckets[i]++;
        }
        spin_unlock(&sbi->ll_pp_extent_lock);
 
        spin_lock(&sbi->ll_process_lock);
-        /* Offset statistics */
-        for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
-                if (process[i].rw_pid == pid) {
-                        if (process[i].rw_last_file != file) {
-                                process[i].rw_range_start = pos;
-                                process[i].rw_last_file_pos = pos + count;
-                                process[i].rw_smallest_extent = count;
-                                process[i].rw_largest_extent = count;
-                                process[i].rw_offset = 0;
-                                process[i].rw_last_file = file;
-                               spin_unlock(&sbi->ll_process_lock);
-                                return;
-                        }
+       process = sbi->ll_rw_process_info;
+       offset = sbi->ll_rw_offset_info;
+       if (!process || !offset)
+               goto out_unlock;
+
+       /* Offset statistics */
+       for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
+               if (process[i].rw_pid == pid) {
+                       if (process[i].rw_last_file != file) {
+                               process[i].rw_range_start = pos;
+                               process[i].rw_last_file_pos = pos + count;
+                               process[i].rw_smallest_extent = count;
+                               process[i].rw_largest_extent = count;
+                               process[i].rw_offset = 0;
+                               process[i].rw_last_file = file;
+                               goto out_unlock;
+                       }
                         if (process[i].rw_last_file_pos != pos) {
                                 *off_count =
                                     (*off_count + 1) % LL_OFFSET_HIST_MAX;
@@ -2152,32 +2253,33 @@ void ll_rw_stats_tally(struct ll_sb_info *sbi, pid_t pid,
                                 process[i].rw_offset = pos -
                                         process[i].rw_last_file_pos;
                         }
-                        if(process[i].rw_smallest_extent > count)
-                                process[i].rw_smallest_extent = count;
-                        if(process[i].rw_largest_extent < count)
-                                process[i].rw_largest_extent = count;
-                        process[i].rw_last_file_pos = pos + count;
-                       spin_unlock(&sbi->ll_process_lock);
-                        return;
-                }
-        }
-        *process_count = (*process_count + 1) % LL_PROCESS_HIST_MAX;
-        process[*process_count].rw_pid = pid;
-        process[*process_count].rw_op = rw;
-        process[*process_count].rw_range_start = pos;
-        process[*process_count].rw_last_file_pos = pos + count;
-        process[*process_count].rw_smallest_extent = count;
-        process[*process_count].rw_largest_extent = count;
-        process[*process_count].rw_offset = 0;
-        process[*process_count].rw_last_file = file;
+                       if (process[i].rw_smallest_extent > count)
+                               process[i].rw_smallest_extent = count;
+                       if (process[i].rw_largest_extent < count)
+                               process[i].rw_largest_extent = count;
+                       process[i].rw_last_file_pos = pos + count;
+                       goto out_unlock;
+               }
+       }
+       *process_count = (*process_count + 1) % LL_PROCESS_HIST_MAX;
+       process[*process_count].rw_pid = pid;
+       process[*process_count].rw_op = rw;
+       process[*process_count].rw_range_start = pos;
+       process[*process_count].rw_last_file_pos = pos + count;
+       process[*process_count].rw_smallest_extent = count;
+       process[*process_count].rw_largest_extent = count;
+       process[*process_count].rw_offset = 0;
+       process[*process_count].rw_last_file = file;
+
+out_unlock:
        spin_unlock(&sbi->ll_process_lock);
 }
 
 static int ll_rw_offset_stats_seq_show(struct seq_file *seq, void *v)
 {
        struct ll_sb_info *sbi = seq->private;
-       struct ll_rw_process_info *offset = sbi->ll_rw_offset_info;
-       struct ll_rw_process_info *process = sbi->ll_rw_process_info;
+       struct ll_rw_process_info *offset;
+       struct ll_rw_process_info *process;
        int i;
 
        if (!sbi->ll_rw_stats_on) {
@@ -2188,13 +2290,13 @@ static int ll_rw_offset_stats_seq_show(struct seq_file *seq, void *v)
        spin_lock(&sbi->ll_process_lock);
        lprocfs_stats_header(seq, ktime_get(), sbi->ll_process_stats_init, 25,
                             ":", true);
-
        seq_printf(seq, "%3s %10s %14s %14s %17s %17s %14s\n",
                   "R/W", "PID", "RANGE START", "RANGE END",
                   "SMALLEST EXTENT", "LARGEST EXTENT", "OFFSET");
 
        /* We stored the discontiguous offsets here; print them first */
-       for (i = 0; i < LL_OFFSET_HIST_MAX; i++) {
+       offset = sbi->ll_rw_offset_info;
+       for (i = 0; offset && i < LL_OFFSET_HIST_MAX; i++) {
                if (offset[i].rw_pid != 0)
                        seq_printf(seq,
                                  "%3c %10d %14llu %14llu %17lu %17lu %14lld\n",
@@ -2208,7 +2310,8 @@ static int ll_rw_offset_stats_seq_show(struct seq_file *seq, void *v)
        }
 
        /* Then print the current offsets for each process */
-       for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
+       process = sbi->ll_rw_process_info;
+       for (i = 0; process && i < LL_PROCESS_HIST_MAX; i++) {
                if (process[i].rw_pid != 0)
                        seq_printf(seq,
                                  "%3c %10d %14llu %14llu %17lu %17lu %14lld\n",
@@ -2231,8 +2334,6 @@ static ssize_t ll_rw_offset_stats_seq_write(struct file *file,
 {
        struct seq_file *seq = file->private_data;
        struct ll_sb_info *sbi = seq->private;
-       struct ll_rw_process_info *process_info = sbi->ll_rw_process_info;
-       struct ll_rw_process_info *offset_info = sbi->ll_rw_offset_info;
        __s64 value;
 
        if (len == 0)
@@ -2240,19 +2341,28 @@ static ssize_t ll_rw_offset_stats_seq_write(struct file *file,
 
        value = ll_stats_pid_write(buf, len);
 
-       if (value == 0)
+       if (value == 0) {
                sbi->ll_rw_stats_on = 0;
-       else
+       } else {
+               if (!sbi->ll_rw_process_info || !sbi->ll_rw_offset_info) {
+                       int rc = alloc_rw_stats_info(sbi);
+
+                       if (rc)
+                               return rc;
+               }
                sbi->ll_rw_stats_on = 1;
+       }
 
        spin_lock(&sbi->ll_process_lock);
        sbi->ll_offset_process_count = 0;
        sbi->ll_rw_offset_entry_count = 0;
        sbi->ll_process_stats_init = ktime_get();
-       memset(process_info, 0, sizeof(struct ll_rw_process_info) *
-              LL_PROCESS_HIST_MAX);
-       memset(offset_info, 0, sizeof(struct ll_rw_process_info) *
-              LL_OFFSET_HIST_MAX);
+       if (sbi->ll_rw_process_info)
+               memset(sbi->ll_rw_process_info, 0,
+                      sizeof(struct ll_rw_process_info) * LL_PROCESS_HIST_MAX);
+       if (sbi->ll_rw_offset_info)
+               memset(sbi->ll_rw_offset_info, 0,
+                      sizeof(struct ll_rw_process_info) * LL_OFFSET_HIST_MAX);
        spin_unlock(&sbi->ll_process_lock);
 
        return len;