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 | unsigned int memalloc; |
474 | |
475 | trace_ocfs2_recover_local_quota_file(val1: (unsigned long)lqinode->i_ino, val2: type); |
476 | |
477 | list_for_each_entry_safe(rchunk, next, &(rec->r_list[type]), rc_list) { |
478 | chunk = rchunk->rc_chunk; |
479 | hbh = NULL; |
480 | status = ocfs2_read_quota_block(inode: lqinode, |
481 | v_block: ol_quota_chunk_block(sb, c: chunk), |
482 | bh: &hbh); |
483 | if (status) { |
484 | mlog_errno(status); |
485 | break; |
486 | } |
487 | dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data; |
488 | for_each_set_bit(bit, rchunk->rc_bitmap, ol_chunk_entries(sb)) { |
489 | qbh = NULL; |
490 | status = ocfs2_read_quota_block(inode: lqinode, |
491 | v_block: ol_dqblk_block(sb, c: chunk, off: bit), |
492 | bh: &qbh); |
493 | if (status) { |
494 | mlog_errno(status); |
495 | break; |
496 | } |
497 | dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data + |
498 | ol_dqblk_block_off(sb, c: chunk, off: bit)); |
499 | dquot = dqget(sb, |
500 | qid: make_kqid(from: &init_user_ns, type, |
501 | le64_to_cpu(dqblk->dqb_id))); |
502 | if (IS_ERR(ptr: dquot)) { |
503 | status = PTR_ERR(ptr: dquot); |
504 | mlog(ML_ERROR, "Failed to get quota structure " |
505 | "for id %u, type %d. Cannot finish quota " |
506 | "file recovery.\n" , |
507 | (unsigned)le64_to_cpu(dqblk->dqb_id), |
508 | type); |
509 | goto out_put_bh; |
510 | } |
511 | status = ocfs2_lock_global_qf(oinfo, ex: 1); |
512 | if (status < 0) { |
513 | mlog_errno(status); |
514 | goto out_put_dquot; |
515 | } |
516 | |
517 | handle = ocfs2_start_trans(OCFS2_SB(sb), |
518 | OCFS2_QSYNC_CREDITS); |
519 | if (IS_ERR(ptr: handle)) { |
520 | status = PTR_ERR(ptr: handle); |
521 | mlog_errno(status); |
522 | goto out_drop_lock; |
523 | } |
524 | down_write(sem: &sb_dqopt(sb)->dqio_sem); |
525 | memalloc = memalloc_nofs_save(); |
526 | spin_lock(lock: &dquot->dq_dqb_lock); |
527 | /* Add usage from quota entry into quota changes |
528 | * of our node. Auxiliary variables are important |
529 | * due to signedness */ |
530 | spacechange = le64_to_cpu(dqblk->dqb_spacemod); |
531 | inodechange = le64_to_cpu(dqblk->dqb_inodemod); |
532 | dquot->dq_dqb.dqb_curspace += spacechange; |
533 | dquot->dq_dqb.dqb_curinodes += inodechange; |
534 | spin_unlock(lock: &dquot->dq_dqb_lock); |
535 | /* We want to drop reference held by the crashed |
536 | * node. Since we have our own reference we know |
537 | * global structure actually won't be freed. */ |
538 | status = ocfs2_global_release_dquot(dquot); |
539 | if (status < 0) { |
540 | mlog_errno(status); |
541 | goto out_commit; |
542 | } |
543 | /* Release local quota file entry */ |
544 | status = ocfs2_journal_access_dq(handle, |
545 | ci: INODE_CACHE(inode: lqinode), |
546 | bh: qbh, OCFS2_JOURNAL_ACCESS_WRITE); |
547 | if (status < 0) { |
548 | mlog_errno(status); |
549 | goto out_commit; |
550 | } |
551 | lock_buffer(bh: qbh); |
552 | WARN_ON(!ocfs2_test_bit_unaligned(bit, dchunk->dqc_bitmap)); |
553 | ocfs2_clear_bit_unaligned(bit, bitmap: dchunk->dqc_bitmap); |
554 | le32_add_cpu(var: &dchunk->dqc_free, val: 1); |
555 | unlock_buffer(bh: qbh); |
556 | ocfs2_journal_dirty(handle, bh: qbh); |
557 | out_commit: |
558 | memalloc_nofs_restore(flags: memalloc); |
559 | up_write(sem: &sb_dqopt(sb)->dqio_sem); |
560 | ocfs2_commit_trans(OCFS2_SB(sb), handle); |
561 | out_drop_lock: |
562 | ocfs2_unlock_global_qf(oinfo, ex: 1); |
563 | out_put_dquot: |
564 | dqput(dquot); |
565 | out_put_bh: |
566 | brelse(bh: qbh); |
567 | if (status < 0) |
568 | break; |
569 | } |
570 | brelse(bh: hbh); |
571 | list_del(entry: &rchunk->rc_list); |
572 | kfree(objp: rchunk->rc_bitmap); |
573 | kfree(objp: rchunk); |
574 | if (status < 0) |
575 | break; |
576 | } |
577 | if (status < 0) |
578 | free_recovery_list(head: &(rec->r_list[type])); |
579 | if (status) |
580 | mlog_errno(status); |
581 | return status; |
582 | } |
583 | |
584 | /* Recover local quota files for given node different from us */ |
585 | int ocfs2_finish_quota_recovery(struct ocfs2_super *osb, |
586 | struct ocfs2_quota_recovery *rec, |
587 | int slot_num) |
588 | { |
589 | unsigned int ino[OCFS2_MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE, |
590 | LOCAL_GROUP_QUOTA_SYSTEM_INODE }; |
591 | struct super_block *sb = osb->sb; |
592 | struct ocfs2_local_disk_dqinfo *ldinfo; |
593 | struct buffer_head *bh; |
594 | handle_t *handle; |
595 | int type; |
596 | int status = 0; |
597 | struct inode *lqinode; |
598 | unsigned int flags; |
599 | |
600 | printk(KERN_NOTICE "ocfs2: Finishing quota recovery on device (%s) for " |
601 | "slot %u\n" , osb->dev_str, slot_num); |
602 | |
603 | down_read(sem: &sb->s_umount); |
604 | for (type = 0; type < OCFS2_MAXQUOTAS; type++) { |
605 | if (list_empty(head: &(rec->r_list[type]))) |
606 | continue; |
607 | trace_ocfs2_finish_quota_recovery(num: slot_num); |
608 | lqinode = ocfs2_get_system_file_inode(osb, type: ino[type], slot: slot_num); |
609 | if (!lqinode) { |
610 | status = -ENOENT; |
611 | goto out; |
612 | } |
613 | status = ocfs2_inode_lock_full(lqinode, NULL, 1, |
614 | OCFS2_META_LOCK_NOQUEUE); |
615 | /* Someone else is holding the lock? Then he must be |
616 | * doing the recovery. Just skip the file... */ |
617 | if (status == -EAGAIN) { |
618 | printk(KERN_NOTICE "ocfs2: Skipping quota recovery on " |
619 | "device (%s) for slot %d because quota file is " |
620 | "locked.\n" , osb->dev_str, slot_num); |
621 | status = 0; |
622 | goto out_put; |
623 | } else if (status < 0) { |
624 | mlog_errno(status); |
625 | goto out_put; |
626 | } |
627 | /* Now read local header */ |
628 | bh = NULL; |
629 | status = ocfs2_read_quota_block(inode: lqinode, v_block: 0, bh: &bh); |
630 | if (status) { |
631 | mlog_errno(status); |
632 | mlog(ML_ERROR, "failed to read quota file info header " |
633 | "(slot=%d type=%d)\n" , slot_num, type); |
634 | goto out_lock; |
635 | } |
636 | ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + |
637 | OCFS2_LOCAL_INFO_OFF); |
638 | /* Is recovery still needed? */ |
639 | flags = le32_to_cpu(ldinfo->dqi_flags); |
640 | if (!(flags & OLQF_CLEAN)) |
641 | status = ocfs2_recover_local_quota_file(lqinode, |
642 | type, |
643 | rec); |
644 | /* We don't want to mark file as clean when it is actually |
645 | * active */ |
646 | if (slot_num == osb->slot_num) |
647 | goto out_bh; |
648 | /* Mark quota file as clean if we are recovering quota file of |
649 | * some other node. */ |
650 | handle = ocfs2_start_trans(osb, |
651 | OCFS2_LOCAL_QINFO_WRITE_CREDITS); |
652 | if (IS_ERR(ptr: handle)) { |
653 | status = PTR_ERR(ptr: handle); |
654 | mlog_errno(status); |
655 | goto out_bh; |
656 | } |
657 | status = ocfs2_journal_access_dq(handle, ci: INODE_CACHE(inode: lqinode), |
658 | bh, |
659 | OCFS2_JOURNAL_ACCESS_WRITE); |
660 | if (status < 0) { |
661 | mlog_errno(status); |
662 | goto out_trans; |
663 | } |
664 | lock_buffer(bh); |
665 | ldinfo->dqi_flags = cpu_to_le32(flags | OLQF_CLEAN); |
666 | unlock_buffer(bh); |
667 | ocfs2_journal_dirty(handle, bh); |
668 | out_trans: |
669 | ocfs2_commit_trans(osb, handle); |
670 | out_bh: |
671 | brelse(bh); |
672 | out_lock: |
673 | ocfs2_inode_unlock(inode: lqinode, ex: 1); |
674 | out_put: |
675 | iput(lqinode); |
676 | if (status < 0) |
677 | break; |
678 | } |
679 | out: |
680 | up_read(sem: &sb->s_umount); |
681 | kfree(objp: rec); |
682 | return status; |
683 | } |
684 | |
685 | /* Read information header from quota file */ |
686 | static int ocfs2_local_read_info(struct super_block *sb, int type) |
687 | { |
688 | struct ocfs2_local_disk_dqinfo *ldinfo; |
689 | struct mem_dqinfo *info = sb_dqinfo(sb, type); |
690 | struct ocfs2_mem_dqinfo *oinfo; |
691 | struct inode *lqinode = sb_dqopt(sb)->files[type]; |
692 | int status; |
693 | struct buffer_head *bh = NULL; |
694 | struct ocfs2_quota_recovery *rec; |
695 | int locked = 0; |
696 | |
697 | info->dqi_max_spc_limit = 0x7fffffffffffffffLL; |
698 | info->dqi_max_ino_limit = 0x7fffffffffffffffLL; |
699 | oinfo = kmalloc(size: sizeof(struct ocfs2_mem_dqinfo), GFP_NOFS); |
700 | if (!oinfo) { |
701 | mlog(ML_ERROR, "failed to allocate memory for ocfs2 quota" |
702 | " info." ); |
703 | goto out_err; |
704 | } |
705 | info->dqi_priv = oinfo; |
706 | oinfo->dqi_type = type; |
707 | INIT_LIST_HEAD(list: &oinfo->dqi_chunk); |
708 | oinfo->dqi_rec = NULL; |
709 | oinfo->dqi_lqi_bh = NULL; |
710 | oinfo->dqi_libh = NULL; |
711 | |
712 | status = ocfs2_global_read_info(sb, type); |
713 | if (status < 0) |
714 | goto out_err; |
715 | |
716 | status = ocfs2_inode_lock(lqinode, &oinfo->dqi_lqi_bh, 1); |
717 | if (status < 0) { |
718 | mlog_errno(status); |
719 | goto out_err; |
720 | } |
721 | locked = 1; |
722 | |
723 | /* Now read local header */ |
724 | status = ocfs2_read_quota_block(inode: lqinode, v_block: 0, bh: &bh); |
725 | if (status) { |
726 | mlog_errno(status); |
727 | mlog(ML_ERROR, "failed to read quota file info header " |
728 | "(type=%d)\n" , type); |
729 | goto out_err; |
730 | } |
731 | ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + |
732 | OCFS2_LOCAL_INFO_OFF); |
733 | oinfo->dqi_flags = le32_to_cpu(ldinfo->dqi_flags); |
734 | oinfo->dqi_chunks = le32_to_cpu(ldinfo->dqi_chunks); |
735 | oinfo->dqi_blocks = le32_to_cpu(ldinfo->dqi_blocks); |
736 | oinfo->dqi_libh = bh; |
737 | |
738 | /* We crashed when using local quota file? */ |
739 | if (!(oinfo->dqi_flags & OLQF_CLEAN)) { |
740 | rec = OCFS2_SB(sb)->quota_rec; |
741 | if (!rec) { |
742 | rec = ocfs2_alloc_quota_recovery(); |
743 | if (!rec) { |
744 | status = -ENOMEM; |
745 | mlog_errno(status); |
746 | goto out_err; |
747 | } |
748 | OCFS2_SB(sb)->quota_rec = rec; |
749 | } |
750 | |
751 | status = ocfs2_recovery_load_quota(lqinode, ldinfo, type, |
752 | head: &rec->r_list[type]); |
753 | if (status < 0) { |
754 | mlog_errno(status); |
755 | goto out_err; |
756 | } |
757 | } |
758 | |
759 | status = ocfs2_load_local_quota_bitmaps(inode: lqinode, |
760 | ldinfo, |
761 | head: &oinfo->dqi_chunk); |
762 | if (status < 0) { |
763 | mlog_errno(status); |
764 | goto out_err; |
765 | } |
766 | |
767 | /* Now mark quota file as used */ |
768 | oinfo->dqi_flags &= ~OLQF_CLEAN; |
769 | status = ocfs2_modify_bh(inode: lqinode, bh, modify: olq_update_info, private: info); |
770 | if (status < 0) { |
771 | mlog_errno(status); |
772 | goto out_err; |
773 | } |
774 | |
775 | return 0; |
776 | out_err: |
777 | if (oinfo) { |
778 | iput(oinfo->dqi_gqinode); |
779 | ocfs2_simple_drop_lockres(OCFS2_SB(sb), lockres: &oinfo->dqi_gqlock); |
780 | ocfs2_lock_res_free(res: &oinfo->dqi_gqlock); |
781 | brelse(bh: oinfo->dqi_lqi_bh); |
782 | if (locked) |
783 | ocfs2_inode_unlock(inode: lqinode, ex: 1); |
784 | ocfs2_release_local_quota_bitmaps(head: &oinfo->dqi_chunk); |
785 | kfree(objp: oinfo); |
786 | } |
787 | brelse(bh); |
788 | return -1; |
789 | } |
790 | |
791 | /* Write local info to quota file */ |
792 | static int ocfs2_local_write_info(struct super_block *sb, int type) |
793 | { |
794 | struct mem_dqinfo *info = sb_dqinfo(sb, type); |
795 | struct buffer_head *bh = ((struct ocfs2_mem_dqinfo *)info->dqi_priv) |
796 | ->dqi_libh; |
797 | int status; |
798 | |
799 | status = ocfs2_modify_bh(inode: sb_dqopt(sb)->files[type], bh, modify: olq_update_info, |
800 | private: info); |
801 | if (status < 0) { |
802 | mlog_errno(status); |
803 | return -1; |
804 | } |
805 | |
806 | return 0; |
807 | } |
808 | |
809 | /* Release info from memory */ |
810 | static int ocfs2_local_free_info(struct super_block *sb, int type) |
811 | { |
812 | struct mem_dqinfo *info = sb_dqinfo(sb, type); |
813 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; |
814 | struct ocfs2_quota_chunk *chunk; |
815 | struct ocfs2_local_disk_chunk *dchunk; |
816 | int mark_clean = 1, len; |
817 | int status = 0; |
818 | |
819 | iput(oinfo->dqi_gqinode); |
820 | ocfs2_simple_drop_lockres(OCFS2_SB(sb), lockres: &oinfo->dqi_gqlock); |
821 | ocfs2_lock_res_free(res: &oinfo->dqi_gqlock); |
822 | list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) { |
823 | dchunk = (struct ocfs2_local_disk_chunk *) |
824 | (chunk->qc_headerbh->b_data); |
825 | if (chunk->qc_num < oinfo->dqi_chunks - 1) { |
826 | len = ol_chunk_entries(sb); |
827 | } else { |
828 | len = (oinfo->dqi_blocks - |
829 | ol_quota_chunk_block(sb, c: chunk->qc_num) - 1) |
830 | * ol_quota_entries_per_block(sb); |
831 | } |
832 | /* Not all entries free? Bug! */ |
833 | if (le32_to_cpu(dchunk->dqc_free) != len) { |
834 | mlog(ML_ERROR, "releasing quota file with used " |
835 | "entries (type=%d)\n" , type); |
836 | mark_clean = 0; |
837 | } |
838 | } |
839 | ocfs2_release_local_quota_bitmaps(head: &oinfo->dqi_chunk); |
840 | |
841 | /* |
842 | * s_umount held in exclusive mode protects us against racing with |
843 | * recovery thread... |
844 | */ |
845 | if (oinfo->dqi_rec) { |
846 | ocfs2_free_quota_recovery(rec: oinfo->dqi_rec); |
847 | mark_clean = 0; |
848 | } |
849 | |
850 | if (!mark_clean) |
851 | goto out; |
852 | |
853 | /* Mark local file as clean */ |
854 | oinfo->dqi_flags |= OLQF_CLEAN; |
855 | status = ocfs2_modify_bh(inode: sb_dqopt(sb)->files[type], |
856 | bh: oinfo->dqi_libh, |
857 | modify: olq_update_info, |
858 | private: info); |
859 | if (status < 0) |
860 | mlog_errno(status); |
861 | out: |
862 | ocfs2_inode_unlock(inode: sb_dqopt(sb)->files[type], ex: 1); |
863 | brelse(bh: oinfo->dqi_libh); |
864 | brelse(bh: oinfo->dqi_lqi_bh); |
865 | kfree(objp: oinfo); |
866 | return status; |
867 | } |
868 | |
869 | static void olq_set_dquot(struct buffer_head *bh, void *private) |
870 | { |
871 | struct ocfs2_dquot *od = private; |
872 | struct ocfs2_local_disk_dqblk *dqblk; |
873 | struct super_block *sb = od->dq_dquot.dq_sb; |
874 | |
875 | dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data |
876 | + ol_dqblk_block_offset(sb, off: od->dq_local_off)); |
877 | |
878 | dqblk->dqb_id = cpu_to_le64(from_kqid(&init_user_ns, |
879 | od->dq_dquot.dq_id)); |
880 | spin_lock(lock: &od->dq_dquot.dq_dqb_lock); |
881 | dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace - |
882 | od->dq_origspace); |
883 | dqblk->dqb_inodemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curinodes - |
884 | od->dq_originodes); |
885 | spin_unlock(lock: &od->dq_dquot.dq_dqb_lock); |
886 | trace_olq_set_dquot( |
887 | val1: (unsigned long long)le64_to_cpu(dqblk->dqb_spacemod), |
888 | val2: (unsigned long long)le64_to_cpu(dqblk->dqb_inodemod), |
889 | val3: from_kqid(to: &init_user_ns, qid: od->dq_dquot.dq_id)); |
890 | } |
891 | |
892 | /* Write dquot to local quota file */ |
893 | int ocfs2_local_write_dquot(struct dquot *dquot) |
894 | { |
895 | struct super_block *sb = dquot->dq_sb; |
896 | struct ocfs2_dquot *od = OCFS2_DQUOT(dquot); |
897 | struct buffer_head *bh; |
898 | struct inode *lqinode = sb_dqopt(sb)->files[dquot->dq_id.type]; |
899 | int status; |
900 | |
901 | status = ocfs2_read_quota_phys_block(inode: lqinode, p_block: od->dq_local_phys_blk, |
902 | bh: &bh); |
903 | if (status) { |
904 | mlog_errno(status); |
905 | goto out; |
906 | } |
907 | status = ocfs2_modify_bh(inode: lqinode, bh, modify: olq_set_dquot, private: od); |
908 | if (status < 0) { |
909 | mlog_errno(status); |
910 | goto out; |
911 | } |
912 | out: |
913 | brelse(bh); |
914 | return status; |
915 | } |
916 | |
917 | /* Find free entry in local quota file */ |
918 | static struct ocfs2_quota_chunk *ocfs2_find_free_entry(struct super_block *sb, |
919 | int type, |
920 | int *offset) |
921 | { |
922 | struct mem_dqinfo *info = sb_dqinfo(sb, type); |
923 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; |
924 | struct ocfs2_quota_chunk *chunk = NULL, *iter; |
925 | struct ocfs2_local_disk_chunk *dchunk; |
926 | int found = 0, len; |
927 | |
928 | list_for_each_entry(iter, &oinfo->dqi_chunk, qc_chunk) { |
929 | dchunk = (struct ocfs2_local_disk_chunk *) |
930 | iter->qc_headerbh->b_data; |
931 | if (le32_to_cpu(dchunk->dqc_free) > 0) { |
932 | chunk = iter; |
933 | break; |
934 | } |
935 | } |
936 | if (!chunk) |
937 | return NULL; |
938 | |
939 | if (chunk->qc_num < oinfo->dqi_chunks - 1) { |
940 | len = ol_chunk_entries(sb); |
941 | } else { |
942 | len = (oinfo->dqi_blocks - |
943 | ol_quota_chunk_block(sb, c: chunk->qc_num) - 1) |
944 | * ol_quota_entries_per_block(sb); |
945 | } |
946 | |
947 | found = ocfs2_find_next_zero_bit_unaligned(bitmap: dchunk->dqc_bitmap, max: len, start: 0); |
948 | /* We failed? */ |
949 | if (found == len) { |
950 | mlog(ML_ERROR, "Did not find empty entry in chunk %d with %u" |
951 | " entries free (type=%d)\n" , chunk->qc_num, |
952 | le32_to_cpu(dchunk->dqc_free), type); |
953 | return ERR_PTR(error: -EIO); |
954 | } |
955 | *offset = found; |
956 | return chunk; |
957 | } |
958 | |
959 | /* Add new chunk to the local quota file */ |
960 | static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk( |
961 | struct super_block *sb, |
962 | int type, |
963 | int *offset) |
964 | { |
965 | struct mem_dqinfo *info = sb_dqinfo(sb, type); |
966 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; |
967 | struct inode *lqinode = sb_dqopt(sb)->files[type]; |
968 | struct ocfs2_quota_chunk *chunk = NULL; |
969 | struct ocfs2_local_disk_chunk *dchunk; |
970 | int status; |
971 | handle_t *handle; |
972 | struct buffer_head *bh = NULL, *dbh = NULL; |
973 | u64 p_blkno; |
974 | |
975 | /* We are protected by dqio_sem so no locking needed */ |
976 | status = ocfs2_extend_no_holes(inode: lqinode, NULL, |
977 | new_i_size: i_size_read(inode: lqinode) + 2 * sb->s_blocksize, |
978 | zero_to: i_size_read(inode: lqinode)); |
979 | if (status < 0) { |
980 | mlog_errno(status); |
981 | goto out; |
982 | } |
983 | status = ocfs2_simple_size_update(inode: lqinode, di_bh: oinfo->dqi_lqi_bh, |
984 | new_i_size: i_size_read(inode: lqinode) + 2 * sb->s_blocksize); |
985 | if (status < 0) { |
986 | mlog_errno(status); |
987 | goto out; |
988 | } |
989 | |
990 | chunk = kmem_cache_alloc(cachep: ocfs2_qf_chunk_cachep, GFP_NOFS); |
991 | if (!chunk) { |
992 | status = -ENOMEM; |
993 | mlog_errno(status); |
994 | goto out; |
995 | } |
996 | /* Local quota info and two new blocks we initialize */ |
997 | handle = ocfs2_start_trans(OCFS2_SB(sb), |
998 | OCFS2_LOCAL_QINFO_WRITE_CREDITS + |
999 | 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS); |
1000 | if (IS_ERR(ptr: handle)) { |
1001 | status = PTR_ERR(ptr: handle); |
1002 | mlog_errno(status); |
1003 | goto out; |
1004 | } |
1005 | |
1006 | /* Initialize chunk header */ |
1007 | status = ocfs2_extent_map_get_blocks(inode: lqinode, v_blkno: oinfo->dqi_blocks, |
1008 | p_blkno: &p_blkno, NULL, NULL); |
1009 | if (status < 0) { |
1010 | mlog_errno(status); |
1011 | goto out_trans; |
1012 | } |
1013 | bh = sb_getblk(sb, block: p_blkno); |
1014 | if (!bh) { |
1015 | status = -ENOMEM; |
1016 | mlog_errno(status); |
1017 | goto out_trans; |
1018 | } |
1019 | dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data; |
1020 | ocfs2_set_new_buffer_uptodate(ci: INODE_CACHE(inode: lqinode), bh); |
1021 | status = ocfs2_journal_access_dq(handle, ci: INODE_CACHE(inode: lqinode), bh, |
1022 | OCFS2_JOURNAL_ACCESS_CREATE); |
1023 | if (status < 0) { |
1024 | mlog_errno(status); |
1025 | goto out_trans; |
1026 | } |
1027 | lock_buffer(bh); |
1028 | dchunk->dqc_free = cpu_to_le32(ol_quota_entries_per_block(sb)); |
1029 | memset(dchunk->dqc_bitmap, 0, |
1030 | sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) - |
1031 | OCFS2_QBLK_RESERVED_SPACE); |
1032 | unlock_buffer(bh); |
1033 | ocfs2_journal_dirty(handle, bh); |
1034 | |
1035 | /* Initialize new block with structures */ |
1036 | status = ocfs2_extent_map_get_blocks(inode: lqinode, v_blkno: oinfo->dqi_blocks + 1, |
1037 | p_blkno: &p_blkno, NULL, NULL); |
1038 | if (status < 0) { |
1039 | mlog_errno(status); |
1040 | goto out_trans; |
1041 | } |
1042 | dbh = sb_getblk(sb, block: p_blkno); |
1043 | if (!dbh) { |
1044 | status = -ENOMEM; |
1045 | mlog_errno(status); |
1046 | goto out_trans; |
1047 | } |
1048 | ocfs2_set_new_buffer_uptodate(ci: INODE_CACHE(inode: lqinode), bh: dbh); |
1049 | status = ocfs2_journal_access_dq(handle, ci: INODE_CACHE(inode: lqinode), bh: dbh, |
1050 | OCFS2_JOURNAL_ACCESS_CREATE); |
1051 | if (status < 0) { |
1052 | mlog_errno(status); |
1053 | goto out_trans; |
1054 | } |
1055 | lock_buffer(bh: dbh); |
1056 | memset(dbh->b_data, 0, sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE); |
1057 | unlock_buffer(bh: dbh); |
1058 | ocfs2_journal_dirty(handle, bh: dbh); |
1059 | |
1060 | /* Update local quotafile info */ |
1061 | oinfo->dqi_blocks += 2; |
1062 | oinfo->dqi_chunks++; |
1063 | status = ocfs2_local_write_info(sb, type); |
1064 | if (status < 0) { |
1065 | mlog_errno(status); |
1066 | goto out_trans; |
1067 | } |
1068 | status = ocfs2_commit_trans(OCFS2_SB(sb), handle); |
1069 | if (status < 0) { |
1070 | mlog_errno(status); |
1071 | goto out; |
1072 | } |
1073 | |
1074 | list_add_tail(new: &chunk->qc_chunk, head: &oinfo->dqi_chunk); |
1075 | chunk->qc_num = list_entry(chunk->qc_chunk.prev, |
1076 | struct ocfs2_quota_chunk, |
1077 | qc_chunk)->qc_num + 1; |
1078 | chunk->qc_headerbh = bh; |
1079 | *offset = 0; |
1080 | return chunk; |
1081 | out_trans: |
1082 | ocfs2_commit_trans(OCFS2_SB(sb), handle); |
1083 | out: |
1084 | brelse(bh); |
1085 | brelse(bh: dbh); |
1086 | kmem_cache_free(s: ocfs2_qf_chunk_cachep, objp: chunk); |
1087 | return ERR_PTR(error: status); |
1088 | } |
1089 | |
1090 | /* Find free entry in local quota file */ |
1091 | static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file( |
1092 | struct super_block *sb, |
1093 | int type, |
1094 | int *offset) |
1095 | { |
1096 | struct mem_dqinfo *info = sb_dqinfo(sb, type); |
1097 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; |
1098 | struct ocfs2_quota_chunk *chunk; |
1099 | struct inode *lqinode = sb_dqopt(sb)->files[type]; |
1100 | struct ocfs2_local_disk_chunk *dchunk; |
1101 | int epb = ol_quota_entries_per_block(sb); |
1102 | unsigned int chunk_blocks; |
1103 | struct buffer_head *bh; |
1104 | u64 p_blkno; |
1105 | int status; |
1106 | handle_t *handle; |
1107 | |
1108 | if (list_empty(head: &oinfo->dqi_chunk)) |
1109 | return ocfs2_local_quota_add_chunk(sb, type, offset); |
1110 | /* Is the last chunk full? */ |
1111 | chunk = list_entry(oinfo->dqi_chunk.prev, |
1112 | struct ocfs2_quota_chunk, qc_chunk); |
1113 | chunk_blocks = oinfo->dqi_blocks - |
1114 | ol_quota_chunk_block(sb, c: chunk->qc_num) - 1; |
1115 | if (ol_chunk_blocks(sb) == chunk_blocks) |
1116 | return ocfs2_local_quota_add_chunk(sb, type, offset); |
1117 | |
1118 | /* We are protected by dqio_sem so no locking needed */ |
1119 | status = ocfs2_extend_no_holes(inode: lqinode, NULL, |
1120 | new_i_size: i_size_read(inode: lqinode) + sb->s_blocksize, |
1121 | zero_to: i_size_read(inode: lqinode)); |
1122 | if (status < 0) { |
1123 | mlog_errno(status); |
1124 | goto out; |
1125 | } |
1126 | status = ocfs2_simple_size_update(inode: lqinode, di_bh: oinfo->dqi_lqi_bh, |
1127 | new_i_size: i_size_read(inode: lqinode) + sb->s_blocksize); |
1128 | if (status < 0) { |
1129 | mlog_errno(status); |
1130 | goto out; |
1131 | } |
1132 | |
1133 | /* Get buffer from the just added block */ |
1134 | status = ocfs2_extent_map_get_blocks(inode: lqinode, v_blkno: oinfo->dqi_blocks, |
1135 | p_blkno: &p_blkno, NULL, NULL); |
1136 | if (status < 0) { |
1137 | mlog_errno(status); |
1138 | goto out; |
1139 | } |
1140 | bh = sb_getblk(sb, block: p_blkno); |
1141 | if (!bh) { |
1142 | status = -ENOMEM; |
1143 | mlog_errno(status); |
1144 | goto out; |
1145 | } |
1146 | ocfs2_set_new_buffer_uptodate(ci: INODE_CACHE(inode: lqinode), bh); |
1147 | |
1148 | /* Local quota info, chunk header and the new block we initialize */ |
1149 | handle = ocfs2_start_trans(OCFS2_SB(sb), |
1150 | OCFS2_LOCAL_QINFO_WRITE_CREDITS + |
1151 | 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS); |
1152 | if (IS_ERR(ptr: handle)) { |
1153 | status = PTR_ERR(ptr: handle); |
1154 | mlog_errno(status); |
1155 | goto out; |
1156 | } |
1157 | /* Zero created block */ |
1158 | status = ocfs2_journal_access_dq(handle, ci: INODE_CACHE(inode: lqinode), bh, |
1159 | OCFS2_JOURNAL_ACCESS_CREATE); |
1160 | if (status < 0) { |
1161 | mlog_errno(status); |
1162 | goto out_trans; |
1163 | } |
1164 | lock_buffer(bh); |
1165 | memset(bh->b_data, 0, sb->s_blocksize); |
1166 | unlock_buffer(bh); |
1167 | ocfs2_journal_dirty(handle, bh); |
1168 | |
1169 | /* Update chunk header */ |
1170 | status = ocfs2_journal_access_dq(handle, ci: INODE_CACHE(inode: lqinode), |
1171 | bh: chunk->qc_headerbh, |
1172 | OCFS2_JOURNAL_ACCESS_WRITE); |
1173 | if (status < 0) { |
1174 | mlog_errno(status); |
1175 | goto out_trans; |
1176 | } |
1177 | |
1178 | dchunk = (struct ocfs2_local_disk_chunk *)chunk->qc_headerbh->b_data; |
1179 | lock_buffer(bh: chunk->qc_headerbh); |
1180 | le32_add_cpu(var: &dchunk->dqc_free, val: ol_quota_entries_per_block(sb)); |
1181 | unlock_buffer(bh: chunk->qc_headerbh); |
1182 | ocfs2_journal_dirty(handle, bh: chunk->qc_headerbh); |
1183 | |
1184 | /* Update file header */ |
1185 | oinfo->dqi_blocks++; |
1186 | status = ocfs2_local_write_info(sb, type); |
1187 | if (status < 0) { |
1188 | mlog_errno(status); |
1189 | goto out_trans; |
1190 | } |
1191 | |
1192 | status = ocfs2_commit_trans(OCFS2_SB(sb), handle); |
1193 | if (status < 0) { |
1194 | mlog_errno(status); |
1195 | goto out; |
1196 | } |
1197 | *offset = chunk_blocks * epb; |
1198 | return chunk; |
1199 | out_trans: |
1200 | ocfs2_commit_trans(OCFS2_SB(sb), handle); |
1201 | out: |
1202 | return ERR_PTR(error: status); |
1203 | } |
1204 | |
1205 | static void olq_alloc_dquot(struct buffer_head *bh, void *private) |
1206 | { |
1207 | int *offset = private; |
1208 | struct ocfs2_local_disk_chunk *dchunk; |
1209 | |
1210 | dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data; |
1211 | ocfs2_set_bit_unaligned(bit: *offset, bitmap: dchunk->dqc_bitmap); |
1212 | le32_add_cpu(var: &dchunk->dqc_free, val: -1); |
1213 | } |
1214 | |
1215 | /* Create dquot in the local file for given id */ |
1216 | int ocfs2_create_local_dquot(struct dquot *dquot) |
1217 | { |
1218 | struct super_block *sb = dquot->dq_sb; |
1219 | int type = dquot->dq_id.type; |
1220 | struct inode *lqinode = sb_dqopt(sb)->files[type]; |
1221 | struct ocfs2_quota_chunk *chunk; |
1222 | struct ocfs2_dquot *od = OCFS2_DQUOT(dquot); |
1223 | int offset; |
1224 | int status; |
1225 | u64 pcount; |
1226 | |
1227 | down_write(sem: &OCFS2_I(inode: lqinode)->ip_alloc_sem); |
1228 | chunk = ocfs2_find_free_entry(sb, type, offset: &offset); |
1229 | if (!chunk) { |
1230 | chunk = ocfs2_extend_local_quota_file(sb, type, offset: &offset); |
1231 | if (IS_ERR(ptr: chunk)) { |
1232 | status = PTR_ERR(ptr: chunk); |
1233 | goto out; |
1234 | } |
1235 | } else if (IS_ERR(ptr: chunk)) { |
1236 | status = PTR_ERR(ptr: chunk); |
1237 | goto out; |
1238 | } |
1239 | od->dq_local_off = ol_dqblk_off(sb, c: chunk->qc_num, off: offset); |
1240 | od->dq_chunk = chunk; |
1241 | status = ocfs2_extent_map_get_blocks(inode: lqinode, |
1242 | v_blkno: ol_dqblk_block(sb, c: chunk->qc_num, off: offset), |
1243 | p_blkno: &od->dq_local_phys_blk, |
1244 | ret_count: &pcount, |
1245 | NULL); |
1246 | if (status < 0) { |
1247 | mlog_errno(status); |
1248 | goto out; |
1249 | } |
1250 | |
1251 | /* Initialize dquot structure on disk */ |
1252 | status = ocfs2_local_write_dquot(dquot); |
1253 | if (status < 0) { |
1254 | mlog_errno(status); |
1255 | goto out; |
1256 | } |
1257 | |
1258 | /* Mark structure as allocated */ |
1259 | status = ocfs2_modify_bh(inode: lqinode, bh: chunk->qc_headerbh, modify: olq_alloc_dquot, |
1260 | private: &offset); |
1261 | if (status < 0) { |
1262 | mlog_errno(status); |
1263 | goto out; |
1264 | } |
1265 | out: |
1266 | up_write(sem: &OCFS2_I(inode: lqinode)->ip_alloc_sem); |
1267 | return status; |
1268 | } |
1269 | |
1270 | /* |
1271 | * Release dquot structure from local quota file. ocfs2_release_dquot() has |
1272 | * already started a transaction and written all changes to global quota file |
1273 | */ |
1274 | int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot) |
1275 | { |
1276 | int status; |
1277 | int type = dquot->dq_id.type; |
1278 | struct ocfs2_dquot *od = OCFS2_DQUOT(dquot); |
1279 | struct super_block *sb = dquot->dq_sb; |
1280 | struct ocfs2_local_disk_chunk *dchunk; |
1281 | int offset; |
1282 | |
1283 | status = ocfs2_journal_access_dq(handle, |
1284 | ci: INODE_CACHE(inode: sb_dqopt(sb)->files[type]), |
1285 | bh: od->dq_chunk->qc_headerbh, OCFS2_JOURNAL_ACCESS_WRITE); |
1286 | if (status < 0) { |
1287 | mlog_errno(status); |
1288 | goto out; |
1289 | } |
1290 | offset = ol_dqblk_chunk_off(sb, c: od->dq_chunk->qc_num, |
1291 | off: od->dq_local_off); |
1292 | dchunk = (struct ocfs2_local_disk_chunk *) |
1293 | (od->dq_chunk->qc_headerbh->b_data); |
1294 | /* Mark structure as freed */ |
1295 | lock_buffer(bh: od->dq_chunk->qc_headerbh); |
1296 | ocfs2_clear_bit_unaligned(bit: offset, bitmap: dchunk->dqc_bitmap); |
1297 | le32_add_cpu(var: &dchunk->dqc_free, val: 1); |
1298 | unlock_buffer(bh: od->dq_chunk->qc_headerbh); |
1299 | ocfs2_journal_dirty(handle, bh: od->dq_chunk->qc_headerbh); |
1300 | |
1301 | out: |
1302 | return status; |
1303 | } |
1304 | |
1305 | static const struct quota_format_ops ocfs2_format_ops = { |
1306 | .check_quota_file = ocfs2_local_check_quota_file, |
1307 | .read_file_info = ocfs2_local_read_info, |
1308 | .write_file_info = ocfs2_global_write_info, |
1309 | .free_file_info = ocfs2_local_free_info, |
1310 | }; |
1311 | |
1312 | struct quota_format_type ocfs2_quota_format = { |
1313 | .qf_fmt_id = QFMT_OCFS2, |
1314 | .qf_ops = &ocfs2_format_ops, |
1315 | .qf_owner = THIS_MODULE |
1316 | }; |
1317 | |