diff options
author | Mark Fasheh <mfasheh@suse.com> | 2008-10-20 09:36:47 -0400 |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2009-01-05 11:40:24 -0500 |
commit | 171bf93ce11f4c9929fdce6ce63df8da2f3c4475 (patch) | |
tree | a6f29032a86bcc2b0e9971c85ec8fab22b3300ef /fs | |
parent | a90714c150e3ce677c57a9dac3ab1ec342c75a95 (diff) |
ocfs2: Periodic quota syncing
This patch creates a work queue for periodic syncing of locally cached quota
information to the global quota files. We constantly queue a delayed work
item, to get the periodic behavior.
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Acked-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ocfs2/quota.h | 5 | ||||
-rw-r--r-- | fs/ocfs2/quota_global.c | 85 | ||||
-rw-r--r-- | fs/ocfs2/quota_local.c | 4 | ||||
-rw-r--r-- | fs/ocfs2/super.c | 7 |
4 files changed, 101 insertions, 0 deletions
diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h index 1f1c86311b32..e2233d51507f 100644 --- a/fs/ocfs2/quota.h +++ b/fs/ocfs2/quota.h | |||
@@ -39,6 +39,7 @@ struct ocfs2_mem_dqinfo { | |||
39 | unsigned int dqi_chunks; /* Number of chunks in local quota file */ | 39 | unsigned int dqi_chunks; /* Number of chunks in local quota file */ |
40 | unsigned int dqi_blocks; /* Number of blocks allocated for local quota file */ | 40 | unsigned int dqi_blocks; /* Number of blocks allocated for local quota file */ |
41 | unsigned int dqi_syncms; /* How often should we sync with other nodes */ | 41 | unsigned int dqi_syncms; /* How often should we sync with other nodes */ |
42 | unsigned int dqi_syncjiff; /* Precomputed dqi_syncms in jiffies */ | ||
42 | struct list_head dqi_chunk; /* List of chunks */ | 43 | struct list_head dqi_chunk; /* List of chunks */ |
43 | struct inode *dqi_gqinode; /* Global quota file inode */ | 44 | struct inode *dqi_gqinode; /* Global quota file inode */ |
44 | struct ocfs2_lock_res dqi_gqlock; /* Lock protecting quota information structure */ | 45 | struct ocfs2_lock_res dqi_gqlock; /* Lock protecting quota information structure */ |
@@ -47,6 +48,7 @@ struct ocfs2_mem_dqinfo { | |||
47 | struct buffer_head *dqi_lqi_bh; /* Buffer head with local quota file inode */ | 48 | struct buffer_head *dqi_lqi_bh; /* Buffer head with local quota file inode */ |
48 | struct buffer_head *dqi_ibh; /* Buffer with information header */ | 49 | struct buffer_head *dqi_ibh; /* Buffer with information header */ |
49 | struct qtree_mem_dqinfo dqi_gi; /* Info about global file */ | 50 | struct qtree_mem_dqinfo dqi_gi; /* Info about global file */ |
51 | struct delayed_work dqi_sync_work; /* Work for syncing dquots */ | ||
50 | }; | 52 | }; |
51 | 53 | ||
52 | static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot) | 54 | static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot) |
@@ -90,4 +92,7 @@ struct buffer_head *ocfs2_read_quota_block(struct inode *inode, | |||
90 | extern struct dquot_operations ocfs2_quota_operations; | 92 | extern struct dquot_operations ocfs2_quota_operations; |
91 | extern struct quota_format_type ocfs2_quota_format; | 93 | extern struct quota_format_type ocfs2_quota_format; |
92 | 94 | ||
95 | int ocfs2_quota_setup(void); | ||
96 | void ocfs2_quota_shutdown(void); | ||
97 | |||
93 | #endif /* _OCFS2_QUOTA_H */ | 98 | #endif /* _OCFS2_QUOTA_H */ |
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c index af8340c45367..adf53508bdb8 100644 --- a/fs/ocfs2/quota_global.c +++ b/fs/ocfs2/quota_global.c | |||
@@ -1,10 +1,14 @@ | |||
1 | /* | 1 | /* |
2 | * Implementation of operations over global quota file | 2 | * Implementation of operations over global quota file |
3 | */ | 3 | */ |
4 | #include <linux/spinlock.h> | ||
4 | #include <linux/fs.h> | 5 | #include <linux/fs.h> |
5 | #include <linux/quota.h> | 6 | #include <linux/quota.h> |
6 | #include <linux/quotaops.h> | 7 | #include <linux/quotaops.h> |
7 | #include <linux/dqblk_qtree.h> | 8 | #include <linux/dqblk_qtree.h> |
9 | #include <linux/jiffies.h> | ||
10 | #include <linux/writeback.h> | ||
11 | #include <linux/workqueue.h> | ||
8 | 12 | ||
9 | #define MLOG_MASK_PREFIX ML_QUOTA | 13 | #define MLOG_MASK_PREFIX ML_QUOTA |
10 | #include <cluster/masklog.h> | 14 | #include <cluster/masklog.h> |
@@ -20,6 +24,10 @@ | |||
20 | #include "uptodate.h" | 24 | #include "uptodate.h" |
21 | #include "quota.h" | 25 | #include "quota.h" |
22 | 26 | ||
27 | static struct workqueue_struct *ocfs2_quota_wq = NULL; | ||
28 | |||
29 | static void qsync_work_fn(struct work_struct *work); | ||
30 | |||
23 | static void ocfs2_global_disk2memdqb(struct dquot *dquot, void *dp) | 31 | static void ocfs2_global_disk2memdqb(struct dquot *dquot, void *dp) |
24 | { | 32 | { |
25 | struct ocfs2_global_disk_dqblk *d = dp; | 33 | struct ocfs2_global_disk_dqblk *d = dp; |
@@ -313,6 +321,7 @@ int ocfs2_global_read_info(struct super_block *sb, int type) | |||
313 | info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); | 321 | info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); |
314 | info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace); | 322 | info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace); |
315 | oinfo->dqi_syncms = le32_to_cpu(dinfo.dqi_syncms); | 323 | oinfo->dqi_syncms = le32_to_cpu(dinfo.dqi_syncms); |
324 | oinfo->dqi_syncjiff = msecs_to_jiffies(oinfo->dqi_syncms); | ||
316 | oinfo->dqi_gi.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks); | 325 | oinfo->dqi_gi.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks); |
317 | oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk); | 326 | oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk); |
318 | oinfo->dqi_gi.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry); | 327 | oinfo->dqi_gi.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry); |
@@ -320,6 +329,10 @@ int ocfs2_global_read_info(struct super_block *sb, int type) | |||
320 | oinfo->dqi_gi.dqi_usable_bs = sb->s_blocksize - | 329 | oinfo->dqi_gi.dqi_usable_bs = sb->s_blocksize - |
321 | OCFS2_QBLK_RESERVED_SPACE; | 330 | OCFS2_QBLK_RESERVED_SPACE; |
322 | oinfo->dqi_gi.dqi_qtree_depth = qtree_depth(&oinfo->dqi_gi); | 331 | oinfo->dqi_gi.dqi_qtree_depth = qtree_depth(&oinfo->dqi_gi); |
332 | INIT_DELAYED_WORK(&oinfo->dqi_sync_work, qsync_work_fn); | ||
333 | queue_delayed_work(ocfs2_quota_wq, &oinfo->dqi_sync_work, | ||
334 | oinfo->dqi_syncjiff); | ||
335 | |||
323 | out_err: | 336 | out_err: |
324 | mlog_exit(status); | 337 | mlog_exit(status); |
325 | return status; | 338 | return status; |
@@ -520,6 +533,61 @@ out: | |||
520 | } | 533 | } |
521 | 534 | ||
522 | /* | 535 | /* |
536 | * Functions for periodic syncing of dquots with global file | ||
537 | */ | ||
538 | static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type) | ||
539 | { | ||
540 | handle_t *handle; | ||
541 | struct super_block *sb = dquot->dq_sb; | ||
542 | struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv; | ||
543 | struct ocfs2_super *osb = OCFS2_SB(sb); | ||
544 | int status = 0; | ||
545 | |||
546 | mlog_entry("id=%u qtype=%u type=%lu device=%s\n", dquot->dq_id, | ||
547 | dquot->dq_type, type, sb->s_id); | ||
548 | if (type != dquot->dq_type) | ||
549 | goto out; | ||
550 | status = ocfs2_lock_global_qf(oinfo, 1); | ||
551 | if (status < 0) | ||
552 | goto out; | ||
553 | |||
554 | handle = ocfs2_start_trans(osb, OCFS2_QSYNC_CREDITS); | ||
555 | if (IS_ERR(handle)) { | ||
556 | status = PTR_ERR(handle); | ||
557 | mlog_errno(status); | ||
558 | goto out_ilock; | ||
559 | } | ||
560 | mutex_lock(&sb_dqopt(sb)->dqio_mutex); | ||
561 | status = ocfs2_sync_dquot(dquot); | ||
562 | mutex_unlock(&sb_dqopt(sb)->dqio_mutex); | ||
563 | if (status < 0) | ||
564 | mlog_errno(status); | ||
565 | /* We have to write local structure as well... */ | ||
566 | dquot_mark_dquot_dirty(dquot); | ||
567 | status = dquot_commit(dquot); | ||
568 | if (status < 0) | ||
569 | mlog_errno(status); | ||
570 | ocfs2_commit_trans(osb, handle); | ||
571 | out_ilock: | ||
572 | ocfs2_unlock_global_qf(oinfo, 1); | ||
573 | out: | ||
574 | mlog_exit(status); | ||
575 | return status; | ||
576 | } | ||
577 | |||
578 | static void qsync_work_fn(struct work_struct *work) | ||
579 | { | ||
580 | struct ocfs2_mem_dqinfo *oinfo = container_of(work, | ||
581 | struct ocfs2_mem_dqinfo, | ||
582 | dqi_sync_work.work); | ||
583 | struct super_block *sb = oinfo->dqi_gqinode->i_sb; | ||
584 | |||
585 | dquot_scan_active(sb, ocfs2_sync_dquot_helper, oinfo->dqi_type); | ||
586 | queue_delayed_work(ocfs2_quota_wq, &oinfo->dqi_sync_work, | ||
587 | oinfo->dqi_syncjiff); | ||
588 | } | ||
589 | |||
590 | /* | ||
523 | * Wrappers for generic quota functions | 591 | * Wrappers for generic quota functions |
524 | */ | 592 | */ |
525 | 593 | ||
@@ -917,3 +985,20 @@ struct dquot_operations ocfs2_quota_operations = { | |||
917 | .alloc_dquot = ocfs2_alloc_dquot, | 985 | .alloc_dquot = ocfs2_alloc_dquot, |
918 | .destroy_dquot = ocfs2_destroy_dquot, | 986 | .destroy_dquot = ocfs2_destroy_dquot, |
919 | }; | 987 | }; |
988 | |||
989 | int ocfs2_quota_setup(void) | ||
990 | { | ||
991 | ocfs2_quota_wq = create_workqueue("o2quot"); | ||
992 | if (!ocfs2_quota_wq) | ||
993 | return -ENOMEM; | ||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | void ocfs2_quota_shutdown(void) | ||
998 | { | ||
999 | if (ocfs2_quota_wq) { | ||
1000 | flush_workqueue(ocfs2_quota_wq); | ||
1001 | destroy_workqueue(ocfs2_quota_wq); | ||
1002 | ocfs2_quota_wq = NULL; | ||
1003 | } | ||
1004 | } | ||
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index 55c3f2f98dcd..40e82b483136 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c | |||
@@ -368,6 +368,10 @@ static int ocfs2_local_free_info(struct super_block *sb, int type) | |||
368 | int mark_clean = 1, len; | 368 | int mark_clean = 1, len; |
369 | int status; | 369 | int status; |
370 | 370 | ||
371 | /* At this point we know there are no more dquots and thus | ||
372 | * even if there's some sync in the pdflush queue, it won't | ||
373 | * find any dquots and return without doing anything */ | ||
374 | cancel_delayed_work_sync(&oinfo->dqi_sync_work); | ||
371 | iput(oinfo->dqi_gqinode); | 375 | iput(oinfo->dqi_gqinode); |
372 | ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock); | 376 | ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock); |
373 | ocfs2_lock_res_free(&oinfo->dqi_gqlock); | 377 | ocfs2_lock_res_free(&oinfo->dqi_gqlock); |
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 7bb83e41581e..60f1d29421ad 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c | |||
@@ -1107,11 +1107,16 @@ static int __init ocfs2_init(void) | |||
1107 | mlog(ML_ERROR, "Unable to create ocfs2 debugfs root.\n"); | 1107 | mlog(ML_ERROR, "Unable to create ocfs2 debugfs root.\n"); |
1108 | } | 1108 | } |
1109 | 1109 | ||
1110 | status = ocfs2_quota_setup(); | ||
1111 | if (status) | ||
1112 | goto leave; | ||
1113 | |||
1110 | ocfs2_set_locking_protocol(); | 1114 | ocfs2_set_locking_protocol(); |
1111 | 1115 | ||
1112 | status = register_quota_format(&ocfs2_quota_format); | 1116 | status = register_quota_format(&ocfs2_quota_format); |
1113 | leave: | 1117 | leave: |
1114 | if (status < 0) { | 1118 | if (status < 0) { |
1119 | ocfs2_quota_shutdown(); | ||
1115 | ocfs2_free_mem_caches(); | 1120 | ocfs2_free_mem_caches(); |
1116 | exit_ocfs2_uptodate_cache(); | 1121 | exit_ocfs2_uptodate_cache(); |
1117 | } | 1122 | } |
@@ -1128,6 +1133,8 @@ static void __exit ocfs2_exit(void) | |||
1128 | { | 1133 | { |
1129 | mlog_entry_void(); | 1134 | mlog_entry_void(); |
1130 | 1135 | ||
1136 | ocfs2_quota_shutdown(); | ||
1137 | |||
1131 | if (ocfs2_wq) { | 1138 | if (ocfs2_wq) { |
1132 | flush_workqueue(ocfs2_wq); | 1139 | flush_workqueue(ocfs2_wq); |
1133 | destroy_workqueue(ocfs2_wq); | 1140 | destroy_workqueue(ocfs2_wq); |