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