lproc_llite.c revision b9c98cfa717c3912595cee424ba9d9b98db9ced1
1/*
2 * GPL HEADER START
3 *
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19 *
20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21 * CA 95054 USA or visit www.sun.com if you need additional information or
22 * have any questions.
23 *
24 * GPL HEADER END
25 */
26/*
27 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
28 * Use is subject to license terms.
29 *
30 * Copyright (c) 2011, 2012, Intel Corporation.
31 */
32/*
33 * This file is part of Lustre, http://www.lustre.org/
34 * Lustre is a trademark of Sun Microsystems, Inc.
35 */
36#define DEBUG_SUBSYSTEM S_LLITE
37
38#include <lustre_lite.h>
39#include <lprocfs_status.h>
40#include <linux/seq_file.h>
41#include <obd_support.h>
42
43#include "llite_internal.h"
44
45/* /proc/lustre/llite mount point registration */
46extern struct file_operations vvp_dump_pgcache_file_ops;
47static struct file_operations ll_rw_extents_stats_fops;
48static struct file_operations ll_rw_extents_stats_pp_fops;
49static struct file_operations ll_rw_offset_stats_fops;
50
51static int ll_blksize_seq_show(struct seq_file *m, void *v)
52{
53	struct super_block *sb = (struct super_block *)m->private;
54	struct obd_statfs osfs;
55	int rc;
56
57	LASSERT(sb != NULL);
58	rc = ll_statfs_internal(sb, &osfs,
59				cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
60				OBD_STATFS_NODELAY);
61	if (!rc)
62	      rc = seq_printf(m, "%u\n", osfs.os_bsize);
63
64	return rc;
65}
66LPROC_SEQ_FOPS_RO(ll_blksize);
67
68static int ll_kbytestotal_seq_show(struct seq_file *m, void *v)
69{
70	struct super_block *sb = (struct super_block *)m->private;
71	struct obd_statfs osfs;
72	int rc;
73
74	LASSERT(sb != NULL);
75	rc = ll_statfs_internal(sb, &osfs,
76				cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
77				OBD_STATFS_NODELAY);
78	if (!rc) {
79		__u32 blk_size = osfs.os_bsize >> 10;
80		__u64 result = osfs.os_blocks;
81
82		while (blk_size >>= 1)
83			result <<= 1;
84
85		rc = seq_printf(m, LPU64"\n", result);
86	}
87	return rc;
88}
89LPROC_SEQ_FOPS_RO(ll_kbytestotal);
90
91static int ll_kbytesfree_seq_show(struct seq_file *m, void *v)
92{
93	struct super_block *sb = (struct super_block *)m->private;
94	struct obd_statfs osfs;
95	int rc;
96
97	LASSERT(sb != NULL);
98	rc = ll_statfs_internal(sb, &osfs,
99				cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
100				OBD_STATFS_NODELAY);
101	if (!rc) {
102		__u32 blk_size = osfs.os_bsize >> 10;
103		__u64 result = osfs.os_bfree;
104
105		while (blk_size >>= 1)
106			result <<= 1;
107
108		rc = seq_printf(m, LPU64"\n", result);
109	}
110	return rc;
111}
112LPROC_SEQ_FOPS_RO(ll_kbytesfree);
113
114static int ll_kbytesavail_seq_show(struct seq_file *m, void *v)
115{
116	struct super_block *sb = (struct super_block *)m->private;
117	struct obd_statfs osfs;
118	int rc;
119
120	LASSERT(sb != NULL);
121	rc = ll_statfs_internal(sb, &osfs,
122				cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
123				OBD_STATFS_NODELAY);
124	if (!rc) {
125		__u32 blk_size = osfs.os_bsize >> 10;
126		__u64 result = osfs.os_bavail;
127
128		while (blk_size >>= 1)
129			result <<= 1;
130
131		rc = seq_printf(m, LPU64"\n", result);
132	}
133	return rc;
134}
135LPROC_SEQ_FOPS_RO(ll_kbytesavail);
136
137static int ll_filestotal_seq_show(struct seq_file *m, void *v)
138{
139	struct super_block *sb = (struct super_block *)m->private;
140	struct obd_statfs osfs;
141	int rc;
142
143	LASSERT(sb != NULL);
144	rc = ll_statfs_internal(sb, &osfs,
145				cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
146				OBD_STATFS_NODELAY);
147	if (!rc)
148		 rc = seq_printf(m, LPU64"\n", osfs.os_files);
149	return rc;
150}
151LPROC_SEQ_FOPS_RO(ll_filestotal);
152
153static int ll_filesfree_seq_show(struct seq_file *m, void *v)
154{
155	struct super_block *sb = (struct super_block *)m->private;
156	struct obd_statfs osfs;
157	int rc;
158
159	LASSERT(sb != NULL);
160	rc = ll_statfs_internal(sb, &osfs,
161				cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
162				OBD_STATFS_NODELAY);
163	if (!rc)
164		 rc = seq_printf(m, LPU64"\n", osfs.os_ffree);
165	return rc;
166}
167LPROC_SEQ_FOPS_RO(ll_filesfree);
168
169static int ll_client_type_seq_show(struct seq_file *m, void *v)
170{
171	struct ll_sb_info *sbi = ll_s2sbi((struct super_block *)m->private);
172	int rc;
173
174	LASSERT(sbi != NULL);
175
176	if (sbi->ll_flags & LL_SBI_RMT_CLIENT)
177		rc = seq_printf(m, "remote client\n");
178	else
179		rc = seq_printf(m, "local client\n");
180
181	return rc;
182}
183LPROC_SEQ_FOPS_RO(ll_client_type);
184
185static int ll_fstype_seq_show(struct seq_file *m, void *v)
186{
187	struct super_block *sb = (struct super_block *)m->private;
188
189	LASSERT(sb != NULL);
190	return seq_printf(m, "%s\n", sb->s_type->name);
191}
192LPROC_SEQ_FOPS_RO(ll_fstype);
193
194static int ll_sb_uuid_seq_show(struct seq_file *m, void *v)
195{
196	struct super_block *sb = (struct super_block *)m->private;
197
198	LASSERT(sb != NULL);
199	return seq_printf(m, "%s\n", ll_s2sbi(sb)->ll_sb_uuid.uuid);
200}
201LPROC_SEQ_FOPS_RO(ll_sb_uuid);
202
203static int ll_site_stats_seq_show(struct seq_file *m, void *v)
204{
205	struct super_block *sb = m->private;
206
207	/*
208	 * See description of statistical counters in struct cl_site, and
209	 * struct lu_site.
210	 */
211	return cl_site_stats_print(lu2cl_site(ll_s2sbi(sb)->ll_site), m);
212}
213LPROC_SEQ_FOPS_RO(ll_site_stats);
214
215static int ll_max_readahead_mb_seq_show(struct seq_file *m, void *v)
216{
217	struct super_block *sb = m->private;
218	struct ll_sb_info *sbi = ll_s2sbi(sb);
219	long pages_number;
220	int mult;
221
222	spin_lock(&sbi->ll_lock);
223	pages_number = sbi->ll_ra_info.ra_max_pages;
224	spin_unlock(&sbi->ll_lock);
225
226	mult = 1 << (20 - PAGE_CACHE_SHIFT);
227	return lprocfs_seq_read_frac_helper(m, pages_number, mult);
228}
229
230static ssize_t ll_max_readahead_mb_seq_write(struct file *file, const char *buffer,
231					 size_t count, loff_t *off)
232{
233	struct super_block *sb = ((struct seq_file *)file->private_data)->private;
234	struct ll_sb_info *sbi = ll_s2sbi(sb);
235	int mult, rc, pages_number;
236
237	mult = 1 << (20 - PAGE_CACHE_SHIFT);
238	rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
239	if (rc)
240		return rc;
241
242	if (pages_number < 0 || pages_number > totalram_pages / 2) {
243		CERROR("can't set file readahead more than %lu MB\n",
244		       totalram_pages >> (20 - PAGE_CACHE_SHIFT + 1)); /*1/2 of RAM*/
245		return -ERANGE;
246	}
247
248	spin_lock(&sbi->ll_lock);
249	sbi->ll_ra_info.ra_max_pages = pages_number;
250	spin_unlock(&sbi->ll_lock);
251
252	return count;
253}
254LPROC_SEQ_FOPS(ll_max_readahead_mb);
255
256static int ll_max_readahead_per_file_mb_seq_show(struct seq_file *m, void *v)
257{
258	struct super_block *sb = m->private;
259	struct ll_sb_info *sbi = ll_s2sbi(sb);
260	long pages_number;
261	int mult;
262
263	spin_lock(&sbi->ll_lock);
264	pages_number = sbi->ll_ra_info.ra_max_pages_per_file;
265	spin_unlock(&sbi->ll_lock);
266
267	mult = 1 << (20 - PAGE_CACHE_SHIFT);
268	return lprocfs_seq_read_frac_helper(m, pages_number, mult);
269}
270
271static ssize_t ll_max_readahead_per_file_mb_seq_write(struct file *file,
272						  const char *buffer,
273						  size_t count, loff_t *off)
274{
275	struct super_block *sb = ((struct seq_file *)file->private_data)->private;
276	struct ll_sb_info *sbi = ll_s2sbi(sb);
277	int mult, rc, pages_number;
278
279	mult = 1 << (20 - PAGE_CACHE_SHIFT);
280	rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
281	if (rc)
282		return rc;
283
284	if (pages_number < 0 ||
285		pages_number > sbi->ll_ra_info.ra_max_pages) {
286		CERROR("can't set file readahead more than"
287		       "max_read_ahead_mb %lu MB\n",
288		       sbi->ll_ra_info.ra_max_pages);
289		return -ERANGE;
290	}
291
292	spin_lock(&sbi->ll_lock);
293	sbi->ll_ra_info.ra_max_pages_per_file = pages_number;
294	spin_unlock(&sbi->ll_lock);
295
296	return count;
297}
298LPROC_SEQ_FOPS(ll_max_readahead_per_file_mb);
299
300static int ll_max_read_ahead_whole_mb_seq_show(struct seq_file *m, void *unused)
301{
302	struct super_block *sb = m->private;
303	struct ll_sb_info *sbi = ll_s2sbi(sb);
304	long pages_number;
305	int mult;
306
307	spin_lock(&sbi->ll_lock);
308	pages_number = sbi->ll_ra_info.ra_max_read_ahead_whole_pages;
309	spin_unlock(&sbi->ll_lock);
310
311	mult = 1 << (20 - PAGE_CACHE_SHIFT);
312	return lprocfs_seq_read_frac_helper(m, pages_number, mult);
313}
314
315static ssize_t ll_max_read_ahead_whole_mb_seq_write(struct file *file,
316						const char *buffer,
317						size_t count, loff_t *off)
318{
319	struct super_block *sb = ((struct seq_file *)file->private_data)->private;
320	struct ll_sb_info *sbi = ll_s2sbi(sb);
321	int mult, rc, pages_number;
322
323	mult = 1 << (20 - PAGE_CACHE_SHIFT);
324	rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
325	if (rc)
326		return rc;
327
328	/* Cap this at the current max readahead window size, the readahead
329	 * algorithm does this anyway so it's pointless to set it larger. */
330	if (pages_number < 0 ||
331	    pages_number > sbi->ll_ra_info.ra_max_pages_per_file) {
332		CERROR("can't set max_read_ahead_whole_mb more than "
333		       "max_read_ahead_per_file_mb: %lu\n",
334			sbi->ll_ra_info.ra_max_pages_per_file >> (20 - PAGE_CACHE_SHIFT));
335		return -ERANGE;
336	}
337
338	spin_lock(&sbi->ll_lock);
339	sbi->ll_ra_info.ra_max_read_ahead_whole_pages = pages_number;
340	spin_unlock(&sbi->ll_lock);
341
342	return count;
343}
344LPROC_SEQ_FOPS(ll_max_read_ahead_whole_mb);
345
346static int ll_max_cached_mb_seq_show(struct seq_file *m, void *v)
347{
348	struct super_block     *sb    = m->private;
349	struct ll_sb_info      *sbi   = ll_s2sbi(sb);
350	struct cl_client_cache *cache = &sbi->ll_cache;
351	int shift = 20 - PAGE_CACHE_SHIFT;
352	int max_cached_mb;
353	int unused_mb;
354
355	max_cached_mb = cache->ccc_lru_max >> shift;
356	unused_mb = atomic_read(&cache->ccc_lru_left) >> shift;
357	return seq_printf(m,
358			"users: %d\n"
359			"max_cached_mb: %d\n"
360			"used_mb: %d\n"
361			"unused_mb: %d\n"
362			"reclaim_count: %u\n",
363			atomic_read(&cache->ccc_users),
364			max_cached_mb,
365			max_cached_mb - unused_mb,
366			unused_mb,
367			cache->ccc_lru_shrinkers);
368}
369
370static ssize_t ll_max_cached_mb_seq_write(struct file *file, const char *buffer,
371				      size_t count, loff_t *off)
372{
373	struct super_block *sb = ((struct seq_file *)file->private_data)->private;
374	struct ll_sb_info *sbi = ll_s2sbi(sb);
375	struct cl_client_cache *cache = &sbi->ll_cache;
376	int mult, rc, pages_number;
377	int diff = 0;
378	int nrpages = 0;
379
380	mult = 1 << (20 - PAGE_CACHE_SHIFT);
381	buffer = lprocfs_find_named_value(buffer, "max_cached_mb:", &count);
382	rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
383	if (rc)
384		return rc;
385
386	if (pages_number < 0 || pages_number > totalram_pages) {
387		CERROR("%s: can't set max cache more than %lu MB\n",
388		       ll_get_fsname(sb, NULL, 0),
389		       totalram_pages >> (20 - PAGE_CACHE_SHIFT));
390		return -ERANGE;
391	}
392
393	if (sbi->ll_dt_exp == NULL)
394		return -ENODEV;
395
396	spin_lock(&sbi->ll_lock);
397	diff = pages_number - cache->ccc_lru_max;
398	spin_unlock(&sbi->ll_lock);
399
400	/* easy - add more LRU slots. */
401	if (diff >= 0) {
402		atomic_add(diff, &cache->ccc_lru_left);
403		GOTO(out, rc = 0);
404	}
405
406	diff = -diff;
407	while (diff > 0) {
408		int tmp;
409
410		/* reduce LRU budget from free slots. */
411		do {
412			int ov, nv;
413
414			ov = atomic_read(&cache->ccc_lru_left);
415			if (ov == 0)
416				break;
417
418			nv = ov > diff ? ov - diff : 0;
419			rc = atomic_cmpxchg(&cache->ccc_lru_left, ov, nv);
420			if (likely(ov == rc)) {
421				diff -= ov - nv;
422				nrpages += ov - nv;
423				break;
424			}
425		} while (1);
426
427		if (diff <= 0)
428			break;
429
430		/* difficult - have to ask OSCs to drop LRU slots. */
431		tmp = diff << 1;
432		rc = obd_set_info_async(NULL, sbi->ll_dt_exp,
433				sizeof(KEY_CACHE_LRU_SHRINK),
434				KEY_CACHE_LRU_SHRINK,
435				sizeof(tmp), &tmp, NULL);
436		if (rc < 0)
437			break;
438	}
439
440out:
441	if (rc >= 0) {
442		spin_lock(&sbi->ll_lock);
443		cache->ccc_lru_max = pages_number;
444		spin_unlock(&sbi->ll_lock);
445		rc = count;
446	} else {
447		atomic_add(nrpages, &cache->ccc_lru_left);
448	}
449	return rc;
450}
451LPROC_SEQ_FOPS(ll_max_cached_mb);
452
453static int ll_checksum_seq_show(struct seq_file *m, void *v)
454{
455	struct super_block *sb = m->private;
456	struct ll_sb_info *sbi = ll_s2sbi(sb);
457
458	return seq_printf(m, "%u\n", (sbi->ll_flags & LL_SBI_CHECKSUM) ? 1 : 0);
459}
460
461static ssize_t ll_checksum_seq_write(struct file *file, const char *buffer,
462				 size_t count, loff_t *off)
463{
464	struct super_block *sb = ((struct seq_file *)file->private_data)->private;
465	struct ll_sb_info *sbi = ll_s2sbi(sb);
466	int val, rc;
467
468	if (!sbi->ll_dt_exp)
469		/* Not set up yet */
470		return -EAGAIN;
471
472	rc = lprocfs_write_helper(buffer, count, &val);
473	if (rc)
474		return rc;
475	if (val)
476		sbi->ll_flags |= LL_SBI_CHECKSUM;
477	else
478		sbi->ll_flags &= ~LL_SBI_CHECKSUM;
479
480	rc = obd_set_info_async(NULL, sbi->ll_dt_exp, sizeof(KEY_CHECKSUM),
481				KEY_CHECKSUM, sizeof(val), &val, NULL);
482	if (rc)
483		CWARN("Failed to set OSC checksum flags: %d\n", rc);
484
485	return count;
486}
487LPROC_SEQ_FOPS(ll_checksum);
488
489static int ll_max_rw_chunk_seq_show(struct seq_file *m, void *v)
490{
491	struct super_block *sb = m->private;
492
493	return seq_printf(m, "%lu\n", ll_s2sbi(sb)->ll_max_rw_chunk);
494}
495
496static ssize_t ll_max_rw_chunk_seq_write(struct file *file, const char *buffer,
497				     size_t count, loff_t *off)
498{
499	struct super_block *sb = ((struct seq_file *)file->private_data)->private;
500	int rc, val;
501
502	rc = lprocfs_write_helper(buffer, count, &val);
503	if (rc)
504		return rc;
505	ll_s2sbi(sb)->ll_max_rw_chunk = val;
506	return count;
507}
508LPROC_SEQ_FOPS(ll_max_rw_chunk);
509
510static int ll_rd_track_id(struct seq_file *m, enum stats_track_type type)
511{
512	struct super_block *sb = m->private;
513
514	if (ll_s2sbi(sb)->ll_stats_track_type == type) {
515		return seq_printf(m, "%d\n",
516				ll_s2sbi(sb)->ll_stats_track_id);
517
518	} else if (ll_s2sbi(sb)->ll_stats_track_type == STATS_TRACK_ALL) {
519		return seq_printf(m, "0 (all)\n");
520	} else {
521		return seq_printf(m, "untracked\n");
522	}
523}
524
525static int ll_wr_track_id(const char *buffer, unsigned long count, void *data,
526			  enum stats_track_type type)
527{
528	struct super_block *sb = data;
529	int rc, pid;
530
531	rc = lprocfs_write_helper(buffer, count, &pid);
532	if (rc)
533		return rc;
534	ll_s2sbi(sb)->ll_stats_track_id = pid;
535	if (pid == 0)
536		ll_s2sbi(sb)->ll_stats_track_type = STATS_TRACK_ALL;
537	else
538		ll_s2sbi(sb)->ll_stats_track_type = type;
539	lprocfs_clear_stats(ll_s2sbi(sb)->ll_stats);
540	return count;
541}
542
543static int ll_track_pid_seq_show(struct seq_file *m, void *v)
544{
545	return ll_rd_track_id(m, STATS_TRACK_PID);
546}
547
548static ssize_t ll_track_pid_seq_write(struct file *file, const char *buffer,
549				  size_t count, loff_t *off)
550{
551	struct seq_file *seq = file->private_data;
552	return ll_wr_track_id(buffer, count, seq->private, STATS_TRACK_PID);
553}
554LPROC_SEQ_FOPS(ll_track_pid);
555
556static int ll_track_ppid_seq_show(struct seq_file *m, void *v)
557{
558	return ll_rd_track_id(m, STATS_TRACK_PPID);
559}
560
561static ssize_t ll_track_ppid_seq_write(struct file *file, const char *buffer,
562				   size_t count, loff_t *off)
563{
564	struct seq_file *seq = file->private_data;
565	return ll_wr_track_id(buffer, count, seq->private, STATS_TRACK_PPID);
566}
567LPROC_SEQ_FOPS(ll_track_ppid);
568
569static int ll_track_gid_seq_show(struct seq_file *m, void *v)
570{
571	return ll_rd_track_id(m, STATS_TRACK_GID);
572}
573
574static ssize_t ll_track_gid_seq_write(struct file *file, const char *buffer,
575				  size_t count, loff_t *off)
576{
577	struct seq_file *seq = file->private_data;
578	return ll_wr_track_id(buffer, count, seq->private, STATS_TRACK_GID);
579}
580LPROC_SEQ_FOPS(ll_track_gid);
581
582static int ll_statahead_max_seq_show(struct seq_file *m, void *v)
583{
584	struct super_block *sb = m->private;
585	struct ll_sb_info *sbi = ll_s2sbi(sb);
586
587	return seq_printf(m, "%u\n", sbi->ll_sa_max);
588}
589
590static ssize_t ll_statahead_max_seq_write(struct file *file, const char *buffer,
591				      size_t count, loff_t *off)
592{
593	struct super_block *sb = ((struct seq_file *)file->private_data)->private;
594	struct ll_sb_info *sbi = ll_s2sbi(sb);
595	int val, rc;
596
597	rc = lprocfs_write_helper(buffer, count, &val);
598	if (rc)
599		return rc;
600
601	if (val >= 0 && val <= LL_SA_RPC_MAX)
602		sbi->ll_sa_max = val;
603	else
604		CERROR("Bad statahead_max value %d. Valid values are in the "
605		       "range [0, %d]\n", val, LL_SA_RPC_MAX);
606
607	return count;
608}
609LPROC_SEQ_FOPS(ll_statahead_max);
610
611static int ll_statahead_agl_seq_show(struct seq_file *m, void *v)
612{
613	struct super_block *sb = m->private;
614	struct ll_sb_info *sbi = ll_s2sbi(sb);
615
616	return seq_printf(m, "%u\n",
617			sbi->ll_flags & LL_SBI_AGL_ENABLED ? 1 : 0);
618}
619
620static ssize_t ll_statahead_agl_seq_write(struct file *file, const char *buffer,
621				      size_t count, loff_t *off)
622{
623	struct super_block *sb = ((struct seq_file *)file->private_data)->private;
624	struct ll_sb_info *sbi = ll_s2sbi(sb);
625	int val, rc;
626
627	rc = lprocfs_write_helper(buffer, count, &val);
628	if (rc)
629		return rc;
630
631	if (val)
632		sbi->ll_flags |= LL_SBI_AGL_ENABLED;
633	else
634		sbi->ll_flags &= ~LL_SBI_AGL_ENABLED;
635
636	return count;
637}
638LPROC_SEQ_FOPS(ll_statahead_agl);
639
640static int ll_statahead_stats_seq_show(struct seq_file *m, void *v)
641{
642	struct super_block *sb = m->private;
643	struct ll_sb_info *sbi = ll_s2sbi(sb);
644
645	return seq_printf(m,
646			"statahead total: %u\n"
647			"statahead wrong: %u\n"
648			"agl total: %u\n",
649			atomic_read(&sbi->ll_sa_total),
650			atomic_read(&sbi->ll_sa_wrong),
651			atomic_read(&sbi->ll_agl_total));
652}
653LPROC_SEQ_FOPS_RO(ll_statahead_stats);
654
655static int ll_lazystatfs_seq_show(struct seq_file *m, void *v)
656{
657	struct super_block *sb = m->private;
658	struct ll_sb_info *sbi = ll_s2sbi(sb);
659
660	return seq_printf(m, "%u\n",
661			(sbi->ll_flags & LL_SBI_LAZYSTATFS) ? 1 : 0);
662}
663
664static ssize_t ll_lazystatfs_seq_write(struct file *file, const char *buffer,
665				   size_t count, loff_t *off)
666{
667	struct super_block *sb = ((struct seq_file *)file->private_data)->private;
668	struct ll_sb_info *sbi = ll_s2sbi(sb);
669	int val, rc;
670
671	rc = lprocfs_write_helper(buffer, count, &val);
672	if (rc)
673		return rc;
674
675	if (val)
676		sbi->ll_flags |= LL_SBI_LAZYSTATFS;
677	else
678		sbi->ll_flags &= ~LL_SBI_LAZYSTATFS;
679
680	return count;
681}
682LPROC_SEQ_FOPS(ll_lazystatfs);
683
684static int ll_maxea_size_seq_show(struct seq_file *m, void *v)
685{
686	struct super_block *sb = m->private;
687	struct ll_sb_info *sbi = ll_s2sbi(sb);
688	unsigned int ealen;
689	int rc;
690
691	rc = ll_get_max_mdsize(sbi, &ealen);
692	if (rc)
693		return rc;
694
695	return seq_printf(m, "%u\n", ealen);
696}
697LPROC_SEQ_FOPS_RO(ll_maxea_size);
698
699static int ll_sbi_flags_seq_show(struct seq_file *m, void *v)
700{
701	const char *str[] = LL_SBI_FLAGS;
702	struct super_block *sb = m->private;
703	int flags = ll_s2sbi(sb)->ll_flags;
704	int i = 0;
705
706	while (flags != 0) {
707		if (ARRAY_SIZE(str) <= i) {
708			CERROR("%s: Revise array LL_SBI_FLAGS to match sbi "
709				"flags please.\n", ll_get_fsname(sb, NULL, 0));
710			return -EINVAL;
711		}
712
713		if (flags & 0x1)
714			seq_printf(m, "%s ", str[i]);
715		flags >>= 1;
716		++i;
717	}
718	seq_printf(m, "\b\n");
719	return 0;
720}
721LPROC_SEQ_FOPS_RO(ll_sbi_flags);
722
723static int ll_xattr_cache_seq_show(struct seq_file *m, void *v)
724{
725	struct super_block *sb = m->private;
726	struct ll_sb_info *sbi = ll_s2sbi(sb);
727	int rc;
728
729	rc = seq_printf(m, "%u\n", sbi->ll_xattr_cache_enabled);
730
731	return rc;
732}
733
734static ssize_t ll_xattr_cache_seq_write(struct file *file, const char *buffer,
735					size_t count, loff_t *off)
736{
737	struct seq_file *seq = file->private_data;
738	struct super_block *sb = seq->private;
739	struct ll_sb_info *sbi = ll_s2sbi(sb);
740	int val, rc;
741
742	rc = lprocfs_write_helper(buffer, count, &val);
743	if (rc)
744		return rc;
745
746	if (val != 0 && val != 1)
747		return -ERANGE;
748
749	if (val == 1 && !(sbi->ll_flags & LL_SBI_XATTR_CACHE))
750		return -ENOTSUPP;
751
752	sbi->ll_xattr_cache_enabled = val;
753
754	return count;
755}
756LPROC_SEQ_FOPS(ll_xattr_cache);
757
758static struct lprocfs_vars lprocfs_llite_obd_vars[] = {
759	{ "uuid",	  &ll_sb_uuid_fops,	  0, 0 },
760	//{ "mntpt_path",   ll_rd_path,	     0, 0 },
761	{ "fstype",       &ll_fstype_fops,	  0, 0 },
762	{ "site",	  &ll_site_stats_fops,    0, 0 },
763	{ "blocksize",    &ll_blksize_fops,	  0, 0 },
764	{ "kbytestotal",  &ll_kbytestotal_fops,   0, 0 },
765	{ "kbytesfree",   &ll_kbytesfree_fops,    0, 0 },
766	{ "kbytesavail",  &ll_kbytesavail_fops,   0, 0 },
767	{ "filestotal",   &ll_filestotal_fops,    0, 0 },
768	{ "filesfree",    &ll_filesfree_fops,	  0, 0 },
769	{ "client_type",  &ll_client_type_fops,   0, 0 },
770	//{ "filegroups",   lprocfs_rd_filegroups,  0, 0 },
771	{ "max_read_ahead_mb", &ll_max_readahead_mb_fops, 0 },
772	{ "max_read_ahead_per_file_mb", &ll_max_readahead_per_file_mb_fops, 0 },
773	{ "max_read_ahead_whole_mb", &ll_max_read_ahead_whole_mb_fops, 0 },
774	{ "max_cached_mb",    &ll_max_cached_mb_fops, 0 },
775	{ "checksum_pages",   &ll_checksum_fops, 0 },
776	{ "max_rw_chunk",     &ll_max_rw_chunk_fops, 0 },
777	{ "stats_track_pid",  &ll_track_pid_fops, 0 },
778	{ "stats_track_ppid", &ll_track_ppid_fops, 0 },
779	{ "stats_track_gid",  &ll_track_gid_fops, 0 },
780	{ "statahead_max",    &ll_statahead_max_fops, 0 },
781	{ "statahead_agl",    &ll_statahead_agl_fops, 0 },
782	{ "statahead_stats",  &ll_statahead_stats_fops, 0, 0 },
783	{ "lazystatfs",       &ll_lazystatfs_fops, 0 },
784	{ "max_easize",       &ll_maxea_size_fops, 0, 0 },
785	{ "sbi_flags",	      &ll_sbi_flags_fops, 0, 0 },
786	{ "xattr_cache",      &ll_xattr_cache_fops, 0, 0 },
787	{ 0 }
788};
789
790#define MAX_STRING_SIZE 128
791
792struct llite_file_opcode {
793	__u32       opcode;
794	__u32       type;
795	const char *opname;
796} llite_opcode_table[LPROC_LL_FILE_OPCODES] = {
797	/* file operation */
798	{ LPROC_LL_DIRTY_HITS,     LPROCFS_TYPE_REGS, "dirty_pages_hits" },
799	{ LPROC_LL_DIRTY_MISSES,   LPROCFS_TYPE_REGS, "dirty_pages_misses" },
800	{ LPROC_LL_READ_BYTES,     LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
801				   "read_bytes" },
802	{ LPROC_LL_WRITE_BYTES,    LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
803				   "write_bytes" },
804	{ LPROC_LL_BRW_READ,       LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
805				   "brw_read" },
806	{ LPROC_LL_BRW_WRITE,      LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
807				   "brw_write" },
808	{ LPROC_LL_OSC_READ,       LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
809				   "osc_read" },
810	{ LPROC_LL_OSC_WRITE,      LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
811				   "osc_write" },
812	{ LPROC_LL_IOCTL,	  LPROCFS_TYPE_REGS, "ioctl" },
813	{ LPROC_LL_OPEN,	   LPROCFS_TYPE_REGS, "open" },
814	{ LPROC_LL_RELEASE,	LPROCFS_TYPE_REGS, "close" },
815	{ LPROC_LL_MAP,	    LPROCFS_TYPE_REGS, "mmap" },
816	{ LPROC_LL_LLSEEK,	 LPROCFS_TYPE_REGS, "seek" },
817	{ LPROC_LL_FSYNC,	  LPROCFS_TYPE_REGS, "fsync" },
818	{ LPROC_LL_READDIR,	LPROCFS_TYPE_REGS, "readdir" },
819	/* inode operation */
820	{ LPROC_LL_SETATTR,	LPROCFS_TYPE_REGS, "setattr" },
821	{ LPROC_LL_TRUNC,	  LPROCFS_TYPE_REGS, "truncate" },
822	{ LPROC_LL_FLOCK,	  LPROCFS_TYPE_REGS, "flock" },
823	{ LPROC_LL_GETATTR,	LPROCFS_TYPE_REGS, "getattr" },
824	/* dir inode operation */
825	{ LPROC_LL_CREATE,	 LPROCFS_TYPE_REGS, "create" },
826	{ LPROC_LL_LINK,	   LPROCFS_TYPE_REGS, "link" },
827	{ LPROC_LL_UNLINK,	 LPROCFS_TYPE_REGS, "unlink" },
828	{ LPROC_LL_SYMLINK,	LPROCFS_TYPE_REGS, "symlink" },
829	{ LPROC_LL_MKDIR,	  LPROCFS_TYPE_REGS, "mkdir" },
830	{ LPROC_LL_RMDIR,	  LPROCFS_TYPE_REGS, "rmdir" },
831	{ LPROC_LL_MKNOD,	  LPROCFS_TYPE_REGS, "mknod" },
832	{ LPROC_LL_RENAME,	 LPROCFS_TYPE_REGS, "rename" },
833	/* special inode operation */
834	{ LPROC_LL_STAFS,	  LPROCFS_TYPE_REGS, "statfs" },
835	{ LPROC_LL_ALLOC_INODE,    LPROCFS_TYPE_REGS, "alloc_inode" },
836	{ LPROC_LL_SETXATTR,       LPROCFS_TYPE_REGS, "setxattr" },
837	{ LPROC_LL_GETXATTR,       LPROCFS_TYPE_REGS, "getxattr" },
838	{ LPROC_LL_GETXATTR_HITS,  LPROCFS_TYPE_REGS, "getxattr_hits" },
839	{ LPROC_LL_LISTXATTR,      LPROCFS_TYPE_REGS, "listxattr" },
840	{ LPROC_LL_REMOVEXATTR,    LPROCFS_TYPE_REGS, "removexattr" },
841	{ LPROC_LL_INODE_PERM,     LPROCFS_TYPE_REGS, "inode_permission" },
842};
843
844void ll_stats_ops_tally(struct ll_sb_info *sbi, int op, int count)
845{
846	if (!sbi->ll_stats)
847		return;
848	if (sbi->ll_stats_track_type == STATS_TRACK_ALL)
849		lprocfs_counter_add(sbi->ll_stats, op, count);
850	else if (sbi->ll_stats_track_type == STATS_TRACK_PID &&
851		 sbi->ll_stats_track_id == current->pid)
852		lprocfs_counter_add(sbi->ll_stats, op, count);
853	else if (sbi->ll_stats_track_type == STATS_TRACK_PPID &&
854		 sbi->ll_stats_track_id == current->parent->pid)
855		lprocfs_counter_add(sbi->ll_stats, op, count);
856	else if (sbi->ll_stats_track_type == STATS_TRACK_GID &&
857		 sbi->ll_stats_track_id ==
858			from_kgid(&init_user_ns, current_gid()))
859		lprocfs_counter_add(sbi->ll_stats, op, count);
860}
861EXPORT_SYMBOL(ll_stats_ops_tally);
862
863static const char *ra_stat_string[] = {
864	[RA_STAT_HIT] = "hits",
865	[RA_STAT_MISS] = "misses",
866	[RA_STAT_DISTANT_READPAGE] = "readpage not consecutive",
867	[RA_STAT_MISS_IN_WINDOW] = "miss inside window",
868	[RA_STAT_FAILED_GRAB_PAGE] = "failed grab_cache_page",
869	[RA_STAT_FAILED_MATCH] = "failed lock match",
870	[RA_STAT_DISCARDED] = "read but discarded",
871	[RA_STAT_ZERO_LEN] = "zero length file",
872	[RA_STAT_ZERO_WINDOW] = "zero size window",
873	[RA_STAT_EOF] = "read-ahead to EOF",
874	[RA_STAT_MAX_IN_FLIGHT] = "hit max r-a issue",
875	[RA_STAT_WRONG_GRAB_PAGE] = "wrong page from grab_cache_page",
876};
877
878LPROC_SEQ_FOPS_RO_TYPE(llite, name);
879LPROC_SEQ_FOPS_RO_TYPE(llite, uuid);
880
881int lprocfs_register_mountpoint(struct proc_dir_entry *parent,
882				struct super_block *sb, char *osc, char *mdc)
883{
884	struct lprocfs_vars lvars[2];
885	struct lustre_sb_info *lsi = s2lsi(sb);
886	struct ll_sb_info *sbi = ll_s2sbi(sb);
887	struct obd_device *obd;
888	struct proc_dir_entry *dir;
889	char name[MAX_STRING_SIZE + 1], *ptr;
890	int err, id, len, rc;
891
892	memset(lvars, 0, sizeof(lvars));
893
894	name[MAX_STRING_SIZE] = '\0';
895	lvars[0].name = name;
896
897	LASSERT(sbi != NULL);
898	LASSERT(mdc != NULL);
899	LASSERT(osc != NULL);
900
901	/* Get fsname */
902	len = strlen(lsi->lsi_lmd->lmd_profile);
903	ptr = strrchr(lsi->lsi_lmd->lmd_profile, '-');
904	if (ptr && (strcmp(ptr, "-client") == 0))
905		len -= 7;
906
907	/* Mount info */
908	snprintf(name, MAX_STRING_SIZE, "%.*s-%p", len,
909		 lsi->lsi_lmd->lmd_profile, sb);
910
911	sbi->ll_proc_root = lprocfs_register(name, parent, NULL, NULL);
912	if (IS_ERR(sbi->ll_proc_root)) {
913		err = PTR_ERR(sbi->ll_proc_root);
914		sbi->ll_proc_root = NULL;
915		return err;
916	}
917
918	rc = lprocfs_seq_create(sbi->ll_proc_root, "dump_page_cache", 0444,
919				&vvp_dump_pgcache_file_ops, sbi);
920	if (rc)
921		CWARN("Error adding the dump_page_cache file\n");
922
923	rc = lprocfs_seq_create(sbi->ll_proc_root, "extents_stats", 0644,
924				&ll_rw_extents_stats_fops, sbi);
925	if (rc)
926		CWARN("Error adding the extent_stats file\n");
927
928	rc = lprocfs_seq_create(sbi->ll_proc_root, "extents_stats_per_process",
929				0644, &ll_rw_extents_stats_pp_fops, sbi);
930	if (rc)
931		CWARN("Error adding the extents_stats_per_process file\n");
932
933	rc = lprocfs_seq_create(sbi->ll_proc_root, "offset_stats", 0644,
934				&ll_rw_offset_stats_fops, sbi);
935	if (rc)
936		CWARN("Error adding the offset_stats file\n");
937
938	/* File operations stats */
939	sbi->ll_stats = lprocfs_alloc_stats(LPROC_LL_FILE_OPCODES,
940					    LPROCFS_STATS_FLAG_NONE);
941	if (sbi->ll_stats == NULL)
942		GOTO(out, err = -ENOMEM);
943	/* do counter init */
944	for (id = 0; id < LPROC_LL_FILE_OPCODES; id++) {
945		__u32 type = llite_opcode_table[id].type;
946		void *ptr = NULL;
947		if (type & LPROCFS_TYPE_REGS)
948			ptr = "regs";
949		else if (type & LPROCFS_TYPE_BYTES)
950			ptr = "bytes";
951		else if (type & LPROCFS_TYPE_PAGES)
952			ptr = "pages";
953		lprocfs_counter_init(sbi->ll_stats,
954				     llite_opcode_table[id].opcode,
955				     (type & LPROCFS_CNTR_AVGMINMAX),
956				     llite_opcode_table[id].opname, ptr);
957	}
958	err = lprocfs_register_stats(sbi->ll_proc_root, "stats", sbi->ll_stats);
959	if (err)
960		GOTO(out, err);
961
962	sbi->ll_ra_stats = lprocfs_alloc_stats(ARRAY_SIZE(ra_stat_string),
963					       LPROCFS_STATS_FLAG_NONE);
964	if (sbi->ll_ra_stats == NULL)
965		GOTO(out, err = -ENOMEM);
966
967	for (id = 0; id < ARRAY_SIZE(ra_stat_string); id++)
968		lprocfs_counter_init(sbi->ll_ra_stats, id, 0,
969				     ra_stat_string[id], "pages");
970	err = lprocfs_register_stats(sbi->ll_proc_root, "read_ahead_stats",
971				     sbi->ll_ra_stats);
972	if (err)
973		GOTO(out, err);
974
975
976	err = lprocfs_add_vars(sbi->ll_proc_root, lprocfs_llite_obd_vars, sb);
977	if (err)
978		GOTO(out, err);
979
980	/* MDC info */
981	obd = class_name2obd(mdc);
982
983	LASSERT(obd != NULL);
984	LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
985	LASSERT(obd->obd_type->typ_name != NULL);
986
987	dir = proc_mkdir(obd->obd_type->typ_name, sbi->ll_proc_root);
988	if (dir == NULL)
989		GOTO(out, err = -ENOMEM);
990
991	snprintf(name, MAX_STRING_SIZE, "common_name");
992	lvars[0].fops = &llite_name_fops;
993	err = lprocfs_add_vars(dir, lvars, obd);
994	if (err)
995		GOTO(out, err);
996
997	snprintf(name, MAX_STRING_SIZE, "uuid");
998	lvars[0].fops = &llite_uuid_fops;
999	err = lprocfs_add_vars(dir, lvars, obd);
1000	if (err)
1001		GOTO(out, err);
1002
1003	/* OSC */
1004	obd = class_name2obd(osc);
1005
1006	LASSERT(obd != NULL);
1007	LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
1008	LASSERT(obd->obd_type->typ_name != NULL);
1009
1010	dir = proc_mkdir(obd->obd_type->typ_name, sbi->ll_proc_root);
1011	if (dir == NULL)
1012		GOTO(out, err = -ENOMEM);
1013
1014	snprintf(name, MAX_STRING_SIZE, "common_name");
1015	lvars[0].fops = &llite_name_fops;
1016	err = lprocfs_add_vars(dir, lvars, obd);
1017	if (err)
1018		GOTO(out, err);
1019
1020	snprintf(name, MAX_STRING_SIZE, "uuid");
1021	lvars[0].fops = &llite_uuid_fops;
1022	err = lprocfs_add_vars(dir, lvars, obd);
1023out:
1024	if (err) {
1025		lprocfs_remove(&sbi->ll_proc_root);
1026		lprocfs_free_stats(&sbi->ll_ra_stats);
1027		lprocfs_free_stats(&sbi->ll_stats);
1028	}
1029	return err;
1030}
1031
1032void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi)
1033{
1034	if (sbi->ll_proc_root) {
1035		lprocfs_remove(&sbi->ll_proc_root);
1036		lprocfs_free_stats(&sbi->ll_ra_stats);
1037		lprocfs_free_stats(&sbi->ll_stats);
1038	}
1039}
1040#undef MAX_STRING_SIZE
1041
1042#define pct(a,b) (b ? a * 100 / b : 0)
1043
1044static void ll_display_extents_info(struct ll_rw_extents_info *io_extents,
1045				   struct seq_file *seq, int which)
1046{
1047	unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
1048	unsigned long start, end, r, w;
1049	char *unitp = "KMGTPEZY";
1050	int i, units = 10;
1051	struct per_process_info *pp_info = &io_extents->pp_extents[which];
1052
1053	read_cum = 0;
1054	write_cum = 0;
1055	start = 0;
1056
1057	for(i = 0; i < LL_HIST_MAX; i++) {
1058		read_tot += pp_info->pp_r_hist.oh_buckets[i];
1059		write_tot += pp_info->pp_w_hist.oh_buckets[i];
1060	}
1061
1062	for(i = 0; i < LL_HIST_MAX; i++) {
1063		r = pp_info->pp_r_hist.oh_buckets[i];
1064		w = pp_info->pp_w_hist.oh_buckets[i];
1065		read_cum += r;
1066		write_cum += w;
1067		end = 1 << (i + LL_HIST_START - units);
1068		seq_printf(seq, "%4lu%c - %4lu%c%c: %14lu %4lu %4lu  | "
1069			   "%14lu %4lu %4lu\n", start, *unitp, end, *unitp,
1070			   (i == LL_HIST_MAX - 1) ? '+' : ' ',
1071			   r, pct(r, read_tot), pct(read_cum, read_tot),
1072			   w, pct(w, write_tot), pct(write_cum, write_tot));
1073		start = end;
1074		if (start == 1<<10) {
1075			start = 1;
1076			units += 10;
1077			unitp++;
1078		}
1079		if (read_cum == read_tot && write_cum == write_tot)
1080			break;
1081	}
1082}
1083
1084static int ll_rw_extents_stats_pp_seq_show(struct seq_file *seq, void *v)
1085{
1086	struct timeval now;
1087	struct ll_sb_info *sbi = seq->private;
1088	struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
1089	int k;
1090
1091	do_gettimeofday(&now);
1092
1093	if (!sbi->ll_rw_stats_on) {
1094		seq_printf(seq, "disabled\n"
1095				"write anything in this file to activate, "
1096				"then 0 or \"[D/d]isabled\" to deactivate\n");
1097		return 0;
1098	}
1099	seq_printf(seq, "snapshot_time:	 %lu.%lu (secs.usecs)\n",
1100		   now.tv_sec, (unsigned long)now.tv_usec);
1101	seq_printf(seq, "%15s %19s       | %20s\n", " ", "read", "write");
1102	seq_printf(seq, "%13s   %14s %4s %4s  | %14s %4s %4s\n",
1103		   "extents", "calls", "%", "cum%",
1104		   "calls", "%", "cum%");
1105	spin_lock(&sbi->ll_pp_extent_lock);
1106	for (k = 0; k < LL_PROCESS_HIST_MAX; k++) {
1107		if (io_extents->pp_extents[k].pid != 0) {
1108			seq_printf(seq, "\nPID: %d\n",
1109				   io_extents->pp_extents[k].pid);
1110			ll_display_extents_info(io_extents, seq, k);
1111		}
1112	}
1113	spin_unlock(&sbi->ll_pp_extent_lock);
1114	return 0;
1115}
1116
1117static ssize_t ll_rw_extents_stats_pp_seq_write(struct file *file,
1118						const char *buf, size_t len,
1119						loff_t *off)
1120{
1121	struct seq_file *seq = file->private_data;
1122	struct ll_sb_info *sbi = seq->private;
1123	struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
1124	int i;
1125	int value = 1, rc = 0;
1126
1127	rc = lprocfs_write_helper(buf, len, &value);
1128	if (rc < 0 && (strcmp(buf, "disabled") == 0 ||
1129		       strcmp(buf, "Disabled") == 0))
1130		value = 0;
1131
1132	if (value == 0)
1133		sbi->ll_rw_stats_on = 0;
1134	else
1135		sbi->ll_rw_stats_on = 1;
1136
1137	spin_lock(&sbi->ll_pp_extent_lock);
1138	for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
1139		io_extents->pp_extents[i].pid = 0;
1140		lprocfs_oh_clear(&io_extents->pp_extents[i].pp_r_hist);
1141		lprocfs_oh_clear(&io_extents->pp_extents[i].pp_w_hist);
1142	}
1143	spin_unlock(&sbi->ll_pp_extent_lock);
1144	return len;
1145}
1146
1147LPROC_SEQ_FOPS(ll_rw_extents_stats_pp);
1148
1149static int ll_rw_extents_stats_seq_show(struct seq_file *seq, void *v)
1150{
1151	struct timeval now;
1152	struct ll_sb_info *sbi = seq->private;
1153	struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
1154
1155	do_gettimeofday(&now);
1156
1157	if (!sbi->ll_rw_stats_on) {
1158		seq_printf(seq, "disabled\n"
1159				"write anything in this file to activate, "
1160				"then 0 or \"[D/d]isabled\" to deactivate\n");
1161		return 0;
1162	}
1163	seq_printf(seq, "snapshot_time:	 %lu.%lu (secs.usecs)\n",
1164		   now.tv_sec, (unsigned long)now.tv_usec);
1165
1166	seq_printf(seq, "%15s %19s       | %20s\n", " ", "read", "write");
1167	seq_printf(seq, "%13s   %14s %4s %4s  | %14s %4s %4s\n",
1168		   "extents", "calls", "%", "cum%",
1169		   "calls", "%", "cum%");
1170	spin_lock(&sbi->ll_lock);
1171	ll_display_extents_info(io_extents, seq, LL_PROCESS_HIST_MAX);
1172	spin_unlock(&sbi->ll_lock);
1173
1174	return 0;
1175}
1176
1177static ssize_t ll_rw_extents_stats_seq_write(struct file *file, const char *buf,
1178					size_t len, loff_t *off)
1179{
1180	struct seq_file *seq = file->private_data;
1181	struct ll_sb_info *sbi = seq->private;
1182	struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
1183	int i;
1184	int value = 1, rc = 0;
1185
1186	rc = lprocfs_write_helper(buf, len, &value);
1187	if (rc < 0 && (strcmp(buf, "disabled") == 0 ||
1188		       strcmp(buf, "Disabled") == 0))
1189		value = 0;
1190
1191	if (value == 0)
1192		sbi->ll_rw_stats_on = 0;
1193	else
1194		sbi->ll_rw_stats_on = 1;
1195	spin_lock(&sbi->ll_pp_extent_lock);
1196	for (i = 0; i <= LL_PROCESS_HIST_MAX; i++) {
1197		io_extents->pp_extents[i].pid = 0;
1198		lprocfs_oh_clear(&io_extents->pp_extents[i].pp_r_hist);
1199		lprocfs_oh_clear(&io_extents->pp_extents[i].pp_w_hist);
1200	}
1201	spin_unlock(&sbi->ll_pp_extent_lock);
1202
1203	return len;
1204}
1205
1206LPROC_SEQ_FOPS(ll_rw_extents_stats);
1207
1208void ll_rw_stats_tally(struct ll_sb_info *sbi, pid_t pid,
1209		       struct ll_file_data *file, loff_t pos,
1210		       size_t count, int rw)
1211{
1212	int i, cur = -1;
1213	struct ll_rw_process_info *process;
1214	struct ll_rw_process_info *offset;
1215	int *off_count = &sbi->ll_rw_offset_entry_count;
1216	int *process_count = &sbi->ll_offset_process_count;
1217	struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
1218
1219	if(!sbi->ll_rw_stats_on)
1220		return;
1221	process = sbi->ll_rw_process_info;
1222	offset = sbi->ll_rw_offset_info;
1223
1224	spin_lock(&sbi->ll_pp_extent_lock);
1225	/* Extent statistics */
1226	for(i = 0; i < LL_PROCESS_HIST_MAX; i++) {
1227		if(io_extents->pp_extents[i].pid == pid) {
1228			cur = i;
1229			break;
1230		}
1231	}
1232
1233	if (cur == -1) {
1234		/* new process */
1235		sbi->ll_extent_process_count =
1236			(sbi->ll_extent_process_count + 1) % LL_PROCESS_HIST_MAX;
1237		cur = sbi->ll_extent_process_count;
1238		io_extents->pp_extents[cur].pid = pid;
1239		lprocfs_oh_clear(&io_extents->pp_extents[cur].pp_r_hist);
1240		lprocfs_oh_clear(&io_extents->pp_extents[cur].pp_w_hist);
1241	}
1242
1243	for(i = 0; (count >= (1 << LL_HIST_START << i)) &&
1244	     (i < (LL_HIST_MAX - 1)); i++);
1245	if (rw == 0) {
1246		io_extents->pp_extents[cur].pp_r_hist.oh_buckets[i]++;
1247		io_extents->pp_extents[LL_PROCESS_HIST_MAX].pp_r_hist.oh_buckets[i]++;
1248	} else {
1249		io_extents->pp_extents[cur].pp_w_hist.oh_buckets[i]++;
1250		io_extents->pp_extents[LL_PROCESS_HIST_MAX].pp_w_hist.oh_buckets[i]++;
1251	}
1252	spin_unlock(&sbi->ll_pp_extent_lock);
1253
1254	spin_lock(&sbi->ll_process_lock);
1255	/* Offset statistics */
1256	for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
1257		if (process[i].rw_pid == pid) {
1258			if (process[i].rw_last_file != file) {
1259				process[i].rw_range_start = pos;
1260				process[i].rw_last_file_pos = pos + count;
1261				process[i].rw_smallest_extent = count;
1262				process[i].rw_largest_extent = count;
1263				process[i].rw_offset = 0;
1264				process[i].rw_last_file = file;
1265				spin_unlock(&sbi->ll_process_lock);
1266				return;
1267			}
1268			if (process[i].rw_last_file_pos != pos) {
1269				*off_count =
1270				    (*off_count + 1) % LL_OFFSET_HIST_MAX;
1271				offset[*off_count].rw_op = process[i].rw_op;
1272				offset[*off_count].rw_pid = pid;
1273				offset[*off_count].rw_range_start =
1274					process[i].rw_range_start;
1275				offset[*off_count].rw_range_end =
1276					process[i].rw_last_file_pos;
1277				offset[*off_count].rw_smallest_extent =
1278					process[i].rw_smallest_extent;
1279				offset[*off_count].rw_largest_extent =
1280					process[i].rw_largest_extent;
1281				offset[*off_count].rw_offset =
1282					process[i].rw_offset;
1283				process[i].rw_op = rw;
1284				process[i].rw_range_start = pos;
1285				process[i].rw_smallest_extent = count;
1286				process[i].rw_largest_extent = count;
1287				process[i].rw_offset = pos -
1288					process[i].rw_last_file_pos;
1289			}
1290			if(process[i].rw_smallest_extent > count)
1291				process[i].rw_smallest_extent = count;
1292			if(process[i].rw_largest_extent < count)
1293				process[i].rw_largest_extent = count;
1294			process[i].rw_last_file_pos = pos + count;
1295			spin_unlock(&sbi->ll_process_lock);
1296			return;
1297		}
1298	}
1299	*process_count = (*process_count + 1) % LL_PROCESS_HIST_MAX;
1300	process[*process_count].rw_pid = pid;
1301	process[*process_count].rw_op = rw;
1302	process[*process_count].rw_range_start = pos;
1303	process[*process_count].rw_last_file_pos = pos + count;
1304	process[*process_count].rw_smallest_extent = count;
1305	process[*process_count].rw_largest_extent = count;
1306	process[*process_count].rw_offset = 0;
1307	process[*process_count].rw_last_file = file;
1308	spin_unlock(&sbi->ll_process_lock);
1309}
1310
1311static int ll_rw_offset_stats_seq_show(struct seq_file *seq, void *v)
1312{
1313	struct timeval now;
1314	struct ll_sb_info *sbi = seq->private;
1315	struct ll_rw_process_info *offset = sbi->ll_rw_offset_info;
1316	struct ll_rw_process_info *process = sbi->ll_rw_process_info;
1317	int i;
1318
1319	do_gettimeofday(&now);
1320
1321	if (!sbi->ll_rw_stats_on) {
1322		seq_printf(seq, "disabled\n"
1323				"write anything in this file to activate, "
1324				"then 0 or \"[D/d]isabled\" to deactivate\n");
1325		return 0;
1326	}
1327	spin_lock(&sbi->ll_process_lock);
1328
1329	seq_printf(seq, "snapshot_time:	 %lu.%lu (secs.usecs)\n",
1330		   now.tv_sec, (unsigned long)now.tv_usec);
1331	seq_printf(seq, "%3s %10s %14s %14s %17s %17s %14s\n",
1332		   "R/W", "PID", "RANGE START", "RANGE END",
1333		   "SMALLEST EXTENT", "LARGEST EXTENT", "OFFSET");
1334	/* We stored the discontiguous offsets here; print them first */
1335	for(i = 0; i < LL_OFFSET_HIST_MAX; i++) {
1336		if (offset[i].rw_pid != 0)
1337			seq_printf(seq,
1338				   "%3c %10d %14Lu %14Lu %17lu %17lu %14Lu",
1339				   offset[i].rw_op == READ ? 'R' : 'W',
1340				   offset[i].rw_pid,
1341				   offset[i].rw_range_start,
1342				   offset[i].rw_range_end,
1343				   (unsigned long)offset[i].rw_smallest_extent,
1344				   (unsigned long)offset[i].rw_largest_extent,
1345				   offset[i].rw_offset);
1346	}
1347	/* Then print the current offsets for each process */
1348	for(i = 0; i < LL_PROCESS_HIST_MAX; i++) {
1349		if (process[i].rw_pid != 0)
1350			seq_printf(seq,
1351				   "%3c %10d %14Lu %14Lu %17lu %17lu %14Lu",
1352				   process[i].rw_op == READ ? 'R' : 'W',
1353				   process[i].rw_pid,
1354				   process[i].rw_range_start,
1355				   process[i].rw_last_file_pos,
1356				   (unsigned long)process[i].rw_smallest_extent,
1357				   (unsigned long)process[i].rw_largest_extent,
1358				   process[i].rw_offset);
1359	}
1360	spin_unlock(&sbi->ll_process_lock);
1361
1362	return 0;
1363}
1364
1365static ssize_t ll_rw_offset_stats_seq_write(struct file *file, const char *buf,
1366				       size_t len, loff_t *off)
1367{
1368	struct seq_file *seq = file->private_data;
1369	struct ll_sb_info *sbi = seq->private;
1370	struct ll_rw_process_info *process_info = sbi->ll_rw_process_info;
1371	struct ll_rw_process_info *offset_info = sbi->ll_rw_offset_info;
1372	int value = 1, rc = 0;
1373
1374	rc = lprocfs_write_helper(buf, len, &value);
1375
1376	if (rc < 0 && (strcmp(buf, "disabled") == 0 ||
1377			   strcmp(buf, "Disabled") == 0))
1378		value = 0;
1379
1380	if (value == 0)
1381		sbi->ll_rw_stats_on = 0;
1382	else
1383		sbi->ll_rw_stats_on = 1;
1384
1385	spin_lock(&sbi->ll_process_lock);
1386	sbi->ll_offset_process_count = 0;
1387	sbi->ll_rw_offset_entry_count = 0;
1388	memset(process_info, 0, sizeof(struct ll_rw_process_info) *
1389	       LL_PROCESS_HIST_MAX);
1390	memset(offset_info, 0, sizeof(struct ll_rw_process_info) *
1391	       LL_OFFSET_HIST_MAX);
1392	spin_unlock(&sbi->ll_process_lock);
1393
1394	return len;
1395}
1396
1397LPROC_SEQ_FOPS(ll_rw_offset_stats);
1398
1399void lprocfs_llite_init_vars(struct lprocfs_static_vars *lvars)
1400{
1401    lvars->module_vars  = NULL;
1402    lvars->obd_vars     = lprocfs_llite_obd_vars;
1403}
1404