Introduce a new truncate calling sequence into fs/mm subsystems. Rather than setattr > vmtruncate > truncate, have filesystems call their truncate sequence from ->setattr if filesystem specific operations are required. vmtruncate is deprecated, and truncate_pagecache and inode_newsize_ok helpers introduced previously should be used. simple_setattr is introduced for simple in-ram filesystems to implement the new truncate sequence. Eventually all filesystems should be converted to implement a setattr, and the default code in notify_change should go away. simple_setsize is also introduced to perform just the ATTR_SIZE portion of simple_setattr (ie. changing i_size and trimming pagecache). To implement the new truncate sequence: - filesystem specific manipulations (eg freeing blocks) must be done in the setattr method rather than ->truncate. - vmtruncate can not be used by core code to trim blocks past i_size in the event of write failure after allocation, so this must be performed in the fs code. - convert usage of helpers block_write_begin, nobh_write_begin, cont_write_begin, and *blockdev_direct_IO* to use _newtrunc postfixed variants. These avoid calling vmtruncate to trim blocks (see previous). - inode_setattr should not be used. generic_setattr is a new function to be used to copy simple attributes into the generic inode. - make use of the better opportunity to handle errors with the new sequence. Big problem with the previous calling sequence: the filesystem is not called until i_size has already changed. This means it is not allowed to fail the call, and also it does not know what the previous i_size was. Also, generic code calling vmtruncate to truncate allocated blocks in case of error had no good way to return a meaningful error (or, for example, atomically handle block deallocation).
Showing the changes to include/linux (2 of 8 files). Show all
Unified · Split
| @@ -203,6 +203,9 @@ int block_write_full_page_endio(struct page *page, get_block_t *get_block, · 202 unmodified lines | ||
| 203 | 203 | int block_read_full_page(struct page*, get_block_t*); |
| 204 | 204 | int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc, |
| 205 | 205 | unsigned long from); |
| 206 | +int block_write_begin_newtrunc(struct file *, struct address_space *, | |
| 207 | + loff_t, unsigned, unsigned, | |
| 208 | + struct page **, void **, get_block_t*); | |
| 206 | 209 | int block_write_begin(struct file *, struct address_space *, |
| 207 | 210 | loff_t, unsigned, unsigned, |
| 208 | 211 | struct page **, void **, get_block_t*); |
| @@ -214,6 +217,9 @@ int generic_write_end(struct file *, struct address_space *, · 5 unmodified lines | ||
| 214 | 217 | struct page *, void *); |
| 215 | 218 | void page_zero_new_buffers(struct page *page, unsigned from, unsigned to); |
| 216 | 219 | int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*); |
| 220 | +int cont_write_begin_newtrunc(struct file *, struct address_space *, loff_t, | |
| 221 | + unsigned, unsigned, struct page **, void **, | |
| 222 | + get_block_t *, loff_t *); | |
| 217 | 223 | int cont_write_begin(struct file *, struct address_space *, loff_t, |
| 218 | 224 | unsigned, unsigned, struct page **, void **, |
| 219 | 225 | get_block_t *, loff_t *); |
| @@ -225,6 +231,9 @@ void block_sync_page(struct page *); · 5 unmodified lines | ||
| 225 | 231 | sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *); |
| 226 | 232 | int block_truncate_page(struct address_space *, loff_t, get_block_t *); |
| 227 | 233 | int file_fsync(struct file *, int); |
| 234 | +int nobh_write_begin_newtrunc(struct file *, struct address_space *, | |
| 235 | + loff_t, unsigned, unsigned, | |
| 236 | + struct page **, void **, get_block_t*); | |
| 228 | 237 | int nobh_write_begin(struct file *, struct address_space *, |
| 229 | 238 | loff_t, unsigned, unsigned, |
| 230 | 239 | struct page **, void **, get_block_t*); |
| @@ -2257,6 +2257,10 @@ typedef void (dio_submit_t)(int rw, struct bio *bio, struct inode *inode, · 2256 unmodified lines | ||
| 2257 | 2257 | loff_t file_offset); |
| 2258 | 2258 | void dio_end_io(struct bio *bio, int error); |
| 2259 | 2259 | |
| 2260 | +ssize_t __blockdev_direct_IO_newtrunc(int rw, struct kiocb *iocb, struct inode *inode, | |
| 2261 | + struct block_device *bdev, const struct iovec *iov, loff_t offset, | |
| 2262 | + unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, | |
| 2263 | + dio_submit_t submit_io, int lock_type); | |
| 2260 | 2264 | ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, |
| 2261 | 2265 | struct block_device *bdev, const struct iovec *iov, loff_t offset, |
| 2262 | 2266 | unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, |
| @@ -2270,6 +2274,24 @@ enum { · 7 unmodified lines | ||
| 2270 | 2274 | DIO_SKIP_HOLES = 0x02, |
| 2271 | 2275 | }; |
| 2272 | 2276 | |
| 2277 | +static inline ssize_t blockdev_direct_IO_newtrunc(int rw, struct kiocb *iocb, | |
| 2278 | + struct inode *inode, struct block_device *bdev, const struct iovec *iov, | |
| 2279 | + loff_t offset, unsigned long nr_segs, get_block_t get_block, | |
| 2280 | + dio_iodone_t end_io) | |
| 2281 | +{ | |
| 2282 | + return __blockdev_direct_IO_newtrunc(rw, iocb, inode, bdev, iov, offset, | |
| 2283 | + nr_segs, get_block, end_io, NULL, | |
| 2284 | + DIO_LOCKING | DIO_SKIP_HOLES); | |
| 2285 | +} | |
| 2286 | + | |
| 2287 | +static inline ssize_t blockdev_direct_IO_no_locking_newtrunc(int rw, struct kiocb *iocb, | |
| 2288 | + struct inode *inode, struct block_device *bdev, const struct iovec *iov, | |
| 2289 | + loff_t offset, unsigned long nr_segs, get_block_t get_block, | |
| 2290 | + dio_iodone_t end_io) | |
| 2291 | +{ | |
| 2292 | + return __blockdev_direct_IO_newtrunc(rw, iocb, inode, bdev, iov, offset, | |
| 2293 | + nr_segs, get_block, end_io, NULL, 0); | |
| 2294 | +} | |
| 2273 | 2295 | static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb, |
| 2274 | 2296 | struct inode *inode, struct block_device *bdev, const struct iovec *iov, |
| 2275 | 2297 | loff_t offset, unsigned long nr_segs, get_block_t get_block, |
| @@ -2342,12 +2364,14 @@ extern int dcache_dir_open(struct inode *, struct file *); · 66 unmodified lines | ||
| 2342 | 2364 | extern int dcache_dir_close(struct inode *, struct file *); |
| 2343 | 2365 | extern loff_t dcache_dir_lseek(struct file *, loff_t, int); |
| 2344 | 2366 | extern int dcache_readdir(struct file *, void *, filldir_t); |
| 2367 | +extern int simple_setattr(struct dentry *, struct iattr *); | |
| 2345 | 2368 | extern int simple_getattr(struct vfsmount *, struct dentry *, struct kstat *); |
| 2346 | 2369 | extern int simple_statfs(struct dentry *, struct kstatfs *); |
| 2347 | 2370 | extern int simple_link(struct dentry *, struct inode *, struct dentry *); |
| 2348 | 2371 | extern int simple_unlink(struct inode *, struct dentry *); |
| 2349 | 2372 | extern int simple_rmdir(struct inode *, struct dentry *); |
| 2350 | 2373 | extern int simple_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); |
| 2374 | +extern int simple_setsize(struct inode *, loff_t); | |
| 2351 | 2375 | extern int noop_fsync(struct file *, int); |
| 2352 | 2376 | extern int simple_empty(struct dentry *); |
| 2353 | 2377 | extern int simple_readpage(struct file *file, struct page *page); |
| @@ -2384,7 +2408,8 @@ extern int buffer_migrate_page(struct address_space *, · 30 unmodified lines | ||
| 2384 | 2408 | |
| 2385 | 2409 | extern int inode_change_ok(const struct inode *, struct iattr *); |
| 2386 | 2410 | extern int inode_newsize_ok(const struct inode *, loff_t offset); |
| 2387 | −extern int __must_check inode_setattr(struct inode *, struct iattr *); | |
| 2411 | +extern int __must_check inode_setattr(struct inode *, const struct iattr *); | |
| 2412 | +extern void generic_setattr(struct inode *inode, const struct iattr *attr); | |
| 2388 | 2413 | |
| 2389 | 2414 | extern void file_update_time(struct file *file); |
| 2390 | 2415 | |