diff options
author | Koji Sato <sato.koji@lab.ntt.co.jp> | 2009-04-06 22:01:31 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-07 11:31:14 -0400 |
commit | 29619809727a4e524e26dbd7bfdc93ff7f50aa74 (patch) | |
tree | 2c765d9a5d122bc66c5d01079eeadae4edf61a24 /fs/nilfs2 | |
parent | 43bfb45ed4feace26157778889be55e4046b7a4b (diff) |
nilfs2: checkpoint file
This adds a meta data file which holds checkpoint entries in its data
blocks.
Signed-off-by: Koji Sato <sato.koji@lab.ntt.co.jp>
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/nilfs2')
-rw-r--r-- | fs/nilfs2/cpfile.c | 908 | ||||
-rw-r--r-- | fs/nilfs2/cpfile.h | 45 |
2 files changed, 953 insertions, 0 deletions
diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c new file mode 100644 index 00000000000..991633aad1d --- /dev/null +++ b/fs/nilfs2/cpfile.c | |||
@@ -0,0 +1,908 @@ | |||
1 | /* | ||
2 | * cpfile.c - NILFS checkpoint file. | ||
3 | * | ||
4 | * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
19 | * | ||
20 | * Written by Koji Sato <koji@osrg.net>. | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/fs.h> | ||
25 | #include <linux/string.h> | ||
26 | #include <linux/buffer_head.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/nilfs2_fs.h> | ||
29 | #include "mdt.h" | ||
30 | #include "cpfile.h" | ||
31 | |||
32 | |||
33 | static inline unsigned long | ||
34 | nilfs_cpfile_checkpoints_per_block(const struct inode *cpfile) | ||
35 | { | ||
36 | return NILFS_MDT(cpfile)->mi_entries_per_block; | ||
37 | } | ||
38 | |||
39 | /* block number from the beginning of the file */ | ||
40 | static unsigned long | ||
41 | nilfs_cpfile_get_blkoff(const struct inode *cpfile, __u64 cno) | ||
42 | { | ||
43 | __u64 tcno; | ||
44 | |||
45 | BUG_ON(cno == 0); /* checkpoint number 0 is invalid */ | ||
46 | tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1; | ||
47 | do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile)); | ||
48 | return (unsigned long)tcno; | ||
49 | } | ||
50 | |||
51 | /* offset in block */ | ||
52 | static unsigned long | ||
53 | nilfs_cpfile_get_offset(const struct inode *cpfile, __u64 cno) | ||
54 | { | ||
55 | __u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1; | ||
56 | return do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile)); | ||
57 | } | ||
58 | |||
59 | static unsigned long | ||
60 | nilfs_cpfile_checkpoints_in_block(const struct inode *cpfile, | ||
61 | __u64 curr, | ||
62 | __u64 max) | ||
63 | { | ||
64 | return min_t(__u64, | ||
65 | nilfs_cpfile_checkpoints_per_block(cpfile) - | ||
66 | nilfs_cpfile_get_offset(cpfile, curr), | ||
67 | max - curr); | ||
68 | } | ||
69 | |||
70 | static inline int nilfs_cpfile_is_in_first(const struct inode *cpfile, | ||
71 | __u64 cno) | ||
72 | { | ||
73 | return nilfs_cpfile_get_blkoff(cpfile, cno) == 0; | ||
74 | } | ||
75 | |||
76 | static unsigned int | ||
77 | nilfs_cpfile_block_add_valid_checkpoints(const struct inode *cpfile, | ||
78 | struct buffer_head *bh, | ||
79 | void *kaddr, | ||
80 | unsigned int n) | ||
81 | { | ||
82 | struct nilfs_checkpoint *cp = kaddr + bh_offset(bh); | ||
83 | unsigned int count; | ||
84 | |||
85 | count = le32_to_cpu(cp->cp_checkpoints_count) + n; | ||
86 | cp->cp_checkpoints_count = cpu_to_le32(count); | ||
87 | return count; | ||
88 | } | ||
89 | |||
90 | static unsigned int | ||
91 | nilfs_cpfile_block_sub_valid_checkpoints(const struct inode *cpfile, | ||
92 | struct buffer_head *bh, | ||
93 | void *kaddr, | ||
94 | unsigned int n) | ||
95 | { | ||
96 | struct nilfs_checkpoint *cp = kaddr + bh_offset(bh); | ||
97 | unsigned int count; | ||
98 | |||
99 | BUG_ON(le32_to_cpu(cp->cp_checkpoints_count) < n); | ||
100 | count = le32_to_cpu(cp->cp_checkpoints_count) - n; | ||
101 | cp->cp_checkpoints_count = cpu_to_le32(count); | ||
102 | return count; | ||
103 | } | ||
104 | |||
105 | static inline struct nilfs_cpfile_header * | ||
106 | nilfs_cpfile_block_get_header(const struct inode *cpfile, | ||
107 | struct buffer_head *bh, | ||
108 | void *kaddr) | ||
109 | { | ||
110 | return kaddr + bh_offset(bh); | ||
111 | } | ||
112 | |||
113 | static struct nilfs_checkpoint * | ||
114 | nilfs_cpfile_block_get_checkpoint(const struct inode *cpfile, __u64 cno, | ||
115 | struct buffer_head *bh, | ||
116 | void *kaddr) | ||
117 | { | ||
118 | return kaddr + bh_offset(bh) + nilfs_cpfile_get_offset(cpfile, cno) * | ||
119 | NILFS_MDT(cpfile)->mi_entry_size; | ||
120 | } | ||
121 | |||
122 | static void nilfs_cpfile_block_init(struct inode *cpfile, | ||
123 | struct buffer_head *bh, | ||
124 | void *kaddr) | ||
125 | { | ||
126 | struct nilfs_checkpoint *cp = kaddr + bh_offset(bh); | ||
127 | size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; | ||
128 | int n = nilfs_cpfile_checkpoints_per_block(cpfile); | ||
129 | |||
130 | while (n-- > 0) { | ||
131 | nilfs_checkpoint_set_invalid(cp); | ||
132 | cp = (void *)cp + cpsz; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | static inline int nilfs_cpfile_get_header_block(struct inode *cpfile, | ||
137 | struct buffer_head **bhp) | ||
138 | { | ||
139 | return nilfs_mdt_get_block(cpfile, 0, 0, NULL, bhp); | ||
140 | } | ||
141 | |||
142 | static inline int nilfs_cpfile_get_checkpoint_block(struct inode *cpfile, | ||
143 | __u64 cno, | ||
144 | int create, | ||
145 | struct buffer_head **bhp) | ||
146 | { | ||
147 | return nilfs_mdt_get_block(cpfile, | ||
148 | nilfs_cpfile_get_blkoff(cpfile, cno), | ||
149 | create, nilfs_cpfile_block_init, bhp); | ||
150 | } | ||
151 | |||
152 | static inline int nilfs_cpfile_delete_checkpoint_block(struct inode *cpfile, | ||
153 | __u64 cno) | ||
154 | { | ||
155 | return nilfs_mdt_delete_block(cpfile, | ||
156 | nilfs_cpfile_get_blkoff(cpfile, cno)); | ||
157 | } | ||
158 | |||
159 | /** | ||
160 | * nilfs_cpfile_get_checkpoint - get a checkpoint | ||
161 | * @cpfile: inode of checkpoint file | ||
162 | * @cno: checkpoint number | ||
163 | * @create: create flag | ||
164 | * @cpp: pointer to a checkpoint | ||
165 | * @bhp: pointer to a buffer head | ||
166 | * | ||
167 | * Description: nilfs_cpfile_get_checkpoint() acquires the checkpoint | ||
168 | * specified by @cno. A new checkpoint will be created if @cno is the current | ||
169 | * checkpoint number and @create is nonzero. | ||
170 | * | ||
171 | * Return Value: On success, 0 is returned, and the checkpoint and the | ||
172 | * buffer head of the buffer on which the checkpoint is located are stored in | ||
173 | * the place pointed by @cpp and @bhp, respectively. On error, one of the | ||
174 | * following negative error codes is returned. | ||
175 | * | ||
176 | * %-EIO - I/O error. | ||
177 | * | ||
178 | * %-ENOMEM - Insufficient amount of memory available. | ||
179 | * | ||
180 | * %-ENOENT - No such checkpoint. | ||
181 | */ | ||
182 | int nilfs_cpfile_get_checkpoint(struct inode *cpfile, | ||
183 | __u64 cno, | ||
184 | int create, | ||
185 | struct nilfs_checkpoint **cpp, | ||
186 | struct buffer_head **bhp) | ||
187 | { | ||
188 | struct buffer_head *header_bh, *cp_bh; | ||
189 | struct nilfs_cpfile_header *header; | ||
190 | struct nilfs_checkpoint *cp; | ||
191 | void *kaddr; | ||
192 | int ret; | ||
193 | |||
194 | BUG_ON(cno < 1 || cno > nilfs_mdt_cno(cpfile) || | ||
195 | (cno < nilfs_mdt_cno(cpfile) && create)); | ||
196 | |||
197 | down_write(&NILFS_MDT(cpfile)->mi_sem); | ||
198 | |||
199 | ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); | ||
200 | if (ret < 0) | ||
201 | goto out_sem; | ||
202 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, create, &cp_bh); | ||
203 | if (ret < 0) | ||
204 | goto out_header; | ||
205 | kaddr = kmap(cp_bh->b_page); | ||
206 | cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); | ||
207 | if (nilfs_checkpoint_invalid(cp)) { | ||
208 | if (!create) { | ||
209 | kunmap(cp_bh->b_page); | ||
210 | brelse(cp_bh); | ||
211 | ret = -ENOENT; | ||
212 | goto out_header; | ||
213 | } | ||
214 | /* a newly-created checkpoint */ | ||
215 | nilfs_checkpoint_clear_invalid(cp); | ||
216 | if (!nilfs_cpfile_is_in_first(cpfile, cno)) | ||
217 | nilfs_cpfile_block_add_valid_checkpoints(cpfile, cp_bh, | ||
218 | kaddr, 1); | ||
219 | nilfs_mdt_mark_buffer_dirty(cp_bh); | ||
220 | |||
221 | kaddr = kmap_atomic(header_bh->b_page, KM_USER0); | ||
222 | header = nilfs_cpfile_block_get_header(cpfile, header_bh, | ||
223 | kaddr); | ||
224 | le64_add_cpu(&header->ch_ncheckpoints, 1); | ||
225 | kunmap_atomic(kaddr, KM_USER0); | ||
226 | nilfs_mdt_mark_buffer_dirty(header_bh); | ||
227 | nilfs_mdt_mark_dirty(cpfile); | ||
228 | } | ||
229 | |||
230 | if (cpp != NULL) | ||
231 | *cpp = cp; | ||
232 | *bhp = cp_bh; | ||
233 | |||
234 | out_header: | ||
235 | brelse(header_bh); | ||
236 | |||
237 | out_sem: | ||
238 | up_write(&NILFS_MDT(cpfile)->mi_sem); | ||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * nilfs_cpfile_put_checkpoint - put a checkpoint | ||
244 | * @cpfile: inode of checkpoint file | ||
245 | * @cno: checkpoint number | ||
246 | * @bh: buffer head | ||
247 | * | ||
248 | * Description: nilfs_cpfile_put_checkpoint() releases the checkpoint | ||
249 | * specified by @cno. @bh must be the buffer head which has been returned by | ||
250 | * a previous call to nilfs_cpfile_get_checkpoint() with @cno. | ||
251 | */ | ||
252 | void nilfs_cpfile_put_checkpoint(struct inode *cpfile, __u64 cno, | ||
253 | struct buffer_head *bh) | ||
254 | { | ||
255 | kunmap(bh->b_page); | ||
256 | brelse(bh); | ||
257 | } | ||
258 | |||
259 | /** | ||
260 | * nilfs_cpfile_delete_checkpoints - delete checkpoints | ||
261 | * @cpfile: inode of checkpoint file | ||
262 | * @start: start checkpoint number | ||
263 | * @end: end checkpoint numer | ||
264 | * | ||
265 | * Description: nilfs_cpfile_delete_checkpoints() deletes the checkpoints in | ||
266 | * the period from @start to @end, excluding @end itself. The checkpoints | ||
267 | * which have been already deleted are ignored. | ||
268 | * | ||
269 | * Return Value: On success, 0 is returned. On error, one of the following | ||
270 | * negative error codes is returned. | ||
271 | * | ||
272 | * %-EIO - I/O error. | ||
273 | * | ||
274 | * %-ENOMEM - Insufficient amount of memory available. | ||
275 | * | ||
276 | * %-EINVAL - invalid checkpoints. | ||
277 | */ | ||
278 | int nilfs_cpfile_delete_checkpoints(struct inode *cpfile, | ||
279 | __u64 start, | ||
280 | __u64 end) | ||
281 | { | ||
282 | struct buffer_head *header_bh, *cp_bh; | ||
283 | struct nilfs_cpfile_header *header; | ||
284 | struct nilfs_checkpoint *cp; | ||
285 | size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; | ||
286 | __u64 cno; | ||
287 | void *kaddr; | ||
288 | unsigned long tnicps; | ||
289 | int ret, ncps, nicps, count, i; | ||
290 | |||
291 | if ((start == 0) || (start > end)) { | ||
292 | printk(KERN_CRIT "%s: start = %llu, end = %llu\n", | ||
293 | __func__, | ||
294 | (unsigned long long)start, | ||
295 | (unsigned long long)end); | ||
296 | BUG(); | ||
297 | } | ||
298 | |||
299 | /* cannot delete the latest checkpoint */ | ||
300 | if (start == nilfs_mdt_cno(cpfile) - 1) | ||
301 | return -EPERM; | ||
302 | |||
303 | down_write(&NILFS_MDT(cpfile)->mi_sem); | ||
304 | |||
305 | ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); | ||
306 | if (ret < 0) | ||
307 | goto out_sem; | ||
308 | tnicps = 0; | ||
309 | |||
310 | for (cno = start; cno < end; cno += ncps) { | ||
311 | ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, end); | ||
312 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh); | ||
313 | if (ret < 0) { | ||
314 | if (ret != -ENOENT) | ||
315 | goto out_sem; | ||
316 | /* skip hole */ | ||
317 | ret = 0; | ||
318 | continue; | ||
319 | } | ||
320 | |||
321 | kaddr = kmap_atomic(cp_bh->b_page, KM_USER0); | ||
322 | cp = nilfs_cpfile_block_get_checkpoint( | ||
323 | cpfile, cno, cp_bh, kaddr); | ||
324 | nicps = 0; | ||
325 | for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) { | ||
326 | BUG_ON(nilfs_checkpoint_snapshot(cp)); | ||
327 | if (!nilfs_checkpoint_invalid(cp)) { | ||
328 | nilfs_checkpoint_set_invalid(cp); | ||
329 | nicps++; | ||
330 | } | ||
331 | } | ||
332 | if (nicps > 0) { | ||
333 | tnicps += nicps; | ||
334 | nilfs_mdt_mark_buffer_dirty(cp_bh); | ||
335 | nilfs_mdt_mark_dirty(cpfile); | ||
336 | if (!nilfs_cpfile_is_in_first(cpfile, cno) && | ||
337 | (count = nilfs_cpfile_block_sub_valid_checkpoints( | ||
338 | cpfile, cp_bh, kaddr, nicps)) == 0) { | ||
339 | /* make hole */ | ||
340 | kunmap_atomic(kaddr, KM_USER0); | ||
341 | brelse(cp_bh); | ||
342 | ret = nilfs_cpfile_delete_checkpoint_block( | ||
343 | cpfile, cno); | ||
344 | if (ret == 0) | ||
345 | continue; | ||
346 | printk(KERN_ERR "%s: cannot delete block\n", | ||
347 | __func__); | ||
348 | goto out_sem; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | kunmap_atomic(kaddr, KM_USER0); | ||
353 | brelse(cp_bh); | ||
354 | } | ||
355 | |||
356 | if (tnicps > 0) { | ||
357 | kaddr = kmap_atomic(header_bh->b_page, KM_USER0); | ||
358 | header = nilfs_cpfile_block_get_header(cpfile, header_bh, | ||
359 | kaddr); | ||
360 | le64_add_cpu(&header->ch_ncheckpoints, -tnicps); | ||
361 | nilfs_mdt_mark_buffer_dirty(header_bh); | ||
362 | nilfs_mdt_mark_dirty(cpfile); | ||
363 | kunmap_atomic(kaddr, KM_USER0); | ||
364 | } | ||
365 | brelse(header_bh); | ||
366 | |||
367 | out_sem: | ||
368 | up_write(&NILFS_MDT(cpfile)->mi_sem); | ||
369 | return ret; | ||
370 | } | ||
371 | |||
372 | static void nilfs_cpfile_checkpoint_to_cpinfo(struct inode *cpfile, | ||
373 | struct nilfs_checkpoint *cp, | ||
374 | struct nilfs_cpinfo *ci) | ||
375 | { | ||
376 | ci->ci_flags = le32_to_cpu(cp->cp_flags); | ||
377 | ci->ci_cno = le64_to_cpu(cp->cp_cno); | ||
378 | ci->ci_create = le64_to_cpu(cp->cp_create); | ||
379 | ci->ci_nblk_inc = le64_to_cpu(cp->cp_nblk_inc); | ||
380 | ci->ci_inodes_count = le64_to_cpu(cp->cp_inodes_count); | ||
381 | ci->ci_blocks_count = le64_to_cpu(cp->cp_blocks_count); | ||
382 | ci->ci_next = le64_to_cpu(cp->cp_snapshot_list.ssl_next); | ||
383 | } | ||
384 | |||
385 | static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 cno, | ||
386 | struct nilfs_cpinfo *ci, size_t nci) | ||
387 | { | ||
388 | struct nilfs_checkpoint *cp; | ||
389 | struct buffer_head *bh; | ||
390 | size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; | ||
391 | __u64 cur_cno = nilfs_mdt_cno(cpfile); | ||
392 | void *kaddr; | ||
393 | int n, ret; | ||
394 | int ncps, i; | ||
395 | |||
396 | down_read(&NILFS_MDT(cpfile)->mi_sem); | ||
397 | |||
398 | for (n = 0; cno < cur_cno && n < nci; cno += ncps) { | ||
399 | ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, cur_cno); | ||
400 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh); | ||
401 | if (ret < 0) { | ||
402 | if (ret != -ENOENT) | ||
403 | goto out; | ||
404 | continue; /* skip hole */ | ||
405 | } | ||
406 | |||
407 | kaddr = kmap_atomic(bh->b_page, KM_USER0); | ||
408 | cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); | ||
409 | for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) { | ||
410 | if (!nilfs_checkpoint_invalid(cp)) | ||
411 | nilfs_cpfile_checkpoint_to_cpinfo( | ||
412 | cpfile, cp, &ci[n++]); | ||
413 | } | ||
414 | kunmap_atomic(kaddr, KM_USER0); | ||
415 | brelse(bh); | ||
416 | } | ||
417 | |||
418 | ret = n; | ||
419 | |||
420 | out: | ||
421 | up_read(&NILFS_MDT(cpfile)->mi_sem); | ||
422 | return ret; | ||
423 | } | ||
424 | |||
425 | static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 cno, | ||
426 | struct nilfs_cpinfo *ci, size_t nci) | ||
427 | { | ||
428 | struct buffer_head *bh; | ||
429 | struct nilfs_cpfile_header *header; | ||
430 | struct nilfs_checkpoint *cp; | ||
431 | __u64 curr, next; | ||
432 | unsigned long curr_blkoff, next_blkoff; | ||
433 | void *kaddr; | ||
434 | int n, ret; | ||
435 | |||
436 | down_read(&NILFS_MDT(cpfile)->mi_sem); | ||
437 | |||
438 | if (cno == 0) { | ||
439 | ret = nilfs_cpfile_get_header_block(cpfile, &bh); | ||
440 | if (ret < 0) | ||
441 | goto out; | ||
442 | kaddr = kmap_atomic(bh->b_page, KM_USER0); | ||
443 | header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr); | ||
444 | curr = le64_to_cpu(header->ch_snapshot_list.ssl_next); | ||
445 | kunmap_atomic(kaddr, KM_USER0); | ||
446 | brelse(bh); | ||
447 | if (curr == 0) { | ||
448 | ret = 0; | ||
449 | goto out; | ||
450 | } | ||
451 | } else | ||
452 | curr = cno; | ||
453 | curr_blkoff = nilfs_cpfile_get_blkoff(cpfile, curr); | ||
454 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr, 0, &bh); | ||
455 | if (ret < 0) | ||
456 | goto out; | ||
457 | kaddr = kmap_atomic(bh->b_page, KM_USER0); | ||
458 | for (n = 0; n < nci; n++) { | ||
459 | cp = nilfs_cpfile_block_get_checkpoint( | ||
460 | cpfile, curr, bh, kaddr); | ||
461 | nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, &ci[n]); | ||
462 | next = le64_to_cpu(cp->cp_snapshot_list.ssl_next); | ||
463 | if (next == 0) { | ||
464 | curr = next; | ||
465 | n++; | ||
466 | break; | ||
467 | } | ||
468 | next_blkoff = nilfs_cpfile_get_blkoff(cpfile, next); | ||
469 | if (curr_blkoff != next_blkoff) { | ||
470 | kunmap_atomic(kaddr, KM_USER0); | ||
471 | brelse(bh); | ||
472 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, next, | ||
473 | 0, &bh); | ||
474 | if (ret < 0) | ||
475 | goto out; | ||
476 | kaddr = kmap_atomic(bh->b_page, KM_USER0); | ||
477 | } | ||
478 | curr = next; | ||
479 | curr_blkoff = next_blkoff; | ||
480 | } | ||
481 | kunmap_atomic(kaddr, KM_USER0); | ||
482 | brelse(bh); | ||
483 | ret = n; | ||
484 | |||
485 | out: | ||
486 | up_read(&NILFS_MDT(cpfile)->mi_sem); | ||
487 | return ret; | ||
488 | } | ||
489 | |||
490 | /** | ||
491 | * nilfs_cpfile_get_cpinfo - | ||
492 | * @cpfile: | ||
493 | * @cno: | ||
494 | * @ci: | ||
495 | * @nci: | ||
496 | */ | ||
497 | ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, | ||
498 | __u64 cno, int mode, | ||
499 | struct nilfs_cpinfo *ci, size_t nci) | ||
500 | { | ||
501 | switch (mode) { | ||
502 | case NILFS_CHECKPOINT: | ||
503 | return nilfs_cpfile_do_get_cpinfo(cpfile, cno, ci, nci); | ||
504 | case NILFS_SNAPSHOT: | ||
505 | return nilfs_cpfile_do_get_ssinfo(cpfile, cno, ci, nci); | ||
506 | default: | ||
507 | return -EINVAL; | ||
508 | } | ||
509 | } | ||
510 | |||
511 | /** | ||
512 | * nilfs_cpfile_delete_checkpoint - | ||
513 | * @cpfile: | ||
514 | * @cno: | ||
515 | */ | ||
516 | int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno) | ||
517 | { | ||
518 | struct nilfs_cpinfo ci; | ||
519 | ssize_t nci; | ||
520 | int ret; | ||
521 | |||
522 | /* checkpoint number 0 is invalid */ | ||
523 | if (cno == 0) | ||
524 | return -ENOENT; | ||
525 | nci = nilfs_cpfile_do_get_cpinfo(cpfile, cno, &ci, 1); | ||
526 | if (nci < 0) | ||
527 | return nci; | ||
528 | else if (nci == 0 || ci.ci_cno != cno) | ||
529 | return -ENOENT; | ||
530 | |||
531 | /* cannot delete the latest checkpoint nor snapshots */ | ||
532 | ret = nilfs_cpinfo_snapshot(&ci); | ||
533 | if (ret < 0) | ||
534 | return ret; | ||
535 | else if (ret > 0 || cno == nilfs_mdt_cno(cpfile) - 1) | ||
536 | return -EPERM; | ||
537 | |||
538 | return nilfs_cpfile_delete_checkpoints(cpfile, cno, cno + 1); | ||
539 | } | ||
540 | |||
541 | static struct nilfs_snapshot_list * | ||
542 | nilfs_cpfile_block_get_snapshot_list(const struct inode *cpfile, | ||
543 | __u64 cno, | ||
544 | struct buffer_head *bh, | ||
545 | void *kaddr) | ||
546 | { | ||
547 | struct nilfs_cpfile_header *header; | ||
548 | struct nilfs_checkpoint *cp; | ||
549 | struct nilfs_snapshot_list *list; | ||
550 | |||
551 | if (cno != 0) { | ||
552 | cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); | ||
553 | list = &cp->cp_snapshot_list; | ||
554 | } else { | ||
555 | header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr); | ||
556 | list = &header->ch_snapshot_list; | ||
557 | } | ||
558 | return list; | ||
559 | } | ||
560 | |||
561 | static int nilfs_cpfile_set_snapshot(struct inode *cpfile, __u64 cno) | ||
562 | { | ||
563 | struct buffer_head *header_bh, *curr_bh, *prev_bh, *cp_bh; | ||
564 | struct nilfs_cpfile_header *header; | ||
565 | struct nilfs_checkpoint *cp; | ||
566 | struct nilfs_snapshot_list *list; | ||
567 | __u64 curr, prev; | ||
568 | unsigned long curr_blkoff, prev_blkoff; | ||
569 | void *kaddr; | ||
570 | int ret; | ||
571 | |||
572 | down_write(&NILFS_MDT(cpfile)->mi_sem); | ||
573 | |||
574 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh); | ||
575 | if (ret < 0) | ||
576 | goto out_sem; | ||
577 | kaddr = kmap_atomic(cp_bh->b_page, KM_USER0); | ||
578 | cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); | ||
579 | if (nilfs_checkpoint_invalid(cp)) { | ||
580 | ret = -ENOENT; | ||
581 | kunmap_atomic(kaddr, KM_USER0); | ||
582 | goto out_cp; | ||
583 | } | ||
584 | if (nilfs_checkpoint_snapshot(cp)) { | ||
585 | ret = 0; | ||
586 | kunmap_atomic(kaddr, KM_USER0); | ||
587 | goto out_cp; | ||
588 | } | ||
589 | kunmap_atomic(kaddr, KM_USER0); | ||
590 | |||
591 | ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); | ||
592 | if (ret < 0) | ||
593 | goto out_cp; | ||
594 | kaddr = kmap_atomic(header_bh->b_page, KM_USER0); | ||
595 | header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr); | ||
596 | list = &header->ch_snapshot_list; | ||
597 | curr_bh = header_bh; | ||
598 | get_bh(curr_bh); | ||
599 | curr = 0; | ||
600 | curr_blkoff = 0; | ||
601 | prev = le64_to_cpu(list->ssl_prev); | ||
602 | while (prev > cno) { | ||
603 | prev_blkoff = nilfs_cpfile_get_blkoff(cpfile, prev); | ||
604 | curr = prev; | ||
605 | if (curr_blkoff != prev_blkoff) { | ||
606 | kunmap_atomic(kaddr, KM_USER0); | ||
607 | brelse(curr_bh); | ||
608 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr, | ||
609 | 0, &curr_bh); | ||
610 | if (ret < 0) | ||
611 | goto out_header; | ||
612 | kaddr = kmap_atomic(curr_bh->b_page, KM_USER0); | ||
613 | } | ||
614 | curr_blkoff = prev_blkoff; | ||
615 | cp = nilfs_cpfile_block_get_checkpoint( | ||
616 | cpfile, curr, curr_bh, kaddr); | ||
617 | list = &cp->cp_snapshot_list; | ||
618 | prev = le64_to_cpu(list->ssl_prev); | ||
619 | } | ||
620 | kunmap_atomic(kaddr, KM_USER0); | ||
621 | |||
622 | if (prev != 0) { | ||
623 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0, | ||
624 | &prev_bh); | ||
625 | if (ret < 0) | ||
626 | goto out_curr; | ||
627 | } else { | ||
628 | prev_bh = header_bh; | ||
629 | get_bh(prev_bh); | ||
630 | } | ||
631 | |||
632 | kaddr = kmap_atomic(curr_bh->b_page, KM_USER0); | ||
633 | list = nilfs_cpfile_block_get_snapshot_list( | ||
634 | cpfile, curr, curr_bh, kaddr); | ||
635 | list->ssl_prev = cpu_to_le64(cno); | ||
636 | kunmap_atomic(kaddr, KM_USER0); | ||
637 | |||
638 | kaddr = kmap_atomic(cp_bh->b_page, KM_USER0); | ||
639 | cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); | ||
640 | cp->cp_snapshot_list.ssl_next = cpu_to_le64(curr); | ||
641 | cp->cp_snapshot_list.ssl_prev = cpu_to_le64(prev); | ||
642 | nilfs_checkpoint_set_snapshot(cp); | ||
643 | kunmap_atomic(kaddr, KM_USER0); | ||
644 | |||
645 | kaddr = kmap_atomic(prev_bh->b_page, KM_USER0); | ||
646 | list = nilfs_cpfile_block_get_snapshot_list( | ||
647 | cpfile, prev, prev_bh, kaddr); | ||
648 | list->ssl_next = cpu_to_le64(cno); | ||
649 | kunmap_atomic(kaddr, KM_USER0); | ||
650 | |||
651 | kaddr = kmap_atomic(header_bh->b_page, KM_USER0); | ||
652 | header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr); | ||
653 | le64_add_cpu(&header->ch_nsnapshots, 1); | ||
654 | kunmap_atomic(kaddr, KM_USER0); | ||
655 | |||
656 | nilfs_mdt_mark_buffer_dirty(prev_bh); | ||
657 | nilfs_mdt_mark_buffer_dirty(curr_bh); | ||
658 | nilfs_mdt_mark_buffer_dirty(cp_bh); | ||
659 | nilfs_mdt_mark_buffer_dirty(header_bh); | ||
660 | nilfs_mdt_mark_dirty(cpfile); | ||
661 | |||
662 | brelse(prev_bh); | ||
663 | |||
664 | out_curr: | ||
665 | brelse(curr_bh); | ||
666 | |||
667 | out_header: | ||
668 | brelse(header_bh); | ||
669 | |||
670 | out_cp: | ||
671 | brelse(cp_bh); | ||
672 | |||
673 | out_sem: | ||
674 | up_write(&NILFS_MDT(cpfile)->mi_sem); | ||
675 | return ret; | ||
676 | } | ||
677 | |||
678 | static int nilfs_cpfile_clear_snapshot(struct inode *cpfile, __u64 cno) | ||
679 | { | ||
680 | struct buffer_head *header_bh, *next_bh, *prev_bh, *cp_bh; | ||
681 | struct nilfs_cpfile_header *header; | ||
682 | struct nilfs_checkpoint *cp; | ||
683 | struct nilfs_snapshot_list *list; | ||
684 | __u64 next, prev; | ||
685 | void *kaddr; | ||
686 | int ret; | ||
687 | |||
688 | down_write(&NILFS_MDT(cpfile)->mi_sem); | ||
689 | |||
690 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh); | ||
691 | if (ret < 0) | ||
692 | goto out_sem; | ||
693 | kaddr = kmap_atomic(cp_bh->b_page, KM_USER0); | ||
694 | cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); | ||
695 | if (nilfs_checkpoint_invalid(cp)) { | ||
696 | ret = -ENOENT; | ||
697 | kunmap_atomic(kaddr, KM_USER0); | ||
698 | goto out_cp; | ||
699 | } | ||
700 | if (!nilfs_checkpoint_snapshot(cp)) { | ||
701 | ret = 0; | ||
702 | kunmap_atomic(kaddr, KM_USER0); | ||
703 | goto out_cp; | ||
704 | } | ||
705 | |||
706 | list = &cp->cp_snapshot_list; | ||
707 | next = le64_to_cpu(list->ssl_next); | ||
708 | prev = le64_to_cpu(list->ssl_prev); | ||
709 | kunmap_atomic(kaddr, KM_USER0); | ||
710 | |||
711 | ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); | ||
712 | if (ret < 0) | ||
713 | goto out_cp; | ||
714 | if (next != 0) { | ||
715 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, next, 0, | ||
716 | &next_bh); | ||
717 | if (ret < 0) | ||
718 | goto out_header; | ||
719 | } else { | ||
720 | next_bh = header_bh; | ||
721 | get_bh(next_bh); | ||
722 | } | ||
723 | if (prev != 0) { | ||
724 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0, | ||
725 | &prev_bh); | ||
726 | if (ret < 0) | ||
727 | goto out_next; | ||
728 | } else { | ||
729 | prev_bh = header_bh; | ||
730 | get_bh(prev_bh); | ||
731 | } | ||
732 | |||
733 | kaddr = kmap_atomic(next_bh->b_page, KM_USER0); | ||
734 | list = nilfs_cpfile_block_get_snapshot_list( | ||
735 | cpfile, next, next_bh, kaddr); | ||
736 | list->ssl_prev = cpu_to_le64(prev); | ||
737 | kunmap_atomic(kaddr, KM_USER0); | ||
738 | |||
739 | kaddr = kmap_atomic(prev_bh->b_page, KM_USER0); | ||
740 | list = nilfs_cpfile_block_get_snapshot_list( | ||
741 | cpfile, prev, prev_bh, kaddr); | ||
742 | list->ssl_next = cpu_to_le64(next); | ||
743 | kunmap_atomic(kaddr, KM_USER0); | ||
744 | |||
745 | kaddr = kmap_atomic(cp_bh->b_page, KM_USER0); | ||
746 | cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); | ||
747 | cp->cp_snapshot_list.ssl_next = cpu_to_le64(0); | ||
748 | cp->cp_snapshot_list.ssl_prev = cpu_to_le64(0); | ||
749 | nilfs_checkpoint_clear_snapshot(cp); | ||
750 | kunmap_atomic(kaddr, KM_USER0); | ||
751 | |||
752 | kaddr = kmap_atomic(header_bh->b_page, KM_USER0); | ||
753 | header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr); | ||
754 | le64_add_cpu(&header->ch_nsnapshots, -1); | ||
755 | kunmap_atomic(kaddr, KM_USER0); | ||
756 | |||
757 | nilfs_mdt_mark_buffer_dirty(next_bh); | ||
758 | nilfs_mdt_mark_buffer_dirty(prev_bh); | ||
759 | nilfs_mdt_mark_buffer_dirty(cp_bh); | ||
760 | nilfs_mdt_mark_buffer_dirty(header_bh); | ||
761 | nilfs_mdt_mark_dirty(cpfile); | ||
762 | |||
763 | brelse(prev_bh); | ||
764 | |||
765 | out_next: | ||
766 | brelse(next_bh); | ||
767 | |||
768 | out_header: | ||
769 | brelse(header_bh); | ||
770 | |||
771 | out_cp: | ||
772 | brelse(cp_bh); | ||
773 | |||
774 | out_sem: | ||
775 | up_write(&NILFS_MDT(cpfile)->mi_sem); | ||
776 | return ret; | ||
777 | } | ||
778 | |||
779 | /** | ||
780 | * nilfs_cpfile_is_snapshot - | ||
781 | * @cpfile: inode of checkpoint file | ||
782 | * @cno: checkpoint number | ||
783 | * | ||
784 | * Description: | ||
785 | * | ||
786 | * Return Value: On success, 1 is returned if the checkpoint specified by | ||
787 | * @cno is a snapshot, or 0 if not. On error, one of the following negative | ||
788 | * error codes is returned. | ||
789 | * | ||
790 | * %-EIO - I/O error. | ||
791 | * | ||
792 | * %-ENOMEM - Insufficient amount of memory available. | ||
793 | * | ||
794 | * %-ENOENT - No such checkpoint. | ||
795 | */ | ||
796 | int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno) | ||
797 | { | ||
798 | struct buffer_head *bh; | ||
799 | struct nilfs_checkpoint *cp; | ||
800 | void *kaddr; | ||
801 | int ret; | ||
802 | |||
803 | down_read(&NILFS_MDT(cpfile)->mi_sem); | ||
804 | |||
805 | ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh); | ||
806 | if (ret < 0) | ||
807 | goto out; | ||
808 | kaddr = kmap_atomic(bh->b_page, KM_USER0); | ||
809 | cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); | ||
810 | ret = nilfs_checkpoint_snapshot(cp); | ||
811 | kunmap_atomic(kaddr, KM_USER0); | ||
812 | brelse(bh); | ||
813 | |||
814 | out: | ||
815 | up_read(&NILFS_MDT(cpfile)->mi_sem); | ||
816 | return ret; | ||
817 | } | ||
818 | |||
819 | /** | ||
820 | * nilfs_cpfile_change_cpmode - change checkpoint mode | ||
821 | * @cpfile: inode of checkpoint file | ||
822 | * @cno: checkpoint number | ||
823 | * @status: mode of checkpoint | ||
824 | * | ||
825 | * Description: nilfs_change_cpmode() changes the mode of the checkpoint | ||
826 | * specified by @cno. The mode @mode is NILFS_CHECKPOINT or NILFS_SNAPSHOT. | ||
827 | * | ||
828 | * Return Value: On success, 0 is returned. On error, one of the following | ||
829 | * negative error codes is returned. | ||
830 | * | ||
831 | * %-EIO - I/O error. | ||
832 | * | ||
833 | * %-ENOMEM - Insufficient amount of memory available. | ||
834 | * | ||
835 | * %-ENOENT - No such checkpoint. | ||
836 | */ | ||
837 | int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode) | ||
838 | { | ||
839 | struct the_nilfs *nilfs; | ||
840 | int ret; | ||
841 | |||
842 | nilfs = NILFS_MDT(cpfile)->mi_nilfs; | ||
843 | |||
844 | switch (mode) { | ||
845 | case NILFS_CHECKPOINT: | ||
846 | /* | ||
847 | * Check for protecting existing snapshot mounts: | ||
848 | * bd_mount_sem is used to make this operation atomic and | ||
849 | * exclusive with a new mount job. Though it doesn't cover | ||
850 | * umount, it's enough for the purpose. | ||
851 | */ | ||
852 | down(&nilfs->ns_bdev->bd_mount_sem); | ||
853 | if (nilfs_checkpoint_is_mounted(nilfs, cno, 1)) { | ||
854 | /* Current implementation does not have to protect | ||
855 | plain read-only mounts since they are exclusive | ||
856 | with a read/write mount and are protected from the | ||
857 | cleaner. */ | ||
858 | ret = -EBUSY; | ||
859 | } else | ||
860 | ret = nilfs_cpfile_clear_snapshot(cpfile, cno); | ||
861 | up(&nilfs->ns_bdev->bd_mount_sem); | ||
862 | return ret; | ||
863 | case NILFS_SNAPSHOT: | ||
864 | return nilfs_cpfile_set_snapshot(cpfile, cno); | ||
865 | default: | ||
866 | return -EINVAL; | ||
867 | } | ||
868 | } | ||
869 | |||
870 | /** | ||
871 | * nilfs_cpfile_get_stat - get checkpoint statistics | ||
872 | * @cpfile: inode of checkpoint file | ||
873 | * @stat: pointer to a structure of checkpoint statistics | ||
874 | * | ||
875 | * Description: nilfs_cpfile_get_stat() returns information about checkpoints. | ||
876 | * | ||
877 | * Return Value: On success, 0 is returned, and checkpoints information is | ||
878 | * stored in the place pointed by @stat. On error, one of the following | ||
879 | * negative error codes is returned. | ||
880 | * | ||
881 | * %-EIO - I/O error. | ||
882 | * | ||
883 | * %-ENOMEM - Insufficient amount of memory available. | ||
884 | */ | ||
885 | int nilfs_cpfile_get_stat(struct inode *cpfile, struct nilfs_cpstat *cpstat) | ||
886 | { | ||
887 | struct buffer_head *bh; | ||
888 | struct nilfs_cpfile_header *header; | ||
889 | void *kaddr; | ||
890 | int ret; | ||
891 | |||
892 | down_read(&NILFS_MDT(cpfile)->mi_sem); | ||
893 | |||
894 | ret = nilfs_cpfile_get_header_block(cpfile, &bh); | ||
895 | if (ret < 0) | ||
896 | goto out_sem; | ||
897 | kaddr = kmap_atomic(bh->b_page, KM_USER0); | ||
898 | header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr); | ||
899 | cpstat->cs_cno = nilfs_mdt_cno(cpfile); | ||
900 | cpstat->cs_ncps = le64_to_cpu(header->ch_ncheckpoints); | ||
901 | cpstat->cs_nsss = le64_to_cpu(header->ch_nsnapshots); | ||
902 | kunmap_atomic(kaddr, KM_USER0); | ||
903 | brelse(bh); | ||
904 | |||
905 | out_sem: | ||
906 | up_read(&NILFS_MDT(cpfile)->mi_sem); | ||
907 | return ret; | ||
908 | } | ||
diff --git a/fs/nilfs2/cpfile.h b/fs/nilfs2/cpfile.h new file mode 100644 index 00000000000..f097e90fcb2 --- /dev/null +++ b/fs/nilfs2/cpfile.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * cpfile.h - NILFS checkpoint file. | ||
3 | * | ||
4 | * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
19 | * | ||
20 | * Written by Koji Sato <koji@osrg.net>. | ||
21 | */ | ||
22 | |||
23 | #ifndef _NILFS_CPFILE_H | ||
24 | #define _NILFS_CPFILE_H | ||
25 | |||
26 | #include <linux/fs.h> | ||
27 | #include <linux/buffer_head.h> | ||
28 | #include <linux/nilfs2_fs.h> | ||
29 | |||
30 | #define NILFS_CPFILE_GFP NILFS_MDT_GFP | ||
31 | |||
32 | |||
33 | int nilfs_cpfile_get_checkpoint(struct inode *, __u64, int, | ||
34 | struct nilfs_checkpoint **, | ||
35 | struct buffer_head **); | ||
36 | void nilfs_cpfile_put_checkpoint(struct inode *, __u64, struct buffer_head *); | ||
37 | int nilfs_cpfile_delete_checkpoints(struct inode *, __u64, __u64); | ||
38 | int nilfs_cpfile_delete_checkpoint(struct inode *, __u64); | ||
39 | int nilfs_cpfile_change_cpmode(struct inode *, __u64, int); | ||
40 | int nilfs_cpfile_is_snapshot(struct inode *, __u64); | ||
41 | int nilfs_cpfile_get_stat(struct inode *, struct nilfs_cpstat *); | ||
42 | ssize_t nilfs_cpfile_get_cpinfo(struct inode *, __u64, int, | ||
43 | struct nilfs_cpinfo *, size_t); | ||
44 | |||
45 | #endif /* _NILFS_CPFILE_H */ | ||