diff options
author | Dmitry Monakhov <dmonakhov@openvz.org> | 2010-04-26 12:03:33 -0400 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2010-05-21 13:30:41 -0400 |
commit | dde9588853b1bde542eab247f8838c472806688f (patch) | |
tree | 9f9c68bf63120056517bbfce78b75e6820cc4c4b /fs | |
parent | da8d1ba22fa1fd0c0e541a43d75ebb062589b14b (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.c | 102 | ||||
-rw-r--r-- | fs/quota/quota_tree.c | 4 | ||||
-rw-r--r-- | fs/quota/quota_v1.c | 4 |
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 | ||
229 | struct dqstats dqstats; | 229 | struct dqstats dqstats; |
230 | EXPORT_SYMBOL(dqstats); | 230 | EXPORT_SYMBOL(dqstats); |
231 | #ifdef CONFIG_SMP | ||
232 | struct dqstats *dqstats_pcpu; | ||
233 | EXPORT_SYMBOL(dqstats_pcpu); | ||
234 | #endif | ||
231 | 235 | ||
232 | static qsize_t inode_get_rsv_space(struct inode *inode); | 236 | static qsize_t inode_get_rsv_space(struct inode *inode); |
233 | static void __dquot_initialize(struct inode *inode, int type); | 237 | static void __dquot_initialize(struct inode *inode, int type); |
@@ -275,7 +279,7 @@ static struct dquot *find_dquot(unsigned int hashent, struct super_block *sb, | |||
275 | static inline void put_dquot_last(struct dquot *dquot) | 279 | static 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 | ||
281 | static inline void remove_free_dquot(struct dquot *dquot) | 285 | static 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 | ||
289 | static inline void put_inuse(struct dquot *dquot) | 293 | static 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 | ||
297 | static inline void remove_inuse(struct dquot *dquot) | 301 | static 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 | ||
679 | static 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 | ||
692 | static struct shrinker dqcache_shrinker = { | 710 | static 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); | ||
721 | we_slept: | 736 | we_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 | |||
2495 | static 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 | |||
2479 | static ctl_table fs_dqstats_table[] = { | 2506 | static 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); |
636 | out: | 636 | out: |
637 | dqstats.reads++; | 637 | dqstats_inc(DQST_READS); |
638 | return ret; | 638 | return ret; |
639 | } | 639 | } |
640 | EXPORT_SYMBOL(qtree_read_dquot); | 640 | EXPORT_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 | ||
106 | out: | 106 | out: |
107 | dqstats.writes++; | 107 | dqstats_inc(DQST_WRITES); |
108 | 108 | ||
109 | return ret; | 109 | return ret; |
110 | } | 110 | } |