Whamcloud - gitweb
Branch b1_6
[fs/lustre-release.git] / lustre / llite / lproc_llite.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (C) 2002 Cluster File Systems, Inc.
5  *
6  *   This file is part of Lustre, http://www.lustre.org.
7  *
8  *   Lustre is free software; you can redistribute it and/or
9  *   modify it under the terms of version 2 of the GNU General Public
10  *   License as published by the Free Software Foundation.
11  *
12  *   Lustre is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with Lustre; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22 #define DEBUG_SUBSYSTEM S_LLITE
23
24 #include <linux/version.h>
25 #include <lustre_lite.h>
26 #include <lprocfs_status.h>
27 #include <linux/seq_file.h>
28 #include <obd_support.h>
29
30 #include "llite_internal.h"
31
32 struct proc_dir_entry *proc_lustre_fs_root;
33
34 #ifdef LPROCFS
35 /* /proc/lustre/llite mount point registration */
36 struct file_operations llite_dump_pgcache_fops;
37 struct file_operations ll_ra_stats_fops;
38 struct file_operations ll_rw_extents_stats_fops;
39 struct file_operations ll_rw_extents_stats_pp_fops;
40 struct file_operations ll_rw_offset_stats_fops;
41
42 static int ll_rd_blksize(char *page, char **start, off_t off, int count,
43                          int *eof, void *data)
44 {
45         struct super_block *sb = (struct super_block *)data;
46         struct obd_statfs osfs;
47         int rc;
48
49         LASSERT(sb != NULL);
50         rc = ll_statfs_internal(sb, &osfs, cfs_time_current_64() - HZ,
51                                 OBD_STATFS_NODELAY);
52         if (!rc) {
53               *eof = 1;
54               rc = snprintf(page, count, "%u\n", osfs.os_bsize);
55         }
56
57         return rc;
58 }
59
60 static int ll_rd_kbytestotal(char *page, char **start, off_t off, int count,
61                              int *eof, void *data)
62 {
63         struct super_block *sb = (struct super_block *)data;
64         struct obd_statfs osfs;
65         int rc;
66
67         LASSERT(sb != NULL);
68         rc = ll_statfs_internal(sb, &osfs, cfs_time_current_64() - HZ,
69                                 OBD_STATFS_NODELAY);
70         if (!rc) {
71                 __u32 blk_size = osfs.os_bsize >> 10;
72                 __u64 result = osfs.os_blocks;
73
74                 while (blk_size >>= 1)
75                         result <<= 1;
76
77                 *eof = 1;
78                 rc = snprintf(page, count, LPU64"\n", result);
79         }
80         return rc;
81
82 }
83
84 static int ll_rd_kbytesfree(char *page, char **start, off_t off, int count,
85                             int *eof, void *data)
86 {
87         struct super_block *sb = (struct super_block *)data;
88         struct obd_statfs osfs;
89         int rc;
90
91         LASSERT(sb != NULL);
92         rc = ll_statfs_internal(sb, &osfs, cfs_time_current_64() - HZ,
93                                 OBD_STATFS_NODELAY);
94         if (!rc) {
95                 __u32 blk_size = osfs.os_bsize >> 10;
96                 __u64 result = osfs.os_bfree;
97
98                 while (blk_size >>= 1)
99                         result <<= 1;
100
101                 *eof = 1;
102                 rc = snprintf(page, count, LPU64"\n", result);
103         }
104         return rc;
105 }
106
107 static int ll_rd_kbytesavail(char *page, char **start, off_t off, int count,
108                              int *eof, void *data)
109 {
110         struct super_block *sb = (struct super_block *)data;
111         struct obd_statfs osfs;
112         int rc;
113
114         LASSERT(sb != NULL);
115         rc = ll_statfs_internal(sb, &osfs, cfs_time_current_64() - HZ,
116                                 OBD_STATFS_NODELAY);
117         if (!rc) {
118                 __u32 blk_size = osfs.os_bsize >> 10;
119                 __u64 result = osfs.os_bavail;
120
121                 while (blk_size >>= 1)
122                         result <<= 1;
123
124                 *eof = 1;
125                 rc = snprintf(page, count, LPU64"\n", result);
126         }
127         return rc;
128 }
129
130 static int ll_rd_filestotal(char *page, char **start, off_t off, int count,
131                             int *eof, void *data)
132 {
133         struct super_block *sb = (struct super_block *)data;
134         struct obd_statfs osfs;
135         int rc;
136
137         LASSERT(sb != NULL);
138         rc = ll_statfs_internal(sb, &osfs, cfs_time_current_64() - HZ,
139                                 OBD_STATFS_NODELAY);
140         if (!rc) {
141                  *eof = 1;
142                  rc = snprintf(page, count, LPU64"\n", osfs.os_files);
143         }
144         return rc;
145 }
146
147 static int ll_rd_filesfree(char *page, char **start, off_t off, int count,
148                            int *eof, void *data)
149 {
150         struct super_block *sb = (struct super_block *)data;
151         struct obd_statfs osfs;
152         int rc;
153
154         LASSERT(sb != NULL);
155         rc = ll_statfs_internal(sb, &osfs, cfs_time_current_64() - HZ,
156                                 OBD_STATFS_NODELAY);
157         if (!rc) {
158                  *eof = 1;
159                  rc = snprintf(page, count, LPU64"\n", osfs.os_ffree);
160         }
161         return rc;
162
163 }
164
165 static int ll_rd_fstype(char *page, char **start, off_t off, int count,
166                         int *eof, void *data)
167 {
168         struct super_block *sb = (struct super_block*)data;
169
170         LASSERT(sb != NULL);
171         *eof = 1;
172         return snprintf(page, count, "%s\n", sb->s_type->name);
173 }
174
175 static int ll_rd_sb_uuid(char *page, char **start, off_t off, int count,
176                          int *eof, void *data)
177 {
178         struct super_block *sb = (struct super_block *)data;
179
180         LASSERT(sb != NULL);
181         *eof = 1;
182         return snprintf(page, count, "%s\n", ll_s2sbi(sb)->ll_sb_uuid.uuid);
183 }
184
185 static int ll_rd_max_readahead_mb(char *page, char **start, off_t off,
186                                    int count, int *eof, void *data)
187 {
188         struct super_block *sb = data;
189         struct ll_sb_info *sbi = ll_s2sbi(sb);
190         long pages_number;
191         int mult;
192
193         spin_lock(&sbi->ll_lock);
194         pages_number = sbi->ll_ra_info.ra_max_pages;
195         spin_unlock(&sbi->ll_lock);
196
197         mult = 1 << (20 - CFS_PAGE_SHIFT);
198         return lprocfs_read_frac_helper(page, count, pages_number, mult);
199 }
200
201 static int ll_wr_max_readahead_mb(struct file *file, const char *buffer,
202                                    unsigned long count, void *data)
203 {
204         struct super_block *sb = data;
205         struct ll_sb_info *sbi = ll_s2sbi(sb);
206         int mult, rc, pages_number;
207
208         mult = 1 << (20 - CFS_PAGE_SHIFT);
209         rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
210         if (rc)
211                 return rc;
212
213         if (pages_number < 0 || pages_number > num_physpages / 2) {
214                 CERROR("can't set file readahead more than %lu MB\n",
215                         num_physpages >> (20 - CFS_PAGE_SHIFT + 1)); /*1/2 of RAM*/
216                 return -ERANGE;
217         }
218
219         spin_lock(&sbi->ll_lock);
220         sbi->ll_ra_info.ra_max_pages = pages_number;
221         spin_unlock(&sbi->ll_lock);
222
223         return count;
224 }
225
226 static int ll_rd_max_read_ahead_whole_mb(char *page, char **start, off_t off,
227                                        int count, int *eof, void *data)
228 {
229         struct super_block *sb = data;
230         struct ll_sb_info *sbi = ll_s2sbi(sb);
231         long pages_number;
232         int mult;
233
234         spin_lock(&sbi->ll_lock);
235         pages_number = sbi->ll_ra_info.ra_max_read_ahead_whole_pages;
236         spin_unlock(&sbi->ll_lock);
237
238         mult = 1 << (20 - CFS_PAGE_SHIFT);
239         return lprocfs_read_frac_helper(page, count, pages_number, mult);
240 }
241
242 static int ll_wr_max_read_ahead_whole_mb(struct file *file, const char *buffer,
243                                        unsigned long count, void *data)
244 {
245         struct super_block *sb = data;
246         struct ll_sb_info *sbi = ll_s2sbi(sb);
247         int mult, rc, pages_number;
248
249         mult = 1 << (20 - CFS_PAGE_SHIFT);
250         rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
251         if (rc)
252                 return rc;
253
254         /* Cap this at the current max readahead window size, the readahead
255          * algorithm does this anyway so it's pointless to set it larger. */
256         if (pages_number < 0 || pages_number > sbi->ll_ra_info.ra_max_pages) {
257                 CERROR("can't set max_read_ahead_whole_mb more than "
258                        "max_read_ahead_mb: %lu\n",
259                        sbi->ll_ra_info.ra_max_pages >> (20 - CFS_PAGE_SHIFT));
260                 return -ERANGE;
261         }
262
263         spin_lock(&sbi->ll_lock);
264         sbi->ll_ra_info.ra_max_read_ahead_whole_pages = pages_number;
265         spin_unlock(&sbi->ll_lock);
266
267         return count;
268 }
269
270 static int ll_rd_max_cached_mb(char *page, char **start, off_t off,
271                                int count, int *eof, void *data)
272 {
273         struct super_block *sb = data;
274         struct ll_sb_info *sbi = ll_s2sbi(sb);
275         long pages_number;
276         int mult;
277
278         spin_lock(&sbi->ll_lock);
279         pages_number = sbi->ll_async_page_max;
280         spin_unlock(&sbi->ll_lock);
281
282         mult = 1 << (20 - CFS_PAGE_SHIFT);
283         return lprocfs_read_frac_helper(page, count, pages_number, mult);;
284 }
285
286 static int ll_wr_max_cached_mb(struct file *file, const char *buffer,
287                                   unsigned long count, void *data)
288 {
289         struct super_block *sb = data;
290         struct ll_sb_info *sbi = ll_s2sbi(sb);
291         int mult, rc, pages_number;
292
293         mult = 1 << (20 - CFS_PAGE_SHIFT);
294         rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
295         if (rc)
296                 return rc;
297
298         if (pages_number < 0 || pages_number > num_physpages) {
299                 CERROR("can't set max cache more than %lu MB\n",
300                         num_physpages >> (20 - CFS_PAGE_SHIFT));
301                 return -ERANGE;
302         }
303
304         spin_lock(&sbi->ll_lock);
305         sbi->ll_async_page_max = pages_number ;
306         spin_unlock(&sbi->ll_lock);
307         
308         if (!sbi->ll_osc_exp)
309                 /* Not set up yet, don't call llap_shrink_cache */
310                 return count;
311
312         if (sbi->ll_async_page_count >= sbi->ll_async_page_max)
313                 llap_shrink_cache(sbi, 0);
314
315         return count;
316 }
317
318 static int ll_rd_checksum(char *page, char **start, off_t off,
319                           int count, int *eof, void *data)
320 {
321         struct super_block *sb = data;
322         struct ll_sb_info *sbi = ll_s2sbi(sb);
323
324         return snprintf(page, count, "%u\n",
325                         (sbi->ll_flags & LL_SBI_LLITE_CHECKSUM) ? 1 : 0);
326 }
327
328 static int ll_wr_checksum(struct file *file, const char *buffer,
329                           unsigned long count, void *data)
330 {
331         struct super_block *sb = data;
332         struct ll_sb_info *sbi = ll_s2sbi(sb);
333         int val, rc;
334
335         if (!sbi->ll_osc_exp)
336                 /* Not set up yet */
337                 return -EAGAIN;
338
339         rc = lprocfs_write_helper(buffer, count, &val);
340         if (rc)
341                 return rc;
342         if (val)
343                 sbi->ll_flags |=  (LL_SBI_LLITE_CHECKSUM|LL_SBI_DATA_CHECKSUM);
344         else
345                 sbi->ll_flags &= ~(LL_SBI_LLITE_CHECKSUM|LL_SBI_DATA_CHECKSUM);
346
347         rc = obd_set_info_async(sbi->ll_osc_exp, strlen("checksum"), "checksum",
348                                 sizeof(val), &val, NULL);
349         if (rc)
350                 CWARN("Failed to set OSC checksum flags: %d\n", rc);
351
352         return count;
353 }
354
355 static int ll_rd_max_rw_chunk(char *page, char **start, off_t off,
356                           int count, int *eof, void *data)
357 {
358         struct super_block *sb = data;
359
360         return snprintf(page, count, "%lu\n", ll_s2sbi(sb)->ll_max_rw_chunk);
361 }
362
363 static int ll_wr_max_rw_chunk(struct file *file, const char *buffer,
364                           unsigned long count, void *data)
365 {
366         struct super_block *sb = data;
367         int rc, val;
368
369         rc = lprocfs_write_helper(buffer, count, &val);
370         if (rc)
371                 return rc;
372         ll_s2sbi(sb)->ll_max_rw_chunk = val;
373         return count;
374 }
375
376 static int ll_rd_track_id(char *page, int count, void *data, 
377                           enum stats_track_type type)
378 {
379         struct super_block *sb = data;
380
381         if (ll_s2sbi(sb)->ll_stats_track_type == type) {
382                 return snprintf(page, count, "%d\n",
383                                 ll_s2sbi(sb)->ll_stats_track_id);
384         
385         } else if (ll_s2sbi(sb)->ll_stats_track_type == STATS_TRACK_ALL) {
386                 return snprintf(page, count, "0 (all)\n");
387         } else {
388                 return snprintf(page, count, "untracked\n");
389         }
390 }
391
392 static int ll_wr_track_id(const char *buffer, unsigned long count, void *data,
393                           enum stats_track_type type)
394 {
395         struct super_block *sb = data;
396         int rc, pid;
397
398         rc = lprocfs_write_helper(buffer, count, &pid);
399         if (rc)
400                 return rc;
401         ll_s2sbi(sb)->ll_stats_track_id = pid;
402         if (pid == 0)
403                 ll_s2sbi(sb)->ll_stats_track_type = STATS_TRACK_ALL;
404         else
405                 ll_s2sbi(sb)->ll_stats_track_type = type;
406         lprocfs_clear_stats(ll_s2sbi(sb)->ll_stats);
407         return count;
408 }
409
410 static int ll_rd_track_pid(char *page, char **start, off_t off,
411                           int count, int *eof, void *data)
412 {
413         return (ll_rd_track_id(page, count, data, STATS_TRACK_PID));
414 }
415
416 static int ll_wr_track_pid(struct file *file, const char *buffer,
417                           unsigned long count, void *data)
418 {
419         return (ll_wr_track_id(buffer, count, data, STATS_TRACK_PID));
420 }
421
422 static int ll_rd_track_ppid(char *page, char **start, off_t off,
423                           int count, int *eof, void *data)
424 {
425         return (ll_rd_track_id(page, count, data, STATS_TRACK_PPID));
426 }
427
428 static int ll_wr_track_ppid(struct file *file, const char *buffer,
429                           unsigned long count, void *data)
430 {
431         return (ll_wr_track_id(buffer, count, data, STATS_TRACK_PPID));
432 }
433
434 static int ll_rd_track_gid(char *page, char **start, off_t off,
435                           int count, int *eof, void *data)
436 {
437         return (ll_rd_track_id(page, count, data, STATS_TRACK_GID));
438 }
439
440 static int ll_wr_track_gid(struct file *file, const char *buffer,
441                           unsigned long count, void *data)
442 {                                                                 
443         return (ll_wr_track_id(buffer, count, data, STATS_TRACK_GID));
444 }
445
446 static int ll_rd_contention_time(char *page, char **start, off_t off,
447                                  int count, int *eof, void *data)
448 {
449         struct super_block *sb = data;
450
451         *eof = 1;
452         return snprintf(page, count, "%u\n", ll_s2sbi(sb)->ll_contention_time);
453
454 }
455
456 static int ll_wr_contention_time(struct file *file, const char *buffer,
457                                  unsigned long count, void *data)
458 {
459         struct super_block *sb = data;
460         struct ll_sb_info *sbi = ll_s2sbi(sb);
461
462         return lprocfs_write_helper(buffer, count,&sbi->ll_contention_time) ?:
463                 count;
464 }
465
466 static int ll_rd_statahead_count(char *page, char **start, off_t off,
467                                  int count, int *eof, void *data)
468 {
469         struct super_block *sb = data;
470         struct ll_sb_info *sbi = ll_s2sbi(sb);
471
472         return snprintf(page, count, "%u\n", sbi->ll_sa_count);
473 }
474
475 static int ll_rd_statahead_max(char *page, char **start, off_t off,
476                                int count, int *eof, void *data)
477 {
478         struct super_block *sb = data;
479         struct ll_sb_info *sbi = ll_s2sbi(sb);
480
481         return snprintf(page, count, "%u\n", sbi->ll_sa_max);
482 }
483
484 static int ll_wr_statahead_max(struct file *file, const char *buffer,
485                                unsigned long count, void *data)
486 {
487         struct super_block *sb = data;
488         struct ll_sb_info *sbi = ll_s2sbi(sb);
489         int val, rc;
490
491         rc = lprocfs_write_helper(buffer, count, &val);
492         if (rc)
493                 return rc;
494         if (val >= 0 && val <= LL_STATAHEAD_MAX)
495                 sbi->ll_sa_max = val;
496         else
497                 CERROR("Bad statahead_max value %d. Valid values are in the "
498                        "range [0, %d]\n", val, LL_STATAHEAD_MAX);
499
500         return count;
501 }
502
503 static int ll_rd_statahead_stats(char *page, char **start, off_t off,
504                                  int count, int *eof, void *data)
505 {
506         struct super_block *sb = data;
507         struct ll_sb_info *sbi = ll_s2sbi(sb);
508
509         return snprintf(page, count,
510                         "statahead wrong: %u\n"
511                         "statahead total: %u\n"
512                         "ls blocked:      %llu\n"
513                         "ls total:        %llu\n",
514                         sbi->ll_sa_wrong, sbi->ll_sa_total,
515                         sbi->ll_sa_blocked,
516                         sbi->ll_sa_blocked + sbi->ll_sa_cached);
517 }
518
519 static struct lprocfs_vars lprocfs_llite_obd_vars[] = {
520         { "uuid",         ll_rd_sb_uuid,          0, 0 },
521         //{ "mntpt_path",   ll_rd_path,             0, 0 },
522         { "fstype",       ll_rd_fstype,           0, 0 },
523         { "blocksize",    ll_rd_blksize,          0, 0 },
524         { "kbytestotal",  ll_rd_kbytestotal,      0, 0 },
525         { "kbytesfree",   ll_rd_kbytesfree,       0, 0 },
526         { "kbytesavail",  ll_rd_kbytesavail,      0, 0 },
527         { "filestotal",   ll_rd_filestotal,       0, 0 },
528         { "filesfree",    ll_rd_filesfree,        0, 0 },
529         //{ "filegroups",   lprocfs_rd_filegroups,  0, 0 },
530         { "max_read_ahead_mb", ll_rd_max_readahead_mb,
531                                ll_wr_max_readahead_mb, 0 },
532         { "max_read_ahead_whole_mb", ll_rd_max_read_ahead_whole_mb,
533                                      ll_wr_max_read_ahead_whole_mb, 0 },
534         { "max_cached_mb",  ll_rd_max_cached_mb, ll_wr_max_cached_mb, 0 },
535         { "checksum_pages", ll_rd_checksum, ll_wr_checksum, 0 },
536         { "max_rw_chunk",   ll_rd_max_rw_chunk, ll_wr_max_rw_chunk, 0 },
537         { "stats_track_pid",  ll_rd_track_pid, ll_wr_track_pid, 0 },
538         { "stats_track_ppid", ll_rd_track_ppid, ll_wr_track_ppid, 0 },
539         { "stats_track_gid",  ll_rd_track_gid, ll_wr_track_gid, 0 },
540         { "contention_seconds", ll_rd_contention_time, ll_wr_contention_time, 0},
541         { "statahead_count", ll_rd_statahead_count, 0, 0 },
542         { "statahead_max",   ll_rd_statahead_max, ll_wr_statahead_max, 0 },
543         { "statahead_stats", ll_rd_statahead_stats, 0, 0 },
544         { 0 }
545 };
546
547 #define MAX_STRING_SIZE 128
548
549 struct llite_file_opcode {
550         __u32       opcode;
551         __u32       type;
552         const char *opname;
553 } llite_opcode_table[LPROC_LL_FILE_OPCODES] = {
554         /* file operation */
555         { LPROC_LL_DIRTY_HITS,     LPROCFS_TYPE_REGS, "dirty_pages_hits" },
556         { LPROC_LL_DIRTY_MISSES,   LPROCFS_TYPE_REGS, "dirty_pages_misses" },
557         { LPROC_LL_WB_WRITEPAGE,   LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
558                                    "writeback_from_writepage" },
559         { LPROC_LL_WB_PRESSURE,    LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
560                                    "writeback_from_pressure" },
561         { LPROC_LL_WB_OK,          LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
562                                    "writeback_ok_pages" },
563         { LPROC_LL_WB_FAIL,        LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
564                                    "writeback_failed_pages" },
565         { LPROC_LL_READ_BYTES,     LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
566                                    "read_bytes" },
567         { LPROC_LL_WRITE_BYTES,    LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
568                                    "write_bytes" },
569         { LPROC_LL_BRW_READ,       LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
570                                    "brw_read" },
571         { LPROC_LL_BRW_WRITE,      LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
572                                    "brw_write" },
573
574         { LPROC_LL_IOCTL,          LPROCFS_TYPE_REGS, "ioctl" },
575         { LPROC_LL_OPEN,           LPROCFS_TYPE_REGS, "open" },
576         { LPROC_LL_RELEASE,        LPROCFS_TYPE_REGS, "close" },
577         { LPROC_LL_MAP,            LPROCFS_TYPE_REGS, "mmap" },
578         { LPROC_LL_LLSEEK,         LPROCFS_TYPE_REGS, "seek" },
579         { LPROC_LL_FSYNC,          LPROCFS_TYPE_REGS, "fsync" },
580         /* inode operation */
581         { LPROC_LL_SETATTR,        LPROCFS_TYPE_REGS, "setattr" },
582         { LPROC_LL_TRUNC,          LPROCFS_TYPE_REGS, "truncate" },
583         { LPROC_LL_FLOCK,          LPROCFS_TYPE_REGS, "flock" },
584 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
585         { LPROC_LL_GETATTR,        LPROCFS_TYPE_REGS, "getattr" },
586 #else
587         { LPROC_LL_REVALIDATE,     LPROCFS_TYPE_REGS, "getattr" },
588 #endif
589         /* special inode operation */
590         { LPROC_LL_STAFS,          LPROCFS_TYPE_REGS, "statfs" },
591         { LPROC_LL_ALLOC_INODE,    LPROCFS_TYPE_REGS, "alloc_inode" },
592         { LPROC_LL_SETXATTR,       LPROCFS_TYPE_REGS, "setxattr" },
593         { LPROC_LL_GETXATTR,       LPROCFS_TYPE_REGS, "getxattr" },
594         { LPROC_LL_LISTXATTR,      LPROCFS_TYPE_REGS, "listxattr" },
595         { LPROC_LL_REMOVEXATTR,    LPROCFS_TYPE_REGS, "removexattr" },
596         { LPROC_LL_INODE_PERM,     LPROCFS_TYPE_REGS, "inode_permission" },
597         { LPROC_LL_DIRECT_READ,    LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
598                                    "direct_read" },
599         { LPROC_LL_DIRECT_WRITE,   LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
600                                    "direct_write" },
601         { LPROC_LL_LOCKLESS_READ,  LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
602                                    "lockless_read_bytes" },
603         { LPROC_LL_LOCKLESS_WRITE, LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
604                                    "lockless_write_bytes" },
605
606 };
607
608 void ll_stats_ops_tally(struct ll_sb_info *sbi, int op, int count)
609 {
610         if (!sbi->ll_stats)
611                 return;
612         if (sbi->ll_stats_track_type == STATS_TRACK_ALL)
613                 lprocfs_counter_add(sbi->ll_stats, op, count);
614         else if (sbi->ll_stats_track_type == STATS_TRACK_PID &&
615                  sbi->ll_stats_track_id == current->pid)
616                 lprocfs_counter_add(sbi->ll_stats, op, count);
617         else if (sbi->ll_stats_track_type == STATS_TRACK_PPID &&
618                  sbi->ll_stats_track_id == current->p_pptr->pid)
619                 lprocfs_counter_add(sbi->ll_stats, op, count);
620         else if (sbi->ll_stats_track_type == STATS_TRACK_GID &&
621                  sbi->ll_stats_track_id == current->gid)
622                 lprocfs_counter_add(sbi->ll_stats, op, count);
623 }
624 EXPORT_SYMBOL(ll_stats_ops_tally);
625
626 int lprocfs_register_mountpoint(struct proc_dir_entry *parent,
627                                 struct super_block *sb, char *osc, char *mdc)
628 {
629         struct lprocfs_vars lvars[2];
630         struct lustre_sb_info *lsi = s2lsi(sb);
631         struct ll_sb_info *sbi = ll_s2sbi(sb);
632         struct obd_device *obd;
633         char name[MAX_STRING_SIZE + 1], *ptr;
634         int err, id, len;
635         struct proc_dir_entry *entry;
636         ENTRY;
637
638         memset(lvars, 0, sizeof(lvars));
639
640         name[MAX_STRING_SIZE] = '\0';
641         lvars[0].name = name;
642
643         LASSERT(sbi != NULL);
644         LASSERT(mdc != NULL);
645         LASSERT(osc != NULL);
646
647         /* Get fsname */
648         len = strlen(lsi->lsi_lmd->lmd_profile);
649         ptr = strrchr(lsi->lsi_lmd->lmd_profile, '-');
650         if (ptr && (strcmp(ptr, "-client") == 0))
651                 len -= 7; 
652         
653         /* Mount info */
654         snprintf(name, MAX_STRING_SIZE, "%.*s-%p", len,
655                  lsi->lsi_lmd->lmd_profile, sb);
656         
657         sbi->ll_proc_root = lprocfs_register(name, parent, NULL, NULL);
658         if (IS_ERR(sbi->ll_proc_root)) {
659                 err = PTR_ERR(sbi->ll_proc_root);
660                 sbi->ll_proc_root = NULL;
661                 RETURN(err);
662         }
663
664         entry = create_proc_entry("dump_page_cache", 0444, sbi->ll_proc_root);
665         if (entry == NULL)
666                 GOTO(out, err = -ENOMEM);
667         entry->proc_fops = &llite_dump_pgcache_fops;
668         entry->data = sbi;
669
670         entry = create_proc_entry("read_ahead_stats", 0644, sbi->ll_proc_root);
671         if (entry == NULL)
672                 GOTO(out, err = -ENOMEM);
673         entry->proc_fops = &ll_ra_stats_fops;
674         entry->data = sbi;
675
676         entry = create_proc_entry("extents_stats", 0644, sbi->ll_proc_root);
677         if (entry == NULL)
678                  GOTO(out, err = -ENOMEM);
679         entry->proc_fops = &ll_rw_extents_stats_fops;
680         entry->data = sbi;
681
682         entry = create_proc_entry("extents_stats_per_process", 0644,
683                                   sbi->ll_proc_root);
684         if (entry == NULL)
685                  GOTO(out, err = -ENOMEM);
686         entry->proc_fops = &ll_rw_extents_stats_pp_fops;
687         entry->data = sbi;
688
689         entry = create_proc_entry("offset_stats", 0644, sbi->ll_proc_root);
690         if (entry == NULL)
691                 GOTO(out, err = -ENOMEM);
692         entry->proc_fops = &ll_rw_offset_stats_fops;
693         entry->data = sbi;
694
695         /* File operations stats */
696         sbi->ll_stats = lprocfs_alloc_stats(LPROC_LL_FILE_OPCODES, 
697                                             LPROCFS_STATS_FLAG_PERCPU);
698         if (sbi->ll_stats == NULL)
699                 GOTO(out, err = -ENOMEM);
700         /* do counter init */
701         for (id = 0; id < LPROC_LL_FILE_OPCODES; id++) {
702                 __u32 type = llite_opcode_table[id].type;
703                 void *ptr = NULL;
704                 if (type & LPROCFS_TYPE_REGS)
705                         ptr = "regs";
706                 else if (type & LPROCFS_TYPE_BYTES)
707                         ptr = "bytes";
708                 else if (type & LPROCFS_TYPE_PAGES)
709                         ptr = "pages";
710                 lprocfs_counter_init(sbi->ll_stats,
711                                      llite_opcode_table[id].opcode,
712                                      (type & LPROCFS_CNTR_AVGMINMAX),
713                                      llite_opcode_table[id].opname, ptr);
714         }
715         err = lprocfs_register_stats(sbi->ll_proc_root, "stats", sbi->ll_stats);
716         if (err)
717                 GOTO(out, err);
718
719         err = lprocfs_add_vars(sbi->ll_proc_root, lprocfs_llite_obd_vars, sb);
720         if (err)
721                 GOTO(out, err);
722
723         /* MDC info */
724         obd = class_name2obd(mdc);
725
726         LASSERT(obd != NULL);
727         LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
728         LASSERT(obd->obd_type->typ_name != NULL);
729
730         snprintf(name, MAX_STRING_SIZE, "%s/common_name",
731                  obd->obd_type->typ_name);
732         lvars[0].read_fptr = lprocfs_rd_name;
733         err = lprocfs_add_vars(sbi->ll_proc_root, lvars, obd);
734         if (err)
735                 GOTO(out, err);
736
737         snprintf(name, MAX_STRING_SIZE, "%s/uuid", obd->obd_type->typ_name);
738         lvars[0].read_fptr = lprocfs_rd_uuid;
739         err = lprocfs_add_vars(sbi->ll_proc_root, lvars, obd);
740         if (err)
741                 GOTO(out, err);
742
743         /* OSC */
744         obd = class_name2obd(osc);
745
746         LASSERT(obd != NULL);
747         LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
748         LASSERT(obd->obd_type->typ_name != NULL);
749
750         snprintf(name, MAX_STRING_SIZE, "%s/common_name",
751                  obd->obd_type->typ_name);
752         lvars[0].read_fptr = lprocfs_rd_name;
753         err = lprocfs_add_vars(sbi->ll_proc_root, lvars, obd);
754         if (err)
755                 GOTO(out, err);
756
757         snprintf(name, MAX_STRING_SIZE, "%s/uuid", obd->obd_type->typ_name);
758         lvars[0].read_fptr = lprocfs_rd_uuid;
759         err = lprocfs_add_vars(sbi->ll_proc_root, lvars, obd);
760 out:
761         if (err) {
762                 lprocfs_remove(&sbi->ll_proc_root);
763                 lprocfs_free_stats(&sbi->ll_stats);
764         }
765         RETURN(err);
766 }
767
768 void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi)
769 {
770         if (sbi->ll_proc_root) {
771                 lprocfs_remove(&sbi->ll_proc_root);
772                 lprocfs_free_stats(&sbi->ll_stats);
773         }
774 }
775 #undef MAX_STRING_SIZE
776
777 #define seq_page_flag(seq, page, flag, has_flags) do {                  \
778                 if (test_bit(PG_##flag, &(page)->flags)) {              \
779                         if (!has_flags)                                 \
780                                 has_flags = 1;                          \
781                         else                                            \
782                                 seq_putc(seq, '|');                     \
783                         seq_puts(seq, #flag);                           \
784                 }                                                       \
785         } while(0);
786
787 static void *llite_dump_pgcache_seq_start(struct seq_file *seq, loff_t *pos)
788 {
789         struct ll_async_page *dummy_llap = seq->private;
790
791         if (dummy_llap->llap_magic == 2)
792                 return NULL;
793
794         return (void *)1;
795 }
796
797 static int llite_dump_pgcache_seq_show(struct seq_file *seq, void *v)
798 {
799         struct ll_async_page *llap, *dummy_llap = seq->private;
800         struct ll_sb_info *sbi = dummy_llap->llap_cookie;
801
802         /* 2.4 doesn't seem to have SEQ_START_TOKEN, so we implement
803          * it in our own state */
804         if (dummy_llap->llap_magic == 0) {
805                 seq_printf(seq, "gener |  llap  cookie  origin wq du wb | page "
806                                 "inode index count [ page flags ]\n");
807                 return 0;
808         }
809
810         spin_lock(&sbi->ll_lock);
811
812         llap = llite_pglist_next_llap(sbi, &dummy_llap->llap_pglist_item);
813         if (llap != NULL)  {
814                 int has_flags = 0;
815                 struct page *page = llap->llap_page;
816
817                 LASSERTF(llap->llap_origin < LLAP__ORIGIN_MAX, "%u\n",
818                          llap->llap_origin);
819
820                 seq_printf(seq," %5lu | %p %p %s %s %s %s | %p %lu/%u(%p) "
821                            "%lu %u [",
822                            sbi->ll_pglist_gen,
823                            llap, llap->llap_cookie,
824                            llap_origins[llap->llap_origin],
825                            llap->llap_write_queued ? "wq" : "- ",
826                            llap->llap_defer_uptodate ? "du" : "- ",
827                            PageWriteback(page) ? "wb" : "-",
828                            page, page->mapping->host->i_ino,
829                            page->mapping->host->i_generation,
830                            page->mapping->host, page->index,
831                            page_count(page));
832                 seq_page_flag(seq, page, locked, has_flags);
833                 seq_page_flag(seq, page, error, has_flags);
834                 seq_page_flag(seq, page, referenced, has_flags);
835                 seq_page_flag(seq, page, uptodate, has_flags);
836                 seq_page_flag(seq, page, dirty, has_flags);
837 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,12))
838                 seq_page_flag(seq, page, highmem, has_flags);
839 #endif
840                 seq_page_flag(seq, page, writeback, has_flags);
841                 if (!has_flags)
842                         seq_puts(seq, "-]\n");
843                 else
844                         seq_puts(seq, "]\n");
845         }
846
847         spin_unlock(&sbi->ll_lock);
848
849         return 0;
850 }
851
852 static void *llite_dump_pgcache_seq_next(struct seq_file *seq, void *v, 
853                                          loff_t *pos)
854 {
855         struct ll_async_page *llap, *dummy_llap = seq->private;
856         struct ll_sb_info *sbi = dummy_llap->llap_cookie;
857
858         /* bail if we just displayed the banner */
859         if (dummy_llap->llap_magic == 0) {
860                 dummy_llap->llap_magic = 1;
861                 return dummy_llap;
862         }
863
864         /* we've just displayed the llap that is after us in the list.
865          * we advance to a position beyond it, returning null if there
866          * isn't another llap in the list beyond that new position. */
867         spin_lock(&sbi->ll_lock);
868         llap = llite_pglist_next_llap(sbi, &dummy_llap->llap_pglist_item);
869         list_del_init(&dummy_llap->llap_pglist_item);
870         if (llap) {
871                 list_add(&dummy_llap->llap_pglist_item,&llap->llap_pglist_item);
872                 llap =llite_pglist_next_llap(sbi,&dummy_llap->llap_pglist_item);
873         }
874         spin_unlock(&sbi->ll_lock);
875
876         ++*pos;
877         if (llap == NULL) {
878                 dummy_llap->llap_magic = 2;
879                 return NULL;
880         }
881         return dummy_llap;
882 }
883
884 static void null_stop(struct seq_file *seq, void *v)
885 {
886 }
887
888 struct seq_operations llite_dump_pgcache_seq_sops = {
889         .start = llite_dump_pgcache_seq_start,
890         .stop = null_stop,
891         .next = llite_dump_pgcache_seq_next,
892         .show = llite_dump_pgcache_seq_show,
893 };
894
895 /* we're displaying llaps in a list_head list.  we don't want to hold a lock
896  * while we walk the entire list, and we don't want to have to seek into
897  * the right position in the list as an app advances with many syscalls.  we
898  * allocate a dummy llap and hang it off file->private.  its position in
899  * the list records where the app is currently displaying.  this way our
900  * seq .start and .stop don't actually do anything.  .next returns null
901  * when the dummy hits the end of the list which eventually leads to .release
902  * where we tear down.  this kind of displaying is super-racey, so we put
903  * a generation counter on the list so the output shows when the list
904  * changes between reads.
905  */
906 static int llite_dump_pgcache_seq_open(struct inode *inode, struct file *file)
907 {
908         struct proc_dir_entry *dp = PDE(inode);
909         struct ll_async_page *dummy_llap;
910         struct seq_file *seq;
911         struct ll_sb_info *sbi = dp->data;
912         int rc = -ENOMEM;
913
914         LPROCFS_ENTRY_AND_CHECK(dp);
915
916         OBD_ALLOC_PTR_WAIT(dummy_llap);
917         if (dummy_llap == NULL)
918                 GOTO(out, rc);
919
920         dummy_llap->llap_page = NULL;
921         dummy_llap->llap_cookie = sbi;
922         dummy_llap->llap_magic = 0;
923
924         rc = seq_open(file, &llite_dump_pgcache_seq_sops);
925         if (rc) {
926                 OBD_FREE(dummy_llap, sizeof(*dummy_llap));
927                 GOTO(out, rc);
928         }
929         seq = file->private_data;
930         seq->private = dummy_llap;
931
932         spin_lock(&sbi->ll_lock);
933         list_add(&dummy_llap->llap_pglist_item, &sbi->ll_pglist);
934         spin_unlock(&sbi->ll_lock);
935
936 out:
937         if (rc)
938                 LPROCFS_EXIT();
939         return rc;
940 }
941
942 static int llite_dump_pgcache_seq_release(struct inode *inode,
943                                           struct file *file)
944 {
945         struct seq_file *seq = file->private_data;
946         struct ll_async_page *dummy_llap = seq->private;
947         struct ll_sb_info *sbi = dummy_llap->llap_cookie;
948
949         spin_lock(&sbi->ll_lock);
950         if (!list_empty(&dummy_llap->llap_pglist_item))
951                 list_del_init(&dummy_llap->llap_pglist_item);
952         spin_unlock(&sbi->ll_lock);
953         OBD_FREE(dummy_llap, sizeof(*dummy_llap));
954
955         return lprocfs_seq_release(inode, file);
956 }
957
958 struct file_operations llite_dump_pgcache_fops = {
959         .owner   = THIS_MODULE,
960         .open    = llite_dump_pgcache_seq_open,
961         .read    = seq_read,
962         .release = llite_dump_pgcache_seq_release,
963 };
964
965 static int ll_ra_stats_seq_show(struct seq_file *seq, void *v)
966 {
967         struct timeval now;
968         struct ll_sb_info *sbi = seq->private;
969         struct ll_ra_info *ra = &sbi->ll_ra_info;
970         int i;
971         static char *ra_stat_strings[] = {
972                 [RA_STAT_HIT] = "hits",
973                 [RA_STAT_MISS] = "misses",
974                 [RA_STAT_DISTANT_READPAGE] = "readpage not consecutive",
975                 [RA_STAT_MISS_IN_WINDOW] = "miss inside window",
976                 [RA_STAT_FAILED_GRAB_PAGE] = "failed grab_cache_page",
977                 [RA_STAT_FAILED_MATCH] = "failed lock match",
978                 [RA_STAT_DISCARDED] = "read but discarded",
979                 [RA_STAT_ZERO_LEN] = "zero length file",
980                 [RA_STAT_ZERO_WINDOW] = "zero size window",
981                 [RA_STAT_EOF] = "read-ahead to EOF",
982                 [RA_STAT_MAX_IN_FLIGHT] = "hit max r-a issue",
983                 [RA_STAT_WRONG_GRAB_PAGE] = "wrong page from grab_cache_page",
984         };
985
986         do_gettimeofday(&now);
987
988         spin_lock(&sbi->ll_lock);
989
990         seq_printf(seq, "snapshot_time:         %lu.%lu (secs.usecs)\n",
991                    now.tv_sec, now.tv_usec);
992         seq_printf(seq, "pending issued pages:           %lu\n",
993                    ra->ra_cur_pages);
994
995         for(i = 0; i < _NR_RA_STAT; i++)
996                 seq_printf(seq, "%-25s %lu\n", ra_stat_strings[i], 
997                            ra->ra_stats[i]);
998
999         spin_unlock(&sbi->ll_lock);
1000
1001         return 0;
1002 }
1003
1004 static ssize_t ll_ra_stats_seq_write(struct file *file, const char *buf,
1005                                        size_t len, loff_t *off)
1006 {
1007         struct seq_file *seq = file->private_data;
1008         struct ll_sb_info *sbi = seq->private;
1009         struct ll_ra_info *ra = &sbi->ll_ra_info;
1010
1011         spin_lock(&sbi->ll_lock);
1012         memset(ra->ra_stats, 0, sizeof(ra->ra_stats));
1013         spin_unlock(&sbi->ll_lock);
1014
1015         return len;
1016 }
1017
1018 LPROC_SEQ_FOPS(ll_ra_stats);
1019
1020 #define pct(a,b) (b ? a * 100 / b : 0)
1021
1022 static void ll_display_extents_info(struct ll_rw_extents_info *io_extents,
1023                                    struct seq_file *seq, int which)
1024 {
1025         unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
1026         unsigned long start, end, r, w;
1027         char *unitp = "KMGTPEZY";
1028         int i, units = 10;
1029         struct per_process_info *pp_info = &io_extents->pp_extents[which];
1030
1031         read_cum = 0;
1032         write_cum = 0;
1033         start = 0;
1034
1035         for(i = 0; i < LL_HIST_MAX; i++) {
1036                 read_tot += pp_info->pp_r_hist.oh_buckets[i];
1037                 write_tot += pp_info->pp_w_hist.oh_buckets[i];
1038         }
1039
1040         for(i = 0; i < LL_HIST_MAX; i++) {
1041                 r = pp_info->pp_r_hist.oh_buckets[i];
1042                 w = pp_info->pp_w_hist.oh_buckets[i];
1043                 read_cum += r;
1044                 write_cum += w;
1045                 end = 1 << (i + LL_HIST_START - units);
1046                 seq_printf(seq, "%4lu%c - %4lu%c%c: %14lu %4lu %4lu  | "
1047                            "%14lu %4lu %4lu\n", start, *unitp, end, *unitp,
1048                            (i == LL_HIST_MAX - 1) ? '+' : ' ',
1049                            r, pct(r, read_tot), pct(read_cum, read_tot),
1050                            w, pct(w, write_tot), pct(write_cum, write_tot));
1051                 start = end;
1052                 if (start == 1<<10) {
1053                         start = 1;
1054                         units += 10;
1055                         unitp++;
1056                 }
1057                 if (read_cum == read_tot && write_cum == write_tot)
1058                         break;
1059         }
1060 }
1061
1062 static int ll_rw_extents_stats_pp_seq_show(struct seq_file *seq, void *v)
1063 {
1064         struct timeval now;
1065         struct ll_sb_info *sbi = seq->private;
1066         struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
1067         int k;
1068
1069         do_gettimeofday(&now);
1070
1071         if (!sbi->ll_rw_stats_on) {
1072                 seq_printf(seq, "Disabled\n"
1073                                 "Write anything in this file to activate\n");
1074                 return 0;
1075         }
1076         seq_printf(seq, "snapshot_time:         %lu.%lu (secs.usecs)\n",
1077                    now.tv_sec, now.tv_usec);
1078         seq_printf(seq, "%15s %19s       | %20s\n", " ", "read", "write");
1079         seq_printf(seq, "%13s   %14s %4s %4s  | %14s %4s %4s\n", 
1080                    "extents", "calls", "%", "cum%",
1081                    "calls", "%", "cum%");
1082         spin_lock(&sbi->ll_pp_extent_lock);
1083         for(k = 0; k < LL_PROCESS_HIST_MAX; k++) {
1084                 if(io_extents->pp_extents[k].pid != 0) {
1085                         seq_printf(seq, "\nPID: %d\n",
1086                                    io_extents->pp_extents[k].pid);
1087                         ll_display_extents_info(io_extents, seq, k);
1088                 }
1089         }
1090         spin_unlock(&sbi->ll_pp_extent_lock);
1091         return 0;
1092 }
1093
1094 static ssize_t ll_rw_extents_stats_pp_seq_write(struct file *file,
1095                                                 const char *buf, size_t len,
1096                                                 loff_t *off)
1097 {
1098         struct seq_file *seq = file->private_data;
1099         struct ll_sb_info *sbi = seq->private;
1100         struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
1101         int i;
1102
1103         sbi->ll_rw_stats_on = 1;
1104         spin_lock(&sbi->ll_pp_extent_lock);
1105         for(i = 0; i < LL_PROCESS_HIST_MAX; i++) {
1106                 io_extents->pp_extents[i].pid = 0;
1107                 lprocfs_oh_clear(&io_extents->pp_extents[i].pp_r_hist);
1108                 lprocfs_oh_clear(&io_extents->pp_extents[i].pp_w_hist);
1109         }
1110         spin_unlock(&sbi->ll_pp_extent_lock);
1111         return len;
1112 }
1113
1114 LPROC_SEQ_FOPS(ll_rw_extents_stats_pp);
1115
1116 static int ll_rw_extents_stats_seq_show(struct seq_file *seq, void *v)
1117 {
1118         struct timeval now;
1119         struct ll_sb_info *sbi = seq->private;
1120         struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
1121
1122         do_gettimeofday(&now);
1123
1124         if (!sbi->ll_rw_stats_on) {
1125                 seq_printf(seq, "Disabled\n"
1126                                 "Write anything in this file to activate\n");
1127                 return 0;
1128         }
1129         seq_printf(seq, "snapshot_time:         %lu.%lu (secs.usecs)\n",
1130                    now.tv_sec, now.tv_usec);
1131
1132         seq_printf(seq, "%15s %19s       | %20s\n", " ", "read", "write");
1133         seq_printf(seq, "%13s   %14s %4s %4s  | %14s %4s %4s\n", 
1134                    "extents", "calls", "%", "cum%",
1135                    "calls", "%", "cum%");
1136         spin_lock(&sbi->ll_lock);
1137         ll_display_extents_info(io_extents, seq, LL_PROCESS_HIST_MAX);
1138         spin_unlock(&sbi->ll_lock);
1139
1140         return 0;
1141 }
1142
1143 static ssize_t ll_rw_extents_stats_seq_write(struct file *file, const char *buf,
1144                                         size_t len, loff_t *off)
1145 {
1146         struct seq_file *seq = file->private_data;
1147         struct ll_sb_info *sbi = seq->private;
1148         struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
1149         int i;
1150
1151         sbi->ll_rw_stats_on = 1;
1152         spin_lock(&sbi->ll_pp_extent_lock);
1153         for(i = 0; i <= LL_PROCESS_HIST_MAX; i++)
1154         {
1155                 io_extents->pp_extents[i].pid = 0;
1156                 lprocfs_oh_clear(&io_extents->pp_extents[i].pp_r_hist);
1157                 lprocfs_oh_clear(&io_extents->pp_extents[i].pp_w_hist);
1158         }
1159         spin_unlock(&sbi->ll_pp_extent_lock);
1160
1161         return len;
1162 }
1163
1164 LPROC_SEQ_FOPS(ll_rw_extents_stats);
1165
1166 void ll_rw_stats_tally(struct ll_sb_info *sbi, pid_t pid, struct file
1167                                *file, size_t count, int rw)
1168 {
1169         int i, cur = -1;
1170         struct ll_rw_process_info *process;
1171         struct ll_rw_process_info *offset;
1172         int *off_count = &sbi->ll_rw_offset_entry_count;
1173         int *process_count = &sbi->ll_offset_process_count;
1174         struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
1175
1176         if(!sbi->ll_rw_stats_on)
1177                 return;
1178         process = sbi->ll_rw_process_info;
1179         offset = sbi->ll_rw_offset_info;
1180
1181         spin_lock(&sbi->ll_pp_extent_lock);
1182         /* Extent statistics */
1183         for(i = 0; i < LL_PROCESS_HIST_MAX; i++) {
1184                 if(io_extents->pp_extents[i].pid == pid) {
1185                         cur = i;
1186                         break;
1187                 }
1188         }
1189
1190         if (cur == -1) {
1191                 /* new process */
1192                 sbi->ll_extent_process_count = 
1193                         (sbi->ll_extent_process_count + 1) % LL_PROCESS_HIST_MAX;
1194                 cur = sbi->ll_extent_process_count;
1195                 io_extents->pp_extents[cur].pid = pid;
1196                 lprocfs_oh_clear(&io_extents->pp_extents[cur].pp_r_hist);
1197                 lprocfs_oh_clear(&io_extents->pp_extents[cur].pp_w_hist);
1198         }
1199
1200         for(i = 0; (count >= (1 << LL_HIST_START << i)) && 
1201              (i < (LL_HIST_MAX - 1)); i++);
1202         if (rw == 0) {
1203                 io_extents->pp_extents[cur].pp_r_hist.oh_buckets[i]++;
1204                 io_extents->pp_extents[LL_PROCESS_HIST_MAX].pp_r_hist.oh_buckets[i]++;
1205         } else {
1206                 io_extents->pp_extents[cur].pp_w_hist.oh_buckets[i]++;
1207                 io_extents->pp_extents[LL_PROCESS_HIST_MAX].pp_w_hist.oh_buckets[i]++;
1208         }
1209         spin_unlock(&sbi->ll_pp_extent_lock);
1210
1211         spin_lock(&sbi->ll_process_lock);
1212         /* Offset statistics */
1213         for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
1214                 if (process[i].rw_pid == pid) {
1215                         if (process[i].rw_last_file != file) {
1216                                 process[i].rw_range_start = file->f_pos;
1217                                 process[i].rw_last_file_pos =
1218                                                         file->f_pos + count;
1219                                 process[i].rw_smallest_extent = count;
1220                                 process[i].rw_largest_extent = count;
1221                                 process[i].rw_offset = 0;
1222                                 process[i].rw_last_file = file;
1223                                 spin_unlock(&sbi->ll_process_lock);
1224                                 return;
1225                         }
1226                         if (process[i].rw_last_file_pos != file->f_pos) {
1227                                 *off_count =
1228                                     (*off_count + 1) % LL_OFFSET_HIST_MAX;
1229                                 offset[*off_count].rw_op = process[i].rw_op;
1230                                 offset[*off_count].rw_pid = pid;
1231                                 offset[*off_count].rw_range_start =
1232                                         process[i].rw_range_start;
1233                                 offset[*off_count].rw_range_end =
1234                                         process[i].rw_last_file_pos;
1235                                 offset[*off_count].rw_smallest_extent =
1236                                         process[i].rw_smallest_extent;
1237                                 offset[*off_count].rw_largest_extent =
1238                                         process[i].rw_largest_extent;
1239                                 offset[*off_count].rw_offset =
1240                                         process[i].rw_offset;
1241                                 process[i].rw_op = rw;
1242                                 process[i].rw_range_start = file->f_pos;
1243                                 process[i].rw_smallest_extent = count;
1244                                 process[i].rw_largest_extent = count;
1245                                 process[i].rw_offset = file->f_pos -
1246                                         process[i].rw_last_file_pos;
1247                         }
1248                         if(process[i].rw_smallest_extent > count)
1249                                 process[i].rw_smallest_extent = count;
1250                         if(process[i].rw_largest_extent < count)
1251                                 process[i].rw_largest_extent = count;
1252                         process[i].rw_last_file_pos = file->f_pos + count;
1253                         spin_unlock(&sbi->ll_process_lock);
1254                         return;
1255                 }
1256         }
1257         *process_count = (*process_count + 1) % LL_PROCESS_HIST_MAX;
1258         process[*process_count].rw_pid = pid;
1259         process[*process_count].rw_op = rw;
1260         process[*process_count].rw_range_start = file->f_pos;
1261         process[*process_count].rw_last_file_pos = file->f_pos + count;
1262         process[*process_count].rw_smallest_extent = count;
1263         process[*process_count].rw_largest_extent = count;
1264         process[*process_count].rw_offset = 0;
1265         process[*process_count].rw_last_file = file;
1266         spin_unlock(&sbi->ll_process_lock);
1267 }
1268
1269 char lpszt[] = LPSZ;
1270
1271 static int ll_rw_offset_stats_seq_show(struct seq_file *seq, void *v)
1272 {
1273         struct timeval now;
1274         struct ll_sb_info *sbi = seq->private;
1275         struct ll_rw_process_info *offset = sbi->ll_rw_offset_info;
1276         struct ll_rw_process_info *process = sbi->ll_rw_process_info;
1277         char format[50];
1278         int i;
1279
1280         do_gettimeofday(&now);
1281
1282         if (!sbi->ll_rw_stats_on) {
1283                 seq_printf(seq, "Disabled\n"
1284                                 "Write anything in this file to activate\n");
1285                 return 0;
1286         }
1287         spin_lock(&sbi->ll_process_lock);
1288
1289         seq_printf(seq, "snapshot_time:         %lu.%lu (secs.usecs)\n",
1290                    now.tv_sec, now.tv_usec);
1291         seq_printf(seq, "%3s %10s %14s %14s %17s %17s %14s\n",
1292                    "R/W", "PID", "RANGE START", "RANGE END",
1293                    "SMALLEST EXTENT", "LARGEST EXTENT", "OFFSET");
1294         sprintf(format, "%s%s%s%s%s\n",
1295                 "%3c %10d %14Lu %14Lu %17", lpszt+1, " %17", lpszt+1, " %14Ld");
1296         /* We stored the discontiguous offsets here; print them first */
1297         for(i = 0; i < LL_OFFSET_HIST_MAX; i++) {
1298                 if (offset[i].rw_pid != 0)
1299                         /* Is there a way to snip the '%' off of LPSZ? */
1300                         seq_printf(seq, format,
1301                                    offset[i].rw_op ? 'W' : 'R',
1302                                    offset[i].rw_pid,
1303                                    offset[i].rw_range_start,
1304                                    offset[i].rw_range_end,
1305                                    offset[i].rw_smallest_extent,
1306                                    offset[i].rw_largest_extent,
1307                                    offset[i].rw_offset);
1308         }
1309         /* Then print the current offsets for each process */
1310         for(i = 0; i < LL_PROCESS_HIST_MAX; i++) {
1311                 if (process[i].rw_pid != 0)
1312                         seq_printf(seq, format,
1313                                    process[i].rw_op ? 'W' : 'R',
1314                                    process[i].rw_pid,
1315                                    process[i].rw_range_start,
1316                                    process[i].rw_last_file_pos,
1317                                    process[i].rw_smallest_extent,
1318                                    process[i].rw_largest_extent,
1319                                    process[i].rw_offset);
1320         }
1321         spin_unlock(&sbi->ll_process_lock);
1322
1323         return 0;
1324 }
1325
1326 static ssize_t ll_rw_offset_stats_seq_write(struct file *file, const char *buf,
1327                                        size_t len, loff_t *off)
1328 {
1329         struct seq_file *seq = file->private_data;
1330         struct ll_sb_info *sbi = seq->private;
1331         struct ll_rw_process_info *process_info = sbi->ll_rw_process_info;
1332         struct ll_rw_process_info *offset_info = sbi->ll_rw_offset_info;
1333
1334         sbi->ll_rw_stats_on = 1;
1335
1336         spin_lock(&sbi->ll_process_lock);
1337         sbi->ll_offset_process_count = 0;
1338         sbi->ll_rw_offset_entry_count = 0;
1339         memset(process_info, 0, sizeof(struct ll_rw_process_info) *
1340                LL_PROCESS_HIST_MAX);
1341         memset(offset_info, 0, sizeof(struct ll_rw_process_info) *
1342                LL_OFFSET_HIST_MAX);
1343         spin_unlock(&sbi->ll_process_lock);
1344
1345         return len;
1346 }
1347
1348 LPROC_SEQ_FOPS(ll_rw_offset_stats);
1349
1350 void lprocfs_llite_init_vars(struct lprocfs_static_vars *lvars)
1351 {
1352     lvars->module_vars  = NULL;
1353     lvars->obd_vars     = lprocfs_llite_obd_vars;
1354 }
1355 #endif /* LPROCFS */