aboutsummaryrefslogtreecommitdiffstats
path: root/fs/gfs2
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2013-11-04 05:15:08 -0500
committerSteven Whitehouse <swhiteho@redhat.com>2013-11-04 06:17:49 -0500
commit2147dbfd059eb7fefcfd5934f74f25f0693d4a1f (patch)
tree8cc89a827bcc48f529698bef65eb61216562425a /fs/gfs2
parent7d80823e1d83e35977d77ae201bf63af3317ad0a (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.h5
-rw-r--r--fs/gfs2/main.c19
-rw-r--r--fs/gfs2/quota.c118
-rw-r--r--fs/gfs2/quota.h9
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
421struct gfs2_quota_data { 421struct 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
32struct workqueue_struct *gfs2_control_wq; 32struct workqueue_struct *gfs2_control_wq;
33 33
34static struct shrinker qd_shrinker = {
35 .count_objects = gfs2_qd_shrink_count,
36 .scan_objects = gfs2_qd_shrink_scan,
37 .seeks = DEFAULT_SEEKS,
38};
39
40static void gfs2_init_inode_once(void *foo) 34static 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:
179fail_unregister: 177fail_unregister:
180 unregister_filesystem(&gfs2_fs_type); 178 unregister_filesystem(&gfs2_fs_type);
181fail: 179fail:
182 unregister_shrinker(&qd_shrinker); 180 list_lru_destroy(&gfs2_qd_lru);
181fail_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
215static void __exit exit_gfs2_fs(void) 215static 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
75static LIST_HEAD(qd_lru_list); 76/* Lock order: qd_lock -> qd->lockref.lock -> lru lock */
76static atomic_t qd_lru_count = ATOMIC_INIT(0);
77static DEFINE_SPINLOCK(qd_lock); 77static DEFINE_SPINLOCK(qd_lock);
78struct list_lru gfs2_qd_lru;
78 79
79unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink, 80static 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
109static 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
126static 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
119unsigned long gfs2_qd_shrink_count(struct shrinker *shrink, 143static 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
149struct 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
125static u64 qd2index(struct gfs2_quota_data *qd) 157static 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
230static void qd_put(struct gfs2_quota_data *qd) 258static 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
246static int slot_get(struct gfs2_quota_data *qd) 269static 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
13struct gfs2_inode; 15struct gfs2_inode;
14struct gfs2_sbd; 16struct gfs2_sbd;
15struct 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
56extern unsigned long gfs2_qd_shrink_count(struct shrinker *shrink,
57 struct shrink_control *sc);
58extern unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink,
59 struct shrink_control *sc);
60extern const struct quotactl_ops gfs2_quotactl_ops; 57extern const struct quotactl_ops gfs2_quotactl_ops;
58extern struct shrinker gfs2_qd_shrinker;
59extern struct list_lru gfs2_qd_lru;
61 60
62#endif /* __QUOTA_DOT_H__ */ 61#endif /* __QUOTA_DOT_H__ */