diff options
Diffstat (limited to 'fs/ocfs2/quota_local.c')
-rw-r--r-- | fs/ocfs2/quota_local.c | 833 |
1 files changed, 833 insertions, 0 deletions
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c new file mode 100644 index 000000000000..55c3f2f98dcd --- /dev/null +++ b/fs/ocfs2/quota_local.c | |||
@@ -0,0 +1,833 @@ | |||
1 | /* | ||
2 | * Implementation of operations over local quota file | ||
3 | */ | ||
4 | |||
5 | #include <linux/fs.h> | ||
6 | #include <linux/quota.h> | ||
7 | #include <linux/quotaops.h> | ||
8 | #include <linux/module.h> | ||
9 | |||
10 | #define MLOG_MASK_PREFIX ML_QUOTA | ||
11 | #include <cluster/masklog.h> | ||
12 | |||
13 | #include "ocfs2_fs.h" | ||
14 | #include "ocfs2.h" | ||
15 | #include "inode.h" | ||
16 | #include "alloc.h" | ||
17 | #include "file.h" | ||
18 | #include "buffer_head_io.h" | ||
19 | #include "journal.h" | ||
20 | #include "sysfile.h" | ||
21 | #include "dlmglue.h" | ||
22 | #include "quota.h" | ||
23 | |||
24 | /* Number of local quota structures per block */ | ||
25 | static inline unsigned int ol_quota_entries_per_block(struct super_block *sb) | ||
26 | { | ||
27 | return ((sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE) / | ||
28 | sizeof(struct ocfs2_local_disk_dqblk)); | ||
29 | } | ||
30 | |||
31 | /* Number of blocks with entries in one chunk */ | ||
32 | static inline unsigned int ol_chunk_blocks(struct super_block *sb) | ||
33 | { | ||
34 | return ((sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) - | ||
35 | OCFS2_QBLK_RESERVED_SPACE) << 3) / | ||
36 | ol_quota_entries_per_block(sb); | ||
37 | } | ||
38 | |||
39 | /* Number of entries in a chunk bitmap */ | ||
40 | static unsigned int ol_chunk_entries(struct super_block *sb) | ||
41 | { | ||
42 | return ol_chunk_blocks(sb) * ol_quota_entries_per_block(sb); | ||
43 | } | ||
44 | |||
45 | /* Offset of the chunk in quota file */ | ||
46 | static unsigned int ol_quota_chunk_block(struct super_block *sb, int c) | ||
47 | { | ||
48 | /* 1 block for local quota file info, 1 block per chunk for chunk info */ | ||
49 | return 1 + (ol_chunk_blocks(sb) + 1) * c; | ||
50 | } | ||
51 | |||
52 | /* Offset of the dquot structure in the quota file */ | ||
53 | static loff_t ol_dqblk_off(struct super_block *sb, int c, int off) | ||
54 | { | ||
55 | int epb = ol_quota_entries_per_block(sb); | ||
56 | |||
57 | return ((ol_quota_chunk_block(sb, c) + 1 + off / epb) | ||
58 | << sb->s_blocksize_bits) + | ||
59 | (off % epb) * sizeof(struct ocfs2_local_disk_dqblk); | ||
60 | } | ||
61 | |||
62 | /* Compute block number from given offset */ | ||
63 | static inline unsigned int ol_dqblk_file_block(struct super_block *sb, loff_t off) | ||
64 | { | ||
65 | return off >> sb->s_blocksize_bits; | ||
66 | } | ||
67 | |||
68 | static inline unsigned int ol_dqblk_block_offset(struct super_block *sb, loff_t off) | ||
69 | { | ||
70 | return off & ((1 << sb->s_blocksize_bits) - 1); | ||
71 | } | ||
72 | |||
73 | /* Compute offset in the chunk of a structure with the given offset */ | ||
74 | static int ol_dqblk_chunk_off(struct super_block *sb, int c, loff_t off) | ||
75 | { | ||
76 | int epb = ol_quota_entries_per_block(sb); | ||
77 | |||
78 | return ((off >> sb->s_blocksize_bits) - | ||
79 | ol_quota_chunk_block(sb, c) - 1) * epb | ||
80 | + ((unsigned int)(off & ((1 << sb->s_blocksize_bits) - 1))) / | ||
81 | sizeof(struct ocfs2_local_disk_dqblk); | ||
82 | } | ||
83 | |||
84 | /* Write bufferhead into the fs */ | ||
85 | static int ocfs2_modify_bh(struct inode *inode, struct buffer_head *bh, | ||
86 | void (*modify)(struct buffer_head *, void *), void *private) | ||
87 | { | ||
88 | struct super_block *sb = inode->i_sb; | ||
89 | handle_t *handle; | ||
90 | int status; | ||
91 | |||
92 | handle = ocfs2_start_trans(OCFS2_SB(sb), 1); | ||
93 | if (IS_ERR(handle)) { | ||
94 | status = PTR_ERR(handle); | ||
95 | mlog_errno(status); | ||
96 | return status; | ||
97 | } | ||
98 | status = ocfs2_journal_access(handle, inode, bh, | ||
99 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
100 | if (status < 0) { | ||
101 | mlog_errno(status); | ||
102 | ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
103 | return status; | ||
104 | } | ||
105 | lock_buffer(bh); | ||
106 | modify(bh, private); | ||
107 | unlock_buffer(bh); | ||
108 | status = ocfs2_journal_dirty(handle, bh); | ||
109 | if (status < 0) { | ||
110 | mlog_errno(status); | ||
111 | ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
112 | return status; | ||
113 | } | ||
114 | status = ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
115 | if (status < 0) { | ||
116 | mlog_errno(status); | ||
117 | return status; | ||
118 | } | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | /* Check whether we understand format of quota files */ | ||
123 | static int ocfs2_local_check_quota_file(struct super_block *sb, int type) | ||
124 | { | ||
125 | unsigned int lmagics[MAXQUOTAS] = OCFS2_LOCAL_QMAGICS; | ||
126 | unsigned int lversions[MAXQUOTAS] = OCFS2_LOCAL_QVERSIONS; | ||
127 | unsigned int gmagics[MAXQUOTAS] = OCFS2_GLOBAL_QMAGICS; | ||
128 | unsigned int gversions[MAXQUOTAS] = OCFS2_GLOBAL_QVERSIONS; | ||
129 | unsigned int ino[MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE, | ||
130 | GROUP_QUOTA_SYSTEM_INODE }; | ||
131 | struct buffer_head *bh; | ||
132 | struct inode *linode = sb_dqopt(sb)->files[type]; | ||
133 | struct inode *ginode = NULL; | ||
134 | struct ocfs2_disk_dqheader *dqhead; | ||
135 | int status, ret = 0; | ||
136 | |||
137 | /* First check whether we understand local quota file */ | ||
138 | bh = ocfs2_read_quota_block(linode, 0, &status); | ||
139 | if (!bh) { | ||
140 | mlog_errno(status); | ||
141 | mlog(ML_ERROR, "failed to read quota file header (type=%d)\n", | ||
142 | type); | ||
143 | goto out_err; | ||
144 | } | ||
145 | dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data); | ||
146 | if (le32_to_cpu(dqhead->dqh_magic) != lmagics[type]) { | ||
147 | mlog(ML_ERROR, "quota file magic does not match (%u != %u)," | ||
148 | " type=%d\n", le32_to_cpu(dqhead->dqh_magic), | ||
149 | lmagics[type], type); | ||
150 | goto out_err; | ||
151 | } | ||
152 | if (le32_to_cpu(dqhead->dqh_version) != lversions[type]) { | ||
153 | mlog(ML_ERROR, "quota file version does not match (%u != %u)," | ||
154 | " type=%d\n", le32_to_cpu(dqhead->dqh_version), | ||
155 | lversions[type], type); | ||
156 | goto out_err; | ||
157 | } | ||
158 | brelse(bh); | ||
159 | bh = NULL; | ||
160 | |||
161 | /* Next check whether we understand global quota file */ | ||
162 | ginode = ocfs2_get_system_file_inode(OCFS2_SB(sb), ino[type], | ||
163 | OCFS2_INVALID_SLOT); | ||
164 | if (!ginode) { | ||
165 | mlog(ML_ERROR, "cannot get global quota file inode " | ||
166 | "(type=%d)\n", type); | ||
167 | goto out_err; | ||
168 | } | ||
169 | /* Since the header is read only, we don't care about locking */ | ||
170 | bh = ocfs2_read_quota_block(ginode, 0, &status); | ||
171 | if (!bh) { | ||
172 | mlog_errno(status); | ||
173 | mlog(ML_ERROR, "failed to read global quota file header " | ||
174 | "(type=%d)\n", type); | ||
175 | goto out_err; | ||
176 | } | ||
177 | dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data); | ||
178 | if (le32_to_cpu(dqhead->dqh_magic) != gmagics[type]) { | ||
179 | mlog(ML_ERROR, "global quota file magic does not match " | ||
180 | "(%u != %u), type=%d\n", | ||
181 | le32_to_cpu(dqhead->dqh_magic), gmagics[type], type); | ||
182 | goto out_err; | ||
183 | } | ||
184 | if (le32_to_cpu(dqhead->dqh_version) != gversions[type]) { | ||
185 | mlog(ML_ERROR, "global quota file version does not match " | ||
186 | "(%u != %u), type=%d\n", | ||
187 | le32_to_cpu(dqhead->dqh_version), gversions[type], | ||
188 | type); | ||
189 | goto out_err; | ||
190 | } | ||
191 | |||
192 | ret = 1; | ||
193 | out_err: | ||
194 | brelse(bh); | ||
195 | iput(ginode); | ||
196 | return ret; | ||
197 | } | ||
198 | |||
199 | /* Release given list of quota file chunks */ | ||
200 | static void ocfs2_release_local_quota_bitmaps(struct list_head *head) | ||
201 | { | ||
202 | struct ocfs2_quota_chunk *pos, *next; | ||
203 | |||
204 | list_for_each_entry_safe(pos, next, head, qc_chunk) { | ||
205 | list_del(&pos->qc_chunk); | ||
206 | brelse(pos->qc_headerbh); | ||
207 | kmem_cache_free(ocfs2_qf_chunk_cachep, pos); | ||
208 | } | ||
209 | } | ||
210 | |||
211 | /* Load quota bitmaps into memory */ | ||
212 | static int ocfs2_load_local_quota_bitmaps(struct inode *inode, | ||
213 | struct ocfs2_local_disk_dqinfo *ldinfo, | ||
214 | struct list_head *head) | ||
215 | { | ||
216 | struct ocfs2_quota_chunk *newchunk; | ||
217 | int i, status; | ||
218 | |||
219 | INIT_LIST_HEAD(head); | ||
220 | for (i = 0; i < le32_to_cpu(ldinfo->dqi_chunks); i++) { | ||
221 | newchunk = kmem_cache_alloc(ocfs2_qf_chunk_cachep, GFP_NOFS); | ||
222 | if (!newchunk) { | ||
223 | ocfs2_release_local_quota_bitmaps(head); | ||
224 | return -ENOMEM; | ||
225 | } | ||
226 | newchunk->qc_num = i; | ||
227 | newchunk->qc_headerbh = ocfs2_read_quota_block(inode, | ||
228 | ol_quota_chunk_block(inode->i_sb, i), | ||
229 | &status); | ||
230 | if (!newchunk->qc_headerbh) { | ||
231 | mlog_errno(status); | ||
232 | kmem_cache_free(ocfs2_qf_chunk_cachep, newchunk); | ||
233 | ocfs2_release_local_quota_bitmaps(head); | ||
234 | return status; | ||
235 | } | ||
236 | list_add_tail(&newchunk->qc_chunk, head); | ||
237 | } | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | static void olq_update_info(struct buffer_head *bh, void *private) | ||
242 | { | ||
243 | struct mem_dqinfo *info = private; | ||
244 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; | ||
245 | struct ocfs2_local_disk_dqinfo *ldinfo; | ||
246 | |||
247 | ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + | ||
248 | OCFS2_LOCAL_INFO_OFF); | ||
249 | spin_lock(&dq_data_lock); | ||
250 | ldinfo->dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK); | ||
251 | ldinfo->dqi_chunks = cpu_to_le32(oinfo->dqi_chunks); | ||
252 | ldinfo->dqi_blocks = cpu_to_le32(oinfo->dqi_blocks); | ||
253 | spin_unlock(&dq_data_lock); | ||
254 | } | ||
255 | |||
256 | /* Read information header from quota file */ | ||
257 | static int ocfs2_local_read_info(struct super_block *sb, int type) | ||
258 | { | ||
259 | struct ocfs2_local_disk_dqinfo *ldinfo; | ||
260 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
261 | struct ocfs2_mem_dqinfo *oinfo; | ||
262 | struct inode *lqinode = sb_dqopt(sb)->files[type]; | ||
263 | int status; | ||
264 | struct buffer_head *bh = NULL; | ||
265 | int locked = 0; | ||
266 | |||
267 | info->dqi_maxblimit = 0x7fffffffffffffffLL; | ||
268 | info->dqi_maxilimit = 0x7fffffffffffffffLL; | ||
269 | oinfo = kmalloc(sizeof(struct ocfs2_mem_dqinfo), GFP_NOFS); | ||
270 | if (!oinfo) { | ||
271 | mlog(ML_ERROR, "failed to allocate memory for ocfs2 quota" | ||
272 | " info."); | ||
273 | goto out_err; | ||
274 | } | ||
275 | info->dqi_priv = oinfo; | ||
276 | oinfo->dqi_type = type; | ||
277 | INIT_LIST_HEAD(&oinfo->dqi_chunk); | ||
278 | oinfo->dqi_lqi_bh = NULL; | ||
279 | oinfo->dqi_ibh = NULL; | ||
280 | |||
281 | status = ocfs2_global_read_info(sb, type); | ||
282 | if (status < 0) | ||
283 | goto out_err; | ||
284 | |||
285 | status = ocfs2_inode_lock(lqinode, &oinfo->dqi_lqi_bh, 1); | ||
286 | if (status < 0) { | ||
287 | mlog_errno(status); | ||
288 | goto out_err; | ||
289 | } | ||
290 | locked = 1; | ||
291 | |||
292 | /* Now read local header */ | ||
293 | bh = ocfs2_read_quota_block(lqinode, 0, &status); | ||
294 | if (!bh) { | ||
295 | mlog_errno(status); | ||
296 | mlog(ML_ERROR, "failed to read quota file info header " | ||
297 | "(type=%d)\n", type); | ||
298 | goto out_err; | ||
299 | } | ||
300 | ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + | ||
301 | OCFS2_LOCAL_INFO_OFF); | ||
302 | info->dqi_flags = le32_to_cpu(ldinfo->dqi_flags); | ||
303 | oinfo->dqi_chunks = le32_to_cpu(ldinfo->dqi_chunks); | ||
304 | oinfo->dqi_blocks = le32_to_cpu(ldinfo->dqi_blocks); | ||
305 | oinfo->dqi_ibh = bh; | ||
306 | |||
307 | /* We crashed when using local quota file? */ | ||
308 | if (!(info->dqi_flags & OLQF_CLEAN)) | ||
309 | goto out_err; /* So far we just bail out. Later we should resync here */ | ||
310 | |||
311 | status = ocfs2_load_local_quota_bitmaps(sb_dqopt(sb)->files[type], | ||
312 | ldinfo, | ||
313 | &oinfo->dqi_chunk); | ||
314 | if (status < 0) { | ||
315 | mlog_errno(status); | ||
316 | goto out_err; | ||
317 | } | ||
318 | |||
319 | /* Now mark quota file as used */ | ||
320 | info->dqi_flags &= ~OLQF_CLEAN; | ||
321 | status = ocfs2_modify_bh(lqinode, bh, olq_update_info, info); | ||
322 | if (status < 0) { | ||
323 | mlog_errno(status); | ||
324 | goto out_err; | ||
325 | } | ||
326 | |||
327 | return 0; | ||
328 | out_err: | ||
329 | if (oinfo) { | ||
330 | iput(oinfo->dqi_gqinode); | ||
331 | ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock); | ||
332 | ocfs2_lock_res_free(&oinfo->dqi_gqlock); | ||
333 | brelse(oinfo->dqi_lqi_bh); | ||
334 | if (locked) | ||
335 | ocfs2_inode_unlock(lqinode, 1); | ||
336 | ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk); | ||
337 | kfree(oinfo); | ||
338 | } | ||
339 | brelse(bh); | ||
340 | return -1; | ||
341 | } | ||
342 | |||
343 | /* Write local info to quota file */ | ||
344 | static int ocfs2_local_write_info(struct super_block *sb, int type) | ||
345 | { | ||
346 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
347 | struct buffer_head *bh = ((struct ocfs2_mem_dqinfo *)info->dqi_priv) | ||
348 | ->dqi_ibh; | ||
349 | int status; | ||
350 | |||
351 | status = ocfs2_modify_bh(sb_dqopt(sb)->files[type], bh, olq_update_info, | ||
352 | info); | ||
353 | if (status < 0) { | ||
354 | mlog_errno(status); | ||
355 | return -1; | ||
356 | } | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | /* Release info from memory */ | ||
362 | static int ocfs2_local_free_info(struct super_block *sb, int type) | ||
363 | { | ||
364 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
365 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; | ||
366 | struct ocfs2_quota_chunk *chunk; | ||
367 | struct ocfs2_local_disk_chunk *dchunk; | ||
368 | int mark_clean = 1, len; | ||
369 | int status; | ||
370 | |||
371 | iput(oinfo->dqi_gqinode); | ||
372 | ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock); | ||
373 | ocfs2_lock_res_free(&oinfo->dqi_gqlock); | ||
374 | list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) { | ||
375 | dchunk = (struct ocfs2_local_disk_chunk *) | ||
376 | (chunk->qc_headerbh->b_data); | ||
377 | if (chunk->qc_num < oinfo->dqi_chunks - 1) { | ||
378 | len = ol_chunk_entries(sb); | ||
379 | } else { | ||
380 | len = (oinfo->dqi_blocks - | ||
381 | ol_quota_chunk_block(sb, chunk->qc_num) - 1) | ||
382 | * ol_quota_entries_per_block(sb); | ||
383 | } | ||
384 | /* Not all entries free? Bug! */ | ||
385 | if (le32_to_cpu(dchunk->dqc_free) != len) { | ||
386 | mlog(ML_ERROR, "releasing quota file with used " | ||
387 | "entries (type=%d)\n", type); | ||
388 | mark_clean = 0; | ||
389 | } | ||
390 | } | ||
391 | ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk); | ||
392 | |||
393 | if (!mark_clean) | ||
394 | goto out; | ||
395 | |||
396 | /* Mark local file as clean */ | ||
397 | info->dqi_flags |= OLQF_CLEAN; | ||
398 | status = ocfs2_modify_bh(sb_dqopt(sb)->files[type], | ||
399 | oinfo->dqi_ibh, | ||
400 | olq_update_info, | ||
401 | info); | ||
402 | if (status < 0) { | ||
403 | mlog_errno(status); | ||
404 | goto out; | ||
405 | } | ||
406 | |||
407 | out: | ||
408 | ocfs2_inode_unlock(sb_dqopt(sb)->files[type], 1); | ||
409 | brelse(oinfo->dqi_ibh); | ||
410 | brelse(oinfo->dqi_lqi_bh); | ||
411 | kfree(oinfo); | ||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static void olq_set_dquot(struct buffer_head *bh, void *private) | ||
416 | { | ||
417 | struct ocfs2_dquot *od = private; | ||
418 | struct ocfs2_local_disk_dqblk *dqblk; | ||
419 | struct super_block *sb = od->dq_dquot.dq_sb; | ||
420 | |||
421 | dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data | ||
422 | + ol_dqblk_block_offset(sb, od->dq_local_off)); | ||
423 | |||
424 | dqblk->dqb_id = cpu_to_le64(od->dq_dquot.dq_id); | ||
425 | spin_lock(&dq_data_lock); | ||
426 | dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace - | ||
427 | od->dq_origspace); | ||
428 | dqblk->dqb_inodemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curinodes - | ||
429 | od->dq_originodes); | ||
430 | spin_unlock(&dq_data_lock); | ||
431 | mlog(0, "Writing local dquot %u space %lld inodes %lld\n", | ||
432 | od->dq_dquot.dq_id, dqblk->dqb_spacemod, dqblk->dqb_inodemod); | ||
433 | } | ||
434 | |||
435 | /* Write dquot to local quota file */ | ||
436 | static int ocfs2_local_write_dquot(struct dquot *dquot) | ||
437 | { | ||
438 | struct super_block *sb = dquot->dq_sb; | ||
439 | struct ocfs2_dquot *od = OCFS2_DQUOT(dquot); | ||
440 | struct buffer_head *bh; | ||
441 | int status; | ||
442 | |||
443 | bh = ocfs2_read_quota_block(sb_dqopt(sb)->files[dquot->dq_type], | ||
444 | ol_dqblk_file_block(sb, od->dq_local_off), | ||
445 | &status); | ||
446 | if (!bh) { | ||
447 | mlog_errno(status); | ||
448 | goto out; | ||
449 | } | ||
450 | status = ocfs2_modify_bh(sb_dqopt(sb)->files[dquot->dq_type], bh, | ||
451 | olq_set_dquot, od); | ||
452 | if (status < 0) { | ||
453 | mlog_errno(status); | ||
454 | goto out; | ||
455 | } | ||
456 | out: | ||
457 | brelse(bh); | ||
458 | return status; | ||
459 | } | ||
460 | |||
461 | /* Find free entry in local quota file */ | ||
462 | static struct ocfs2_quota_chunk *ocfs2_find_free_entry(struct super_block *sb, | ||
463 | int type, | ||
464 | int *offset) | ||
465 | { | ||
466 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
467 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; | ||
468 | struct ocfs2_quota_chunk *chunk; | ||
469 | struct ocfs2_local_disk_chunk *dchunk; | ||
470 | int found = 0, len; | ||
471 | |||
472 | list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) { | ||
473 | dchunk = (struct ocfs2_local_disk_chunk *) | ||
474 | chunk->qc_headerbh->b_data; | ||
475 | if (le32_to_cpu(dchunk->dqc_free) > 0) { | ||
476 | found = 1; | ||
477 | break; | ||
478 | } | ||
479 | } | ||
480 | if (!found) | ||
481 | return NULL; | ||
482 | |||
483 | if (chunk->qc_num < oinfo->dqi_chunks - 1) { | ||
484 | len = ol_chunk_entries(sb); | ||
485 | } else { | ||
486 | len = (oinfo->dqi_blocks - | ||
487 | ol_quota_chunk_block(sb, chunk->qc_num) - 1) | ||
488 | * ol_quota_entries_per_block(sb); | ||
489 | } | ||
490 | |||
491 | found = ocfs2_find_next_zero_bit(dchunk->dqc_bitmap, len, 0); | ||
492 | /* We failed? */ | ||
493 | if (found == len) { | ||
494 | mlog(ML_ERROR, "Did not find empty entry in chunk %d with %u" | ||
495 | " entries free (type=%d)\n", chunk->qc_num, | ||
496 | le32_to_cpu(dchunk->dqc_free), type); | ||
497 | return ERR_PTR(-EIO); | ||
498 | } | ||
499 | *offset = found; | ||
500 | return chunk; | ||
501 | } | ||
502 | |||
503 | /* Add new chunk to the local quota file */ | ||
504 | static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk( | ||
505 | struct super_block *sb, | ||
506 | int type, | ||
507 | int *offset) | ||
508 | { | ||
509 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
510 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; | ||
511 | struct inode *lqinode = sb_dqopt(sb)->files[type]; | ||
512 | struct ocfs2_quota_chunk *chunk = NULL; | ||
513 | struct ocfs2_local_disk_chunk *dchunk; | ||
514 | int status; | ||
515 | handle_t *handle; | ||
516 | struct buffer_head *bh = NULL; | ||
517 | u64 p_blkno; | ||
518 | |||
519 | /* We are protected by dqio_sem so no locking needed */ | ||
520 | status = ocfs2_extend_no_holes(lqinode, | ||
521 | lqinode->i_size + 2 * sb->s_blocksize, | ||
522 | lqinode->i_size); | ||
523 | if (status < 0) { | ||
524 | mlog_errno(status); | ||
525 | goto out; | ||
526 | } | ||
527 | status = ocfs2_simple_size_update(lqinode, oinfo->dqi_lqi_bh, | ||
528 | lqinode->i_size + 2 * sb->s_blocksize); | ||
529 | if (status < 0) { | ||
530 | mlog_errno(status); | ||
531 | goto out; | ||
532 | } | ||
533 | |||
534 | chunk = kmem_cache_alloc(ocfs2_qf_chunk_cachep, GFP_NOFS); | ||
535 | if (!chunk) { | ||
536 | status = -ENOMEM; | ||
537 | mlog_errno(status); | ||
538 | goto out; | ||
539 | } | ||
540 | |||
541 | down_read(&OCFS2_I(lqinode)->ip_alloc_sem); | ||
542 | status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks, | ||
543 | &p_blkno, NULL, NULL); | ||
544 | up_read(&OCFS2_I(lqinode)->ip_alloc_sem); | ||
545 | if (status < 0) { | ||
546 | mlog_errno(status); | ||
547 | goto out; | ||
548 | } | ||
549 | bh = sb_getblk(sb, p_blkno); | ||
550 | if (!bh) { | ||
551 | status = -ENOMEM; | ||
552 | mlog_errno(status); | ||
553 | goto out; | ||
554 | } | ||
555 | dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data; | ||
556 | |||
557 | handle = ocfs2_start_trans(OCFS2_SB(sb), 2); | ||
558 | if (IS_ERR(handle)) { | ||
559 | status = PTR_ERR(handle); | ||
560 | mlog_errno(status); | ||
561 | goto out; | ||
562 | } | ||
563 | |||
564 | status = ocfs2_journal_access(handle, lqinode, bh, | ||
565 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
566 | if (status < 0) { | ||
567 | mlog_errno(status); | ||
568 | goto out_trans; | ||
569 | } | ||
570 | lock_buffer(bh); | ||
571 | dchunk->dqc_free = ol_quota_entries_per_block(sb); | ||
572 | memset(dchunk->dqc_bitmap, 0, | ||
573 | sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) - | ||
574 | OCFS2_QBLK_RESERVED_SPACE); | ||
575 | set_buffer_uptodate(bh); | ||
576 | unlock_buffer(bh); | ||
577 | status = ocfs2_journal_dirty(handle, bh); | ||
578 | if (status < 0) { | ||
579 | mlog_errno(status); | ||
580 | goto out_trans; | ||
581 | } | ||
582 | |||
583 | oinfo->dqi_blocks += 2; | ||
584 | oinfo->dqi_chunks++; | ||
585 | status = ocfs2_local_write_info(sb, type); | ||
586 | if (status < 0) { | ||
587 | mlog_errno(status); | ||
588 | goto out_trans; | ||
589 | } | ||
590 | status = ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
591 | if (status < 0) { | ||
592 | mlog_errno(status); | ||
593 | goto out; | ||
594 | } | ||
595 | |||
596 | list_add_tail(&chunk->qc_chunk, &oinfo->dqi_chunk); | ||
597 | chunk->qc_num = list_entry(chunk->qc_chunk.prev, | ||
598 | struct ocfs2_quota_chunk, | ||
599 | qc_chunk)->qc_num + 1; | ||
600 | chunk->qc_headerbh = bh; | ||
601 | *offset = 0; | ||
602 | return chunk; | ||
603 | out_trans: | ||
604 | ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
605 | out: | ||
606 | brelse(bh); | ||
607 | kmem_cache_free(ocfs2_qf_chunk_cachep, chunk); | ||
608 | return ERR_PTR(status); | ||
609 | } | ||
610 | |||
611 | /* Find free entry in local quota file */ | ||
612 | static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file( | ||
613 | struct super_block *sb, | ||
614 | int type, | ||
615 | int *offset) | ||
616 | { | ||
617 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | ||
618 | struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; | ||
619 | struct ocfs2_quota_chunk *chunk; | ||
620 | struct inode *lqinode = sb_dqopt(sb)->files[type]; | ||
621 | struct ocfs2_local_disk_chunk *dchunk; | ||
622 | int epb = ol_quota_entries_per_block(sb); | ||
623 | unsigned int chunk_blocks; | ||
624 | int status; | ||
625 | handle_t *handle; | ||
626 | |||
627 | if (list_empty(&oinfo->dqi_chunk)) | ||
628 | return ocfs2_local_quota_add_chunk(sb, type, offset); | ||
629 | /* Is the last chunk full? */ | ||
630 | chunk = list_entry(oinfo->dqi_chunk.prev, | ||
631 | struct ocfs2_quota_chunk, qc_chunk); | ||
632 | chunk_blocks = oinfo->dqi_blocks - | ||
633 | ol_quota_chunk_block(sb, chunk->qc_num) - 1; | ||
634 | if (ol_chunk_blocks(sb) == chunk_blocks) | ||
635 | return ocfs2_local_quota_add_chunk(sb, type, offset); | ||
636 | |||
637 | /* We are protected by dqio_sem so no locking needed */ | ||
638 | status = ocfs2_extend_no_holes(lqinode, | ||
639 | lqinode->i_size + sb->s_blocksize, | ||
640 | lqinode->i_size); | ||
641 | if (status < 0) { | ||
642 | mlog_errno(status); | ||
643 | goto out; | ||
644 | } | ||
645 | status = ocfs2_simple_size_update(lqinode, oinfo->dqi_lqi_bh, | ||
646 | lqinode->i_size + sb->s_blocksize); | ||
647 | if (status < 0) { | ||
648 | mlog_errno(status); | ||
649 | goto out; | ||
650 | } | ||
651 | handle = ocfs2_start_trans(OCFS2_SB(sb), 2); | ||
652 | if (IS_ERR(handle)) { | ||
653 | status = PTR_ERR(handle); | ||
654 | mlog_errno(status); | ||
655 | goto out; | ||
656 | } | ||
657 | status = ocfs2_journal_access(handle, lqinode, chunk->qc_headerbh, | ||
658 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
659 | if (status < 0) { | ||
660 | mlog_errno(status); | ||
661 | goto out_trans; | ||
662 | } | ||
663 | |||
664 | dchunk = (struct ocfs2_local_disk_chunk *)chunk->qc_headerbh->b_data; | ||
665 | lock_buffer(chunk->qc_headerbh); | ||
666 | le32_add_cpu(&dchunk->dqc_free, ol_quota_entries_per_block(sb)); | ||
667 | unlock_buffer(chunk->qc_headerbh); | ||
668 | status = ocfs2_journal_dirty(handle, chunk->qc_headerbh); | ||
669 | if (status < 0) { | ||
670 | mlog_errno(status); | ||
671 | goto out_trans; | ||
672 | } | ||
673 | oinfo->dqi_blocks++; | ||
674 | status = ocfs2_local_write_info(sb, type); | ||
675 | if (status < 0) { | ||
676 | mlog_errno(status); | ||
677 | goto out_trans; | ||
678 | } | ||
679 | |||
680 | status = ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
681 | if (status < 0) { | ||
682 | mlog_errno(status); | ||
683 | goto out; | ||
684 | } | ||
685 | *offset = chunk_blocks * epb; | ||
686 | return chunk; | ||
687 | out_trans: | ||
688 | ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
689 | out: | ||
690 | return ERR_PTR(status); | ||
691 | } | ||
692 | |||
693 | void olq_alloc_dquot(struct buffer_head *bh, void *private) | ||
694 | { | ||
695 | int *offset = private; | ||
696 | struct ocfs2_local_disk_chunk *dchunk; | ||
697 | |||
698 | dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data; | ||
699 | ocfs2_set_bit(*offset, dchunk->dqc_bitmap); | ||
700 | le32_add_cpu(&dchunk->dqc_free, -1); | ||
701 | } | ||
702 | |||
703 | /* Create dquot in the local file for given id */ | ||
704 | static int ocfs2_create_local_dquot(struct dquot *dquot) | ||
705 | { | ||
706 | struct super_block *sb = dquot->dq_sb; | ||
707 | int type = dquot->dq_type; | ||
708 | struct inode *lqinode = sb_dqopt(sb)->files[type]; | ||
709 | struct ocfs2_quota_chunk *chunk; | ||
710 | struct ocfs2_dquot *od = OCFS2_DQUOT(dquot); | ||
711 | int offset; | ||
712 | int status; | ||
713 | |||
714 | chunk = ocfs2_find_free_entry(sb, type, &offset); | ||
715 | if (!chunk) { | ||
716 | chunk = ocfs2_extend_local_quota_file(sb, type, &offset); | ||
717 | if (IS_ERR(chunk)) | ||
718 | return PTR_ERR(chunk); | ||
719 | } else if (IS_ERR(chunk)) { | ||
720 | return PTR_ERR(chunk); | ||
721 | } | ||
722 | od->dq_local_off = ol_dqblk_off(sb, chunk->qc_num, offset); | ||
723 | od->dq_chunk = chunk; | ||
724 | |||
725 | /* Initialize dquot structure on disk */ | ||
726 | status = ocfs2_local_write_dquot(dquot); | ||
727 | if (status < 0) { | ||
728 | mlog_errno(status); | ||
729 | goto out; | ||
730 | } | ||
731 | |||
732 | /* Mark structure as allocated */ | ||
733 | status = ocfs2_modify_bh(lqinode, chunk->qc_headerbh, olq_alloc_dquot, | ||
734 | &offset); | ||
735 | if (status < 0) { | ||
736 | mlog_errno(status); | ||
737 | goto out; | ||
738 | } | ||
739 | out: | ||
740 | return status; | ||
741 | } | ||
742 | |||
743 | /* Create entry in local file for dquot, load data from the global file */ | ||
744 | static int ocfs2_local_read_dquot(struct dquot *dquot) | ||
745 | { | ||
746 | int status; | ||
747 | |||
748 | mlog_entry("id=%u, type=%d\n", dquot->dq_id, dquot->dq_type); | ||
749 | |||
750 | status = ocfs2_global_read_dquot(dquot); | ||
751 | if (status < 0) { | ||
752 | mlog_errno(status); | ||
753 | goto out_err; | ||
754 | } | ||
755 | |||
756 | /* Now create entry in the local quota file */ | ||
757 | status = ocfs2_create_local_dquot(dquot); | ||
758 | if (status < 0) { | ||
759 | mlog_errno(status); | ||
760 | goto out_err; | ||
761 | } | ||
762 | mlog_exit(0); | ||
763 | return 0; | ||
764 | out_err: | ||
765 | mlog_exit(status); | ||
766 | return status; | ||
767 | } | ||
768 | |||
769 | /* Release dquot structure from local quota file. ocfs2_release_dquot() has | ||
770 | * already started a transaction and obtained exclusive lock for global | ||
771 | * quota file. */ | ||
772 | static int ocfs2_local_release_dquot(struct dquot *dquot) | ||
773 | { | ||
774 | int status; | ||
775 | int type = dquot->dq_type; | ||
776 | struct ocfs2_dquot *od = OCFS2_DQUOT(dquot); | ||
777 | struct super_block *sb = dquot->dq_sb; | ||
778 | struct ocfs2_local_disk_chunk *dchunk; | ||
779 | int offset; | ||
780 | handle_t *handle = journal_current_handle(); | ||
781 | |||
782 | BUG_ON(!handle); | ||
783 | /* First write all local changes to global file */ | ||
784 | status = ocfs2_global_release_dquot(dquot); | ||
785 | if (status < 0) { | ||
786 | mlog_errno(status); | ||
787 | goto out; | ||
788 | } | ||
789 | |||
790 | status = ocfs2_journal_access(handle, sb_dqopt(sb)->files[type], | ||
791 | od->dq_chunk->qc_headerbh, OCFS2_JOURNAL_ACCESS_WRITE); | ||
792 | if (status < 0) { | ||
793 | mlog_errno(status); | ||
794 | goto out; | ||
795 | } | ||
796 | offset = ol_dqblk_chunk_off(sb, od->dq_chunk->qc_num, | ||
797 | od->dq_local_off); | ||
798 | dchunk = (struct ocfs2_local_disk_chunk *) | ||
799 | (od->dq_chunk->qc_headerbh->b_data); | ||
800 | /* Mark structure as freed */ | ||
801 | lock_buffer(od->dq_chunk->qc_headerbh); | ||
802 | ocfs2_clear_bit(offset, dchunk->dqc_bitmap); | ||
803 | le32_add_cpu(&dchunk->dqc_free, 1); | ||
804 | unlock_buffer(od->dq_chunk->qc_headerbh); | ||
805 | status = ocfs2_journal_dirty(handle, od->dq_chunk->qc_headerbh); | ||
806 | if (status < 0) { | ||
807 | mlog_errno(status); | ||
808 | goto out; | ||
809 | } | ||
810 | status = 0; | ||
811 | out: | ||
812 | /* Clear the read bit so that next time someone uses this | ||
813 | * dquot he reads fresh info from disk and allocates local | ||
814 | * dquot structure */ | ||
815 | clear_bit(DQ_READ_B, &dquot->dq_flags); | ||
816 | return status; | ||
817 | } | ||
818 | |||
819 | static struct quota_format_ops ocfs2_format_ops = { | ||
820 | .check_quota_file = ocfs2_local_check_quota_file, | ||
821 | .read_file_info = ocfs2_local_read_info, | ||
822 | .write_file_info = ocfs2_global_write_info, | ||
823 | .free_file_info = ocfs2_local_free_info, | ||
824 | .read_dqblk = ocfs2_local_read_dquot, | ||
825 | .commit_dqblk = ocfs2_local_write_dquot, | ||
826 | .release_dqblk = ocfs2_local_release_dquot, | ||
827 | }; | ||
828 | |||
829 | struct quota_format_type ocfs2_quota_format = { | ||
830 | .qf_fmt_id = QFMT_OCFS2, | ||
831 | .qf_ops = &ocfs2_format_ops, | ||
832 | .qf_owner = THIS_MODULE | ||
833 | }; | ||