aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDmitry Monakhov <dmonakhov@openvz.org>2010-04-26 12:03:33 -0400
committerJan Kara <jack@suse.cz>2010-05-21 13:30:41 -0400
commitdde9588853b1bde542eab247f8838c472806688f (patch)
tree9f9c68bf63120056517bbfce78b75e6820cc4c4b /fs
parentda8d1ba22fa1fd0c0e541a43d75ebb062589b14b (diff)
quota: Make quota stat accounting lockless.
Quota stats is mostly writable data structure. Let's alloc percpu bucket for each value. NOTE: dqstats_read() function is racy against dqstats_{inc,dec} and may return inconsistent value. But this is ok since absolute accuracy is not required. Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org> Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r--fs/quota/dquot.c102
-rw-r--r--fs/quota/quota_tree.c4
-rw-r--r--fs/quota/quota_v1.c4
3 files changed, 72 insertions, 38 deletions
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index ae766056350d..01347e81d0ca 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -82,7 +82,7 @@
82 82
83/* 83/*
84 * There are three quota SMP locks. dq_list_lock protects all lists with quotas 84 * There are three quota SMP locks. dq_list_lock protects all lists with quotas
85 * and quota formats, dqstats structure containing statistics about the lists 85 * and quota formats.
86 * dq_data_lock protects data from dq_dqb and also mem_dqinfo structures and 86 * dq_data_lock protects data from dq_dqb and also mem_dqinfo structures and
87 * also guards consistency of dquot->dq_dqb with inode->i_blocks, i_bytes. 87 * also guards consistency of dquot->dq_dqb with inode->i_blocks, i_bytes.
88 * i_blocks and i_bytes updates itself are guarded by i_lock acquired directly 88 * i_blocks and i_bytes updates itself are guarded by i_lock acquired directly
@@ -228,6 +228,10 @@ static struct hlist_head *dquot_hash;
228 228
229struct dqstats dqstats; 229struct dqstats dqstats;
230EXPORT_SYMBOL(dqstats); 230EXPORT_SYMBOL(dqstats);
231#ifdef CONFIG_SMP
232struct dqstats *dqstats_pcpu;
233EXPORT_SYMBOL(dqstats_pcpu);
234#endif
231 235
232static qsize_t inode_get_rsv_space(struct inode *inode); 236static qsize_t inode_get_rsv_space(struct inode *inode);
233static void __dquot_initialize(struct inode *inode, int type); 237static void __dquot_initialize(struct inode *inode, int type);
@@ -275,7 +279,7 @@ static struct dquot *find_dquot(unsigned int hashent, struct super_block *sb,
275static inline void put_dquot_last(struct dquot *dquot) 279static inline void put_dquot_last(struct dquot *dquot)
276{ 280{
277 list_add_tail(&dquot->dq_free, &free_dquots); 281 list_add_tail(&dquot->dq_free, &free_dquots);
278 dqstats.free_dquots++; 282 dqstats_inc(DQST_FREE_DQUOTS);
279} 283}
280 284
281static inline void remove_free_dquot(struct dquot *dquot) 285static inline void remove_free_dquot(struct dquot *dquot)
@@ -283,7 +287,7 @@ static inline void remove_free_dquot(struct dquot *dquot)
283 if (list_empty(&dquot->dq_free)) 287 if (list_empty(&dquot->dq_free))
284 return; 288 return;
285 list_del_init(&dquot->dq_free); 289 list_del_init(&dquot->dq_free);
286 dqstats.free_dquots--; 290 dqstats_dec(DQST_FREE_DQUOTS);
287} 291}
288 292
289static inline void put_inuse(struct dquot *dquot) 293static inline void put_inuse(struct dquot *dquot)
@@ -291,12 +295,12 @@ static inline void put_inuse(struct dquot *dquot)
291 /* We add to the back of inuse list so we don't have to restart 295 /* We add to the back of inuse list so we don't have to restart
292 * when traversing this list and we block */ 296 * when traversing this list and we block */
293 list_add_tail(&dquot->dq_inuse, &inuse_list); 297 list_add_tail(&dquot->dq_inuse, &inuse_list);
294 dqstats.allocated_dquots++; 298 dqstats_inc(DQST_ALLOC_DQUOTS);
295} 299}
296 300
297static inline void remove_inuse(struct dquot *dquot) 301static inline void remove_inuse(struct dquot *dquot)
298{ 302{
299 dqstats.allocated_dquots--; 303 dqstats_dec(DQST_ALLOC_DQUOTS);
300 list_del(&dquot->dq_inuse); 304 list_del(&dquot->dq_inuse);
301} 305}
302/* 306/*
@@ -561,8 +565,8 @@ int dquot_scan_active(struct super_block *sb,
561 continue; 565 continue;
562 /* Now we have active dquot so we can just increase use count */ 566 /* Now we have active dquot so we can just increase use count */
563 atomic_inc(&dquot->dq_count); 567 atomic_inc(&dquot->dq_count);
564 dqstats.lookups++;
565 spin_unlock(&dq_list_lock); 568 spin_unlock(&dq_list_lock);
569 dqstats_inc(DQST_LOOKUPS);
566 dqput(old_dquot); 570 dqput(old_dquot);
567 old_dquot = dquot; 571 old_dquot = dquot;
568 ret = fn(dquot, priv); 572 ret = fn(dquot, priv);
@@ -607,8 +611,8 @@ int vfs_quota_sync(struct super_block *sb, int type, int wait)
607 * holding reference so we can safely just increase 611 * holding reference so we can safely just increase
608 * use count */ 612 * use count */
609 atomic_inc(&dquot->dq_count); 613 atomic_inc(&dquot->dq_count);
610 dqstats.lookups++;
611 spin_unlock(&dq_list_lock); 614 spin_unlock(&dq_list_lock);
615 dqstats_inc(DQST_LOOKUPS);
612 sb->dq_op->write_dquot(dquot); 616 sb->dq_op->write_dquot(dquot);
613 dqput(dquot); 617 dqput(dquot);
614 spin_lock(&dq_list_lock); 618 spin_lock(&dq_list_lock);
@@ -620,9 +624,7 @@ int vfs_quota_sync(struct super_block *sb, int type, int wait)
620 if ((cnt == type || type == -1) && sb_has_quota_active(sb, cnt) 624 if ((cnt == type || type == -1) && sb_has_quota_active(sb, cnt)
621 && info_dirty(&dqopt->info[cnt])) 625 && info_dirty(&dqopt->info[cnt]))
622 sb->dq_op->write_info(sb, cnt); 626 sb->dq_op->write_info(sb, cnt);
623 spin_lock(&dq_list_lock); 627 dqstats_inc(DQST_SYNCS);
624 dqstats.syncs++;
625 spin_unlock(&dq_list_lock);
626 mutex_unlock(&dqopt->dqonoff_mutex); 628 mutex_unlock(&dqopt->dqonoff_mutex);
627 629
628 if (!wait || (sb_dqopt(sb)->flags & DQUOT_QUOTA_SYS_FILE)) 630 if (!wait || (sb_dqopt(sb)->flags & DQUOT_QUOTA_SYS_FILE))
@@ -674,6 +676,22 @@ static void prune_dqcache(int count)
674 } 676 }
675} 677}
676 678
679static int dqstats_read(unsigned int type)
680{
681 int count = 0;
682#ifdef CONFIG_SMP
683 int cpu;
684 for_each_possible_cpu(cpu)
685 count += per_cpu_ptr(dqstats_pcpu, cpu)->stat[type];
686 /* Statistics reading is racy, but absolute accuracy isn't required */
687 if (count < 0)
688 count = 0;
689#else
690 count = dqstats.stat[type];
691#endif
692 return count;
693}
694
677/* 695/*
678 * This is called from kswapd when we think we need some 696 * This is called from kswapd when we think we need some
679 * more memory 697 * more memory
@@ -686,7 +704,7 @@ static int shrink_dqcache_memory(int nr, gfp_t gfp_mask)
686 prune_dqcache(nr); 704 prune_dqcache(nr);
687 spin_unlock(&dq_list_lock); 705 spin_unlock(&dq_list_lock);
688 } 706 }
689 return (dqstats.free_dquots / 100) * sysctl_vfs_cache_pressure; 707 return (dqstats_read(DQST_FREE_DQUOTS)/100) * sysctl_vfs_cache_pressure;
690} 708}
691 709
692static struct shrinker dqcache_shrinker = { 710static struct shrinker dqcache_shrinker = {
@@ -714,10 +732,7 @@ void dqput(struct dquot *dquot)
714 BUG(); 732 BUG();
715 } 733 }
716#endif 734#endif
717 735 dqstats_inc(DQST_DROPS);
718 spin_lock(&dq_list_lock);
719 dqstats.drops++;
720 spin_unlock(&dq_list_lock);
721we_slept: 736we_slept:
722 spin_lock(&dq_list_lock); 737 spin_lock(&dq_list_lock);
723 if (atomic_read(&dquot->dq_count) > 1) { 738 if (atomic_read(&dquot->dq_count) > 1) {
@@ -834,15 +849,15 @@ we_slept:
834 put_inuse(dquot); 849 put_inuse(dquot);
835 /* hash it first so it can be found */ 850 /* hash it first so it can be found */
836 insert_dquot_hash(dquot); 851 insert_dquot_hash(dquot);
837 dqstats.lookups++;
838 spin_unlock(&dq_list_lock); 852 spin_unlock(&dq_list_lock);
853 dqstats_inc(DQST_LOOKUPS);
839 } else { 854 } else {
840 if (!atomic_read(&dquot->dq_count)) 855 if (!atomic_read(&dquot->dq_count))
841 remove_free_dquot(dquot); 856 remove_free_dquot(dquot);
842 atomic_inc(&dquot->dq_count); 857 atomic_inc(&dquot->dq_count);
843 dqstats.cache_hits++;
844 dqstats.lookups++;
845 spin_unlock(&dq_list_lock); 858 spin_unlock(&dq_list_lock);
859 dqstats_inc(DQST_CACHE_HITS);
860 dqstats_inc(DQST_LOOKUPS);
846 } 861 }
847 /* Wait for dq_lock - after this we know that either dquot_release() is 862 /* Wait for dq_lock - after this we know that either dquot_release() is
848 * already finished or it will be canceled due to dq_count > 1 test */ 863 * already finished or it will be canceled due to dq_count > 1 test */
@@ -2476,62 +2491,74 @@ const struct quotactl_ops vfs_quotactl_ops = {
2476 .set_dqblk = vfs_set_dqblk 2491 .set_dqblk = vfs_set_dqblk
2477}; 2492};
2478 2493
2494
2495static int do_proc_dqstats(struct ctl_table *table, int write,
2496 void __user *buffer, size_t *lenp, loff_t *ppos)
2497{
2498#ifdef CONFIG_SMP
2499 /* Update global table */
2500 unsigned int type = (int *)table->data - dqstats.stat;
2501 dqstats.stat[type] = dqstats_read(type);
2502#endif
2503 return proc_dointvec(table, write, buffer, lenp, ppos);
2504}
2505
2479static ctl_table fs_dqstats_table[] = { 2506static ctl_table fs_dqstats_table[] = {
2480 { 2507 {
2481 .procname = "lookups", 2508 .procname = "lookups",
2482 .data = &dqstats.lookups, 2509 .data = &dqstats.stat[DQST_LOOKUPS],
2483 .maxlen = sizeof(int), 2510 .maxlen = sizeof(int),
2484 .mode = 0444, 2511 .mode = 0444,
2485 .proc_handler = proc_dointvec, 2512 .proc_handler = do_proc_dqstats,
2486 }, 2513 },
2487 { 2514 {
2488 .procname = "drops", 2515 .procname = "drops",
2489 .data = &dqstats.drops, 2516 .data = &dqstats.stat[DQST_DROPS],
2490 .maxlen = sizeof(int), 2517 .maxlen = sizeof(int),
2491 .mode = 0444, 2518 .mode = 0444,
2492 .proc_handler = proc_dointvec, 2519 .proc_handler = do_proc_dqstats,
2493 }, 2520 },
2494 { 2521 {
2495 .procname = "reads", 2522 .procname = "reads",
2496 .data = &dqstats.reads, 2523 .data = &dqstats.stat[DQST_READS],
2497 .maxlen = sizeof(int), 2524 .maxlen = sizeof(int),
2498 .mode = 0444, 2525 .mode = 0444,
2499 .proc_handler = proc_dointvec, 2526 .proc_handler = do_proc_dqstats,
2500 }, 2527 },
2501 { 2528 {
2502 .procname = "writes", 2529 .procname = "writes",
2503 .data = &dqstats.writes, 2530 .data = &dqstats.stat[DQST_WRITES],
2504 .maxlen = sizeof(int), 2531 .maxlen = sizeof(int),
2505 .mode = 0444, 2532 .mode = 0444,
2506 .proc_handler = proc_dointvec, 2533 .proc_handler = do_proc_dqstats,
2507 }, 2534 },
2508 { 2535 {
2509 .procname = "cache_hits", 2536 .procname = "cache_hits",
2510 .data = &dqstats.cache_hits, 2537 .data = &dqstats.stat[DQST_CACHE_HITS],
2511 .maxlen = sizeof(int), 2538 .maxlen = sizeof(int),
2512 .mode = 0444, 2539 .mode = 0444,
2513 .proc_handler = proc_dointvec, 2540 .proc_handler = do_proc_dqstats,
2514 }, 2541 },
2515 { 2542 {
2516 .procname = "allocated_dquots", 2543 .procname = "allocated_dquots",
2517 .data = &dqstats.allocated_dquots, 2544 .data = &dqstats.stat[DQST_ALLOC_DQUOTS],
2518 .maxlen = sizeof(int), 2545 .maxlen = sizeof(int),
2519 .mode = 0444, 2546 .mode = 0444,
2520 .proc_handler = proc_dointvec, 2547 .proc_handler = do_proc_dqstats,
2521 }, 2548 },
2522 { 2549 {
2523 .procname = "free_dquots", 2550 .procname = "free_dquots",
2524 .data = &dqstats.free_dquots, 2551 .data = &dqstats.stat[DQST_FREE_DQUOTS],
2525 .maxlen = sizeof(int), 2552 .maxlen = sizeof(int),
2526 .mode = 0444, 2553 .mode = 0444,
2527 .proc_handler = proc_dointvec, 2554 .proc_handler = do_proc_dqstats,
2528 }, 2555 },
2529 { 2556 {
2530 .procname = "syncs", 2557 .procname = "syncs",
2531 .data = &dqstats.syncs, 2558 .data = &dqstats.stat[DQST_SYNCS],
2532 .maxlen = sizeof(int), 2559 .maxlen = sizeof(int),
2533 .mode = 0444, 2560 .mode = 0444,
2534 .proc_handler = proc_dointvec, 2561 .proc_handler = do_proc_dqstats,
2535 }, 2562 },
2536#ifdef CONFIG_PRINT_QUOTA_WARNING 2563#ifdef CONFIG_PRINT_QUOTA_WARNING
2537 { 2564 {
@@ -2583,6 +2610,13 @@ static int __init dquot_init(void)
2583 if (!dquot_hash) 2610 if (!dquot_hash)
2584 panic("Cannot create dquot hash table"); 2611 panic("Cannot create dquot hash table");
2585 2612
2613#ifdef CONFIG_SMP
2614 dqstats_pcpu = alloc_percpu(struct dqstats);
2615 if (!dqstats_pcpu)
2616 panic("Cannot create dquot stats table");
2617#endif
2618 memset(&dqstats, 0, sizeof(struct dqstats));
2619
2586 /* Find power-of-two hlist_heads which can fit into allocation */ 2620 /* Find power-of-two hlist_heads which can fit into allocation */
2587 nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct hlist_head); 2621 nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct hlist_head);
2588 dq_hash_bits = 0; 2622 dq_hash_bits = 0;
diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c
index f81f4bcfb178..5b7f7416ec7a 100644
--- a/fs/quota/quota_tree.c
+++ b/fs/quota/quota_tree.c
@@ -384,7 +384,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
384 } else { 384 } else {
385 ret = 0; 385 ret = 0;
386 } 386 }
387 dqstats.writes++; 387 dqstats_inc(DQST_WRITES);
388 kfree(ddquot); 388 kfree(ddquot);
389 389
390 return ret; 390 return ret;
@@ -634,7 +634,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
634 spin_unlock(&dq_data_lock); 634 spin_unlock(&dq_data_lock);
635 kfree(ddquot); 635 kfree(ddquot);
636out: 636out:
637 dqstats.reads++; 637 dqstats_inc(DQST_READS);
638 return ret; 638 return ret;
639} 639}
640EXPORT_SYMBOL(qtree_read_dquot); 640EXPORT_SYMBOL(qtree_read_dquot);
diff --git a/fs/quota/quota_v1.c b/fs/quota/quota_v1.c
index 2ae757e9c008..4af344c5852a 100644
--- a/fs/quota/quota_v1.c
+++ b/fs/quota/quota_v1.c
@@ -71,7 +71,7 @@ static int v1_read_dqblk(struct dquot *dquot)
71 dquot->dq_dqb.dqb_ihardlimit == 0 && 71 dquot->dq_dqb.dqb_ihardlimit == 0 &&
72 dquot->dq_dqb.dqb_isoftlimit == 0) 72 dquot->dq_dqb.dqb_isoftlimit == 0)
73 set_bit(DQ_FAKE_B, &dquot->dq_flags); 73 set_bit(DQ_FAKE_B, &dquot->dq_flags);
74 dqstats.reads++; 74 dqstats_inc(DQST_READS);
75 75
76 return 0; 76 return 0;
77} 77}
@@ -104,7 +104,7 @@ static int v1_commit_dqblk(struct dquot *dquot)
104 ret = 0; 104 ret = 0;
105 105
106out: 106out:
107 dqstats.writes++; 107 dqstats_inc(DQST_WRITES);
108 108
109 return ret; 109 return ret;
110} 110}