diff options
author | Steven Whitehouse <swhiteho@redhat.com> | 2013-11-04 05:15:08 -0500 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2013-11-04 06:17:49 -0500 |
commit | 2147dbfd059eb7fefcfd5934f74f25f0693d4a1f (patch) | |
tree | 8cc89a827bcc48f529698bef65eb61216562425a /fs/gfs2 | |
parent | 7d80823e1d83e35977d77ae201bf63af3317ad0a (diff) |
GFS2: Use generic list_lru for quota
By using the generic list_lru code, we can now separate the
per sb quota list locking from the lru locking. The lru
lock is made into the inner-most lock.
As a result of this new lock order, we may occasionally see
items on the per-sb quota list which are "dead" so that the
two places where we traverse that list are updated to take
account of that.
As a result of this patch, the gfs2 quota shrinker is now
NUMA zone aware, and we are also laying the foundations for
further improvments in due course.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Signed-off-by: Abhijith Das <adas@redhat.com>
Tested-by: Abhijith Das <adas@redhat.com>
Cc: Dave Chinner <dchinner@redhat.com>
Diffstat (limited to 'fs/gfs2')
-rw-r--r-- | fs/gfs2/incore.h | 5 | ||||
-rw-r--r-- | fs/gfs2/main.c | 19 | ||||
-rw-r--r-- | fs/gfs2/quota.c | 118 | ||||
-rw-r--r-- | fs/gfs2/quota.h | 9 |
4 files changed, 85 insertions, 66 deletions
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 9d778044cc6e..ba1ea67f4eeb 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h | |||
@@ -420,11 +420,10 @@ enum { | |||
420 | 420 | ||
421 | struct gfs2_quota_data { | 421 | struct gfs2_quota_data { |
422 | struct list_head qd_list; | 422 | struct list_head qd_list; |
423 | struct list_head qd_reclaim; | 423 | struct kqid qd_id; |
424 | |||
425 | struct lockref qd_lockref; | 424 | struct lockref qd_lockref; |
425 | struct list_head qd_lru; | ||
426 | 426 | ||
427 | struct kqid qd_id; | ||
428 | unsigned long qd_flags; /* QDF_... */ | 427 | unsigned long qd_flags; /* QDF_... */ |
429 | 428 | ||
430 | s64 qd_change; | 429 | s64 qd_change; |
diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index 351586e24e30..0650db2541ef 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c | |||
@@ -31,12 +31,6 @@ | |||
31 | 31 | ||
32 | struct workqueue_struct *gfs2_control_wq; | 32 | struct workqueue_struct *gfs2_control_wq; |
33 | 33 | ||
34 | static struct shrinker qd_shrinker = { | ||
35 | .count_objects = gfs2_qd_shrink_count, | ||
36 | .scan_objects = gfs2_qd_shrink_scan, | ||
37 | .seeks = DEFAULT_SEEKS, | ||
38 | }; | ||
39 | |||
40 | static void gfs2_init_inode_once(void *foo) | 34 | static void gfs2_init_inode_once(void *foo) |
41 | { | 35 | { |
42 | struct gfs2_inode *ip = foo; | 36 | struct gfs2_inode *ip = foo; |
@@ -87,6 +81,10 @@ static int __init init_gfs2_fs(void) | |||
87 | if (error) | 81 | if (error) |
88 | return error; | 82 | return error; |
89 | 83 | ||
84 | error = list_lru_init(&gfs2_qd_lru); | ||
85 | if (error) | ||
86 | goto fail_lru; | ||
87 | |||
90 | error = gfs2_glock_init(); | 88 | error = gfs2_glock_init(); |
91 | if (error) | 89 | if (error) |
92 | goto fail; | 90 | goto fail; |
@@ -139,7 +137,7 @@ static int __init init_gfs2_fs(void) | |||
139 | if (!gfs2_rsrv_cachep) | 137 | if (!gfs2_rsrv_cachep) |
140 | goto fail; | 138 | goto fail; |
141 | 139 | ||
142 | register_shrinker(&qd_shrinker); | 140 | register_shrinker(&gfs2_qd_shrinker); |
143 | 141 | ||
144 | error = register_filesystem(&gfs2_fs_type); | 142 | error = register_filesystem(&gfs2_fs_type); |
145 | if (error) | 143 | if (error) |
@@ -179,7 +177,9 @@ fail_wq: | |||
179 | fail_unregister: | 177 | fail_unregister: |
180 | unregister_filesystem(&gfs2_fs_type); | 178 | unregister_filesystem(&gfs2_fs_type); |
181 | fail: | 179 | fail: |
182 | unregister_shrinker(&qd_shrinker); | 180 | list_lru_destroy(&gfs2_qd_lru); |
181 | fail_lru: | ||
182 | unregister_shrinker(&gfs2_qd_shrinker); | ||
183 | gfs2_glock_exit(); | 183 | gfs2_glock_exit(); |
184 | 184 | ||
185 | if (gfs2_rsrv_cachep) | 185 | if (gfs2_rsrv_cachep) |
@@ -214,13 +214,14 @@ fail: | |||
214 | 214 | ||
215 | static void __exit exit_gfs2_fs(void) | 215 | static void __exit exit_gfs2_fs(void) |
216 | { | 216 | { |
217 | unregister_shrinker(&qd_shrinker); | 217 | unregister_shrinker(&gfs2_qd_shrinker); |
218 | gfs2_glock_exit(); | 218 | gfs2_glock_exit(); |
219 | gfs2_unregister_debugfs(); | 219 | gfs2_unregister_debugfs(); |
220 | unregister_filesystem(&gfs2_fs_type); | 220 | unregister_filesystem(&gfs2_fs_type); |
221 | unregister_filesystem(&gfs2meta_fs_type); | 221 | unregister_filesystem(&gfs2meta_fs_type); |
222 | destroy_workqueue(gfs_recovery_wq); | 222 | destroy_workqueue(gfs_recovery_wq); |
223 | destroy_workqueue(gfs2_control_wq); | 223 | destroy_workqueue(gfs2_control_wq); |
224 | list_lru_destroy(&gfs2_qd_lru); | ||
224 | 225 | ||
225 | rcu_barrier(); | 226 | rcu_barrier(); |
226 | 227 | ||
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 466516ac5e57..453b50eaddec 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c | |||
@@ -51,6 +51,7 @@ | |||
51 | #include <linux/quota.h> | 51 | #include <linux/quota.h> |
52 | #include <linux/dqblk_xfs.h> | 52 | #include <linux/dqblk_xfs.h> |
53 | #include <linux/lockref.h> | 53 | #include <linux/lockref.h> |
54 | #include <linux/list_lru.h> | ||
54 | 55 | ||
55 | #include "gfs2.h" | 56 | #include "gfs2.h" |
56 | #include "incore.h" | 57 | #include "incore.h" |
@@ -72,29 +73,25 @@ struct gfs2_quota_change_host { | |||
72 | struct kqid qc_id; | 73 | struct kqid qc_id; |
73 | }; | 74 | }; |
74 | 75 | ||
75 | static LIST_HEAD(qd_lru_list); | 76 | /* Lock order: qd_lock -> qd->lockref.lock -> lru lock */ |
76 | static atomic_t qd_lru_count = ATOMIC_INIT(0); | ||
77 | static DEFINE_SPINLOCK(qd_lock); | 77 | static DEFINE_SPINLOCK(qd_lock); |
78 | struct list_lru gfs2_qd_lru; | ||
78 | 79 | ||
79 | unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink, | 80 | static void gfs2_qd_dispose(struct list_head *list) |
80 | struct shrink_control *sc) | ||
81 | { | 81 | { |
82 | struct gfs2_quota_data *qd; | 82 | struct gfs2_quota_data *qd; |
83 | struct gfs2_sbd *sdp; | 83 | struct gfs2_sbd *sdp; |
84 | int nr_to_scan = sc->nr_to_scan; | ||
85 | long freed = 0; | ||
86 | 84 | ||
87 | if (!(sc->gfp_mask & __GFP_FS)) | 85 | while (!list_empty(list)) { |
88 | return SHRINK_STOP; | 86 | qd = list_entry(list->next, struct gfs2_quota_data, qd_lru); |
89 | |||
90 | spin_lock(&qd_lock); | ||
91 | while (nr_to_scan && !list_empty(&qd_lru_list)) { | ||
92 | qd = list_entry(qd_lru_list.next, | ||
93 | struct gfs2_quota_data, qd_reclaim); | ||
94 | sdp = qd->qd_gl->gl_sbd; | 87 | sdp = qd->qd_gl->gl_sbd; |
95 | 88 | ||
89 | list_del(&qd->qd_lru); | ||
90 | |||
96 | /* Free from the filesystem-specific list */ | 91 | /* Free from the filesystem-specific list */ |
92 | spin_lock(&qd_lock); | ||
97 | list_del(&qd->qd_list); | 93 | list_del(&qd->qd_list); |
94 | spin_unlock(&qd_lock); | ||
98 | 95 | ||
99 | gfs2_assert_warn(sdp, !qd->qd_change); | 96 | gfs2_assert_warn(sdp, !qd->qd_change); |
100 | gfs2_assert_warn(sdp, !qd->qd_slot_count); | 97 | gfs2_assert_warn(sdp, !qd->qd_slot_count); |
@@ -104,24 +101,59 @@ unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink, | |||
104 | atomic_dec(&sdp->sd_quota_count); | 101 | atomic_dec(&sdp->sd_quota_count); |
105 | 102 | ||
106 | /* Delete it from the common reclaim list */ | 103 | /* Delete it from the common reclaim list */ |
107 | list_del_init(&qd->qd_reclaim); | ||
108 | atomic_dec(&qd_lru_count); | ||
109 | spin_unlock(&qd_lock); | ||
110 | kmem_cache_free(gfs2_quotad_cachep, qd); | 104 | kmem_cache_free(gfs2_quotad_cachep, qd); |
111 | spin_lock(&qd_lock); | ||
112 | nr_to_scan--; | ||
113 | freed++; | ||
114 | } | 105 | } |
115 | spin_unlock(&qd_lock); | 106 | } |
107 | |||
108 | |||
109 | static enum lru_status gfs2_qd_isolate(struct list_head *item, spinlock_t *lock, void *arg) | ||
110 | { | ||
111 | struct list_head *dispose = arg; | ||
112 | struct gfs2_quota_data *qd = list_entry(item, struct gfs2_quota_data, qd_lru); | ||
113 | |||
114 | if (!spin_trylock(&qd->qd_lockref.lock)) | ||
115 | return LRU_SKIP; | ||
116 | |||
117 | if (qd->qd_lockref.count == 0) { | ||
118 | lockref_mark_dead(&qd->qd_lockref); | ||
119 | list_move(&qd->qd_lru, dispose); | ||
120 | } | ||
121 | |||
122 | spin_unlock(&qd->qd_lockref.lock); | ||
123 | return LRU_REMOVED; | ||
124 | } | ||
125 | |||
126 | static unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink, | ||
127 | struct shrink_control *sc) | ||
128 | { | ||
129 | LIST_HEAD(dispose); | ||
130 | unsigned long freed; | ||
131 | |||
132 | if (!(sc->gfp_mask & __GFP_FS)) | ||
133 | return SHRINK_STOP; | ||
134 | |||
135 | freed = list_lru_walk_node(&gfs2_qd_lru, sc->nid, gfs2_qd_isolate, | ||
136 | &dispose, &sc->nr_to_scan); | ||
137 | |||
138 | gfs2_qd_dispose(&dispose); | ||
139 | |||
116 | return freed; | 140 | return freed; |
117 | } | 141 | } |
118 | 142 | ||
119 | unsigned long gfs2_qd_shrink_count(struct shrinker *shrink, | 143 | static unsigned long gfs2_qd_shrink_count(struct shrinker *shrink, |
120 | struct shrink_control *sc) | 144 | struct shrink_control *sc) |
121 | { | 145 | { |
122 | return vfs_pressure_ratio(atomic_read(&qd_lru_count)); | 146 | return vfs_pressure_ratio(list_lru_count_node(&gfs2_qd_lru, sc->nid)); |
123 | } | 147 | } |
124 | 148 | ||
149 | struct shrinker gfs2_qd_shrinker = { | ||
150 | .count_objects = gfs2_qd_shrink_count, | ||
151 | .scan_objects = gfs2_qd_shrink_scan, | ||
152 | .seeks = DEFAULT_SEEKS, | ||
153 | .flags = SHRINKER_NUMA_AWARE, | ||
154 | }; | ||
155 | |||
156 | |||
125 | static u64 qd2index(struct gfs2_quota_data *qd) | 157 | static u64 qd2index(struct gfs2_quota_data *qd) |
126 | { | 158 | { |
127 | struct kqid qid = qd->qd_id; | 159 | struct kqid qid = qd->qd_id; |
@@ -153,7 +185,7 @@ static int qd_alloc(struct gfs2_sbd *sdp, struct kqid qid, | |||
153 | spin_lock_init(&qd->qd_lockref.lock); | 185 | spin_lock_init(&qd->qd_lockref.lock); |
154 | qd->qd_id = qid; | 186 | qd->qd_id = qid; |
155 | qd->qd_slot = -1; | 187 | qd->qd_slot = -1; |
156 | INIT_LIST_HEAD(&qd->qd_reclaim); | 188 | INIT_LIST_HEAD(&qd->qd_lru); |
157 | 189 | ||
158 | error = gfs2_glock_get(sdp, qd2index(qd), | 190 | error = gfs2_glock_get(sdp, qd2index(qd), |
159 | &gfs2_quota_glops, CREATE, &qd->qd_gl); | 191 | &gfs2_quota_glops, CREATE, &qd->qd_gl); |
@@ -181,13 +213,9 @@ static int qd_get(struct gfs2_sbd *sdp, struct kqid qid, | |||
181 | found = 0; | 213 | found = 0; |
182 | spin_lock(&qd_lock); | 214 | spin_lock(&qd_lock); |
183 | list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { | 215 | list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { |
184 | if (qid_eq(qd->qd_id, qid)) { | 216 | if (qid_eq(qd->qd_id, qid) && |
185 | lockref_get(&qd->qd_lockref); | 217 | lockref_get_not_dead(&qd->qd_lockref)) { |
186 | if (!list_empty(&qd->qd_reclaim)) { | 218 | list_lru_del(&gfs2_qd_lru, &qd->qd_lru); |
187 | /* Remove it from reclaim list */ | ||
188 | list_del_init(&qd->qd_reclaim); | ||
189 | atomic_dec(&qd_lru_count); | ||
190 | } | ||
191 | found = 1; | 219 | found = 1; |
192 | break; | 220 | break; |
193 | } | 221 | } |
@@ -229,18 +257,13 @@ static void qd_hold(struct gfs2_quota_data *qd) | |||
229 | 257 | ||
230 | static void qd_put(struct gfs2_quota_data *qd) | 258 | static void qd_put(struct gfs2_quota_data *qd) |
231 | { | 259 | { |
232 | spin_lock(&qd_lock); | 260 | if (lockref_put_or_lock(&qd->qd_lockref)) |
233 | 261 | return; | |
234 | if (!lockref_put_or_lock(&qd->qd_lockref)) { | ||
235 | 262 | ||
236 | /* Add to the reclaim list */ | 263 | qd->qd_lockref.count = 0; |
237 | list_add_tail(&qd->qd_reclaim, &qd_lru_list); | 264 | list_lru_add(&gfs2_qd_lru, &qd->qd_lru); |
238 | atomic_inc(&qd_lru_count); | 265 | spin_unlock(&qd->qd_lockref.lock); |
239 | 266 | ||
240 | spin_unlock(&qd->qd_lockref.lock); | ||
241 | } | ||
242 | |||
243 | spin_unlock(&qd_lock); | ||
244 | } | 267 | } |
245 | 268 | ||
246 | static int slot_get(struct gfs2_quota_data *qd) | 269 | static int slot_get(struct gfs2_quota_data *qd) |
@@ -398,11 +421,11 @@ static int qd_check_sync(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd, | |||
398 | (sync_gen && (qd->qd_sync_gen >= *sync_gen))) | 421 | (sync_gen && (qd->qd_sync_gen >= *sync_gen))) |
399 | return 0; | 422 | return 0; |
400 | 423 | ||
401 | list_move_tail(&qd->qd_list, &sdp->sd_quota_list); | 424 | if (!lockref_get_not_dead(&qd->qd_lockref)) |
425 | return 0; | ||
402 | 426 | ||
427 | list_move_tail(&qd->qd_list, &sdp->sd_quota_list); | ||
403 | set_bit(QDF_LOCKED, &qd->qd_flags); | 428 | set_bit(QDF_LOCKED, &qd->qd_flags); |
404 | gfs2_assert_warn(sdp, !__lockref_is_dead(&qd->qd_lockref)); | ||
405 | lockref_get(&qd->qd_lockref); | ||
406 | qd->qd_change_sync = qd->qd_change; | 429 | qd->qd_change_sync = qd->qd_change; |
407 | gfs2_assert_warn(sdp, qd->qd_slot_count); | 430 | gfs2_assert_warn(sdp, qd->qd_slot_count); |
408 | qd->qd_slot_count++; | 431 | qd->qd_slot_count++; |
@@ -1329,10 +1352,7 @@ void gfs2_quota_cleanup(struct gfs2_sbd *sdp) | |||
1329 | 1352 | ||
1330 | list_del(&qd->qd_list); | 1353 | list_del(&qd->qd_list); |
1331 | /* Also remove if this qd exists in the reclaim list */ | 1354 | /* Also remove if this qd exists in the reclaim list */ |
1332 | if (!list_empty(&qd->qd_reclaim)) { | 1355 | list_lru_del(&gfs2_qd_lru, &qd->qd_lru); |
1333 | list_del_init(&qd->qd_reclaim); | ||
1334 | atomic_dec(&qd_lru_count); | ||
1335 | } | ||
1336 | atomic_dec(&sdp->sd_quota_count); | 1356 | atomic_dec(&sdp->sd_quota_count); |
1337 | spin_unlock(&qd_lock); | 1357 | spin_unlock(&qd_lock); |
1338 | 1358 | ||
@@ -1487,7 +1507,7 @@ static int gfs2_quota_get_xstate(struct super_block *sb, | |||
1487 | } | 1507 | } |
1488 | fqs->qs_uquota.qfs_nextents = 1; /* unsupported */ | 1508 | fqs->qs_uquota.qfs_nextents = 1; /* unsupported */ |
1489 | fqs->qs_gquota = fqs->qs_uquota; /* its the same inode in both cases */ | 1509 | fqs->qs_gquota = fqs->qs_uquota; /* its the same inode in both cases */ |
1490 | fqs->qs_incoredqs = atomic_read(&qd_lru_count); | 1510 | fqs->qs_incoredqs = list_lru_count(&gfs2_qd_lru); |
1491 | return 0; | 1511 | return 0; |
1492 | } | 1512 | } |
1493 | 1513 | ||
diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index 0f64d9deb1b0..96e4f34a03b0 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h | |||
@@ -10,9 +10,10 @@ | |||
10 | #ifndef __QUOTA_DOT_H__ | 10 | #ifndef __QUOTA_DOT_H__ |
11 | #define __QUOTA_DOT_H__ | 11 | #define __QUOTA_DOT_H__ |
12 | 12 | ||
13 | #include <linux/list_lru.h> | ||
14 | |||
13 | struct gfs2_inode; | 15 | struct gfs2_inode; |
14 | struct gfs2_sbd; | 16 | struct gfs2_sbd; |
15 | struct shrink_control; | ||
16 | 17 | ||
17 | #define NO_UID_QUOTA_CHANGE INVALID_UID | 18 | #define NO_UID_QUOTA_CHANGE INVALID_UID |
18 | #define NO_GID_QUOTA_CHANGE INVALID_GID | 19 | #define NO_GID_QUOTA_CHANGE INVALID_GID |
@@ -53,10 +54,8 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip) | |||
53 | return ret; | 54 | return ret; |
54 | } | 55 | } |
55 | 56 | ||
56 | extern unsigned long gfs2_qd_shrink_count(struct shrinker *shrink, | ||
57 | struct shrink_control *sc); | ||
58 | extern unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink, | ||
59 | struct shrink_control *sc); | ||
60 | extern const struct quotactl_ops gfs2_quotactl_ops; | 57 | extern const struct quotactl_ops gfs2_quotactl_ops; |
58 | extern struct shrinker gfs2_qd_shrinker; | ||
59 | extern struct list_lru gfs2_qd_lru; | ||
61 | 60 | ||
62 | #endif /* __QUOTA_DOT_H__ */ | 61 | #endif /* __QUOTA_DOT_H__ */ |