diff options
Diffstat (limited to 'fs/block_dev.c')
-rw-r--r-- | fs/block_dev.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c index b3c1efff5e1d..8c3c6899ccf3 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/blkpg.h> | 19 | #include <linux/blkpg.h> |
20 | #include <linux/buffer_head.h> | 20 | #include <linux/buffer_head.h> |
21 | #include <linux/pagevec.h> | ||
21 | #include <linux/writeback.h> | 22 | #include <linux/writeback.h> |
22 | #include <linux/mpage.h> | 23 | #include <linux/mpage.h> |
23 | #include <linux/mount.h> | 24 | #include <linux/mount.h> |
@@ -174,6 +175,151 @@ blkdev_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, | |||
174 | iov, offset, nr_segs, blkdev_get_blocks, NULL); | 175 | iov, offset, nr_segs, blkdev_get_blocks, NULL); |
175 | } | 176 | } |
176 | 177 | ||
178 | /* | ||
179 | * Write out and wait upon all the dirty data associated with a block | ||
180 | * device via its mapping. Does not take the superblock lock. | ||
181 | */ | ||
182 | int sync_blockdev(struct block_device *bdev) | ||
183 | { | ||
184 | int ret = 0; | ||
185 | |||
186 | if (bdev) | ||
187 | ret = filemap_write_and_wait(bdev->bd_inode->i_mapping); | ||
188 | return ret; | ||
189 | } | ||
190 | EXPORT_SYMBOL(sync_blockdev); | ||
191 | |||
192 | /* | ||
193 | * Write out and wait upon all dirty data associated with this | ||
194 | * device. Filesystem data as well as the underlying block | ||
195 | * device. Takes the superblock lock. | ||
196 | */ | ||
197 | int fsync_bdev(struct block_device *bdev) | ||
198 | { | ||
199 | struct super_block *sb = get_super(bdev); | ||
200 | if (sb) { | ||
201 | int res = fsync_super(sb); | ||
202 | drop_super(sb); | ||
203 | return res; | ||
204 | } | ||
205 | return sync_blockdev(bdev); | ||
206 | } | ||
207 | |||
208 | /** | ||
209 | * freeze_bdev -- lock a filesystem and force it into a consistent state | ||
210 | * @bdev: blockdevice to lock | ||
211 | * | ||
212 | * This takes the block device bd_mount_sem to make sure no new mounts | ||
213 | * happen on bdev until thaw_bdev() is called. | ||
214 | * If a superblock is found on this device, we take the s_umount semaphore | ||
215 | * on it to make sure nobody unmounts until the snapshot creation is done. | ||
216 | * The reference counter (bd_fsfreeze_count) guarantees that only the last | ||
217 | * unfreeze process can unfreeze the frozen filesystem actually when multiple | ||
218 | * freeze requests arrive simultaneously. It counts up in freeze_bdev() and | ||
219 | * count down in thaw_bdev(). When it becomes 0, thaw_bdev() will unfreeze | ||
220 | * actually. | ||
221 | */ | ||
222 | struct super_block *freeze_bdev(struct block_device *bdev) | ||
223 | { | ||
224 | struct super_block *sb; | ||
225 | int error = 0; | ||
226 | |||
227 | mutex_lock(&bdev->bd_fsfreeze_mutex); | ||
228 | if (bdev->bd_fsfreeze_count > 0) { | ||
229 | bdev->bd_fsfreeze_count++; | ||
230 | sb = get_super(bdev); | ||
231 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | ||
232 | return sb; | ||
233 | } | ||
234 | bdev->bd_fsfreeze_count++; | ||
235 | |||
236 | down(&bdev->bd_mount_sem); | ||
237 | sb = get_super(bdev); | ||
238 | if (sb && !(sb->s_flags & MS_RDONLY)) { | ||
239 | sb->s_frozen = SB_FREEZE_WRITE; | ||
240 | smp_wmb(); | ||
241 | |||
242 | __fsync_super(sb); | ||
243 | |||
244 | sb->s_frozen = SB_FREEZE_TRANS; | ||
245 | smp_wmb(); | ||
246 | |||
247 | sync_blockdev(sb->s_bdev); | ||
248 | |||
249 | if (sb->s_op->freeze_fs) { | ||
250 | error = sb->s_op->freeze_fs(sb); | ||
251 | if (error) { | ||
252 | printk(KERN_ERR | ||
253 | "VFS:Filesystem freeze failed\n"); | ||
254 | sb->s_frozen = SB_UNFROZEN; | ||
255 | drop_super(sb); | ||
256 | up(&bdev->bd_mount_sem); | ||
257 | bdev->bd_fsfreeze_count--; | ||
258 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | ||
259 | return ERR_PTR(error); | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | |||
264 | sync_blockdev(bdev); | ||
265 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | ||
266 | |||
267 | return sb; /* thaw_bdev releases s->s_umount and bd_mount_sem */ | ||
268 | } | ||
269 | EXPORT_SYMBOL(freeze_bdev); | ||
270 | |||
271 | /** | ||
272 | * thaw_bdev -- unlock filesystem | ||
273 | * @bdev: blockdevice to unlock | ||
274 | * @sb: associated superblock | ||
275 | * | ||
276 | * Unlocks the filesystem and marks it writeable again after freeze_bdev(). | ||
277 | */ | ||
278 | int thaw_bdev(struct block_device *bdev, struct super_block *sb) | ||
279 | { | ||
280 | int error = 0; | ||
281 | |||
282 | mutex_lock(&bdev->bd_fsfreeze_mutex); | ||
283 | if (!bdev->bd_fsfreeze_count) { | ||
284 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | ||
285 | return -EINVAL; | ||
286 | } | ||
287 | |||
288 | bdev->bd_fsfreeze_count--; | ||
289 | if (bdev->bd_fsfreeze_count > 0) { | ||
290 | if (sb) | ||
291 | drop_super(sb); | ||
292 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | ||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | if (sb) { | ||
297 | BUG_ON(sb->s_bdev != bdev); | ||
298 | if (!(sb->s_flags & MS_RDONLY)) { | ||
299 | if (sb->s_op->unfreeze_fs) { | ||
300 | error = sb->s_op->unfreeze_fs(sb); | ||
301 | if (error) { | ||
302 | printk(KERN_ERR | ||
303 | "VFS:Filesystem thaw failed\n"); | ||
304 | sb->s_frozen = SB_FREEZE_TRANS; | ||
305 | bdev->bd_fsfreeze_count++; | ||
306 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | ||
307 | return error; | ||
308 | } | ||
309 | } | ||
310 | sb->s_frozen = SB_UNFROZEN; | ||
311 | smp_wmb(); | ||
312 | wake_up(&sb->s_wait_unfrozen); | ||
313 | } | ||
314 | drop_super(sb); | ||
315 | } | ||
316 | |||
317 | up(&bdev->bd_mount_sem); | ||
318 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | ||
319 | return 0; | ||
320 | } | ||
321 | EXPORT_SYMBOL(thaw_bdev); | ||
322 | |||
177 | static int blkdev_writepage(struct page *page, struct writeback_control *wbc) | 323 | static int blkdev_writepage(struct page *page, struct writeback_control *wbc) |
178 | { | 324 | { |
179 | return block_write_full_page(page, blkdev_get_block, wbc); | 325 | return block_write_full_page(page, blkdev_get_block, wbc); |