Commit 619205da5b567504310daf829dede1187fa29bbc
1 parent
56eb553885
Exists in
master
and in
7 other branches
nilfs2: add ioctl which limits range of segment to be allocated
This adds a new ioctl command which limits range of segment to be allocated. This is intended to gather data whithin a range of the partition before shrinking the filesystem, or to control new log location for some purpose. If a range is specified by the ioctl, segment allocator of nilfs tries to allocate new segments from the range unless no free segments are available there. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Showing 4 changed files with 100 additions and 10 deletions Side-by-side Diff
fs/nilfs2/ioctl.c
... | ... | @@ -698,6 +698,38 @@ |
698 | 698 | return 0; |
699 | 699 | } |
700 | 700 | |
701 | +static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp) | |
702 | +{ | |
703 | + struct the_nilfs *nilfs = inode->i_sb->s_fs_info; | |
704 | + __u64 range[2]; | |
705 | + __u64 minseg, maxseg; | |
706 | + unsigned long segbytes; | |
707 | + int ret = -EPERM; | |
708 | + | |
709 | + if (!capable(CAP_SYS_ADMIN)) | |
710 | + goto out; | |
711 | + | |
712 | + ret = -EFAULT; | |
713 | + if (copy_from_user(range, argp, sizeof(__u64[2]))) | |
714 | + goto out; | |
715 | + | |
716 | + ret = -ERANGE; | |
717 | + if (range[1] > i_size_read(inode->i_sb->s_bdev->bd_inode)) | |
718 | + goto out; | |
719 | + | |
720 | + segbytes = nilfs->ns_blocks_per_segment * nilfs->ns_blocksize; | |
721 | + | |
722 | + minseg = range[0] + segbytes - 1; | |
723 | + do_div(minseg, segbytes); | |
724 | + maxseg = NILFS_SB2_OFFSET_BYTES(range[1]); | |
725 | + do_div(maxseg, segbytes); | |
726 | + maxseg--; | |
727 | + | |
728 | + ret = nilfs_sufile_set_alloc_range(nilfs->ns_sufile, minseg, maxseg); | |
729 | +out: | |
730 | + return ret; | |
731 | +} | |
732 | + | |
701 | 733 | static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, |
702 | 734 | unsigned int cmd, void __user *argp, |
703 | 735 | size_t membsz, |
... | ... | @@ -763,6 +795,8 @@ |
763 | 795 | return nilfs_ioctl_clean_segments(inode, filp, cmd, argp); |
764 | 796 | case NILFS_IOCTL_SYNC: |
765 | 797 | return nilfs_ioctl_sync(inode, filp, cmd, argp); |
798 | + case NILFS_IOCTL_SET_ALLOC_RANGE: | |
799 | + return nilfs_ioctl_set_alloc_range(inode, argp); | |
766 | 800 | default: |
767 | 801 | return -ENOTTY; |
768 | 802 | } |
fs/nilfs2/sufile.c
... | ... | @@ -33,7 +33,9 @@ |
33 | 33 | |
34 | 34 | struct nilfs_sufile_info { |
35 | 35 | struct nilfs_mdt_info mi; |
36 | - unsigned long ncleansegs; | |
36 | + unsigned long ncleansegs;/* number of clean segments */ | |
37 | + __u64 allocmin; /* lower limit of allocatable segment range */ | |
38 | + __u64 allocmax; /* upper limit of allocatable segment range */ | |
37 | 39 | }; |
38 | 40 | |
39 | 41 | static inline struct nilfs_sufile_info *NILFS_SUI(struct inode *sufile) |
... | ... | @@ -248,6 +250,35 @@ |
248 | 250 | } |
249 | 251 | |
250 | 252 | /** |
253 | + * nilfs_sufile_set_alloc_range - limit range of segment to be allocated | |
254 | + * @sufile: inode of segment usage file | |
255 | + * @start: minimum segment number of allocatable region (inclusive) | |
256 | + * @end: maximum segment number of allocatable region (inclusive) | |
257 | + * | |
258 | + * Return Value: On success, 0 is returned. On error, one of the | |
259 | + * following negative error codes is returned. | |
260 | + * | |
261 | + * %-ERANGE - invalid segment region | |
262 | + */ | |
263 | +int nilfs_sufile_set_alloc_range(struct inode *sufile, __u64 start, __u64 end) | |
264 | +{ | |
265 | + struct nilfs_sufile_info *sui = NILFS_SUI(sufile); | |
266 | + __u64 nsegs; | |
267 | + int ret = -ERANGE; | |
268 | + | |
269 | + down_write(&NILFS_MDT(sufile)->mi_sem); | |
270 | + nsegs = nilfs_sufile_get_nsegments(sufile); | |
271 | + | |
272 | + if (start <= end && end < nsegs) { | |
273 | + sui->allocmin = start; | |
274 | + sui->allocmax = end; | |
275 | + ret = 0; | |
276 | + } | |
277 | + up_write(&NILFS_MDT(sufile)->mi_sem); | |
278 | + return ret; | |
279 | +} | |
280 | + | |
281 | +/** | |
251 | 282 | * nilfs_sufile_alloc - allocate a segment |
252 | 283 | * @sufile: inode of segment usage file |
253 | 284 | * @segnump: pointer to segment number |
254 | 285 | |
... | ... | @@ -269,11 +300,12 @@ |
269 | 300 | struct buffer_head *header_bh, *su_bh; |
270 | 301 | struct nilfs_sufile_header *header; |
271 | 302 | struct nilfs_segment_usage *su; |
303 | + struct nilfs_sufile_info *sui = NILFS_SUI(sufile); | |
272 | 304 | size_t susz = NILFS_MDT(sufile)->mi_entry_size; |
273 | 305 | __u64 segnum, maxsegnum, last_alloc; |
274 | 306 | void *kaddr; |
275 | - unsigned long nsegments, ncleansegs, nsus; | |
276 | - int ret, i, j; | |
307 | + unsigned long nsegments, ncleansegs, nsus, cnt; | |
308 | + int ret, j; | |
277 | 309 | |
278 | 310 | down_write(&NILFS_MDT(sufile)->mi_sem); |
279 | 311 | |
280 | 312 | |
... | ... | @@ -287,13 +319,31 @@ |
287 | 319 | kunmap_atomic(kaddr, KM_USER0); |
288 | 320 | |
289 | 321 | nsegments = nilfs_sufile_get_nsegments(sufile); |
322 | + maxsegnum = sui->allocmax; | |
290 | 323 | segnum = last_alloc + 1; |
291 | - maxsegnum = nsegments - 1; | |
292 | - for (i = 0; i < nsegments; i += nsus) { | |
293 | - if (segnum >= nsegments) { | |
294 | - /* wrap around */ | |
295 | - segnum = 0; | |
296 | - maxsegnum = last_alloc; | |
324 | + if (segnum < sui->allocmin || segnum > sui->allocmax) | |
325 | + segnum = sui->allocmin; | |
326 | + | |
327 | + for (cnt = 0; cnt < nsegments; cnt += nsus) { | |
328 | + if (segnum > maxsegnum) { | |
329 | + if (cnt < sui->allocmax - sui->allocmin + 1) { | |
330 | + /* | |
331 | + * wrap around in the limited region. | |
332 | + * if allocation started from | |
333 | + * sui->allocmin, this never happens. | |
334 | + */ | |
335 | + segnum = sui->allocmin; | |
336 | + maxsegnum = last_alloc; | |
337 | + } else if (segnum > sui->allocmin && | |
338 | + sui->allocmax + 1 < nsegments) { | |
339 | + segnum = sui->allocmax + 1; | |
340 | + maxsegnum = nsegments - 1; | |
341 | + } else if (sui->allocmin > 0) { | |
342 | + segnum = 0; | |
343 | + maxsegnum = sui->allocmin - 1; | |
344 | + } else { | |
345 | + break; /* never happens */ | |
346 | + } | |
297 | 347 | } |
298 | 348 | ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 1, |
299 | 349 | &su_bh); |
... | ... | @@ -319,7 +369,7 @@ |
319 | 369 | header->sh_last_alloc = cpu_to_le64(segnum); |
320 | 370 | kunmap_atomic(kaddr, KM_USER0); |
321 | 371 | |
322 | - NILFS_SUI(sufile)->ncleansegs--; | |
372 | + sui->ncleansegs--; | |
323 | 373 | nilfs_mdt_mark_buffer_dirty(header_bh); |
324 | 374 | nilfs_mdt_mark_buffer_dirty(su_bh); |
325 | 375 | nilfs_mdt_mark_dirty(sufile); |
... | ... | @@ -678,6 +728,9 @@ |
678 | 728 | sui->ncleansegs = le64_to_cpu(header->sh_ncleansegs); |
679 | 729 | kunmap_atomic(kaddr, KM_USER0); |
680 | 730 | brelse(header_bh); |
731 | + | |
732 | + sui->allocmax = nilfs_sufile_get_nsegments(sufile) - 1; | |
733 | + sui->allocmin = 0; | |
681 | 734 | |
682 | 735 | unlock_new_inode(sufile); |
683 | 736 | out: |
fs/nilfs2/sufile.h
... | ... | @@ -36,6 +36,7 @@ |
36 | 36 | |
37 | 37 | unsigned long nilfs_sufile_get_ncleansegs(struct inode *sufile); |
38 | 38 | |
39 | +int nilfs_sufile_set_alloc_range(struct inode *sufile, __u64 start, __u64 end); | |
39 | 40 | int nilfs_sufile_alloc(struct inode *, __u64 *); |
40 | 41 | int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum); |
41 | 42 | int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum, |
include/linux/nilfs2_fs.h