aboutsummaryrefslogtreecommitdiffstats
path: root/fs/gfs2
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2013-12-12 05:47:59 -0500
committerSteven Whitehouse <swhiteho@redhat.com>2014-01-14 14:27:56 -0500
commitc754fbbb1b6bf462c6ddba48b19f20adf2335cac (patch)
tree4d716f692b1366d77067e9fea74f6a5a9dbe61b8 /fs/gfs2
parent086352f1aa8d717eaf565074d6634c7bdd26aca0 (diff)
GFS2: Use RCU/hlist_bl based hash for quotas
Prior to this patch, GFS2 kept all the quotas for each super block in a single linked list. This is rather slow when there are large numbers of quotas. This patch introduces a hlist_bl based hash table, similar to the one used for glocks. The initial look up of the quota is now lockless in the case where it is already cached, although we still have to take the per quota spinlock in order to bump the ref count. Either way though, this is a big improvement on what was there before. The qd_lock and the per super block list is preserved, for the time being. However it is intended that since this is no longer used for its original role, it should be possible to shrink the number of items on that list in due course and remove the requirement to take qd_lock in qd_get. Signed-off-by: Steven Whitehouse <swhiteho@redhat.com> Cc: Abhijith Das <adas@redhat.com> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'fs/gfs2')
-rw-r--r--fs/gfs2/incore.h4
-rw-r--r--fs/gfs2/main.c1
-rw-r--r--fs/gfs2/quota.c168
-rw-r--r--fs/gfs2/quota.h1
4 files changed, 126 insertions, 48 deletions
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index a99f60c98845..59d99ec9d875 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -428,10 +428,13 @@ enum {
428}; 428};
429 429
430struct gfs2_quota_data { 430struct gfs2_quota_data {
431 struct hlist_bl_node qd_hlist;
431 struct list_head qd_list; 432 struct list_head qd_list;
432 struct kqid qd_id; 433 struct kqid qd_id;
434 struct gfs2_sbd *qd_sbd;
433 struct lockref qd_lockref; 435 struct lockref qd_lockref;
434 struct list_head qd_lru; 436 struct list_head qd_lru;
437 unsigned qd_hash;
435 438
436 unsigned long qd_flags; /* QDF_... */ 439 unsigned long qd_flags; /* QDF_... */
437 440
@@ -450,6 +453,7 @@ struct gfs2_quota_data {
450 453
451 u64 qd_sync_gen; 454 u64 qd_sync_gen;
452 unsigned long qd_last_warn; 455 unsigned long qd_last_warn;
456 struct rcu_head qd_rcu;
453}; 457};
454 458
455struct gfs2_trans { 459struct gfs2_trans {
diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c
index 0650db2541ef..c272e73063de 100644
--- a/fs/gfs2/main.c
+++ b/fs/gfs2/main.c
@@ -76,6 +76,7 @@ static int __init init_gfs2_fs(void)
76 76
77 gfs2_str2qstr(&gfs2_qdot, "."); 77 gfs2_str2qstr(&gfs2_qdot, ".");
78 gfs2_str2qstr(&gfs2_qdotdot, ".."); 78 gfs2_str2qstr(&gfs2_qdotdot, "..");
79 gfs2_quota_hash_init();
79 80
80 error = gfs2_sys_init(); 81 error = gfs2_sys_init();
81 if (error) 82 if (error)
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index 1b6b3675ee1d..a1df01d381a8 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -52,6 +52,10 @@
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#include <linux/list_lru.h>
55#include <linux/rcupdate.h>
56#include <linux/rculist_bl.h>
57#include <linux/bit_spinlock.h>
58#include <linux/jhash.h>
55 59
56#include "gfs2.h" 60#include "gfs2.h"
57#include "incore.h" 61#include "incore.h"
@@ -67,10 +71,43 @@
67#include "inode.h" 71#include "inode.h"
68#include "util.h" 72#include "util.h"
69 73
70/* Lock order: qd_lock -> qd->lockref.lock -> lru lock */ 74#define GFS2_QD_HASH_SHIFT 12
75#define GFS2_QD_HASH_SIZE (1 << GFS2_QD_HASH_SHIFT)
76#define GFS2_QD_HASH_MASK (GFS2_QD_HASH_SIZE - 1)
77
78/* Lock order: qd_lock -> bucket lock -> qd->lockref.lock -> lru lock */
71static DEFINE_SPINLOCK(qd_lock); 79static DEFINE_SPINLOCK(qd_lock);
72struct list_lru gfs2_qd_lru; 80struct list_lru gfs2_qd_lru;
73 81
82static struct hlist_bl_head qd_hash_table[GFS2_QD_HASH_SIZE];
83
84static unsigned int gfs2_qd_hash(const struct gfs2_sbd *sdp,
85 const struct kqid qid)
86{
87 unsigned int h;
88
89 h = jhash(&sdp, sizeof(struct gfs2_sbd *), 0);
90 h = jhash(&qid, sizeof(struct kqid), h);
91
92 return h & GFS2_QD_HASH_MASK;
93}
94
95static inline void spin_lock_bucket(unsigned int hash)
96{
97 hlist_bl_lock(&qd_hash_table[hash]);
98}
99
100static inline void spin_unlock_bucket(unsigned int hash)
101{
102 hlist_bl_unlock(&qd_hash_table[hash]);
103}
104
105static void gfs2_qd_dealloc(struct rcu_head *rcu)
106{
107 struct gfs2_quota_data *qd = container_of(rcu, struct gfs2_quota_data, qd_rcu);
108 kmem_cache_free(gfs2_quotad_cachep, qd);
109}
110
74static void gfs2_qd_dispose(struct list_head *list) 111static void gfs2_qd_dispose(struct list_head *list)
75{ 112{
76 struct gfs2_quota_data *qd; 113 struct gfs2_quota_data *qd;
@@ -87,6 +124,10 @@ static void gfs2_qd_dispose(struct list_head *list)
87 list_del(&qd->qd_list); 124 list_del(&qd->qd_list);
88 spin_unlock(&qd_lock); 125 spin_unlock(&qd_lock);
89 126
127 spin_lock_bucket(qd->qd_hash);
128 hlist_bl_del_rcu(&qd->qd_hlist);
129 spin_unlock_bucket(qd->qd_hash);
130
90 gfs2_assert_warn(sdp, !qd->qd_change); 131 gfs2_assert_warn(sdp, !qd->qd_change);
91 gfs2_assert_warn(sdp, !qd->qd_slot_count); 132 gfs2_assert_warn(sdp, !qd->qd_slot_count);
92 gfs2_assert_warn(sdp, !qd->qd_bh_count); 133 gfs2_assert_warn(sdp, !qd->qd_bh_count);
@@ -95,7 +136,7 @@ static void gfs2_qd_dispose(struct list_head *list)
95 atomic_dec(&sdp->sd_quota_count); 136 atomic_dec(&sdp->sd_quota_count);
96 137
97 /* Delete it from the common reclaim list */ 138 /* Delete it from the common reclaim list */
98 kmem_cache_free(gfs2_quotad_cachep, qd); 139 call_rcu(&qd->qd_rcu, gfs2_qd_dealloc);
99 } 140 }
100} 141}
101 142
@@ -165,83 +206,95 @@ static u64 qd2offset(struct gfs2_quota_data *qd)
165 return offset; 206 return offset;
166} 207}
167 208
168static int qd_alloc(struct gfs2_sbd *sdp, struct kqid qid, 209static struct gfs2_quota_data *qd_alloc(unsigned hash, struct gfs2_sbd *sdp, struct kqid qid)
169 struct gfs2_quota_data **qdp)
170{ 210{
171 struct gfs2_quota_data *qd; 211 struct gfs2_quota_data *qd;
172 int error; 212 int error;
173 213
174 qd = kmem_cache_zalloc(gfs2_quotad_cachep, GFP_NOFS); 214 qd = kmem_cache_zalloc(gfs2_quotad_cachep, GFP_NOFS);
175 if (!qd) 215 if (!qd)
176 return -ENOMEM; 216 return NULL;
177 217
218 qd->qd_sbd = sdp;
178 qd->qd_lockref.count = 1; 219 qd->qd_lockref.count = 1;
179 spin_lock_init(&qd->qd_lockref.lock); 220 spin_lock_init(&qd->qd_lockref.lock);
180 qd->qd_id = qid; 221 qd->qd_id = qid;
181 qd->qd_slot = -1; 222 qd->qd_slot = -1;
182 INIT_LIST_HEAD(&qd->qd_lru); 223 INIT_LIST_HEAD(&qd->qd_lru);
224 qd->qd_hash = hash;
183 225
184 error = gfs2_glock_get(sdp, qd2index(qd), 226 error = gfs2_glock_get(sdp, qd2index(qd),
185 &gfs2_quota_glops, CREATE, &qd->qd_gl); 227 &gfs2_quota_glops, CREATE, &qd->qd_gl);
186 if (error) 228 if (error)
187 goto fail; 229 goto fail;
188 230
189 *qdp = qd; 231 return qd;
190
191 return 0;
192 232
193fail: 233fail:
194 kmem_cache_free(gfs2_quotad_cachep, qd); 234 kmem_cache_free(gfs2_quotad_cachep, qd);
195 return error; 235 return NULL;
196} 236}
197 237
198static int qd_get(struct gfs2_sbd *sdp, struct kqid qid, 238static struct gfs2_quota_data *gfs2_qd_search_bucket(unsigned int hash,
199 struct gfs2_quota_data **qdp) 239 const struct gfs2_sbd *sdp,
240 struct kqid qid)
200{ 241{
201 struct gfs2_quota_data *qd = NULL, *new_qd = NULL; 242 struct gfs2_quota_data *qd;
202 int error, found; 243 struct hlist_bl_node *h;
203
204 *qdp = NULL;
205 244
206 for (;;) { 245 hlist_bl_for_each_entry_rcu(qd, h, &qd_hash_table[hash], qd_hlist) {
207 found = 0; 246 if (!qid_eq(qd->qd_id, qid))
208 spin_lock(&qd_lock); 247 continue;
209 list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { 248 if (qd->qd_sbd != sdp)
210 if (qid_eq(qd->qd_id, qid) && 249 continue;
211 lockref_get_not_dead(&qd->qd_lockref)) { 250 if (lockref_get_not_dead(&qd->qd_lockref)) {
212 list_lru_del(&gfs2_qd_lru, &qd->qd_lru); 251 list_lru_del(&gfs2_qd_lru, &qd->qd_lru);
213 found = 1; 252 return qd;
214 break;
215 }
216 } 253 }
254 }
217 255
218 if (!found) 256 return NULL;
219 qd = NULL; 257}
220 258
221 if (!qd && new_qd) {
222 qd = new_qd;
223 list_add(&qd->qd_list, &sdp->sd_quota_list);
224 atomic_inc(&sdp->sd_quota_count);
225 new_qd = NULL;
226 }
227 259
228 spin_unlock(&qd_lock); 260static int qd_get(struct gfs2_sbd *sdp, struct kqid qid,
261 struct gfs2_quota_data **qdp)
262{
263 struct gfs2_quota_data *qd, *new_qd;
264 unsigned int hash = gfs2_qd_hash(sdp, qid);
229 265
230 if (qd) { 266 rcu_read_lock();
231 if (new_qd) { 267 *qdp = qd = gfs2_qd_search_bucket(hash, sdp, qid);
232 gfs2_glock_put(new_qd->qd_gl); 268 rcu_read_unlock();
233 kmem_cache_free(gfs2_quotad_cachep, new_qd);
234 }
235 *qdp = qd;
236 return 0;
237 }
238 269
239 error = qd_alloc(sdp, qid, &new_qd); 270 if (qd)
240 if (error) 271 return 0;
241 return error; 272
273 new_qd = qd_alloc(hash, sdp, qid);
274 if (!new_qd)
275 return -ENOMEM;
276
277 spin_lock(&qd_lock);
278 spin_lock_bucket(hash);
279 *qdp = qd = gfs2_qd_search_bucket(hash, sdp, qid);
280 if (qd == NULL) {
281 *qdp = new_qd;
282 list_add(&new_qd->qd_list, &sdp->sd_quota_list);
283 hlist_bl_add_head_rcu(&new_qd->qd_hlist, &qd_hash_table[hash]);
284 atomic_inc(&sdp->sd_quota_count);
285 }
286 spin_unlock_bucket(hash);
287 spin_unlock(&qd_lock);
288
289 if (qd) {
290 gfs2_glock_put(new_qd->qd_gl);
291 kmem_cache_free(gfs2_quotad_cachep, new_qd);
242 } 292 }
293
294 return 0;
243} 295}
244 296
297
245static void qd_hold(struct gfs2_quota_data *qd) 298static void qd_hold(struct gfs2_quota_data *qd)
246{ 299{
247 struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; 300 struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
@@ -1215,6 +1268,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp)
1215 unsigned int blocks = size >> sdp->sd_sb.sb_bsize_shift; 1268 unsigned int blocks = size >> sdp->sd_sb.sb_bsize_shift;
1216 unsigned int x, slot = 0; 1269 unsigned int x, slot = 0;
1217 unsigned int found = 0; 1270 unsigned int found = 0;
1271 unsigned int hash;
1218 u64 dblock; 1272 u64 dblock;
1219 u32 extlen = 0; 1273 u32 extlen = 0;
1220 int error; 1274 int error;
@@ -1272,8 +1326,9 @@ int gfs2_quota_init(struct gfs2_sbd *sdp)
1272 if (!qc_change) 1326 if (!qc_change)
1273 continue; 1327 continue;
1274 1328
1275 error = qd_alloc(sdp, qc_id, &qd); 1329 hash = gfs2_qd_hash(sdp, qc_id);
1276 if (error) { 1330 qd = qd_alloc(hash, sdp, qc_id);
1331 if (qd == NULL) {
1277 brelse(bh); 1332 brelse(bh);
1278 goto fail; 1333 goto fail;
1279 } 1334 }
@@ -1289,6 +1344,10 @@ int gfs2_quota_init(struct gfs2_sbd *sdp)
1289 atomic_inc(&sdp->sd_quota_count); 1344 atomic_inc(&sdp->sd_quota_count);
1290 spin_unlock(&qd_lock); 1345 spin_unlock(&qd_lock);
1291 1346
1347 spin_lock_bucket(hash);
1348 hlist_bl_add_head_rcu(&qd->qd_hlist, &qd_hash_table[hash]);
1349 spin_unlock_bucket(hash);
1350
1292 found++; 1351 found++;
1293 } 1352 }
1294 1353
@@ -1335,11 +1394,16 @@ void gfs2_quota_cleanup(struct gfs2_sbd *sdp)
1335 spin_unlock(&qd->qd_lockref.lock); 1394 spin_unlock(&qd->qd_lockref.lock);
1336 1395
1337 list_del(&qd->qd_list); 1396 list_del(&qd->qd_list);
1397
1338 /* Also remove if this qd exists in the reclaim list */ 1398 /* Also remove if this qd exists in the reclaim list */
1339 list_lru_del(&gfs2_qd_lru, &qd->qd_lru); 1399 list_lru_del(&gfs2_qd_lru, &qd->qd_lru);
1340 atomic_dec(&sdp->sd_quota_count); 1400 atomic_dec(&sdp->sd_quota_count);
1341 spin_unlock(&qd_lock); 1401 spin_unlock(&qd_lock);
1342 1402
1403 spin_lock_bucket(qd->qd_hash);
1404 hlist_bl_del_rcu(&qd->qd_hlist);
1405 spin_unlock_bucket(qd->qd_hash);
1406
1343 if (!qd->qd_lockref.count) { 1407 if (!qd->qd_lockref.count) {
1344 gfs2_assert_warn(sdp, !qd->qd_change); 1408 gfs2_assert_warn(sdp, !qd->qd_change);
1345 gfs2_assert_warn(sdp, !qd->qd_slot_count); 1409 gfs2_assert_warn(sdp, !qd->qd_slot_count);
@@ -1348,7 +1412,7 @@ void gfs2_quota_cleanup(struct gfs2_sbd *sdp)
1348 gfs2_assert_warn(sdp, !qd->qd_bh_count); 1412 gfs2_assert_warn(sdp, !qd->qd_bh_count);
1349 1413
1350 gfs2_glock_put(qd->qd_gl); 1414 gfs2_glock_put(qd->qd_gl);
1351 kmem_cache_free(gfs2_quotad_cachep, qd); 1415 call_rcu(&qd->qd_rcu, gfs2_qd_dealloc);
1352 1416
1353 spin_lock(&qd_lock); 1417 spin_lock(&qd_lock);
1354 } 1418 }
@@ -1643,3 +1707,11 @@ const struct quotactl_ops gfs2_quotactl_ops = {
1643 .get_dqblk = gfs2_get_dqblk, 1707 .get_dqblk = gfs2_get_dqblk,
1644 .set_dqblk = gfs2_set_dqblk, 1708 .set_dqblk = gfs2_set_dqblk,
1645}; 1709};
1710
1711void __init gfs2_quota_hash_init(void)
1712{
1713 unsigned i;
1714
1715 for(i = 0; i < GFS2_QD_HASH_SIZE; i++)
1716 INIT_HLIST_BL_HEAD(&qd_hash_table[i]);
1717}
diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h
index 96e4f34a03b0..55d506eb3c4a 100644
--- a/fs/gfs2/quota.h
+++ b/fs/gfs2/quota.h
@@ -57,5 +57,6 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip)
57extern const struct quotactl_ops gfs2_quotactl_ops; 57extern const struct quotactl_ops gfs2_quotactl_ops;
58extern struct shrinker gfs2_qd_shrinker; 58extern struct shrinker gfs2_qd_shrinker;
59extern struct list_lru gfs2_qd_lru; 59extern struct list_lru gfs2_qd_lru;
60extern void __init gfs2_quota_hash_init(void);
60 61
61#endif /* __QUOTA_DOT_H__ */ 62#endif /* __QUOTA_DOT_H__ */