Add patch SR-ext4-resize-mark-new-group-EXT_BG_INODE_ZEROED.patch
[ext4-patch-queue/an.git] / defrag-06-add-ioc-free-blocks-info-ioctl
blob52b0b0eec2bc8541b65286667292bffb83ffd9a0
1 ext4: online defrag -- Add the EXT4_IOC_FREE_BLOCKS_INFO ioctl.
3 From: Akira Fujita <a-fujita@rs.jp.nec.com>
5 The EXT4_IOC_FREE_BLOCKS_INFO ioctl gets free extents
6 information of the target block group.
7 This ioctl is used only in the force defrag (-f).
9 Defragger calculates the victim extents (which will be moved
10 into other block group in order to make contiguous free space for the target file)
11 based on used/unused extents information.
13 ext4_defrag_fblocks_distribution() was renamed as ext4_mb_search_free_extents()
14 and moved into mballoc.c.  Because it uses ext4_buddy structure.
16 Signed-off-by: Akira Fujita <a-fujita@rs.jp.nec.com>
17 Signed-off-by: Takashi Sato <t-sato@yk.jp.nec.com>
18 ---
19  fs/ext4/ext4.h    |   27 +++++++++++++++++
20  fs/ext4/ioctl.c   |   20 +++++++++++++
21  fs/ext4/mballoc.c |   81 +++++++++++++++++++++++++++++++++++++++++++++++++++++
22  3 files changed, 128 insertions(+), 0 deletions(-)
24 diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
25 index 94c4186..f7b092d 100644
26 --- a/fs/ext4/ext4.h
27 +++ b/fs/ext4/ext4.h
28 @@ -304,6 +304,7 @@ struct ext4_new_group_data {
29   /* note ioctl 11 reserved for filesystem-independent FIEMAP ioctl */
30  #define EXT4_IOC_DEFRAG                        _IOW('f', 15, struct ext4_ext_defrag_data)
31  #define EXT4_IOC_SUPER_INFO            _IOR('f', 16, struct ext4_super_block)
32 +#define EXT4_IOC_FREE_BLOCKS_INFO      _IOWR('f', 17, struct ext4_extents_info)
34  /*
35   * ioctl commands in 32 bit emulation
36 @@ -321,12 +322,36 @@ struct ext4_new_group_data {
37  #define EXT4_IOC32_GETVERSION_OLD      FS_IOC32_GETVERSION
38  #define EXT4_IOC32_SETVERSION_OLD      FS_IOC32_SETVERSION
40 +/*
41 + * The following four macros are used for the defrag force mode.
42 + *
43 + * DEFRAG_MAX_ENT:     the maximum number of extents for exchanging between
44 + *                     kernel-space and user-space per an ioctl
45 + */
46 +#define DEFRAG_MAX_ENT         32
48 +struct ext4_extent_data {
49 +       ext4_lblk_t block;              /* start logical block number */
50 +       ext4_fsblk_t start;             /* start physical block number */
51 +       int len;                        /* blocks count */
52 +};
54  struct ext4_ext_defrag_data {
55         ext4_lblk_t start_offset;       /* start offset to defrag in blocks */
56         ext4_lblk_t defrag_size;        /* size of defrag in blocks */
57         ext4_fsblk_t goal;              /* block offset for allocation */
58  };
60 +struct ext4_extents_info {
61 +       unsigned long long ino;         /* inode number */
62 +       int max_entries;                /* maximum extents count */
63 +       int entries;                    /* extent number/count */
64 +       ext4_lblk_t f_offset;           /* file offset */
65 +       ext4_grpblk_t g_offset;         /* group offset */
66 +       ext4_fsblk_t goal;              /* block offset for allocation */
67 +       struct ext4_extent_data ext[DEFRAG_MAX_ENT];
68 +};
70  #define EXT4_TRANS_META_BLOCKS 4 /* bitmap + group desc + sb + inode */
72  /*
73 @@ -1069,6 +1094,8 @@ extern int ext4_mb_add_more_groupinfo(struct super_block *sb,
74                 ext4_group_t i, struct ext4_group_desc *desc);
75  extern void ext4_mb_update_group_info(struct ext4_group_info *grp,
76                 ext4_grpblk_t add);
77 +extern int ext4_mb_search_free_extents(struct inode *inode,
78 +                                       struct ext4_extents_info *ext_info);
81  /* inode.c */
82 diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
83 index f8ce89b..5709574 100644
84 --- a/fs/ext4/ioctl.c
85 +++ b/fs/ext4/ioctl.c
86 @@ -256,6 +256,26 @@ setversion_out:
87                         return 0;
88         }
90 +       case EXT4_IOC_FREE_BLOCKS_INFO: {
91 +               struct ext4_extents_info ext_info;
92 +               int err;
94 +               if (copy_from_user(&ext_info,
95 +                       (struct ext4_extents_info __user *)arg,
96 +                                               sizeof(ext_info)))
97 +                       return -EFAULT;
99 +               BUG_ON(ext_info.ino != inode->i_ino);
101 +               err = ext4_mb_search_free_extents(inode, &ext_info);
103 +               if (!err)
104 +                       err = copy_to_user(
105 +                       (struct ext4_extents_info __user *)arg,
106 +                               &ext_info, sizeof(ext_info));
107 +               return err;
108 +       }
110         case EXT4_IOC_GROUP_ADD: {
111                 struct ext4_new_group_data input;
112                 struct super_block *sb = inode->i_sb;
113 diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
114 index dfe17a1..eebcf7c 100644
115 --- a/fs/ext4/mballoc.c
116 +++ b/fs/ext4/mballoc.c
117 @@ -4654,3 +4654,84 @@ error_return:
118                 kmem_cache_free(ext4_ac_cachep, ac);
119         return;
122 +/**
123 + * ext4_mb_search_free_extents
124 + *    - Search free extents in the block group where target inode is located
125 + *
126 + * @inode:     target inode
127 + * @ext_info:  ext4_extents_info is used to store free extents
128 + *
129 + * This function returns 0 if succeed, otherwise returns error value.
130 + */
131 +int
132 +ext4_mb_search_free_extents(struct inode *inode,
133 +                       struct ext4_extents_info *ext_info)
135 +       struct super_block *sb = inode->i_sb;
136 +       struct ext4_buddy e4b;
137 +       ext4_group_t group_no;
138 +       ext4_grpblk_t start, end;
139 +       ext4_fsblk_t start_block = 0;
140 +       int i, err;
141 +       int num = 0;
142 +       int len = 0;
143 +       int block_set = 0;
144 +       int extra_block = 0;
146 +       if (!sb) {
147 +               printk(KERN_ERR "ext4 defrag: Non-existent device\n");
148 +               return -ENOSPC;
149 +       }
151 +       group_no = (inode->i_ino - 1) / EXT4_INODES_PER_GROUP(sb);
152 +       start = ext_info->g_offset;
153 +       end = EXT4_BLOCKS_PER_GROUP(sb) - 1;
155 +       err = ext4_mb_load_buddy(sb, group_no, &e4b);
156 +       if (err)
157 +               return err;
159 +       /* We consider about the boot block if bs = 1k */
160 +       if (sb->s_blocksize == 1024)
161 +               extra_block = 1;
163 +       ext4_lock_group(sb, group_no);
165 +       for (i = start; i <= end ; i++) {
166 +               if (!mb_test_bit(i, e4b.bd_bitmap)) {
167 +                       len++;
168 +                       /*
169 +                        * Reset start_block if the free block is
170 +                        * the head of region.
171 +                        */
172 +                       if (!block_set) {
173 +                               start_block =
174 +                                i + group_no * EXT4_BLOCKS_PER_GROUP(sb) +
175 +                                extra_block;
176 +                               block_set = 1;
177 +                       }
178 +               } else if (len) {
179 +                       ext_info->ext[num].start = start_block;
180 +                       ext_info->ext[num].len = len;
181 +                       num++;
182 +                       len = 0;
183 +                       block_set = 0;
184 +                       if (num == ext_info->max_entries) {
185 +                               ext_info->g_offset = i + 1;
186 +                               break;
187 +                       }
188 +               }
189 +               if (i == end && len) {
190 +                       ext_info->ext[num].start = start_block;
191 +                       ext_info->ext[num].len = len;
192 +                       num++;
193 +               }
194 +       }
195 +       ext_info->entries = num;
197 +       ext4_unlock_group(sb, group_no);
198 +       ext4_mb_release_desc(&e4b);
200 +       return err;