diff options
-rw-r--r-- | fs/gfs2/incore.h | 4 | ||||
-rw-r--r-- | fs/gfs2/main.c | 1 | ||||
-rw-r--r-- | fs/gfs2/quota.c | 168 | ||||
-rw-r--r-- | fs/gfs2/quota.h | 1 |
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 | ||
430 | struct gfs2_quota_data { | 430 | struct 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 | ||
455 | struct gfs2_trans { | 459 | struct 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 */ | ||
71 | static DEFINE_SPINLOCK(qd_lock); | 79 | static DEFINE_SPINLOCK(qd_lock); |
72 | struct list_lru gfs2_qd_lru; | 80 | struct list_lru gfs2_qd_lru; |
73 | 81 | ||
82 | static struct hlist_bl_head qd_hash_table[GFS2_QD_HASH_SIZE]; | ||
83 | |||
84 | static 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 | |||
95 | static inline void spin_lock_bucket(unsigned int hash) | ||
96 | { | ||
97 | hlist_bl_lock(&qd_hash_table[hash]); | ||
98 | } | ||
99 | |||
100 | static inline void spin_unlock_bucket(unsigned int hash) | ||
101 | { | ||
102 | hlist_bl_unlock(&qd_hash_table[hash]); | ||
103 | } | ||
104 | |||
105 | static 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 | |||
74 | static void gfs2_qd_dispose(struct list_head *list) | 111 | static 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 | ||
168 | static int qd_alloc(struct gfs2_sbd *sdp, struct kqid qid, | 209 | static 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 | ||
193 | fail: | 233 | fail: |
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 | ||
198 | static int qd_get(struct gfs2_sbd *sdp, struct kqid qid, | 238 | static 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); | 260 | static 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 | |||
245 | static void qd_hold(struct gfs2_quota_data *qd) | 298 | static 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 | |||
1711 | void __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) | |||
57 | extern const struct quotactl_ops gfs2_quotactl_ops; | 57 | extern const struct quotactl_ops gfs2_quotactl_ops; |
58 | extern struct shrinker gfs2_qd_shrinker; | 58 | extern struct shrinker gfs2_qd_shrinker; |
59 | extern struct list_lru gfs2_qd_lru; | 59 | extern struct list_lru gfs2_qd_lru; |
60 | extern void __init gfs2_quota_hash_init(void); | ||
60 | 61 | ||
61 | #endif /* __QUOTA_DOT_H__ */ | 62 | #endif /* __QUOTA_DOT_H__ */ |