Commit a9bae189542e71f91e61a4428adf6e5a7dfe8063
Committed by
Linus Torvalds
1 parent
6edacf05c8
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
nilfs2: fix fix very long mount time issue
There exists a situation when GC can work in background alone without any other filesystem activity during significant time. The nilfs_clean_segments() method calls nilfs_segctor_construct() that updates superblocks in the case of NILFS_SC_SUPER_ROOT and THE_NILFS_DISCONTINUED flags are set. But when GC is working alone the nilfs_clean_segments() is called with unset THE_NILFS_DISCONTINUED flag. As a result, the update of superblocks doesn't occurred all this time and in the case of SPOR superblocks keep very old values of last super root placement. SYMPTOMS: Trying to mount a NILFS2 volume after SPOR in such environment ends with very long mounting time (it can achieve about several hours in some cases). REPRODUCING PATH: 1. It needs to use external USB HDD, disable automount and doesn't make any additional filesystem activity on the NILFS2 volume. 2. Generate temporary file with size about 100 - 500 GB (for example, dd if=/dev/zero of=<file_name> bs=1073741824 count=200). The size of file defines duration of GC working. 3. Then it needs to delete file. 4. Start GC manually by means of command "nilfs-clean -p 0". When you start GC by means of such way then, at the end, superblocks is updated by once. So, for simulation of SPOR, it needs to wait sometime (15 - 40 minutes) and simply switch off USB HDD manually. 5. Switch on USB HDD again and try to mount NILFS2 volume. As a result, NILFS2 volume will mount during very long time. REPRODUCIBILITY: 100% FIX: This patch adds checking that superblocks need to update and set THE_NILFS_DISCONTINUED flag before nilfs_clean_segments() call. Reported-by: Sergey Alexandrov <splavgm@gmail.com> Signed-off-by: Vyacheslav Dubeyko <slava@dubeyko.com> Tested-by: Vyacheslav Dubeyko <slava@dubeyko.com> Acked-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Tested-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 1 changed file with 4 additions and 1 deletions Inline Diff
fs/nilfs2/ioctl.c
1 | /* | 1 | /* |
2 | * ioctl.c - NILFS ioctl operations. | 2 | * ioctl.c - NILFS ioctl operations. |
3 | * | 3 | * |
4 | * Copyright (C) 2007, 2008 Nippon Telegraph and Telephone Corporation. | 4 | * Copyright (C) 2007, 2008 Nippon Telegraph and Telephone Corporation. |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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 | 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 | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 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 | 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 | 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
19 | * | 19 | * |
20 | * Written by Koji Sato <koji@osrg.net>. | 20 | * Written by Koji Sato <koji@osrg.net>. |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <linux/fs.h> | 23 | #include <linux/fs.h> |
24 | #include <linux/wait.h> | 24 | #include <linux/wait.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/capability.h> /* capable() */ | 26 | #include <linux/capability.h> /* capable() */ |
27 | #include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */ | 27 | #include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */ |
28 | #include <linux/vmalloc.h> | 28 | #include <linux/vmalloc.h> |
29 | #include <linux/compat.h> /* compat_ptr() */ | 29 | #include <linux/compat.h> /* compat_ptr() */ |
30 | #include <linux/mount.h> /* mnt_want_write_file(), mnt_drop_write_file() */ | 30 | #include <linux/mount.h> /* mnt_want_write_file(), mnt_drop_write_file() */ |
31 | #include <linux/buffer_head.h> | 31 | #include <linux/buffer_head.h> |
32 | #include <linux/nilfs2_fs.h> | 32 | #include <linux/nilfs2_fs.h> |
33 | #include "nilfs.h" | 33 | #include "nilfs.h" |
34 | #include "segment.h" | 34 | #include "segment.h" |
35 | #include "bmap.h" | 35 | #include "bmap.h" |
36 | #include "cpfile.h" | 36 | #include "cpfile.h" |
37 | #include "sufile.h" | 37 | #include "sufile.h" |
38 | #include "dat.h" | 38 | #include "dat.h" |
39 | 39 | ||
40 | 40 | ||
41 | static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs, | 41 | static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs, |
42 | struct nilfs_argv *argv, int dir, | 42 | struct nilfs_argv *argv, int dir, |
43 | ssize_t (*dofunc)(struct the_nilfs *, | 43 | ssize_t (*dofunc)(struct the_nilfs *, |
44 | __u64 *, int, | 44 | __u64 *, int, |
45 | void *, size_t, size_t)) | 45 | void *, size_t, size_t)) |
46 | { | 46 | { |
47 | void *buf; | 47 | void *buf; |
48 | void __user *base = (void __user *)(unsigned long)argv->v_base; | 48 | void __user *base = (void __user *)(unsigned long)argv->v_base; |
49 | size_t maxmembs, total, n; | 49 | size_t maxmembs, total, n; |
50 | ssize_t nr; | 50 | ssize_t nr; |
51 | int ret, i; | 51 | int ret, i; |
52 | __u64 pos, ppos; | 52 | __u64 pos, ppos; |
53 | 53 | ||
54 | if (argv->v_nmembs == 0) | 54 | if (argv->v_nmembs == 0) |
55 | return 0; | 55 | return 0; |
56 | 56 | ||
57 | if (argv->v_size > PAGE_SIZE) | 57 | if (argv->v_size > PAGE_SIZE) |
58 | return -EINVAL; | 58 | return -EINVAL; |
59 | 59 | ||
60 | buf = (void *)__get_free_pages(GFP_NOFS, 0); | 60 | buf = (void *)__get_free_pages(GFP_NOFS, 0); |
61 | if (unlikely(!buf)) | 61 | if (unlikely(!buf)) |
62 | return -ENOMEM; | 62 | return -ENOMEM; |
63 | maxmembs = PAGE_SIZE / argv->v_size; | 63 | maxmembs = PAGE_SIZE / argv->v_size; |
64 | 64 | ||
65 | ret = 0; | 65 | ret = 0; |
66 | total = 0; | 66 | total = 0; |
67 | pos = argv->v_index; | 67 | pos = argv->v_index; |
68 | for (i = 0; i < argv->v_nmembs; i += n) { | 68 | for (i = 0; i < argv->v_nmembs; i += n) { |
69 | n = (argv->v_nmembs - i < maxmembs) ? | 69 | n = (argv->v_nmembs - i < maxmembs) ? |
70 | argv->v_nmembs - i : maxmembs; | 70 | argv->v_nmembs - i : maxmembs; |
71 | if ((dir & _IOC_WRITE) && | 71 | if ((dir & _IOC_WRITE) && |
72 | copy_from_user(buf, base + argv->v_size * i, | 72 | copy_from_user(buf, base + argv->v_size * i, |
73 | argv->v_size * n)) { | 73 | argv->v_size * n)) { |
74 | ret = -EFAULT; | 74 | ret = -EFAULT; |
75 | break; | 75 | break; |
76 | } | 76 | } |
77 | ppos = pos; | 77 | ppos = pos; |
78 | nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size, | 78 | nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size, |
79 | n); | 79 | n); |
80 | if (nr < 0) { | 80 | if (nr < 0) { |
81 | ret = nr; | 81 | ret = nr; |
82 | break; | 82 | break; |
83 | } | 83 | } |
84 | if ((dir & _IOC_READ) && | 84 | if ((dir & _IOC_READ) && |
85 | copy_to_user(base + argv->v_size * i, buf, | 85 | copy_to_user(base + argv->v_size * i, buf, |
86 | argv->v_size * nr)) { | 86 | argv->v_size * nr)) { |
87 | ret = -EFAULT; | 87 | ret = -EFAULT; |
88 | break; | 88 | break; |
89 | } | 89 | } |
90 | total += nr; | 90 | total += nr; |
91 | if ((size_t)nr < n) | 91 | if ((size_t)nr < n) |
92 | break; | 92 | break; |
93 | if (pos == ppos) | 93 | if (pos == ppos) |
94 | pos += n; | 94 | pos += n; |
95 | } | 95 | } |
96 | argv->v_nmembs = total; | 96 | argv->v_nmembs = total; |
97 | 97 | ||
98 | free_pages((unsigned long)buf, 0); | 98 | free_pages((unsigned long)buf, 0); |
99 | return ret; | 99 | return ret; |
100 | } | 100 | } |
101 | 101 | ||
102 | static int nilfs_ioctl_getflags(struct inode *inode, void __user *argp) | 102 | static int nilfs_ioctl_getflags(struct inode *inode, void __user *argp) |
103 | { | 103 | { |
104 | unsigned int flags = NILFS_I(inode)->i_flags & FS_FL_USER_VISIBLE; | 104 | unsigned int flags = NILFS_I(inode)->i_flags & FS_FL_USER_VISIBLE; |
105 | 105 | ||
106 | return put_user(flags, (int __user *)argp); | 106 | return put_user(flags, (int __user *)argp); |
107 | } | 107 | } |
108 | 108 | ||
109 | static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp, | 109 | static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp, |
110 | void __user *argp) | 110 | void __user *argp) |
111 | { | 111 | { |
112 | struct nilfs_transaction_info ti; | 112 | struct nilfs_transaction_info ti; |
113 | unsigned int flags, oldflags; | 113 | unsigned int flags, oldflags; |
114 | int ret; | 114 | int ret; |
115 | 115 | ||
116 | if (!inode_owner_or_capable(inode)) | 116 | if (!inode_owner_or_capable(inode)) |
117 | return -EACCES; | 117 | return -EACCES; |
118 | 118 | ||
119 | if (get_user(flags, (int __user *)argp)) | 119 | if (get_user(flags, (int __user *)argp)) |
120 | return -EFAULT; | 120 | return -EFAULT; |
121 | 121 | ||
122 | ret = mnt_want_write_file(filp); | 122 | ret = mnt_want_write_file(filp); |
123 | if (ret) | 123 | if (ret) |
124 | return ret; | 124 | return ret; |
125 | 125 | ||
126 | flags = nilfs_mask_flags(inode->i_mode, flags); | 126 | flags = nilfs_mask_flags(inode->i_mode, flags); |
127 | 127 | ||
128 | mutex_lock(&inode->i_mutex); | 128 | mutex_lock(&inode->i_mutex); |
129 | 129 | ||
130 | oldflags = NILFS_I(inode)->i_flags; | 130 | oldflags = NILFS_I(inode)->i_flags; |
131 | 131 | ||
132 | /* | 132 | /* |
133 | * The IMMUTABLE and APPEND_ONLY flags can only be changed by the | 133 | * The IMMUTABLE and APPEND_ONLY flags can only be changed by the |
134 | * relevant capability. | 134 | * relevant capability. |
135 | */ | 135 | */ |
136 | ret = -EPERM; | 136 | ret = -EPERM; |
137 | if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) && | 137 | if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) && |
138 | !capable(CAP_LINUX_IMMUTABLE)) | 138 | !capable(CAP_LINUX_IMMUTABLE)) |
139 | goto out; | 139 | goto out; |
140 | 140 | ||
141 | ret = nilfs_transaction_begin(inode->i_sb, &ti, 0); | 141 | ret = nilfs_transaction_begin(inode->i_sb, &ti, 0); |
142 | if (ret) | 142 | if (ret) |
143 | goto out; | 143 | goto out; |
144 | 144 | ||
145 | NILFS_I(inode)->i_flags = (oldflags & ~FS_FL_USER_MODIFIABLE) | | 145 | NILFS_I(inode)->i_flags = (oldflags & ~FS_FL_USER_MODIFIABLE) | |
146 | (flags & FS_FL_USER_MODIFIABLE); | 146 | (flags & FS_FL_USER_MODIFIABLE); |
147 | 147 | ||
148 | nilfs_set_inode_flags(inode); | 148 | nilfs_set_inode_flags(inode); |
149 | inode->i_ctime = CURRENT_TIME; | 149 | inode->i_ctime = CURRENT_TIME; |
150 | if (IS_SYNC(inode)) | 150 | if (IS_SYNC(inode)) |
151 | nilfs_set_transaction_flag(NILFS_TI_SYNC); | 151 | nilfs_set_transaction_flag(NILFS_TI_SYNC); |
152 | 152 | ||
153 | nilfs_mark_inode_dirty(inode); | 153 | nilfs_mark_inode_dirty(inode); |
154 | ret = nilfs_transaction_commit(inode->i_sb); | 154 | ret = nilfs_transaction_commit(inode->i_sb); |
155 | out: | 155 | out: |
156 | mutex_unlock(&inode->i_mutex); | 156 | mutex_unlock(&inode->i_mutex); |
157 | mnt_drop_write_file(filp); | 157 | mnt_drop_write_file(filp); |
158 | return ret; | 158 | return ret; |
159 | } | 159 | } |
160 | 160 | ||
161 | static int nilfs_ioctl_getversion(struct inode *inode, void __user *argp) | 161 | static int nilfs_ioctl_getversion(struct inode *inode, void __user *argp) |
162 | { | 162 | { |
163 | return put_user(inode->i_generation, (int __user *)argp); | 163 | return put_user(inode->i_generation, (int __user *)argp); |
164 | } | 164 | } |
165 | 165 | ||
166 | static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp, | 166 | static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp, |
167 | unsigned int cmd, void __user *argp) | 167 | unsigned int cmd, void __user *argp) |
168 | { | 168 | { |
169 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; | 169 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; |
170 | struct nilfs_transaction_info ti; | 170 | struct nilfs_transaction_info ti; |
171 | struct nilfs_cpmode cpmode; | 171 | struct nilfs_cpmode cpmode; |
172 | int ret; | 172 | int ret; |
173 | 173 | ||
174 | if (!capable(CAP_SYS_ADMIN)) | 174 | if (!capable(CAP_SYS_ADMIN)) |
175 | return -EPERM; | 175 | return -EPERM; |
176 | 176 | ||
177 | ret = mnt_want_write_file(filp); | 177 | ret = mnt_want_write_file(filp); |
178 | if (ret) | 178 | if (ret) |
179 | return ret; | 179 | return ret; |
180 | 180 | ||
181 | ret = -EFAULT; | 181 | ret = -EFAULT; |
182 | if (copy_from_user(&cpmode, argp, sizeof(cpmode))) | 182 | if (copy_from_user(&cpmode, argp, sizeof(cpmode))) |
183 | goto out; | 183 | goto out; |
184 | 184 | ||
185 | mutex_lock(&nilfs->ns_snapshot_mount_mutex); | 185 | mutex_lock(&nilfs->ns_snapshot_mount_mutex); |
186 | 186 | ||
187 | nilfs_transaction_begin(inode->i_sb, &ti, 0); | 187 | nilfs_transaction_begin(inode->i_sb, &ti, 0); |
188 | ret = nilfs_cpfile_change_cpmode( | 188 | ret = nilfs_cpfile_change_cpmode( |
189 | nilfs->ns_cpfile, cpmode.cm_cno, cpmode.cm_mode); | 189 | nilfs->ns_cpfile, cpmode.cm_cno, cpmode.cm_mode); |
190 | if (unlikely(ret < 0)) | 190 | if (unlikely(ret < 0)) |
191 | nilfs_transaction_abort(inode->i_sb); | 191 | nilfs_transaction_abort(inode->i_sb); |
192 | else | 192 | else |
193 | nilfs_transaction_commit(inode->i_sb); /* never fails */ | 193 | nilfs_transaction_commit(inode->i_sb); /* never fails */ |
194 | 194 | ||
195 | mutex_unlock(&nilfs->ns_snapshot_mount_mutex); | 195 | mutex_unlock(&nilfs->ns_snapshot_mount_mutex); |
196 | out: | 196 | out: |
197 | mnt_drop_write_file(filp); | 197 | mnt_drop_write_file(filp); |
198 | return ret; | 198 | return ret; |
199 | } | 199 | } |
200 | 200 | ||
201 | static int | 201 | static int |
202 | nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp, | 202 | nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp, |
203 | unsigned int cmd, void __user *argp) | 203 | unsigned int cmd, void __user *argp) |
204 | { | 204 | { |
205 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; | 205 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; |
206 | struct nilfs_transaction_info ti; | 206 | struct nilfs_transaction_info ti; |
207 | __u64 cno; | 207 | __u64 cno; |
208 | int ret; | 208 | int ret; |
209 | 209 | ||
210 | if (!capable(CAP_SYS_ADMIN)) | 210 | if (!capable(CAP_SYS_ADMIN)) |
211 | return -EPERM; | 211 | return -EPERM; |
212 | 212 | ||
213 | ret = mnt_want_write_file(filp); | 213 | ret = mnt_want_write_file(filp); |
214 | if (ret) | 214 | if (ret) |
215 | return ret; | 215 | return ret; |
216 | 216 | ||
217 | ret = -EFAULT; | 217 | ret = -EFAULT; |
218 | if (copy_from_user(&cno, argp, sizeof(cno))) | 218 | if (copy_from_user(&cno, argp, sizeof(cno))) |
219 | goto out; | 219 | goto out; |
220 | 220 | ||
221 | nilfs_transaction_begin(inode->i_sb, &ti, 0); | 221 | nilfs_transaction_begin(inode->i_sb, &ti, 0); |
222 | ret = nilfs_cpfile_delete_checkpoint(nilfs->ns_cpfile, cno); | 222 | ret = nilfs_cpfile_delete_checkpoint(nilfs->ns_cpfile, cno); |
223 | if (unlikely(ret < 0)) | 223 | if (unlikely(ret < 0)) |
224 | nilfs_transaction_abort(inode->i_sb); | 224 | nilfs_transaction_abort(inode->i_sb); |
225 | else | 225 | else |
226 | nilfs_transaction_commit(inode->i_sb); /* never fails */ | 226 | nilfs_transaction_commit(inode->i_sb); /* never fails */ |
227 | out: | 227 | out: |
228 | mnt_drop_write_file(filp); | 228 | mnt_drop_write_file(filp); |
229 | return ret; | 229 | return ret; |
230 | } | 230 | } |
231 | 231 | ||
232 | static ssize_t | 232 | static ssize_t |
233 | nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, | 233 | nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, |
234 | void *buf, size_t size, size_t nmembs) | 234 | void *buf, size_t size, size_t nmembs) |
235 | { | 235 | { |
236 | int ret; | 236 | int ret; |
237 | 237 | ||
238 | down_read(&nilfs->ns_segctor_sem); | 238 | down_read(&nilfs->ns_segctor_sem); |
239 | ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, | 239 | ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, |
240 | size, nmembs); | 240 | size, nmembs); |
241 | up_read(&nilfs->ns_segctor_sem); | 241 | up_read(&nilfs->ns_segctor_sem); |
242 | return ret; | 242 | return ret; |
243 | } | 243 | } |
244 | 244 | ||
245 | static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp, | 245 | static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp, |
246 | unsigned int cmd, void __user *argp) | 246 | unsigned int cmd, void __user *argp) |
247 | { | 247 | { |
248 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; | 248 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; |
249 | struct nilfs_cpstat cpstat; | 249 | struct nilfs_cpstat cpstat; |
250 | int ret; | 250 | int ret; |
251 | 251 | ||
252 | down_read(&nilfs->ns_segctor_sem); | 252 | down_read(&nilfs->ns_segctor_sem); |
253 | ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat); | 253 | ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat); |
254 | up_read(&nilfs->ns_segctor_sem); | 254 | up_read(&nilfs->ns_segctor_sem); |
255 | if (ret < 0) | 255 | if (ret < 0) |
256 | return ret; | 256 | return ret; |
257 | 257 | ||
258 | if (copy_to_user(argp, &cpstat, sizeof(cpstat))) | 258 | if (copy_to_user(argp, &cpstat, sizeof(cpstat))) |
259 | ret = -EFAULT; | 259 | ret = -EFAULT; |
260 | return ret; | 260 | return ret; |
261 | } | 261 | } |
262 | 262 | ||
263 | static ssize_t | 263 | static ssize_t |
264 | nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, | 264 | nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, |
265 | void *buf, size_t size, size_t nmembs) | 265 | void *buf, size_t size, size_t nmembs) |
266 | { | 266 | { |
267 | int ret; | 267 | int ret; |
268 | 268 | ||
269 | down_read(&nilfs->ns_segctor_sem); | 269 | down_read(&nilfs->ns_segctor_sem); |
270 | ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size, | 270 | ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size, |
271 | nmembs); | 271 | nmembs); |
272 | up_read(&nilfs->ns_segctor_sem); | 272 | up_read(&nilfs->ns_segctor_sem); |
273 | return ret; | 273 | return ret; |
274 | } | 274 | } |
275 | 275 | ||
276 | static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp, | 276 | static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp, |
277 | unsigned int cmd, void __user *argp) | 277 | unsigned int cmd, void __user *argp) |
278 | { | 278 | { |
279 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; | 279 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; |
280 | struct nilfs_sustat sustat; | 280 | struct nilfs_sustat sustat; |
281 | int ret; | 281 | int ret; |
282 | 282 | ||
283 | down_read(&nilfs->ns_segctor_sem); | 283 | down_read(&nilfs->ns_segctor_sem); |
284 | ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat); | 284 | ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat); |
285 | up_read(&nilfs->ns_segctor_sem); | 285 | up_read(&nilfs->ns_segctor_sem); |
286 | if (ret < 0) | 286 | if (ret < 0) |
287 | return ret; | 287 | return ret; |
288 | 288 | ||
289 | if (copy_to_user(argp, &sustat, sizeof(sustat))) | 289 | if (copy_to_user(argp, &sustat, sizeof(sustat))) |
290 | ret = -EFAULT; | 290 | ret = -EFAULT; |
291 | return ret; | 291 | return ret; |
292 | } | 292 | } |
293 | 293 | ||
294 | static ssize_t | 294 | static ssize_t |
295 | nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, | 295 | nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, |
296 | void *buf, size_t size, size_t nmembs) | 296 | void *buf, size_t size, size_t nmembs) |
297 | { | 297 | { |
298 | int ret; | 298 | int ret; |
299 | 299 | ||
300 | down_read(&nilfs->ns_segctor_sem); | 300 | down_read(&nilfs->ns_segctor_sem); |
301 | ret = nilfs_dat_get_vinfo(nilfs->ns_dat, buf, size, nmembs); | 301 | ret = nilfs_dat_get_vinfo(nilfs->ns_dat, buf, size, nmembs); |
302 | up_read(&nilfs->ns_segctor_sem); | 302 | up_read(&nilfs->ns_segctor_sem); |
303 | return ret; | 303 | return ret; |
304 | } | 304 | } |
305 | 305 | ||
306 | static ssize_t | 306 | static ssize_t |
307 | nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags, | 307 | nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags, |
308 | void *buf, size_t size, size_t nmembs) | 308 | void *buf, size_t size, size_t nmembs) |
309 | { | 309 | { |
310 | struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap; | 310 | struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap; |
311 | struct nilfs_bdesc *bdescs = buf; | 311 | struct nilfs_bdesc *bdescs = buf; |
312 | int ret, i; | 312 | int ret, i; |
313 | 313 | ||
314 | down_read(&nilfs->ns_segctor_sem); | 314 | down_read(&nilfs->ns_segctor_sem); |
315 | for (i = 0; i < nmembs; i++) { | 315 | for (i = 0; i < nmembs; i++) { |
316 | ret = nilfs_bmap_lookup_at_level(bmap, | 316 | ret = nilfs_bmap_lookup_at_level(bmap, |
317 | bdescs[i].bd_offset, | 317 | bdescs[i].bd_offset, |
318 | bdescs[i].bd_level + 1, | 318 | bdescs[i].bd_level + 1, |
319 | &bdescs[i].bd_blocknr); | 319 | &bdescs[i].bd_blocknr); |
320 | if (ret < 0) { | 320 | if (ret < 0) { |
321 | if (ret != -ENOENT) { | 321 | if (ret != -ENOENT) { |
322 | up_read(&nilfs->ns_segctor_sem); | 322 | up_read(&nilfs->ns_segctor_sem); |
323 | return ret; | 323 | return ret; |
324 | } | 324 | } |
325 | bdescs[i].bd_blocknr = 0; | 325 | bdescs[i].bd_blocknr = 0; |
326 | } | 326 | } |
327 | } | 327 | } |
328 | up_read(&nilfs->ns_segctor_sem); | 328 | up_read(&nilfs->ns_segctor_sem); |
329 | return nmembs; | 329 | return nmembs; |
330 | } | 330 | } |
331 | 331 | ||
332 | static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp, | 332 | static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp, |
333 | unsigned int cmd, void __user *argp) | 333 | unsigned int cmd, void __user *argp) |
334 | { | 334 | { |
335 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; | 335 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; |
336 | struct nilfs_argv argv; | 336 | struct nilfs_argv argv; |
337 | int ret; | 337 | int ret; |
338 | 338 | ||
339 | if (copy_from_user(&argv, argp, sizeof(argv))) | 339 | if (copy_from_user(&argv, argp, sizeof(argv))) |
340 | return -EFAULT; | 340 | return -EFAULT; |
341 | 341 | ||
342 | if (argv.v_size != sizeof(struct nilfs_bdesc)) | 342 | if (argv.v_size != sizeof(struct nilfs_bdesc)) |
343 | return -EINVAL; | 343 | return -EINVAL; |
344 | 344 | ||
345 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), | 345 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), |
346 | nilfs_ioctl_do_get_bdescs); | 346 | nilfs_ioctl_do_get_bdescs); |
347 | if (ret < 0) | 347 | if (ret < 0) |
348 | return ret; | 348 | return ret; |
349 | 349 | ||
350 | if (copy_to_user(argp, &argv, sizeof(argv))) | 350 | if (copy_to_user(argp, &argv, sizeof(argv))) |
351 | ret = -EFAULT; | 351 | ret = -EFAULT; |
352 | return ret; | 352 | return ret; |
353 | } | 353 | } |
354 | 354 | ||
355 | static int nilfs_ioctl_move_inode_block(struct inode *inode, | 355 | static int nilfs_ioctl_move_inode_block(struct inode *inode, |
356 | struct nilfs_vdesc *vdesc, | 356 | struct nilfs_vdesc *vdesc, |
357 | struct list_head *buffers) | 357 | struct list_head *buffers) |
358 | { | 358 | { |
359 | struct buffer_head *bh; | 359 | struct buffer_head *bh; |
360 | int ret; | 360 | int ret; |
361 | 361 | ||
362 | if (vdesc->vd_flags == 0) | 362 | if (vdesc->vd_flags == 0) |
363 | ret = nilfs_gccache_submit_read_data( | 363 | ret = nilfs_gccache_submit_read_data( |
364 | inode, vdesc->vd_offset, vdesc->vd_blocknr, | 364 | inode, vdesc->vd_offset, vdesc->vd_blocknr, |
365 | vdesc->vd_vblocknr, &bh); | 365 | vdesc->vd_vblocknr, &bh); |
366 | else | 366 | else |
367 | ret = nilfs_gccache_submit_read_node( | 367 | ret = nilfs_gccache_submit_read_node( |
368 | inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh); | 368 | inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh); |
369 | 369 | ||
370 | if (unlikely(ret < 0)) { | 370 | if (unlikely(ret < 0)) { |
371 | if (ret == -ENOENT) | 371 | if (ret == -ENOENT) |
372 | printk(KERN_CRIT | 372 | printk(KERN_CRIT |
373 | "%s: invalid virtual block address (%s): " | 373 | "%s: invalid virtual block address (%s): " |
374 | "ino=%llu, cno=%llu, offset=%llu, " | 374 | "ino=%llu, cno=%llu, offset=%llu, " |
375 | "blocknr=%llu, vblocknr=%llu\n", | 375 | "blocknr=%llu, vblocknr=%llu\n", |
376 | __func__, vdesc->vd_flags ? "node" : "data", | 376 | __func__, vdesc->vd_flags ? "node" : "data", |
377 | (unsigned long long)vdesc->vd_ino, | 377 | (unsigned long long)vdesc->vd_ino, |
378 | (unsigned long long)vdesc->vd_cno, | 378 | (unsigned long long)vdesc->vd_cno, |
379 | (unsigned long long)vdesc->vd_offset, | 379 | (unsigned long long)vdesc->vd_offset, |
380 | (unsigned long long)vdesc->vd_blocknr, | 380 | (unsigned long long)vdesc->vd_blocknr, |
381 | (unsigned long long)vdesc->vd_vblocknr); | 381 | (unsigned long long)vdesc->vd_vblocknr); |
382 | return ret; | 382 | return ret; |
383 | } | 383 | } |
384 | if (unlikely(!list_empty(&bh->b_assoc_buffers))) { | 384 | if (unlikely(!list_empty(&bh->b_assoc_buffers))) { |
385 | printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, " | 385 | printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, " |
386 | "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n", | 386 | "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n", |
387 | __func__, vdesc->vd_flags ? "node" : "data", | 387 | __func__, vdesc->vd_flags ? "node" : "data", |
388 | (unsigned long long)vdesc->vd_ino, | 388 | (unsigned long long)vdesc->vd_ino, |
389 | (unsigned long long)vdesc->vd_cno, | 389 | (unsigned long long)vdesc->vd_cno, |
390 | (unsigned long long)vdesc->vd_offset, | 390 | (unsigned long long)vdesc->vd_offset, |
391 | (unsigned long long)vdesc->vd_blocknr, | 391 | (unsigned long long)vdesc->vd_blocknr, |
392 | (unsigned long long)vdesc->vd_vblocknr); | 392 | (unsigned long long)vdesc->vd_vblocknr); |
393 | brelse(bh); | 393 | brelse(bh); |
394 | return -EEXIST; | 394 | return -EEXIST; |
395 | } | 395 | } |
396 | list_add_tail(&bh->b_assoc_buffers, buffers); | 396 | list_add_tail(&bh->b_assoc_buffers, buffers); |
397 | return 0; | 397 | return 0; |
398 | } | 398 | } |
399 | 399 | ||
400 | static int nilfs_ioctl_move_blocks(struct super_block *sb, | 400 | static int nilfs_ioctl_move_blocks(struct super_block *sb, |
401 | struct nilfs_argv *argv, void *buf) | 401 | struct nilfs_argv *argv, void *buf) |
402 | { | 402 | { |
403 | size_t nmembs = argv->v_nmembs; | 403 | size_t nmembs = argv->v_nmembs; |
404 | struct the_nilfs *nilfs = sb->s_fs_info; | 404 | struct the_nilfs *nilfs = sb->s_fs_info; |
405 | struct inode *inode; | 405 | struct inode *inode; |
406 | struct nilfs_vdesc *vdesc; | 406 | struct nilfs_vdesc *vdesc; |
407 | struct buffer_head *bh, *n; | 407 | struct buffer_head *bh, *n; |
408 | LIST_HEAD(buffers); | 408 | LIST_HEAD(buffers); |
409 | ino_t ino; | 409 | ino_t ino; |
410 | __u64 cno; | 410 | __u64 cno; |
411 | int i, ret; | 411 | int i, ret; |
412 | 412 | ||
413 | for (i = 0, vdesc = buf; i < nmembs; ) { | 413 | for (i = 0, vdesc = buf; i < nmembs; ) { |
414 | ino = vdesc->vd_ino; | 414 | ino = vdesc->vd_ino; |
415 | cno = vdesc->vd_cno; | 415 | cno = vdesc->vd_cno; |
416 | inode = nilfs_iget_for_gc(sb, ino, cno); | 416 | inode = nilfs_iget_for_gc(sb, ino, cno); |
417 | if (IS_ERR(inode)) { | 417 | if (IS_ERR(inode)) { |
418 | ret = PTR_ERR(inode); | 418 | ret = PTR_ERR(inode); |
419 | goto failed; | 419 | goto failed; |
420 | } | 420 | } |
421 | if (list_empty(&NILFS_I(inode)->i_dirty)) { | 421 | if (list_empty(&NILFS_I(inode)->i_dirty)) { |
422 | /* | 422 | /* |
423 | * Add the inode to GC inode list. Garbage Collection | 423 | * Add the inode to GC inode list. Garbage Collection |
424 | * is serialized and no two processes manipulate the | 424 | * is serialized and no two processes manipulate the |
425 | * list simultaneously. | 425 | * list simultaneously. |
426 | */ | 426 | */ |
427 | igrab(inode); | 427 | igrab(inode); |
428 | list_add(&NILFS_I(inode)->i_dirty, | 428 | list_add(&NILFS_I(inode)->i_dirty, |
429 | &nilfs->ns_gc_inodes); | 429 | &nilfs->ns_gc_inodes); |
430 | } | 430 | } |
431 | 431 | ||
432 | do { | 432 | do { |
433 | ret = nilfs_ioctl_move_inode_block(inode, vdesc, | 433 | ret = nilfs_ioctl_move_inode_block(inode, vdesc, |
434 | &buffers); | 434 | &buffers); |
435 | if (unlikely(ret < 0)) { | 435 | if (unlikely(ret < 0)) { |
436 | iput(inode); | 436 | iput(inode); |
437 | goto failed; | 437 | goto failed; |
438 | } | 438 | } |
439 | vdesc++; | 439 | vdesc++; |
440 | } while (++i < nmembs && | 440 | } while (++i < nmembs && |
441 | vdesc->vd_ino == ino && vdesc->vd_cno == cno); | 441 | vdesc->vd_ino == ino && vdesc->vd_cno == cno); |
442 | 442 | ||
443 | iput(inode); /* The inode still remains in GC inode list */ | 443 | iput(inode); /* The inode still remains in GC inode list */ |
444 | } | 444 | } |
445 | 445 | ||
446 | list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { | 446 | list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { |
447 | ret = nilfs_gccache_wait_and_mark_dirty(bh); | 447 | ret = nilfs_gccache_wait_and_mark_dirty(bh); |
448 | if (unlikely(ret < 0)) { | 448 | if (unlikely(ret < 0)) { |
449 | WARN_ON(ret == -EEXIST); | 449 | WARN_ON(ret == -EEXIST); |
450 | goto failed; | 450 | goto failed; |
451 | } | 451 | } |
452 | list_del_init(&bh->b_assoc_buffers); | 452 | list_del_init(&bh->b_assoc_buffers); |
453 | brelse(bh); | 453 | brelse(bh); |
454 | } | 454 | } |
455 | return nmembs; | 455 | return nmembs; |
456 | 456 | ||
457 | failed: | 457 | failed: |
458 | list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { | 458 | list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { |
459 | list_del_init(&bh->b_assoc_buffers); | 459 | list_del_init(&bh->b_assoc_buffers); |
460 | brelse(bh); | 460 | brelse(bh); |
461 | } | 461 | } |
462 | return ret; | 462 | return ret; |
463 | } | 463 | } |
464 | 464 | ||
465 | static int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs, | 465 | static int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs, |
466 | struct nilfs_argv *argv, void *buf) | 466 | struct nilfs_argv *argv, void *buf) |
467 | { | 467 | { |
468 | size_t nmembs = argv->v_nmembs; | 468 | size_t nmembs = argv->v_nmembs; |
469 | struct inode *cpfile = nilfs->ns_cpfile; | 469 | struct inode *cpfile = nilfs->ns_cpfile; |
470 | struct nilfs_period *periods = buf; | 470 | struct nilfs_period *periods = buf; |
471 | int ret, i; | 471 | int ret, i; |
472 | 472 | ||
473 | for (i = 0; i < nmembs; i++) { | 473 | for (i = 0; i < nmembs; i++) { |
474 | ret = nilfs_cpfile_delete_checkpoints( | 474 | ret = nilfs_cpfile_delete_checkpoints( |
475 | cpfile, periods[i].p_start, periods[i].p_end); | 475 | cpfile, periods[i].p_start, periods[i].p_end); |
476 | if (ret < 0) | 476 | if (ret < 0) |
477 | return ret; | 477 | return ret; |
478 | } | 478 | } |
479 | return nmembs; | 479 | return nmembs; |
480 | } | 480 | } |
481 | 481 | ||
482 | static int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs, | 482 | static int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs, |
483 | struct nilfs_argv *argv, void *buf) | 483 | struct nilfs_argv *argv, void *buf) |
484 | { | 484 | { |
485 | size_t nmembs = argv->v_nmembs; | 485 | size_t nmembs = argv->v_nmembs; |
486 | int ret; | 486 | int ret; |
487 | 487 | ||
488 | ret = nilfs_dat_freev(nilfs->ns_dat, buf, nmembs); | 488 | ret = nilfs_dat_freev(nilfs->ns_dat, buf, nmembs); |
489 | 489 | ||
490 | return (ret < 0) ? ret : nmembs; | 490 | return (ret < 0) ? ret : nmembs; |
491 | } | 491 | } |
492 | 492 | ||
493 | static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs, | 493 | static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs, |
494 | struct nilfs_argv *argv, void *buf) | 494 | struct nilfs_argv *argv, void *buf) |
495 | { | 495 | { |
496 | size_t nmembs = argv->v_nmembs; | 496 | size_t nmembs = argv->v_nmembs; |
497 | struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap; | 497 | struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap; |
498 | struct nilfs_bdesc *bdescs = buf; | 498 | struct nilfs_bdesc *bdescs = buf; |
499 | int ret, i; | 499 | int ret, i; |
500 | 500 | ||
501 | for (i = 0; i < nmembs; i++) { | 501 | for (i = 0; i < nmembs; i++) { |
502 | /* XXX: use macro or inline func to check liveness */ | 502 | /* XXX: use macro or inline func to check liveness */ |
503 | ret = nilfs_bmap_lookup_at_level(bmap, | 503 | ret = nilfs_bmap_lookup_at_level(bmap, |
504 | bdescs[i].bd_offset, | 504 | bdescs[i].bd_offset, |
505 | bdescs[i].bd_level + 1, | 505 | bdescs[i].bd_level + 1, |
506 | &bdescs[i].bd_blocknr); | 506 | &bdescs[i].bd_blocknr); |
507 | if (ret < 0) { | 507 | if (ret < 0) { |
508 | if (ret != -ENOENT) | 508 | if (ret != -ENOENT) |
509 | return ret; | 509 | return ret; |
510 | bdescs[i].bd_blocknr = 0; | 510 | bdescs[i].bd_blocknr = 0; |
511 | } | 511 | } |
512 | if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr) | 512 | if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr) |
513 | /* skip dead block */ | 513 | /* skip dead block */ |
514 | continue; | 514 | continue; |
515 | if (bdescs[i].bd_level == 0) { | 515 | if (bdescs[i].bd_level == 0) { |
516 | ret = nilfs_mdt_mark_block_dirty(nilfs->ns_dat, | 516 | ret = nilfs_mdt_mark_block_dirty(nilfs->ns_dat, |
517 | bdescs[i].bd_offset); | 517 | bdescs[i].bd_offset); |
518 | if (ret < 0) { | 518 | if (ret < 0) { |
519 | WARN_ON(ret == -ENOENT); | 519 | WARN_ON(ret == -ENOENT); |
520 | return ret; | 520 | return ret; |
521 | } | 521 | } |
522 | } else { | 522 | } else { |
523 | ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset, | 523 | ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset, |
524 | bdescs[i].bd_level); | 524 | bdescs[i].bd_level); |
525 | if (ret < 0) { | 525 | if (ret < 0) { |
526 | WARN_ON(ret == -ENOENT); | 526 | WARN_ON(ret == -ENOENT); |
527 | return ret; | 527 | return ret; |
528 | } | 528 | } |
529 | } | 529 | } |
530 | } | 530 | } |
531 | return nmembs; | 531 | return nmembs; |
532 | } | 532 | } |
533 | 533 | ||
534 | int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, | 534 | int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, |
535 | struct nilfs_argv *argv, void **kbufs) | 535 | struct nilfs_argv *argv, void **kbufs) |
536 | { | 536 | { |
537 | const char *msg; | 537 | const char *msg; |
538 | int ret; | 538 | int ret; |
539 | 539 | ||
540 | ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]); | 540 | ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]); |
541 | if (ret < 0) { | 541 | if (ret < 0) { |
542 | /* | 542 | /* |
543 | * can safely abort because checkpoints can be removed | 543 | * can safely abort because checkpoints can be removed |
544 | * independently. | 544 | * independently. |
545 | */ | 545 | */ |
546 | msg = "cannot delete checkpoints"; | 546 | msg = "cannot delete checkpoints"; |
547 | goto failed; | 547 | goto failed; |
548 | } | 548 | } |
549 | ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], kbufs[2]); | 549 | ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], kbufs[2]); |
550 | if (ret < 0) { | 550 | if (ret < 0) { |
551 | /* | 551 | /* |
552 | * can safely abort because DAT file is updated atomically | 552 | * can safely abort because DAT file is updated atomically |
553 | * using a copy-on-write technique. | 553 | * using a copy-on-write technique. |
554 | */ | 554 | */ |
555 | msg = "cannot delete virtual blocks from DAT file"; | 555 | msg = "cannot delete virtual blocks from DAT file"; |
556 | goto failed; | 556 | goto failed; |
557 | } | 557 | } |
558 | ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], kbufs[3]); | 558 | ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], kbufs[3]); |
559 | if (ret < 0) { | 559 | if (ret < 0) { |
560 | /* | 560 | /* |
561 | * can safely abort because the operation is nondestructive. | 561 | * can safely abort because the operation is nondestructive. |
562 | */ | 562 | */ |
563 | msg = "cannot mark copying blocks dirty"; | 563 | msg = "cannot mark copying blocks dirty"; |
564 | goto failed; | 564 | goto failed; |
565 | } | 565 | } |
566 | return 0; | 566 | return 0; |
567 | 567 | ||
568 | failed: | 568 | failed: |
569 | printk(KERN_ERR "NILFS: GC failed during preparation: %s: err=%d\n", | 569 | printk(KERN_ERR "NILFS: GC failed during preparation: %s: err=%d\n", |
570 | msg, ret); | 570 | msg, ret); |
571 | return ret; | 571 | return ret; |
572 | } | 572 | } |
573 | 573 | ||
574 | static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, | 574 | static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, |
575 | unsigned int cmd, void __user *argp) | 575 | unsigned int cmd, void __user *argp) |
576 | { | 576 | { |
577 | struct nilfs_argv argv[5]; | 577 | struct nilfs_argv argv[5]; |
578 | static const size_t argsz[5] = { | 578 | static const size_t argsz[5] = { |
579 | sizeof(struct nilfs_vdesc), | 579 | sizeof(struct nilfs_vdesc), |
580 | sizeof(struct nilfs_period), | 580 | sizeof(struct nilfs_period), |
581 | sizeof(__u64), | 581 | sizeof(__u64), |
582 | sizeof(struct nilfs_bdesc), | 582 | sizeof(struct nilfs_bdesc), |
583 | sizeof(__u64), | 583 | sizeof(__u64), |
584 | }; | 584 | }; |
585 | void __user *base; | 585 | void __user *base; |
586 | void *kbufs[5]; | 586 | void *kbufs[5]; |
587 | struct the_nilfs *nilfs; | 587 | struct the_nilfs *nilfs; |
588 | size_t len, nsegs; | 588 | size_t len, nsegs; |
589 | int n, ret; | 589 | int n, ret; |
590 | 590 | ||
591 | if (!capable(CAP_SYS_ADMIN)) | 591 | if (!capable(CAP_SYS_ADMIN)) |
592 | return -EPERM; | 592 | return -EPERM; |
593 | 593 | ||
594 | ret = mnt_want_write_file(filp); | 594 | ret = mnt_want_write_file(filp); |
595 | if (ret) | 595 | if (ret) |
596 | return ret; | 596 | return ret; |
597 | 597 | ||
598 | ret = -EFAULT; | 598 | ret = -EFAULT; |
599 | if (copy_from_user(argv, argp, sizeof(argv))) | 599 | if (copy_from_user(argv, argp, sizeof(argv))) |
600 | goto out; | 600 | goto out; |
601 | 601 | ||
602 | ret = -EINVAL; | 602 | ret = -EINVAL; |
603 | nsegs = argv[4].v_nmembs; | 603 | nsegs = argv[4].v_nmembs; |
604 | if (argv[4].v_size != argsz[4]) | 604 | if (argv[4].v_size != argsz[4]) |
605 | goto out; | 605 | goto out; |
606 | if (nsegs > UINT_MAX / sizeof(__u64)) | 606 | if (nsegs > UINT_MAX / sizeof(__u64)) |
607 | goto out; | 607 | goto out; |
608 | 608 | ||
609 | /* | 609 | /* |
610 | * argv[4] points to segment numbers this ioctl cleans. We | 610 | * argv[4] points to segment numbers this ioctl cleans. We |
611 | * use kmalloc() for its buffer because memory used for the | 611 | * use kmalloc() for its buffer because memory used for the |
612 | * segment numbers is enough small. | 612 | * segment numbers is enough small. |
613 | */ | 613 | */ |
614 | kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base, | 614 | kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base, |
615 | nsegs * sizeof(__u64)); | 615 | nsegs * sizeof(__u64)); |
616 | if (IS_ERR(kbufs[4])) { | 616 | if (IS_ERR(kbufs[4])) { |
617 | ret = PTR_ERR(kbufs[4]); | 617 | ret = PTR_ERR(kbufs[4]); |
618 | goto out; | 618 | goto out; |
619 | } | 619 | } |
620 | nilfs = inode->i_sb->s_fs_info; | 620 | nilfs = inode->i_sb->s_fs_info; |
621 | 621 | ||
622 | for (n = 0; n < 4; n++) { | 622 | for (n = 0; n < 4; n++) { |
623 | ret = -EINVAL; | 623 | ret = -EINVAL; |
624 | if (argv[n].v_size != argsz[n]) | 624 | if (argv[n].v_size != argsz[n]) |
625 | goto out_free; | 625 | goto out_free; |
626 | 626 | ||
627 | if (argv[n].v_nmembs > nsegs * nilfs->ns_blocks_per_segment) | 627 | if (argv[n].v_nmembs > nsegs * nilfs->ns_blocks_per_segment) |
628 | goto out_free; | 628 | goto out_free; |
629 | 629 | ||
630 | if (argv[n].v_nmembs >= UINT_MAX / argv[n].v_size) | 630 | if (argv[n].v_nmembs >= UINT_MAX / argv[n].v_size) |
631 | goto out_free; | 631 | goto out_free; |
632 | 632 | ||
633 | len = argv[n].v_size * argv[n].v_nmembs; | 633 | len = argv[n].v_size * argv[n].v_nmembs; |
634 | base = (void __user *)(unsigned long)argv[n].v_base; | 634 | base = (void __user *)(unsigned long)argv[n].v_base; |
635 | if (len == 0) { | 635 | if (len == 0) { |
636 | kbufs[n] = NULL; | 636 | kbufs[n] = NULL; |
637 | continue; | 637 | continue; |
638 | } | 638 | } |
639 | 639 | ||
640 | kbufs[n] = vmalloc(len); | 640 | kbufs[n] = vmalloc(len); |
641 | if (!kbufs[n]) { | 641 | if (!kbufs[n]) { |
642 | ret = -ENOMEM; | 642 | ret = -ENOMEM; |
643 | goto out_free; | 643 | goto out_free; |
644 | } | 644 | } |
645 | if (copy_from_user(kbufs[n], base, len)) { | 645 | if (copy_from_user(kbufs[n], base, len)) { |
646 | ret = -EFAULT; | 646 | ret = -EFAULT; |
647 | vfree(kbufs[n]); | 647 | vfree(kbufs[n]); |
648 | goto out_free; | 648 | goto out_free; |
649 | } | 649 | } |
650 | } | 650 | } |
651 | 651 | ||
652 | /* | 652 | /* |
653 | * nilfs_ioctl_move_blocks() will call nilfs_iget_for_gc(), | 653 | * nilfs_ioctl_move_blocks() will call nilfs_iget_for_gc(), |
654 | * which will operates an inode list without blocking. | 654 | * which will operates an inode list without blocking. |
655 | * To protect the list from concurrent operations, | 655 | * To protect the list from concurrent operations, |
656 | * nilfs_ioctl_move_blocks should be atomic operation. | 656 | * nilfs_ioctl_move_blocks should be atomic operation. |
657 | */ | 657 | */ |
658 | if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) { | 658 | if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) { |
659 | ret = -EBUSY; | 659 | ret = -EBUSY; |
660 | goto out_free; | 660 | goto out_free; |
661 | } | 661 | } |
662 | 662 | ||
663 | ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]); | 663 | ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]); |
664 | if (ret < 0) | 664 | if (ret < 0) |
665 | printk(KERN_ERR "NILFS: GC failed during preparation: " | 665 | printk(KERN_ERR "NILFS: GC failed during preparation: " |
666 | "cannot read source blocks: err=%d\n", ret); | 666 | "cannot read source blocks: err=%d\n", ret); |
667 | else | 667 | else { |
668 | if (nilfs_sb_need_update(nilfs)) | ||
669 | set_nilfs_discontinued(nilfs); | ||
668 | ret = nilfs_clean_segments(inode->i_sb, argv, kbufs); | 670 | ret = nilfs_clean_segments(inode->i_sb, argv, kbufs); |
671 | } | ||
669 | 672 | ||
670 | nilfs_remove_all_gcinodes(nilfs); | 673 | nilfs_remove_all_gcinodes(nilfs); |
671 | clear_nilfs_gc_running(nilfs); | 674 | clear_nilfs_gc_running(nilfs); |
672 | 675 | ||
673 | out_free: | 676 | out_free: |
674 | while (--n >= 0) | 677 | while (--n >= 0) |
675 | vfree(kbufs[n]); | 678 | vfree(kbufs[n]); |
676 | kfree(kbufs[4]); | 679 | kfree(kbufs[4]); |
677 | out: | 680 | out: |
678 | mnt_drop_write_file(filp); | 681 | mnt_drop_write_file(filp); |
679 | return ret; | 682 | return ret; |
680 | } | 683 | } |
681 | 684 | ||
682 | static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, | 685 | static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, |
683 | unsigned int cmd, void __user *argp) | 686 | unsigned int cmd, void __user *argp) |
684 | { | 687 | { |
685 | __u64 cno; | 688 | __u64 cno; |
686 | int ret; | 689 | int ret; |
687 | struct the_nilfs *nilfs; | 690 | struct the_nilfs *nilfs; |
688 | 691 | ||
689 | ret = nilfs_construct_segment(inode->i_sb); | 692 | ret = nilfs_construct_segment(inode->i_sb); |
690 | if (ret < 0) | 693 | if (ret < 0) |
691 | return ret; | 694 | return ret; |
692 | 695 | ||
693 | nilfs = inode->i_sb->s_fs_info; | 696 | nilfs = inode->i_sb->s_fs_info; |
694 | if (nilfs_test_opt(nilfs, BARRIER)) { | 697 | if (nilfs_test_opt(nilfs, BARRIER)) { |
695 | ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); | 698 | ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); |
696 | if (ret == -EIO) | 699 | if (ret == -EIO) |
697 | return ret; | 700 | return ret; |
698 | } | 701 | } |
699 | 702 | ||
700 | if (argp != NULL) { | 703 | if (argp != NULL) { |
701 | down_read(&nilfs->ns_segctor_sem); | 704 | down_read(&nilfs->ns_segctor_sem); |
702 | cno = nilfs->ns_cno - 1; | 705 | cno = nilfs->ns_cno - 1; |
703 | up_read(&nilfs->ns_segctor_sem); | 706 | up_read(&nilfs->ns_segctor_sem); |
704 | if (copy_to_user(argp, &cno, sizeof(cno))) | 707 | if (copy_to_user(argp, &cno, sizeof(cno))) |
705 | return -EFAULT; | 708 | return -EFAULT; |
706 | } | 709 | } |
707 | return 0; | 710 | return 0; |
708 | } | 711 | } |
709 | 712 | ||
710 | static int nilfs_ioctl_resize(struct inode *inode, struct file *filp, | 713 | static int nilfs_ioctl_resize(struct inode *inode, struct file *filp, |
711 | void __user *argp) | 714 | void __user *argp) |
712 | { | 715 | { |
713 | __u64 newsize; | 716 | __u64 newsize; |
714 | int ret = -EPERM; | 717 | int ret = -EPERM; |
715 | 718 | ||
716 | if (!capable(CAP_SYS_ADMIN)) | 719 | if (!capable(CAP_SYS_ADMIN)) |
717 | goto out; | 720 | goto out; |
718 | 721 | ||
719 | ret = mnt_want_write_file(filp); | 722 | ret = mnt_want_write_file(filp); |
720 | if (ret) | 723 | if (ret) |
721 | goto out; | 724 | goto out; |
722 | 725 | ||
723 | ret = -EFAULT; | 726 | ret = -EFAULT; |
724 | if (copy_from_user(&newsize, argp, sizeof(newsize))) | 727 | if (copy_from_user(&newsize, argp, sizeof(newsize))) |
725 | goto out_drop_write; | 728 | goto out_drop_write; |
726 | 729 | ||
727 | ret = nilfs_resize_fs(inode->i_sb, newsize); | 730 | ret = nilfs_resize_fs(inode->i_sb, newsize); |
728 | 731 | ||
729 | out_drop_write: | 732 | out_drop_write: |
730 | mnt_drop_write_file(filp); | 733 | mnt_drop_write_file(filp); |
731 | out: | 734 | out: |
732 | return ret; | 735 | return ret; |
733 | } | 736 | } |
734 | 737 | ||
735 | static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp) | 738 | static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp) |
736 | { | 739 | { |
737 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; | 740 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; |
738 | __u64 range[2]; | 741 | __u64 range[2]; |
739 | __u64 minseg, maxseg; | 742 | __u64 minseg, maxseg; |
740 | unsigned long segbytes; | 743 | unsigned long segbytes; |
741 | int ret = -EPERM; | 744 | int ret = -EPERM; |
742 | 745 | ||
743 | if (!capable(CAP_SYS_ADMIN)) | 746 | if (!capable(CAP_SYS_ADMIN)) |
744 | goto out; | 747 | goto out; |
745 | 748 | ||
746 | ret = -EFAULT; | 749 | ret = -EFAULT; |
747 | if (copy_from_user(range, argp, sizeof(__u64[2]))) | 750 | if (copy_from_user(range, argp, sizeof(__u64[2]))) |
748 | goto out; | 751 | goto out; |
749 | 752 | ||
750 | ret = -ERANGE; | 753 | ret = -ERANGE; |
751 | if (range[1] > i_size_read(inode->i_sb->s_bdev->bd_inode)) | 754 | if (range[1] > i_size_read(inode->i_sb->s_bdev->bd_inode)) |
752 | goto out; | 755 | goto out; |
753 | 756 | ||
754 | segbytes = nilfs->ns_blocks_per_segment * nilfs->ns_blocksize; | 757 | segbytes = nilfs->ns_blocks_per_segment * nilfs->ns_blocksize; |
755 | 758 | ||
756 | minseg = range[0] + segbytes - 1; | 759 | minseg = range[0] + segbytes - 1; |
757 | do_div(minseg, segbytes); | 760 | do_div(minseg, segbytes); |
758 | maxseg = NILFS_SB2_OFFSET_BYTES(range[1]); | 761 | maxseg = NILFS_SB2_OFFSET_BYTES(range[1]); |
759 | do_div(maxseg, segbytes); | 762 | do_div(maxseg, segbytes); |
760 | maxseg--; | 763 | maxseg--; |
761 | 764 | ||
762 | ret = nilfs_sufile_set_alloc_range(nilfs->ns_sufile, minseg, maxseg); | 765 | ret = nilfs_sufile_set_alloc_range(nilfs->ns_sufile, minseg, maxseg); |
763 | out: | 766 | out: |
764 | return ret; | 767 | return ret; |
765 | } | 768 | } |
766 | 769 | ||
767 | static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, | 770 | static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, |
768 | unsigned int cmd, void __user *argp, | 771 | unsigned int cmd, void __user *argp, |
769 | size_t membsz, | 772 | size_t membsz, |
770 | ssize_t (*dofunc)(struct the_nilfs *, | 773 | ssize_t (*dofunc)(struct the_nilfs *, |
771 | __u64 *, int, | 774 | __u64 *, int, |
772 | void *, size_t, size_t)) | 775 | void *, size_t, size_t)) |
773 | 776 | ||
774 | { | 777 | { |
775 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; | 778 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; |
776 | struct nilfs_argv argv; | 779 | struct nilfs_argv argv; |
777 | int ret; | 780 | int ret; |
778 | 781 | ||
779 | if (copy_from_user(&argv, argp, sizeof(argv))) | 782 | if (copy_from_user(&argv, argp, sizeof(argv))) |
780 | return -EFAULT; | 783 | return -EFAULT; |
781 | 784 | ||
782 | if (argv.v_size < membsz) | 785 | if (argv.v_size < membsz) |
783 | return -EINVAL; | 786 | return -EINVAL; |
784 | 787 | ||
785 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc); | 788 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc); |
786 | if (ret < 0) | 789 | if (ret < 0) |
787 | return ret; | 790 | return ret; |
788 | 791 | ||
789 | if (copy_to_user(argp, &argv, sizeof(argv))) | 792 | if (copy_to_user(argp, &argv, sizeof(argv))) |
790 | ret = -EFAULT; | 793 | ret = -EFAULT; |
791 | return ret; | 794 | return ret; |
792 | } | 795 | } |
793 | 796 | ||
794 | long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 797 | long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
795 | { | 798 | { |
796 | struct inode *inode = filp->f_dentry->d_inode; | 799 | struct inode *inode = filp->f_dentry->d_inode; |
797 | void __user *argp = (void __user *)arg; | 800 | void __user *argp = (void __user *)arg; |
798 | 801 | ||
799 | switch (cmd) { | 802 | switch (cmd) { |
800 | case FS_IOC_GETFLAGS: | 803 | case FS_IOC_GETFLAGS: |
801 | return nilfs_ioctl_getflags(inode, argp); | 804 | return nilfs_ioctl_getflags(inode, argp); |
802 | case FS_IOC_SETFLAGS: | 805 | case FS_IOC_SETFLAGS: |
803 | return nilfs_ioctl_setflags(inode, filp, argp); | 806 | return nilfs_ioctl_setflags(inode, filp, argp); |
804 | case FS_IOC_GETVERSION: | 807 | case FS_IOC_GETVERSION: |
805 | return nilfs_ioctl_getversion(inode, argp); | 808 | return nilfs_ioctl_getversion(inode, argp); |
806 | case NILFS_IOCTL_CHANGE_CPMODE: | 809 | case NILFS_IOCTL_CHANGE_CPMODE: |
807 | return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp); | 810 | return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp); |
808 | case NILFS_IOCTL_DELETE_CHECKPOINT: | 811 | case NILFS_IOCTL_DELETE_CHECKPOINT: |
809 | return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp); | 812 | return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp); |
810 | case NILFS_IOCTL_GET_CPINFO: | 813 | case NILFS_IOCTL_GET_CPINFO: |
811 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, | 814 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, |
812 | sizeof(struct nilfs_cpinfo), | 815 | sizeof(struct nilfs_cpinfo), |
813 | nilfs_ioctl_do_get_cpinfo); | 816 | nilfs_ioctl_do_get_cpinfo); |
814 | case NILFS_IOCTL_GET_CPSTAT: | 817 | case NILFS_IOCTL_GET_CPSTAT: |
815 | return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp); | 818 | return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp); |
816 | case NILFS_IOCTL_GET_SUINFO: | 819 | case NILFS_IOCTL_GET_SUINFO: |
817 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, | 820 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, |
818 | sizeof(struct nilfs_suinfo), | 821 | sizeof(struct nilfs_suinfo), |
819 | nilfs_ioctl_do_get_suinfo); | 822 | nilfs_ioctl_do_get_suinfo); |
820 | case NILFS_IOCTL_GET_SUSTAT: | 823 | case NILFS_IOCTL_GET_SUSTAT: |
821 | return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); | 824 | return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); |
822 | case NILFS_IOCTL_GET_VINFO: | 825 | case NILFS_IOCTL_GET_VINFO: |
823 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, | 826 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, |
824 | sizeof(struct nilfs_vinfo), | 827 | sizeof(struct nilfs_vinfo), |
825 | nilfs_ioctl_do_get_vinfo); | 828 | nilfs_ioctl_do_get_vinfo); |
826 | case NILFS_IOCTL_GET_BDESCS: | 829 | case NILFS_IOCTL_GET_BDESCS: |
827 | return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp); | 830 | return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp); |
828 | case NILFS_IOCTL_CLEAN_SEGMENTS: | 831 | case NILFS_IOCTL_CLEAN_SEGMENTS: |
829 | return nilfs_ioctl_clean_segments(inode, filp, cmd, argp); | 832 | return nilfs_ioctl_clean_segments(inode, filp, cmd, argp); |
830 | case NILFS_IOCTL_SYNC: | 833 | case NILFS_IOCTL_SYNC: |
831 | return nilfs_ioctl_sync(inode, filp, cmd, argp); | 834 | return nilfs_ioctl_sync(inode, filp, cmd, argp); |
832 | case NILFS_IOCTL_RESIZE: | 835 | case NILFS_IOCTL_RESIZE: |
833 | return nilfs_ioctl_resize(inode, filp, argp); | 836 | return nilfs_ioctl_resize(inode, filp, argp); |
834 | case NILFS_IOCTL_SET_ALLOC_RANGE: | 837 | case NILFS_IOCTL_SET_ALLOC_RANGE: |
835 | return nilfs_ioctl_set_alloc_range(inode, argp); | 838 | return nilfs_ioctl_set_alloc_range(inode, argp); |
836 | default: | 839 | default: |
837 | return -ENOTTY; | 840 | return -ENOTTY; |
838 | } | 841 | } |
839 | } | 842 | } |
840 | 843 | ||
841 | #ifdef CONFIG_COMPAT | 844 | #ifdef CONFIG_COMPAT |
842 | long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 845 | long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
843 | { | 846 | { |
844 | switch (cmd) { | 847 | switch (cmd) { |
845 | case FS_IOC32_GETFLAGS: | 848 | case FS_IOC32_GETFLAGS: |
846 | cmd = FS_IOC_GETFLAGS; | 849 | cmd = FS_IOC_GETFLAGS; |
847 | break; | 850 | break; |
848 | case FS_IOC32_SETFLAGS: | 851 | case FS_IOC32_SETFLAGS: |
849 | cmd = FS_IOC_SETFLAGS; | 852 | cmd = FS_IOC_SETFLAGS; |
850 | break; | 853 | break; |
851 | case FS_IOC32_GETVERSION: | 854 | case FS_IOC32_GETVERSION: |
852 | cmd = FS_IOC_GETVERSION; | 855 | cmd = FS_IOC_GETVERSION; |
853 | break; | 856 | break; |
854 | case NILFS_IOCTL_CHANGE_CPMODE: | 857 | case NILFS_IOCTL_CHANGE_CPMODE: |
855 | case NILFS_IOCTL_DELETE_CHECKPOINT: | 858 | case NILFS_IOCTL_DELETE_CHECKPOINT: |
856 | case NILFS_IOCTL_GET_CPINFO: | 859 | case NILFS_IOCTL_GET_CPINFO: |
857 | case NILFS_IOCTL_GET_CPSTAT: | 860 | case NILFS_IOCTL_GET_CPSTAT: |
858 | case NILFS_IOCTL_GET_SUINFO: | 861 | case NILFS_IOCTL_GET_SUINFO: |
859 | case NILFS_IOCTL_GET_SUSTAT: | 862 | case NILFS_IOCTL_GET_SUSTAT: |
860 | case NILFS_IOCTL_GET_VINFO: | 863 | case NILFS_IOCTL_GET_VINFO: |
861 | case NILFS_IOCTL_GET_BDESCS: | 864 | case NILFS_IOCTL_GET_BDESCS: |
862 | case NILFS_IOCTL_CLEAN_SEGMENTS: | 865 | case NILFS_IOCTL_CLEAN_SEGMENTS: |
863 | case NILFS_IOCTL_SYNC: | 866 | case NILFS_IOCTL_SYNC: |
864 | case NILFS_IOCTL_RESIZE: | 867 | case NILFS_IOCTL_RESIZE: |
865 | case NILFS_IOCTL_SET_ALLOC_RANGE: | 868 | case NILFS_IOCTL_SET_ALLOC_RANGE: |
866 | break; | 869 | break; |
867 | default: | 870 | default: |
868 | return -ENOIOCTLCMD; | 871 | return -ENOIOCTLCMD; |
869 | } | 872 | } |
870 | return nilfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); | 873 | return nilfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); |
871 | } | 874 | } |
872 | #endif | 875 | #endif |
873 | 876 |