diff options
author | Mikulas Patocka <mikulas@twibright.com> | 2015-06-28 09:16:57 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-07-09 16:35:30 -0400 |
commit | a27b5b97d6fe91f55058ad8ac28a8768700201ab (patch) | |
tree | 84272380472c484455a662d17b0b544421a26fe2 /fs/hpfs | |
parent | 9abea2d64ce93b6909de7f83a7f681f572369708 (diff) |
hpfs: add fstrim support
This patch adds support for fstrim to the HPFS filesystem.
Signed-off-by: Mikulas Patocka <mikulas@twibright.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/hpfs')
-rw-r--r-- | fs/hpfs/alloc.c | 95 | ||||
-rw-r--r-- | fs/hpfs/dir.c | 1 | ||||
-rw-r--r-- | fs/hpfs/file.c | 1 | ||||
-rw-r--r-- | fs/hpfs/hpfs_fn.h | 4 | ||||
-rw-r--r-- | fs/hpfs/super.c | 27 |
5 files changed, 128 insertions, 0 deletions
diff --git a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c index f005046e1591..d6a4b55d2ab0 100644 --- a/fs/hpfs/alloc.c +++ b/fs/hpfs/alloc.c | |||
@@ -484,3 +484,98 @@ struct anode *hpfs_alloc_anode(struct super_block *s, secno near, anode_secno *a | |||
484 | a->btree.first_free = cpu_to_le16(8); | 484 | a->btree.first_free = cpu_to_le16(8); |
485 | return a; | 485 | return a; |
486 | } | 486 | } |
487 | |||
488 | static unsigned find_run(__le32 *bmp, unsigned *idx) | ||
489 | { | ||
490 | unsigned len; | ||
491 | while (tstbits(bmp, *idx, 1)) { | ||
492 | (*idx)++; | ||
493 | if (unlikely(*idx >= 0x4000)) | ||
494 | return 0; | ||
495 | } | ||
496 | len = 1; | ||
497 | while (!tstbits(bmp, *idx + len, 1)) | ||
498 | len++; | ||
499 | return len; | ||
500 | } | ||
501 | |||
502 | static int do_trim(struct super_block *s, secno start, unsigned len, secno limit_start, secno limit_end, unsigned minlen, unsigned *result) | ||
503 | { | ||
504 | int err; | ||
505 | secno end; | ||
506 | if (fatal_signal_pending(current)) | ||
507 | return -EINTR; | ||
508 | end = start + len; | ||
509 | if (start < limit_start) | ||
510 | start = limit_start; | ||
511 | if (end > limit_end) | ||
512 | end = limit_end; | ||
513 | if (start >= end) | ||
514 | return 0; | ||
515 | if (end - start < minlen) | ||
516 | return 0; | ||
517 | err = sb_issue_discard(s, start, end - start, GFP_NOFS, 0); | ||
518 | if (err) | ||
519 | return err; | ||
520 | *result += end - start; | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | int hpfs_trim_fs(struct super_block *s, u64 start, u64 end, u64 minlen, unsigned *result) | ||
525 | { | ||
526 | int err = 0; | ||
527 | struct hpfs_sb_info *sbi = hpfs_sb(s); | ||
528 | unsigned idx, len, start_bmp, end_bmp; | ||
529 | __le32 *bmp; | ||
530 | struct quad_buffer_head qbh; | ||
531 | |||
532 | *result = 0; | ||
533 | if (!end || end > sbi->sb_fs_size) | ||
534 | end = sbi->sb_fs_size; | ||
535 | if (start >= sbi->sb_fs_size) | ||
536 | return 0; | ||
537 | if (minlen > 0x4000) | ||
538 | return 0; | ||
539 | if (start < sbi->sb_dirband_start + sbi->sb_dirband_size && end > sbi->sb_dirband_start) { | ||
540 | hpfs_lock(s); | ||
541 | if (s->s_flags & MS_RDONLY) { | ||
542 | err = -EROFS; | ||
543 | goto unlock_1; | ||
544 | } | ||
545 | if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) { | ||
546 | err = -EIO; | ||
547 | goto unlock_1; | ||
548 | } | ||
549 | idx = 0; | ||
550 | while ((len = find_run(bmp, &idx)) && !err) { | ||
551 | err = do_trim(s, sbi->sb_dirband_start + idx * 4, len * 4, start, end, minlen, result); | ||
552 | idx += len; | ||
553 | } | ||
554 | hpfs_brelse4(&qbh); | ||
555 | unlock_1: | ||
556 | hpfs_unlock(s); | ||
557 | } | ||
558 | start_bmp = start >> 14; | ||
559 | end_bmp = (end + 0x3fff) >> 14; | ||
560 | while (start_bmp < end_bmp && !err) { | ||
561 | hpfs_lock(s); | ||
562 | if (s->s_flags & MS_RDONLY) { | ||
563 | err = -EROFS; | ||
564 | goto unlock_2; | ||
565 | } | ||
566 | if (!(bmp = hpfs_map_bitmap(s, start_bmp, &qbh, "trim"))) { | ||
567 | err = -EIO; | ||
568 | goto unlock_2; | ||
569 | } | ||
570 | idx = 0; | ||
571 | while ((len = find_run(bmp, &idx)) && !err) { | ||
572 | err = do_trim(s, (start_bmp << 14) + idx, len, start, end, minlen, result); | ||
573 | idx += len; | ||
574 | } | ||
575 | hpfs_brelse4(&qbh); | ||
576 | unlock_2: | ||
577 | hpfs_unlock(s); | ||
578 | start_bmp++; | ||
579 | } | ||
580 | return err; | ||
581 | } | ||
diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c index 2a8e07425de0..dc540bfcee1d 100644 --- a/fs/hpfs/dir.c +++ b/fs/hpfs/dir.c | |||
@@ -327,4 +327,5 @@ const struct file_operations hpfs_dir_ops = | |||
327 | .iterate = hpfs_readdir, | 327 | .iterate = hpfs_readdir, |
328 | .release = hpfs_dir_release, | 328 | .release = hpfs_dir_release, |
329 | .fsync = hpfs_file_fsync, | 329 | .fsync = hpfs_file_fsync, |
330 | .unlocked_ioctl = hpfs_ioctl, | ||
330 | }; | 331 | }; |
diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index 6d8cfe9b52d6..7ca28d604bf7 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c | |||
@@ -203,6 +203,7 @@ const struct file_operations hpfs_file_ops = | |||
203 | .release = hpfs_file_release, | 203 | .release = hpfs_file_release, |
204 | .fsync = hpfs_file_fsync, | 204 | .fsync = hpfs_file_fsync, |
205 | .splice_read = generic_file_splice_read, | 205 | .splice_read = generic_file_splice_read, |
206 | .unlocked_ioctl = hpfs_ioctl, | ||
206 | }; | 207 | }; |
207 | 208 | ||
208 | const struct inode_operations hpfs_file_iops = | 209 | const struct inode_operations hpfs_file_iops = |
diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index bb04b58d1d69..c4867b5116dd 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h | |||
@@ -18,6 +18,8 @@ | |||
18 | #include <linux/pagemap.h> | 18 | #include <linux/pagemap.h> |
19 | #include <linux/buffer_head.h> | 19 | #include <linux/buffer_head.h> |
20 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
21 | #include <linux/sched.h> | ||
22 | #include <linux/blkdev.h> | ||
21 | #include <asm/unaligned.h> | 23 | #include <asm/unaligned.h> |
22 | 24 | ||
23 | #include "hpfs.h" | 25 | #include "hpfs.h" |
@@ -200,6 +202,7 @@ void hpfs_free_dnode(struct super_block *, secno); | |||
200 | struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *); | 202 | struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *); |
201 | struct fnode *hpfs_alloc_fnode(struct super_block *, secno, fnode_secno *, struct buffer_head **); | 203 | struct fnode *hpfs_alloc_fnode(struct super_block *, secno, fnode_secno *, struct buffer_head **); |
202 | struct anode *hpfs_alloc_anode(struct super_block *, secno, anode_secno *, struct buffer_head **); | 204 | struct anode *hpfs_alloc_anode(struct super_block *, secno, anode_secno *, struct buffer_head **); |
205 | int hpfs_trim_fs(struct super_block *, u64, u64, u64, unsigned *); | ||
203 | 206 | ||
204 | /* anode.c */ | 207 | /* anode.c */ |
205 | 208 | ||
@@ -318,6 +321,7 @@ __printf(2, 3) | |||
318 | void hpfs_error(struct super_block *, const char *, ...); | 321 | void hpfs_error(struct super_block *, const char *, ...); |
319 | int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *); | 322 | int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *); |
320 | unsigned hpfs_get_free_dnodes(struct super_block *); | 323 | unsigned hpfs_get_free_dnodes(struct super_block *); |
324 | long hpfs_ioctl(struct file *file, unsigned cmd, unsigned long arg); | ||
321 | 325 | ||
322 | /* | 326 | /* |
323 | * local time (HPFS) to GMT (Unix) | 327 | * local time (HPFS) to GMT (Unix) |
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index 7cd00d3a7c9b..037e3e597ff4 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c | |||
@@ -196,6 +196,33 @@ static int hpfs_statfs(struct dentry *dentry, struct kstatfs *buf) | |||
196 | return 0; | 196 | return 0; |
197 | } | 197 | } |
198 | 198 | ||
199 | |||
200 | long hpfs_ioctl(struct file *file, unsigned cmd, unsigned long arg) | ||
201 | { | ||
202 | switch (cmd) { | ||
203 | case FITRIM: { | ||
204 | struct fstrim_range range; | ||
205 | secno n_trimmed; | ||
206 | int r; | ||
207 | if (!capable(CAP_SYS_ADMIN)) | ||
208 | return -EPERM; | ||
209 | if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range))) | ||
210 | return -EFAULT; | ||
211 | r = hpfs_trim_fs(file_inode(file)->i_sb, range.start >> 9, (range.start + range.len) >> 9, (range.minlen + 511) >> 9, &n_trimmed); | ||
212 | if (r) | ||
213 | return r; | ||
214 | range.len = (u64)n_trimmed << 9; | ||
215 | if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range))) | ||
216 | return -EFAULT; | ||
217 | return 0; | ||
218 | } | ||
219 | default: { | ||
220 | return -ENOIOCTLCMD; | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | |||
225 | |||
199 | static struct kmem_cache * hpfs_inode_cachep; | 226 | static struct kmem_cache * hpfs_inode_cachep; |
200 | 227 | ||
201 | static struct inode *hpfs_alloc_inode(struct super_block *sb) | 228 | static struct inode *hpfs_alloc_inode(struct super_block *sb) |