diff options
Diffstat (limited to 'fs/ocfs2/quota_local.c')
-rw-r--r-- | fs/ocfs2/quota_local.c | 1253 |
1 files changed, 1253 insertions, 0 deletions
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c new file mode 100644 index 000000000000..07deec5e9721 --- /dev/null +++ b/fs/ocfs2/quota_local.c | |||
@@ -0,0 +1,1253 @@ | |||
1 | /* | ||
2 | * Implementation of operations over local quota file | ||
3 | */ | ||
4 | |||
5 | #include <linux/fs.h> | ||
6 | #include <linux/quota.h> | ||
7 | #include <linux/quotaops.h> | ||
8 | #include <linux/module.h> | ||
9 | |||
10 | #define MLOG_MASK_PREFIX ML_QUOTA | ||
11 | #include <cluster/masklog.h> | ||
12 | |||
13 | #include "ocfs2_fs.h" | ||
14 | #include "ocfs2.h" | ||
15 | #include "inode.h" | ||
16 | #include "alloc.h" | ||
17 | #include "file.h" | ||
18 | #include "buffer_head_io.h" | ||
19 | #include "journal.h" | ||
20 | #include "sysfile.h" | ||
21 | #include "dlmglue.h" | ||
22 | #include "quota.h" | ||
23 | |||
24 | /* Number of local quota structures per block */ | ||
25 | static inline unsigned int ol_quota_entries_per_block(struct super_block *sb) | ||
26 | { | ||
27 | return ((sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE) / | ||
28 | sizeof(struct ocfs2_local_disk_dqblk)); | ||
29 | } | ||
30 | |||
31 | /* Number of blocks with entries in one chunk */ | ||
32 | static inline unsigned int ol_chunk_blocks(struct super_block *sb) | ||
33 | { | ||
34 | return ((sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) - | ||
35 | OCFS2_QBLK_RESERVED_SPACE) << 3) / | ||
36 | ol_quota_entries_per_block(sb); | ||
37 | } | ||
38 | |||
39 | /* Number of entries in a chunk bitmap */ | ||
40 | static unsigned int ol_chunk_entries(struct super_block *sb) | ||
41 | { | ||
42 | return ol_chunk_blocks(sb) * ol_quota_entries_per_block(sb); | ||
43 | } | ||
44 | |||
45 | /* Offset of the chunk in quota file */ | ||
46 | static unsigned int ol_quota_chunk_block(struct super_block *sb, int c) | ||
47 | { | ||
48 | /* 1 block for local quota file info, 1 block per chunk for chunk info */ | ||
49 | return 1 + (ol_chunk_blocks(sb) + 1) * c; | ||
50 | } | ||
51 | |||
52 | static unsigned int ol_dqblk_block(struct super_block *sb, int c, int off) | ||
53 | { | ||
54 | int epb = ol_quota_entries_per_block(sb); | ||
55 | |||
56 | return ol_quota_chunk_block(sb, c) + 1 + off / epb; | ||
57 | } | ||
58 | |||
59 | static unsigned int ol_dqblk_block_off(struct super_block *sb, int c, int off) | ||
60 | { | ||
61 | int epb = ol_quota_entries_per_block(sb); | ||
62 | |||
63 | return (off % epb) * sizeof(struct ocfs2_local_disk_dqblk); | ||
64 | } | ||
65 | |||
66 | /* Offset of the dquot structure in the quota file */ | ||
67 | static loff_t ol_dqblk_off(struct super_block *sb, int c, int off) | ||
68 | { | ||
69 | return (ol_dqblk_block(sb, c, off) << sb->s_blocksize_bits) + | ||
70 | ol_dqblk_block_off(sb, c, off); | ||
71 | } | ||
72 | |||
73 | /* Compute block number from given offset */ | ||
74 | static inline unsigned int ol_dqblk_file_block(struct super_block *sb, loff_t off) | ||
75 | { | ||
76 | return off >> sb->s_blocksize_bits; | ||
77 | } | ||
78 | |||
79 | static inline unsigned int ol_dqblk_block_offset(struct super_block *sb, loff_t off) | ||
80 | { | ||
81 | return off & ((1 << sb->s_blocksize_bits) - 1); | ||
82 | } | ||
83 | |||
84 | /* Compute offset in the chunk of a structure with the given offset */ | ||
85 | static int ol_dqblk_chunk_off(struct super_block *sb, int c, loff_t off) | ||
86 | { | ||
87 | int epb = ol_quota_entries_per_block(sb); | ||
88 | |||
89 | return ((off >> sb->s_blocksize_bits) - | ||
90 | ol_quota_chunk_block(sb, c) - 1) * epb | ||
91 | + ((unsigned int)(off & ((1 << sb->s_blocksize_bits) - 1))) / | ||
92 | sizeof(struct ocfs2_local_disk_dqblk); | ||
93 | } | ||
94 | |||
95 | /* Write bufferhead into the fs */ | ||
96 | static int ocfs2_modify_bh(struct inode *inode, struct buffer_head *bh, | ||
97 | void (*modify)(struct buffer_head *, void *), void *private) | ||
98 | { | ||
99 | struct super_block *sb = inode->i_sb; | ||
100 | handle_t *handle; | ||
101 | int status; | ||
102 | |||
103 | handle = ocfs2_start_trans(OCFS2_SB(sb), 1); | ||
104 | if (IS_ERR(handle)) { | ||
105 | status = PTR_ERR(handle); | ||
106 | mlog_errno(status); | ||
107 | return status; | ||
108 | } | ||
109 | status = ocfs2_journal_access_dq(handle, inode, bh, | ||
110 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
111 | if (status < 0) { | ||
112 | mlog_errno(status); | ||
113 | ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
114 | return status; | ||
115 | } | ||
116 | lock_buffer(bh); | ||
117 | modify(bh, private); | ||
118 | unlock_buffer(bh); | ||
119 | status = ocfs2_journal_dirty(handle, bh); | ||
120 | if (status < 0) { | ||
121 | mlog_errno(status); | ||
122 | ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
123 | return status; | ||
124 | } | ||
125 | status = ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
126 | if (status < 0) { | ||
127 | mlog_errno(status); | ||
128 | return status; | ||
129 | } | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | /* Check whether we understand format of quota files */ | ||
134 | static int ocfs2_local_check_quota_file(struct super_block *sb, int type) | ||
135 | { | ||
136 | unsigned int lmagics[MAXQUOTAS] = OCFS2_LOCAL_QMAGICS; | ||
137 | unsigned int lversions[MAXQUOTAS] = OCFS2_LOCAL_QVERSIONS; | ||
138 | unsigned int gmagics[MAXQUOTAS] = OCFS2_GLOBAL_QMAGICS; | ||
139 | unsigned int gversions[MAXQUOTAS] = OCFS2_GLOBAL_QVERSIONS; | ||
140 | unsigned int ino[MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE, | ||
141 | GROUP_QUOTA_SYSTEM_INODE }; | ||
142 | struct buffer_head *bh = NULL; | ||
143 | struct inode *linode = sb_dqopt(sb)->files[type]; | ||
144 | struct inode *ginode = NULL; | ||
145 | struct ocfs2_disk_dqheader *dqhead; | ||
146 | int status, ret = 0; | ||
147 | |||
148 | /* First check whether we understand local quota file */ | ||
149 | status = ocfs2_read_quota_block(linode, 0, &bh); | ||
150 | if (status) { | ||
151 | mlog_errno(status); | ||
152 | mlog(ML_ERROR, "failed to read quota file header (type=%d)\n", | ||
153 | type); | ||
154 | goto out_err; | ||
155 | } | ||
156 | dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data); | ||
157 | if (le32_to_cpu(dqhead->dqh_magic) != lmagics[type]) { | ||
158 | mlog(ML_ERROR, "quota file magic does not match (%u != %u)," | ||
159 | " type=%d\n", le32_to_cpu(dqhead->dqh_magic), | ||
160 | lmagics[type], type); | ||
161 | goto out_err; | ||
162 | } | ||
163 | if (le32_to_cpu(dqhead->dqh_version) != lversions[type]) { | ||
164 | mlog(ML_ERROR, "quota file version does not match (%u != %u)," | ||
165 | " type=%d\n", le32_to_cpu(dqhead->dqh_version), | ||
166 | lversions[type], type); | ||
167 | goto out_err; | ||
168 | } | ||
169 | brelse(bh); | ||
170 | bh = NULL; | ||
171 | |||
172 | /* Next check whether we understand global quota file */ | ||
173 | ginode = ocfs2_get_system_file_inode(OCFS2_SB(sb), ino[type], | ||
174 | OCFS2_INVALID_SLOT); | ||
175 | if (!ginode) { | ||
176 | mlog(ML_ERROR, "cannot get global quota file inode " | ||
177 | "(type=%d)\n", type); | ||
178 | goto out_err; | ||
179 | } | ||
180 | /* Since the header is read only, we don't care about locking */ | ||
181 | status = ocfs2_read_quota_block(ginode, 0, &bh); | ||
182 | if (status) { | ||
183 | mlog_errno(status); | ||
184 | mlog(ML_ERROR, "failed to read global quota file header " | ||
185 | "(type=%d)\n", type); | ||
186 | goto out_err; | ||
187 | } | ||
188 | dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data); | ||
189 | if (le32_to_cpu(dqhead->dqh_magic) != gmagics[type]) { | ||
190 | mlog(ML_ERROR, "global quota file magic does not match " | ||
191 | "(%u != %u), type=%d\n", | ||
192 | le32_to_cpu(dqhead->dqh_magic), gmagics[type], type); | ||
193 | goto out_err; | ||
194 | } | ||
195 | if (le32_to_cpu(dqhead->dqh_version) != gversions[type]) { | ||
196 | mlog(ML_ERROR, "global quota file version does not match " | ||
197 | "(%u != %u), type=%d\n", | ||
198 | le32_to_cpu(dqhead->dqh_version), gversions[type], | ||
199 | type); | ||
200 | goto out_err; | ||
201 | } | ||
202 | |||
203 | ret = 1; | ||
204 | out_err: | ||
205 | brelse(bh); | ||
206 | iput(ginode); | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | /* Release given list of quota file chunks */ | ||
211 | static void ocfs2_release_local_quota_bitmaps(struct list_head *head) | ||
212 | { | ||
213 | struct ocfs2_quota_chunk *pos, *next; | ||
214 | |||
215 | list_for_each_entry_safe(pos, next, head, qc_chunk) { | ||
216 | list_del(&pos->qc_chunk); | ||
217 | brelse(pos->qc_headerbh); | ||
218 | kmem_cache_free(ocfs2_qf_chunk_cachep, pos); | ||
219 | } | ||
220 | } | ||
221 | |||
222 | /* Load quota bitmaps into memory */ | ||
223 | static int ocfs2_load_local_quota_bitmaps(struct inode *inode, | ||
224 | struct ocfs2_local_disk_dqinfo *ldinfo, | ||
225 | struct list_head *head) | ||
226 | { | ||
227 | struct ocfs2_quota_chunk *newchunk; | ||
228 | int i, status; | ||
229 | |||
230 | INIT_LIST_HEAD(head); | ||
231 | for (i = 0; i < le32_to_cpu(ldinfo->dqi_chunks); i++) { | ||
232 | newchunk = kmem_cache_alloc(ocfs2_qf_chunk_cachep, GFP_NOFS); | ||
233 | if (!newchunk) { | ||
234 | ocfs2_release_local_quota_bitmaps(head); | ||
235 | return -ENOMEM; | ||
236 | } | ||
237 | newchunk->qc_num = i; | ||
238 | newchunk->qc_headerbh = NULL; | ||
239 | status = ocfs2_read_quota_block(inode, | ||
240 | ol_quota_chunk_block(inode->i_sb, i), | ||
241 | &newchunk->qc_headerbh); | ||
242 | if (status) { | ||
243 | mlog_errno(status); | ||
244 | kmem_cache_free(ocfs2_qf_chunk_cachep, newchunk); | ||
245 | ocfs2_release_local_quota_bitmaps(head); | ||
246 | return status; | ||
247 | } | ||
248 | list_add_tail(&newchunk->qc_chunk, head); | ||
249 | } | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static void olq_update_info(struct buffer_head *bh, void *private) | ||
254 | { | ||
255 | struct mem_dqinfo *info = private; | ||
256 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; | ||
257 | struct ocfs2_local_disk_dqinfo *ldinfo; | ||
258 | |||
259 | ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + | ||
260 | OCFS2_LOCAL_INFO_OFF); | ||
261 | spin_lock(&dq_data_lock); | ||
262 | ldinfo->dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK); | ||
263 | ldinfo->dqi_chunks = cpu_to_le32(oinfo->dqi_chunks); | ||
264 | ldinfo->dqi_blocks = cpu_to_le32(oinfo->dqi_blocks); | ||
265 | spin_unlock(&dq_data_lock); | ||
266 | } | ||
267 | |||
268 | static int ocfs2_add_recovery_chunk(struct super_block *sb, | ||
269 | struct ocfs2_local_disk_chunk *dchunk, | ||
270 | int chunk, | ||
271 | struct list_head *head) | ||
272 | { | ||
273 | struct ocfs2_recovery_chunk *rc; | ||
274 | |||
275 | rc = kmalloc(sizeof(struct ocfs2_recovery_chunk), GFP_NOFS); | ||
276 | if (!rc) | ||
277 | return -ENOMEM; | ||
278 | rc->rc_chunk = chunk; | ||
279 | rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS); | ||
280 | if (!rc->rc_bitmap) { | ||
281 | kfree(rc); | ||
282 | return -ENOMEM; | ||
283 | } | ||
284 | memcpy(rc->rc_bitmap, dchunk->dqc_bitmap, | ||
285 | (ol_chunk_entries(sb) + 7) >> 3); | ||
286 | list_add_tail(&rc->rc_list, head); | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static void free_recovery_list(struct list_head *head) | ||
291 | { | ||
292 | struct ocfs2_recovery_chunk *next; | ||
293 | struct ocfs2_recovery_chunk *rchunk; | ||
294 | |||
295 | list_for_each_entry_safe(rchunk, next, head, rc_list) { | ||
296 | list_del(&rchunk->rc_list); | ||
297 | kfree(rchunk->rc_bitmap); | ||
298 | kfree(rchunk); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | void ocfs2_free_quota_recovery(struct ocfs2_quota_recovery *rec) | ||
303 | { | ||
304 | int type; | ||
305 | |||
306 | for (type = 0; type < MAXQUOTAS; type++) | ||
307 | free_recovery_list(&(rec->r_list[type])); | ||
308 | kfree(rec); | ||
309 | } | ||
310 | |||
311 | /* Load entries in our quota file we have to recover*/ | ||
312 | static int ocfs2_recovery_load_quota(struct inode *lqinode, | ||
313 | struct ocfs2_local_disk_dqinfo *ldinfo, | ||
314 | int type, | ||
315 | struct list_head *head) | ||
316 | { | ||
317 | struct super_block *sb = lqinode->i_sb; | ||
318 | struct buffer_head *hbh; | ||
319 | struct ocfs2_local_disk_chunk *dchunk; | ||
320 | int i, chunks = le32_to_cpu(ldinfo->dqi_chunks); | ||
321 | int status = 0; | ||
322 | |||
323 | for (i = 0; i < chunks; i++) { | ||
324 | hbh = NULL; | ||
325 | status = ocfs2_read_quota_block(lqinode, | ||
326 | ol_quota_chunk_block(sb, i), | ||
327 | &hbh); | ||
328 | if (status) { | ||
329 | mlog_errno(status); | ||
330 | break; | ||
331 | } | ||
332 | dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data; | ||
333 | if (le32_to_cpu(dchunk->dqc_free) < ol_chunk_entries(sb)) | ||
334 | status = ocfs2_add_recovery_chunk(sb, dchunk, i, head); | ||
335 | brelse(hbh); | ||
336 | if (status < 0) | ||
337 | break; | ||
338 | } | ||
339 | if (status < 0) | ||
340 | free_recovery_list(head); | ||
341 | return status; | ||
342 | } | ||
343 | |||
344 | static struct ocfs2_quota_recovery *ocfs2_alloc_quota_recovery(void) | ||
345 | { | ||
346 | int type; | ||
347 | struct ocfs2_quota_recovery *rec; | ||
348 | |||
349 | rec = kmalloc(sizeof(struct ocfs2_quota_recovery), GFP_NOFS); | ||
350 | if (!rec) | ||
351 | return NULL; | ||
352 | for (type = 0; type < MAXQUOTAS; type++) | ||
353 | INIT_LIST_HEAD(&(rec->r_list[type])); | ||
354 | return rec; | ||
355 | } | ||
356 | |||
357 | /* Load information we need for quota recovery into memory */ | ||
358 | struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery( | ||
359 | struct ocfs2_super *osb, | ||
360 | int slot_num) | ||
361 | { | ||
362 | unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, | ||
363 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; | ||
364 | unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE, | ||
365 | LOCAL_GROUP_QUOTA_SYSTEM_INODE }; | ||
366 | struct super_block *sb = osb->sb; | ||
367 | struct ocfs2_local_disk_dqinfo *ldinfo; | ||
368 | struct inode *lqinode; | ||
369 | struct buffer_head *bh; | ||
370 | int type; | ||
371 | int status = 0; | ||
372 | struct ocfs2_quota_recovery *rec; | ||
373 | |||
374 | mlog(ML_NOTICE, "Beginning quota recovery in slot %u\n", slot_num); | ||
375 | rec = ocfs2_alloc_quota_recovery(); | ||
376 | if (!rec) | ||
377 | return ERR_PTR(-ENOMEM); | ||
378 | /* First init... */ | ||
379 | |||
380 | for (type = 0; type < MAXQUOTAS; type++) { | ||
381 | if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) | ||
382 | continue; | ||
383 | /* At this point, journal of the slot is already replayed so | ||
384 | * we can trust metadata and data of the quota file */ | ||
385 | lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num); | ||
386 | if (!lqinode) { | ||
387 | status = -ENOENT; | ||
388 | goto out; | ||
389 | } | ||
390 | status = ocfs2_inode_lock_full(lqinode, NULL, 1, | ||
391 | OCFS2_META_LOCK_RECOVERY); | ||
392 | if (status < 0) { | ||
393 | mlog_errno(status); | ||
394 | goto out_put; | ||
395 | } | ||
396 | /* Now read local header */ | ||
397 | bh = NULL; | ||
398 | status = ocfs2_read_quota_block(lqinode, 0, &bh); | ||
399 | if (status) { | ||
400 | mlog_errno(status); | ||
401 | mlog(ML_ERROR, "failed to read quota file info header " | ||
402 | "(slot=%d type=%d)\n", slot_num, type); | ||
403 | goto out_lock; | ||
404 | } | ||
405 | ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + | ||
406 | OCFS2_LOCAL_INFO_OFF); | ||
407 | status = ocfs2_recovery_load_quota(lqinode, ldinfo, type, | ||
408 | &rec->r_list[type]); | ||
409 | brelse(bh); | ||
410 | out_lock: | ||
411 | ocfs2_inode_unlock(lqinode, 1); | ||
412 | out_put: | ||
413 | iput(lqinode); | ||
414 | if (status < 0) | ||
415 | break; | ||
416 | } | ||
417 | out: | ||
418 | if (status < 0) { | ||
419 | ocfs2_free_quota_recovery(rec); | ||
420 | rec = ERR_PTR(status); | ||
421 | } | ||
422 | return rec; | ||
423 | } | ||
424 | |||
425 | /* Sync changes in local quota file into global quota file and | ||
426 | * reinitialize local quota file. | ||
427 | * The function expects local quota file to be already locked and | ||
428 | * dqonoff_mutex locked. */ | ||
429 | static int ocfs2_recover_local_quota_file(struct inode *lqinode, | ||
430 | int type, | ||
431 | struct ocfs2_quota_recovery *rec) | ||
432 | { | ||
433 | struct super_block *sb = lqinode->i_sb; | ||
434 | struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv; | ||
435 | struct ocfs2_local_disk_chunk *dchunk; | ||
436 | struct ocfs2_local_disk_dqblk *dqblk; | ||
437 | struct dquot *dquot; | ||
438 | handle_t *handle; | ||
439 | struct buffer_head *hbh = NULL, *qbh = NULL; | ||
440 | int status = 0; | ||
441 | int bit, chunk; | ||
442 | struct ocfs2_recovery_chunk *rchunk, *next; | ||
443 | qsize_t spacechange, inodechange; | ||
444 | |||
445 | mlog_entry("ino=%lu type=%u", (unsigned long)lqinode->i_ino, type); | ||
446 | |||
447 | status = ocfs2_lock_global_qf(oinfo, 1); | ||
448 | if (status < 0) | ||
449 | goto out; | ||
450 | |||
451 | list_for_each_entry_safe(rchunk, next, &(rec->r_list[type]), rc_list) { | ||
452 | chunk = rchunk->rc_chunk; | ||
453 | hbh = NULL; | ||
454 | status = ocfs2_read_quota_block(lqinode, | ||
455 | ol_quota_chunk_block(sb, chunk), | ||
456 | &hbh); | ||
457 | if (status) { | ||
458 | mlog_errno(status); | ||
459 | break; | ||
460 | } | ||
461 | dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data; | ||
462 | for_each_bit(bit, rchunk->rc_bitmap, ol_chunk_entries(sb)) { | ||
463 | qbh = NULL; | ||
464 | status = ocfs2_read_quota_block(lqinode, | ||
465 | ol_dqblk_block(sb, chunk, bit), | ||
466 | &qbh); | ||
467 | if (status) { | ||
468 | mlog_errno(status); | ||
469 | break; | ||
470 | } | ||
471 | dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data + | ||
472 | ol_dqblk_block_off(sb, chunk, bit)); | ||
473 | dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type); | ||
474 | if (!dquot) { | ||
475 | status = -EIO; | ||
476 | mlog(ML_ERROR, "Failed to get quota structure " | ||
477 | "for id %u, type %d. Cannot finish quota " | ||
478 | "file recovery.\n", | ||
479 | (unsigned)le64_to_cpu(dqblk->dqb_id), | ||
480 | type); | ||
481 | goto out_put_bh; | ||
482 | } | ||
483 | handle = ocfs2_start_trans(OCFS2_SB(sb), | ||
484 | OCFS2_QSYNC_CREDITS); | ||
485 | if (IS_ERR(handle)) { | ||
486 | status = PTR_ERR(handle); | ||
487 | mlog_errno(status); | ||
488 | goto out_put_dquot; | ||
489 | } | ||
490 | mutex_lock(&sb_dqopt(sb)->dqio_mutex); | ||
491 | spin_lock(&dq_data_lock); | ||
492 | /* Add usage from quota entry into quota changes | ||
493 | * of our node. Auxiliary variables are important | ||
494 | * due to signedness */ | ||
495 | spacechange = le64_to_cpu(dqblk->dqb_spacemod); | ||
496 | inodechange = le64_to_cpu(dqblk->dqb_inodemod); | ||
497 | dquot->dq_dqb.dqb_curspace += spacechange; | ||
498 | dquot->dq_dqb.dqb_curinodes += inodechange; | ||
499 | spin_unlock(&dq_data_lock); | ||
500 | /* We want to drop reference held by the crashed | ||
501 | * node. Since we have our own reference we know | ||
502 | * global structure actually won't be freed. */ | ||
503 | status = ocfs2_global_release_dquot(dquot); | ||
504 | if (status < 0) { | ||
505 | mlog_errno(status); | ||
506 | goto out_commit; | ||
507 | } | ||
508 | /* Release local quota file entry */ | ||
509 | status = ocfs2_journal_access_dq(handle, lqinode, | ||
510 | qbh, OCFS2_JOURNAL_ACCESS_WRITE); | ||
511 | if (status < 0) { | ||
512 | mlog_errno(status); | ||
513 | goto out_commit; | ||
514 | } | ||
515 | lock_buffer(qbh); | ||
516 | WARN_ON(!ocfs2_test_bit(bit, dchunk->dqc_bitmap)); | ||
517 | ocfs2_clear_bit(bit, dchunk->dqc_bitmap); | ||
518 | le32_add_cpu(&dchunk->dqc_free, 1); | ||
519 | unlock_buffer(qbh); | ||
520 | status = ocfs2_journal_dirty(handle, qbh); | ||
521 | if (status < 0) | ||
522 | mlog_errno(status); | ||
523 | out_commit: | ||
524 | mutex_unlock(&sb_dqopt(sb)->dqio_mutex); | ||
525 | ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
526 | out_put_dquot: | ||
527 | dqput(dquot); | ||
528 | out_put_bh: | ||
529 | brelse(qbh); | ||
530 | if (status < 0) | ||
531 | break; | ||
532 | } | ||
533 | brelse(hbh); | ||
534 | list_del(&rchunk->rc_list); | ||
535 | kfree(rchunk->rc_bitmap); | ||
536 | kfree(rchunk); | ||
537 | if (status < 0) | ||
538 | break; | ||
539 | } | ||
540 | ocfs2_unlock_global_qf(oinfo, 1); | ||
541 | out: | ||
542 | if (status < 0) | ||
543 | free_recovery_list(&(rec->r_list[type])); | ||
544 | mlog_exit(status); | ||
545 | return status; | ||
546 | } | ||
547 | |||
548 | /* Recover local quota files for given node different from us */ | ||
549 | int ocfs2_finish_quota_recovery(struct ocfs2_super *osb, | ||
550 | struct ocfs2_quota_recovery *rec, | ||
551 | int slot_num) | ||
552 | { | ||
553 | unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE, | ||
554 | LOCAL_GROUP_QUOTA_SYSTEM_INODE }; | ||
555 | struct super_block *sb = osb->sb; | ||
556 | struct ocfs2_local_disk_dqinfo *ldinfo; | ||
557 | struct buffer_head *bh; | ||
558 | handle_t *handle; | ||
559 | int type; | ||
560 | int status = 0; | ||
561 | struct inode *lqinode; | ||
562 | unsigned int flags; | ||
563 | |||
564 | mlog(ML_NOTICE, "Finishing quota recovery in slot %u\n", slot_num); | ||
565 | mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); | ||
566 | for (type = 0; type < MAXQUOTAS; type++) { | ||
567 | if (list_empty(&(rec->r_list[type]))) | ||
568 | continue; | ||
569 | mlog(0, "Recovering quota in slot %d\n", slot_num); | ||
570 | lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num); | ||
571 | if (!lqinode) { | ||
572 | status = -ENOENT; | ||
573 | goto out; | ||
574 | } | ||
575 | status = ocfs2_inode_lock_full(lqinode, NULL, 1, | ||
576 | OCFS2_META_LOCK_NOQUEUE); | ||
577 | /* Someone else is holding the lock? Then he must be | ||
578 | * doing the recovery. Just skip the file... */ | ||
579 | if (status == -EAGAIN) { | ||
580 | mlog(ML_NOTICE, "skipping quota recovery for slot %d " | ||
581 | "because quota file is locked.\n", slot_num); | ||
582 | status = 0; | ||
583 | goto out_put; | ||
584 | } else if (status < 0) { | ||
585 | mlog_errno(status); | ||
586 | goto out_put; | ||
587 | } | ||
588 | /* Now read local header */ | ||
589 | bh = NULL; | ||
590 | status = ocfs2_read_quota_block(lqinode, 0, &bh); | ||
591 | if (status) { | ||
592 | mlog_errno(status); | ||
593 | mlog(ML_ERROR, "failed to read quota file info header " | ||
594 | "(slot=%d type=%d)\n", slot_num, type); | ||
595 | goto out_lock; | ||
596 | } | ||
597 | ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + | ||
598 | OCFS2_LOCAL_INFO_OFF); | ||
599 | /* Is recovery still needed? */ | ||
600 | flags = le32_to_cpu(ldinfo->dqi_flags); | ||
601 | if (!(flags & OLQF_CLEAN)) | ||
602 | status = ocfs2_recover_local_quota_file(lqinode, | ||
603 | type, | ||
604 | rec); | ||
605 | /* We don't want to mark file as clean when it is actually | ||
606 | * active */ | ||
607 | if (slot_num == osb->slot_num) | ||
608 | goto out_bh; | ||
609 | /* Mark quota file as clean if we are recovering quota file of | ||
610 | * some other node. */ | ||
611 | handle = ocfs2_start_trans(osb, 1); | ||
612 | if (IS_ERR(handle)) { | ||
613 | status = PTR_ERR(handle); | ||
614 | mlog_errno(status); | ||
615 | goto out_bh; | ||
616 | } | ||
617 | status = ocfs2_journal_access_dq(handle, lqinode, bh, | ||
618 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
619 | if (status < 0) { | ||
620 | mlog_errno(status); | ||
621 | goto out_trans; | ||
622 | } | ||
623 | lock_buffer(bh); | ||
624 | ldinfo->dqi_flags = cpu_to_le32(flags | OLQF_CLEAN); | ||
625 | unlock_buffer(bh); | ||
626 | status = ocfs2_journal_dirty(handle, bh); | ||
627 | if (status < 0) | ||
628 | mlog_errno(status); | ||
629 | out_trans: | ||
630 | ocfs2_commit_trans(osb, handle); | ||
631 | out_bh: | ||
632 | brelse(bh); | ||
633 | out_lock: | ||
634 | ocfs2_inode_unlock(lqinode, 1); | ||
635 | out_put: | ||
636 | iput(lqinode); | ||
637 | if (status < 0) | ||
638 | break; | ||
639 | } | ||
640 | out: | ||
641 | mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); | ||
642 | kfree(rec); | ||
643 | return status; | ||
644 | } | ||
645 | |||
646 | /* Read information header from quota file */ | ||
647 | static int ocfs2_local_read_info(struct super_block *sb, int type) | ||
648 | { | ||
649 | struct ocfs2_local_disk_dqinfo *ldinfo; | ||
650 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
651 | struct ocfs2_mem_dqinfo *oinfo; | ||
652 | struct inode *lqinode = sb_dqopt(sb)->files[type]; | ||
653 | int status; | ||
654 | struct buffer_head *bh = NULL; | ||
655 | struct ocfs2_quota_recovery *rec; | ||
656 | int locked = 0; | ||
657 | |||
658 | info->dqi_maxblimit = 0x7fffffffffffffffLL; | ||
659 | info->dqi_maxilimit = 0x7fffffffffffffffLL; | ||
660 | oinfo = kmalloc(sizeof(struct ocfs2_mem_dqinfo), GFP_NOFS); | ||
661 | if (!oinfo) { | ||
662 | mlog(ML_ERROR, "failed to allocate memory for ocfs2 quota" | ||
663 | " info."); | ||
664 | goto out_err; | ||
665 | } | ||
666 | info->dqi_priv = oinfo; | ||
667 | oinfo->dqi_type = type; | ||
668 | INIT_LIST_HEAD(&oinfo->dqi_chunk); | ||
669 | oinfo->dqi_rec = NULL; | ||
670 | oinfo->dqi_lqi_bh = NULL; | ||
671 | oinfo->dqi_ibh = NULL; | ||
672 | |||
673 | status = ocfs2_global_read_info(sb, type); | ||
674 | if (status < 0) | ||
675 | goto out_err; | ||
676 | |||
677 | status = ocfs2_inode_lock(lqinode, &oinfo->dqi_lqi_bh, 1); | ||
678 | if (status < 0) { | ||
679 | mlog_errno(status); | ||
680 | goto out_err; | ||
681 | } | ||
682 | locked = 1; | ||
683 | |||
684 | /* Now read local header */ | ||
685 | status = ocfs2_read_quota_block(lqinode, 0, &bh); | ||
686 | if (status) { | ||
687 | mlog_errno(status); | ||
688 | mlog(ML_ERROR, "failed to read quota file info header " | ||
689 | "(type=%d)\n", type); | ||
690 | goto out_err; | ||
691 | } | ||
692 | ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + | ||
693 | OCFS2_LOCAL_INFO_OFF); | ||
694 | info->dqi_flags = le32_to_cpu(ldinfo->dqi_flags); | ||
695 | oinfo->dqi_chunks = le32_to_cpu(ldinfo->dqi_chunks); | ||
696 | oinfo->dqi_blocks = le32_to_cpu(ldinfo->dqi_blocks); | ||
697 | oinfo->dqi_ibh = bh; | ||
698 | |||
699 | /* We crashed when using local quota file? */ | ||
700 | if (!(info->dqi_flags & OLQF_CLEAN)) { | ||
701 | rec = OCFS2_SB(sb)->quota_rec; | ||
702 | if (!rec) { | ||
703 | rec = ocfs2_alloc_quota_recovery(); | ||
704 | if (!rec) { | ||
705 | status = -ENOMEM; | ||
706 | mlog_errno(status); | ||
707 | goto out_err; | ||
708 | } | ||
709 | OCFS2_SB(sb)->quota_rec = rec; | ||
710 | } | ||
711 | |||
712 | status = ocfs2_recovery_load_quota(lqinode, ldinfo, type, | ||
713 | &rec->r_list[type]); | ||
714 | if (status < 0) { | ||
715 | mlog_errno(status); | ||
716 | goto out_err; | ||
717 | } | ||
718 | } | ||
719 | |||
720 | status = ocfs2_load_local_quota_bitmaps(lqinode, | ||
721 | ldinfo, | ||
722 | &oinfo->dqi_chunk); | ||
723 | if (status < 0) { | ||
724 | mlog_errno(status); | ||
725 | goto out_err; | ||
726 | } | ||
727 | |||
728 | /* Now mark quota file as used */ | ||
729 | info->dqi_flags &= ~OLQF_CLEAN; | ||
730 | status = ocfs2_modify_bh(lqinode, bh, olq_update_info, info); | ||
731 | if (status < 0) { | ||
732 | mlog_errno(status); | ||
733 | goto out_err; | ||
734 | } | ||
735 | |||
736 | return 0; | ||
737 | out_err: | ||
738 | if (oinfo) { | ||
739 | iput(oinfo->dqi_gqinode); | ||
740 | ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock); | ||
741 | ocfs2_lock_res_free(&oinfo->dqi_gqlock); | ||
742 | brelse(oinfo->dqi_lqi_bh); | ||
743 | if (locked) | ||
744 | ocfs2_inode_unlock(lqinode, 1); | ||
745 | ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk); | ||
746 | kfree(oinfo); | ||
747 | } | ||
748 | brelse(bh); | ||
749 | return -1; | ||
750 | } | ||
751 | |||
752 | /* Write local info to quota file */ | ||
753 | static int ocfs2_local_write_info(struct super_block *sb, int type) | ||
754 | { | ||
755 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
756 | struct buffer_head *bh = ((struct ocfs2_mem_dqinfo *)info->dqi_priv) | ||
757 | ->dqi_ibh; | ||
758 | int status; | ||
759 | |||
760 | status = ocfs2_modify_bh(sb_dqopt(sb)->files[type], bh, olq_update_info, | ||
761 | info); | ||
762 | if (status < 0) { | ||
763 | mlog_errno(status); | ||
764 | return -1; | ||
765 | } | ||
766 | |||
767 | return 0; | ||
768 | } | ||
769 | |||
770 | /* Release info from memory */ | ||
771 | static int ocfs2_local_free_info(struct super_block *sb, int type) | ||
772 | { | ||
773 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
774 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; | ||
775 | struct ocfs2_quota_chunk *chunk; | ||
776 | struct ocfs2_local_disk_chunk *dchunk; | ||
777 | int mark_clean = 1, len; | ||
778 | int status; | ||
779 | |||
780 | /* At this point we know there are no more dquots and thus | ||
781 | * even if there's some sync in the pdflush queue, it won't | ||
782 | * find any dquots and return without doing anything */ | ||
783 | cancel_delayed_work_sync(&oinfo->dqi_sync_work); | ||
784 | iput(oinfo->dqi_gqinode); | ||
785 | ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock); | ||
786 | ocfs2_lock_res_free(&oinfo->dqi_gqlock); | ||
787 | list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) { | ||
788 | dchunk = (struct ocfs2_local_disk_chunk *) | ||
789 | (chunk->qc_headerbh->b_data); | ||
790 | if (chunk->qc_num < oinfo->dqi_chunks - 1) { | ||
791 | len = ol_chunk_entries(sb); | ||
792 | } else { | ||
793 | len = (oinfo->dqi_blocks - | ||
794 | ol_quota_chunk_block(sb, chunk->qc_num) - 1) | ||
795 | * ol_quota_entries_per_block(sb); | ||
796 | } | ||
797 | /* Not all entries free? Bug! */ | ||
798 | if (le32_to_cpu(dchunk->dqc_free) != len) { | ||
799 | mlog(ML_ERROR, "releasing quota file with used " | ||
800 | "entries (type=%d)\n", type); | ||
801 | mark_clean = 0; | ||
802 | } | ||
803 | } | ||
804 | ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk); | ||
805 | |||
806 | /* dqonoff_mutex protects us against racing with recovery thread... */ | ||
807 | if (oinfo->dqi_rec) { | ||
808 | ocfs2_free_quota_recovery(oinfo->dqi_rec); | ||
809 | mark_clean = 0; | ||
810 | } | ||
811 | |||
812 | if (!mark_clean) | ||
813 | goto out; | ||
814 | |||
815 | /* Mark local file as clean */ | ||
816 | info->dqi_flags |= OLQF_CLEAN; | ||
817 | status = ocfs2_modify_bh(sb_dqopt(sb)->files[type], | ||
818 | oinfo->dqi_ibh, | ||
819 | olq_update_info, | ||
820 | info); | ||
821 | if (status < 0) { | ||
822 | mlog_errno(status); | ||
823 | goto out; | ||
824 | } | ||
825 | |||
826 | out: | ||
827 | ocfs2_inode_unlock(sb_dqopt(sb)->files[type], 1); | ||
828 | brelse(oinfo->dqi_ibh); | ||
829 | brelse(oinfo->dqi_lqi_bh); | ||
830 | kfree(oinfo); | ||
831 | return 0; | ||
832 | } | ||
833 | |||
834 | static void olq_set_dquot(struct buffer_head *bh, void *private) | ||
835 | { | ||
836 | struct ocfs2_dquot *od = private; | ||
837 | struct ocfs2_local_disk_dqblk *dqblk; | ||
838 | struct super_block *sb = od->dq_dquot.dq_sb; | ||
839 | |||
840 | dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data | ||
841 | + ol_dqblk_block_offset(sb, od->dq_local_off)); | ||
842 | |||
843 | dqblk->dqb_id = cpu_to_le64(od->dq_dquot.dq_id); | ||
844 | spin_lock(&dq_data_lock); | ||
845 | dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace - | ||
846 | od->dq_origspace); | ||
847 | dqblk->dqb_inodemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curinodes - | ||
848 | od->dq_originodes); | ||
849 | spin_unlock(&dq_data_lock); | ||
850 | mlog(0, "Writing local dquot %u space %lld inodes %lld\n", | ||
851 | od->dq_dquot.dq_id, (long long)le64_to_cpu(dqblk->dqb_spacemod), | ||
852 | (long long)le64_to_cpu(dqblk->dqb_inodemod)); | ||
853 | } | ||
854 | |||
855 | /* Write dquot to local quota file */ | ||
856 | static int ocfs2_local_write_dquot(struct dquot *dquot) | ||
857 | { | ||
858 | struct super_block *sb = dquot->dq_sb; | ||
859 | struct ocfs2_dquot *od = OCFS2_DQUOT(dquot); | ||
860 | struct buffer_head *bh = NULL; | ||
861 | int status; | ||
862 | |||
863 | status = ocfs2_read_quota_block(sb_dqopt(sb)->files[dquot->dq_type], | ||
864 | ol_dqblk_file_block(sb, od->dq_local_off), | ||
865 | &bh); | ||
866 | if (status) { | ||
867 | mlog_errno(status); | ||
868 | goto out; | ||
869 | } | ||
870 | status = ocfs2_modify_bh(sb_dqopt(sb)->files[dquot->dq_type], bh, | ||
871 | olq_set_dquot, od); | ||
872 | if (status < 0) { | ||
873 | mlog_errno(status); | ||
874 | goto out; | ||
875 | } | ||
876 | out: | ||
877 | brelse(bh); | ||
878 | return status; | ||
879 | } | ||
880 | |||
881 | /* Find free entry in local quota file */ | ||
882 | static struct ocfs2_quota_chunk *ocfs2_find_free_entry(struct super_block *sb, | ||
883 | int type, | ||
884 | int *offset) | ||
885 | { | ||
886 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
887 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; | ||
888 | struct ocfs2_quota_chunk *chunk; | ||
889 | struct ocfs2_local_disk_chunk *dchunk; | ||
890 | int found = 0, len; | ||
891 | |||
892 | list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) { | ||
893 | dchunk = (struct ocfs2_local_disk_chunk *) | ||
894 | chunk->qc_headerbh->b_data; | ||
895 | if (le32_to_cpu(dchunk->dqc_free) > 0) { | ||
896 | found = 1; | ||
897 | break; | ||
898 | } | ||
899 | } | ||
900 | if (!found) | ||
901 | return NULL; | ||
902 | |||
903 | if (chunk->qc_num < oinfo->dqi_chunks - 1) { | ||
904 | len = ol_chunk_entries(sb); | ||
905 | } else { | ||
906 | len = (oinfo->dqi_blocks - | ||
907 | ol_quota_chunk_block(sb, chunk->qc_num) - 1) | ||
908 | * ol_quota_entries_per_block(sb); | ||
909 | } | ||
910 | |||
911 | found = ocfs2_find_next_zero_bit(dchunk->dqc_bitmap, len, 0); | ||
912 | /* We failed? */ | ||
913 | if (found == len) { | ||
914 | mlog(ML_ERROR, "Did not find empty entry in chunk %d with %u" | ||
915 | " entries free (type=%d)\n", chunk->qc_num, | ||
916 | le32_to_cpu(dchunk->dqc_free), type); | ||
917 | return ERR_PTR(-EIO); | ||
918 | } | ||
919 | *offset = found; | ||
920 | return chunk; | ||
921 | } | ||
922 | |||
923 | /* Add new chunk to the local quota file */ | ||
924 | static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk( | ||
925 | struct super_block *sb, | ||
926 | int type, | ||
927 | int *offset) | ||
928 | { | ||
929 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
930 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; | ||
931 | struct inode *lqinode = sb_dqopt(sb)->files[type]; | ||
932 | struct ocfs2_quota_chunk *chunk = NULL; | ||
933 | struct ocfs2_local_disk_chunk *dchunk; | ||
934 | int status; | ||
935 | handle_t *handle; | ||
936 | struct buffer_head *bh = NULL; | ||
937 | u64 p_blkno; | ||
938 | |||
939 | /* We are protected by dqio_sem so no locking needed */ | ||
940 | status = ocfs2_extend_no_holes(lqinode, | ||
941 | lqinode->i_size + 2 * sb->s_blocksize, | ||
942 | lqinode->i_size); | ||
943 | if (status < 0) { | ||
944 | mlog_errno(status); | ||
945 | goto out; | ||
946 | } | ||
947 | status = ocfs2_simple_size_update(lqinode, oinfo->dqi_lqi_bh, | ||
948 | lqinode->i_size + 2 * sb->s_blocksize); | ||
949 | if (status < 0) { | ||
950 | mlog_errno(status); | ||
951 | goto out; | ||
952 | } | ||
953 | |||
954 | chunk = kmem_cache_alloc(ocfs2_qf_chunk_cachep, GFP_NOFS); | ||
955 | if (!chunk) { | ||
956 | status = -ENOMEM; | ||
957 | mlog_errno(status); | ||
958 | goto out; | ||
959 | } | ||
960 | |||
961 | down_read(&OCFS2_I(lqinode)->ip_alloc_sem); | ||
962 | status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks, | ||
963 | &p_blkno, NULL, NULL); | ||
964 | up_read(&OCFS2_I(lqinode)->ip_alloc_sem); | ||
965 | if (status < 0) { | ||
966 | mlog_errno(status); | ||
967 | goto out; | ||
968 | } | ||
969 | bh = sb_getblk(sb, p_blkno); | ||
970 | if (!bh) { | ||
971 | status = -ENOMEM; | ||
972 | mlog_errno(status); | ||
973 | goto out; | ||
974 | } | ||
975 | dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data; | ||
976 | |||
977 | handle = ocfs2_start_trans(OCFS2_SB(sb), 2); | ||
978 | if (IS_ERR(handle)) { | ||
979 | status = PTR_ERR(handle); | ||
980 | mlog_errno(status); | ||
981 | goto out; | ||
982 | } | ||
983 | |||
984 | status = ocfs2_journal_access_dq(handle, lqinode, bh, | ||
985 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
986 | if (status < 0) { | ||
987 | mlog_errno(status); | ||
988 | goto out_trans; | ||
989 | } | ||
990 | lock_buffer(bh); | ||
991 | dchunk->dqc_free = cpu_to_le32(ol_quota_entries_per_block(sb)); | ||
992 | memset(dchunk->dqc_bitmap, 0, | ||
993 | sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) - | ||
994 | OCFS2_QBLK_RESERVED_SPACE); | ||
995 | set_buffer_uptodate(bh); | ||
996 | unlock_buffer(bh); | ||
997 | status = ocfs2_journal_dirty(handle, bh); | ||
998 | if (status < 0) { | ||
999 | mlog_errno(status); | ||
1000 | goto out_trans; | ||
1001 | } | ||
1002 | |||
1003 | oinfo->dqi_blocks += 2; | ||
1004 | oinfo->dqi_chunks++; | ||
1005 | status = ocfs2_local_write_info(sb, type); | ||
1006 | if (status < 0) { | ||
1007 | mlog_errno(status); | ||
1008 | goto out_trans; | ||
1009 | } | ||
1010 | status = ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
1011 | if (status < 0) { | ||
1012 | mlog_errno(status); | ||
1013 | goto out; | ||
1014 | } | ||
1015 | |||
1016 | list_add_tail(&chunk->qc_chunk, &oinfo->dqi_chunk); | ||
1017 | chunk->qc_num = list_entry(chunk->qc_chunk.prev, | ||
1018 | struct ocfs2_quota_chunk, | ||
1019 | qc_chunk)->qc_num + 1; | ||
1020 | chunk->qc_headerbh = bh; | ||
1021 | *offset = 0; | ||
1022 | return chunk; | ||
1023 | out_trans: | ||
1024 | ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
1025 | out: | ||
1026 | brelse(bh); | ||
1027 | kmem_cache_free(ocfs2_qf_chunk_cachep, chunk); | ||
1028 | return ERR_PTR(status); | ||
1029 | } | ||
1030 | |||
1031 | /* Find free entry in local quota file */ | ||
1032 | static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file( | ||
1033 | struct super_block *sb, | ||
1034 | int type, | ||
1035 | int *offset) | ||
1036 | { | ||
1037 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
1038 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; | ||
1039 | struct ocfs2_quota_chunk *chunk; | ||
1040 | struct inode *lqinode = sb_dqopt(sb)->files[type]; | ||
1041 | struct ocfs2_local_disk_chunk *dchunk; | ||
1042 | int epb = ol_quota_entries_per_block(sb); | ||
1043 | unsigned int chunk_blocks; | ||
1044 | int status; | ||
1045 | handle_t *handle; | ||
1046 | |||
1047 | if (list_empty(&oinfo->dqi_chunk)) | ||
1048 | return ocfs2_local_quota_add_chunk(sb, type, offset); | ||
1049 | /* Is the last chunk full? */ | ||
1050 | chunk = list_entry(oinfo->dqi_chunk.prev, | ||
1051 | struct ocfs2_quota_chunk, qc_chunk); | ||
1052 | chunk_blocks = oinfo->dqi_blocks - | ||
1053 | ol_quota_chunk_block(sb, chunk->qc_num) - 1; | ||
1054 | if (ol_chunk_blocks(sb) == chunk_blocks) | ||
1055 | return ocfs2_local_quota_add_chunk(sb, type, offset); | ||
1056 | |||
1057 | /* We are protected by dqio_sem so no locking needed */ | ||
1058 | status = ocfs2_extend_no_holes(lqinode, | ||
1059 | lqinode->i_size + sb->s_blocksize, | ||
1060 | lqinode->i_size); | ||
1061 | if (status < 0) { | ||
1062 | mlog_errno(status); | ||
1063 | goto out; | ||
1064 | } | ||
1065 | status = ocfs2_simple_size_update(lqinode, oinfo->dqi_lqi_bh, | ||
1066 | lqinode->i_size + sb->s_blocksize); | ||
1067 | if (status < 0) { | ||
1068 | mlog_errno(status); | ||
1069 | goto out; | ||
1070 | } | ||
1071 | handle = ocfs2_start_trans(OCFS2_SB(sb), 2); | ||
1072 | if (IS_ERR(handle)) { | ||
1073 | status = PTR_ERR(handle); | ||
1074 | mlog_errno(status); | ||
1075 | goto out; | ||
1076 | } | ||
1077 | status = ocfs2_journal_access_dq(handle, lqinode, chunk->qc_headerbh, | ||
1078 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
1079 | if (status < 0) { | ||
1080 | mlog_errno(status); | ||
1081 | goto out_trans; | ||
1082 | } | ||
1083 | |||
1084 | dchunk = (struct ocfs2_local_disk_chunk *)chunk->qc_headerbh->b_data; | ||
1085 | lock_buffer(chunk->qc_headerbh); | ||
1086 | le32_add_cpu(&dchunk->dqc_free, ol_quota_entries_per_block(sb)); | ||
1087 | unlock_buffer(chunk->qc_headerbh); | ||
1088 | status = ocfs2_journal_dirty(handle, chunk->qc_headerbh); | ||
1089 | if (status < 0) { | ||
1090 | mlog_errno(status); | ||
1091 | goto out_trans; | ||
1092 | } | ||
1093 | oinfo->dqi_blocks++; | ||
1094 | status = ocfs2_local_write_info(sb, type); | ||
1095 | if (status < 0) { | ||
1096 | mlog_errno(status); | ||
1097 | goto out_trans; | ||
1098 | } | ||
1099 | |||
1100 | status = ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
1101 | if (status < 0) { | ||
1102 | mlog_errno(status); | ||
1103 | goto out; | ||
1104 | } | ||
1105 | *offset = chunk_blocks * epb; | ||
1106 | return chunk; | ||
1107 | out_trans: | ||
1108 | ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
1109 | out: | ||
1110 | return ERR_PTR(status); | ||
1111 | } | ||
1112 | |||
1113 | static void olq_alloc_dquot(struct buffer_head *bh, void *private) | ||
1114 | { | ||
1115 | int *offset = private; | ||
1116 | struct ocfs2_local_disk_chunk *dchunk; | ||
1117 | |||
1118 | dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data; | ||
1119 | ocfs2_set_bit(*offset, dchunk->dqc_bitmap); | ||
1120 | le32_add_cpu(&dchunk->dqc_free, -1); | ||
1121 | } | ||
1122 | |||
1123 | /* Create dquot in the local file for given id */ | ||
1124 | static int ocfs2_create_local_dquot(struct dquot *dquot) | ||
1125 | { | ||
1126 | struct super_block *sb = dquot->dq_sb; | ||
1127 | int type = dquot->dq_type; | ||
1128 | struct inode *lqinode = sb_dqopt(sb)->files[type]; | ||
1129 | struct ocfs2_quota_chunk *chunk; | ||
1130 | struct ocfs2_dquot *od = OCFS2_DQUOT(dquot); | ||
1131 | int offset; | ||
1132 | int status; | ||
1133 | |||
1134 | chunk = ocfs2_find_free_entry(sb, type, &offset); | ||
1135 | if (!chunk) { | ||
1136 | chunk = ocfs2_extend_local_quota_file(sb, type, &offset); | ||
1137 | if (IS_ERR(chunk)) | ||
1138 | return PTR_ERR(chunk); | ||
1139 | } else if (IS_ERR(chunk)) { | ||
1140 | return PTR_ERR(chunk); | ||
1141 | } | ||
1142 | od->dq_local_off = ol_dqblk_off(sb, chunk->qc_num, offset); | ||
1143 | od->dq_chunk = chunk; | ||
1144 | |||
1145 | /* Initialize dquot structure on disk */ | ||
1146 | status = ocfs2_local_write_dquot(dquot); | ||
1147 | if (status < 0) { | ||
1148 | mlog_errno(status); | ||
1149 | goto out; | ||
1150 | } | ||
1151 | |||
1152 | /* Mark structure as allocated */ | ||
1153 | status = ocfs2_modify_bh(lqinode, chunk->qc_headerbh, olq_alloc_dquot, | ||
1154 | &offset); | ||
1155 | if (status < 0) { | ||
1156 | mlog_errno(status); | ||
1157 | goto out; | ||
1158 | } | ||
1159 | out: | ||
1160 | return status; | ||
1161 | } | ||
1162 | |||
1163 | /* Create entry in local file for dquot, load data from the global file */ | ||
1164 | static int ocfs2_local_read_dquot(struct dquot *dquot) | ||
1165 | { | ||
1166 | int status; | ||
1167 | |||
1168 | mlog_entry("id=%u, type=%d\n", dquot->dq_id, dquot->dq_type); | ||
1169 | |||
1170 | status = ocfs2_global_read_dquot(dquot); | ||
1171 | if (status < 0) { | ||
1172 | mlog_errno(status); | ||
1173 | goto out_err; | ||
1174 | } | ||
1175 | |||
1176 | /* Now create entry in the local quota file */ | ||
1177 | status = ocfs2_create_local_dquot(dquot); | ||
1178 | if (status < 0) { | ||
1179 | mlog_errno(status); | ||
1180 | goto out_err; | ||
1181 | } | ||
1182 | mlog_exit(0); | ||
1183 | return 0; | ||
1184 | out_err: | ||
1185 | mlog_exit(status); | ||
1186 | return status; | ||
1187 | } | ||
1188 | |||
1189 | /* Release dquot structure from local quota file. ocfs2_release_dquot() has | ||
1190 | * already started a transaction and obtained exclusive lock for global | ||
1191 | * quota file. */ | ||
1192 | static int ocfs2_local_release_dquot(struct dquot *dquot) | ||
1193 | { | ||
1194 | int status; | ||
1195 | int type = dquot->dq_type; | ||
1196 | struct ocfs2_dquot *od = OCFS2_DQUOT(dquot); | ||
1197 | struct super_block *sb = dquot->dq_sb; | ||
1198 | struct ocfs2_local_disk_chunk *dchunk; | ||
1199 | int offset; | ||
1200 | handle_t *handle = journal_current_handle(); | ||
1201 | |||
1202 | BUG_ON(!handle); | ||
1203 | /* First write all local changes to global file */ | ||
1204 | status = ocfs2_global_release_dquot(dquot); | ||
1205 | if (status < 0) { | ||
1206 | mlog_errno(status); | ||
1207 | goto out; | ||
1208 | } | ||
1209 | |||
1210 | status = ocfs2_journal_access_dq(handle, sb_dqopt(sb)->files[type], | ||
1211 | od->dq_chunk->qc_headerbh, OCFS2_JOURNAL_ACCESS_WRITE); | ||
1212 | if (status < 0) { | ||
1213 | mlog_errno(status); | ||
1214 | goto out; | ||
1215 | } | ||
1216 | offset = ol_dqblk_chunk_off(sb, od->dq_chunk->qc_num, | ||
1217 | od->dq_local_off); | ||
1218 | dchunk = (struct ocfs2_local_disk_chunk *) | ||
1219 | (od->dq_chunk->qc_headerbh->b_data); | ||
1220 | /* Mark structure as freed */ | ||
1221 | lock_buffer(od->dq_chunk->qc_headerbh); | ||
1222 | ocfs2_clear_bit(offset, dchunk->dqc_bitmap); | ||
1223 | le32_add_cpu(&dchunk->dqc_free, 1); | ||
1224 | unlock_buffer(od->dq_chunk->qc_headerbh); | ||
1225 | status = ocfs2_journal_dirty(handle, od->dq_chunk->qc_headerbh); | ||
1226 | if (status < 0) { | ||
1227 | mlog_errno(status); | ||
1228 | goto out; | ||
1229 | } | ||
1230 | status = 0; | ||
1231 | out: | ||
1232 | /* Clear the read bit so that next time someone uses this | ||
1233 | * dquot he reads fresh info from disk and allocates local | ||
1234 | * dquot structure */ | ||
1235 | clear_bit(DQ_READ_B, &dquot->dq_flags); | ||
1236 | return status; | ||
1237 | } | ||
1238 | |||
1239 | static struct quota_format_ops ocfs2_format_ops = { | ||
1240 | .check_quota_file = ocfs2_local_check_quota_file, | ||
1241 | .read_file_info = ocfs2_local_read_info, | ||
1242 | .write_file_info = ocfs2_global_write_info, | ||
1243 | .free_file_info = ocfs2_local_free_info, | ||
1244 | .read_dqblk = ocfs2_local_read_dquot, | ||
1245 | .commit_dqblk = ocfs2_local_write_dquot, | ||
1246 | .release_dqblk = ocfs2_local_release_dquot, | ||
1247 | }; | ||
1248 | |||
1249 | struct quota_format_type ocfs2_quota_format = { | ||
1250 | .qf_fmt_id = QFMT_OCFS2, | ||
1251 | .qf_ops = &ocfs2_format_ops, | ||
1252 | .qf_owner = THIS_MODULE | ||
1253 | }; | ||