diff options
| author | npiggin@suse.de <npiggin@suse.de> | 2010-05-26 11:05:33 -0400 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-05-27 22:15:33 -0400 |
| commit | 7bb46a6734a7e1ad4beaecc11cae7ed3ff81d30f (patch) | |
| tree | e575d9c55e2a6ccc645dcb3ae2564de458b428f2 | |
| parent | 7000d3c424e5bb350e502a477fb0e1ed42f8b10e (diff) | |
fs: introduce new truncate sequence
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).
Cc: Christoph Hellwig <hch@lst.de>
Acked-by: Jan Kara <jack@suse.cz>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
| -rw-r--r-- | Documentation/filesystems/vfs.txt | 7 | ||||
| -rw-r--r-- | fs/attr.c | 50 | ||||
| -rw-r--r-- | fs/buffer.c | 123 | ||||
| -rw-r--r-- | fs/direct-io.c | 61 | ||||
| -rw-r--r-- | fs/libfs.c | 76 | ||||
| -rw-r--r-- | include/linux/buffer_head.h | 9 | ||||
| -rw-r--r-- | include/linux/fs.h | 27 | ||||
| -rw-r--r-- | mm/truncate.c | 10 |
8 files changed, 300 insertions, 63 deletions
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index d4f5731dcbbb..94677e7dcb13 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt | |||
| @@ -401,11 +401,16 @@ otherwise noted. | |||
| 401 | started might not be in the page cache at the end of the | 401 | started might not be in the page cache at the end of the |
| 402 | walk). | 402 | walk). |
| 403 | 403 | ||
| 404 | truncate: called by the VFS to change the size of a file. The | 404 | truncate: Deprecated. This will not be called if ->setsize is defined. |
| 405 | Called by the VFS to change the size of a file. The | ||
| 405 | i_size field of the inode is set to the desired size by the | 406 | i_size field of the inode is set to the desired size by the |
| 406 | VFS before this method is called. This method is called by | 407 | VFS before this method is called. This method is called by |
| 407 | the truncate(2) system call and related functionality. | 408 | the truncate(2) system call and related functionality. |
| 408 | 409 | ||
| 410 | Note: ->truncate and vmtruncate are deprecated. Do not add new | ||
| 411 | instances/calls of these. Filesystems should be converted to do their | ||
| 412 | truncate sequence via ->setattr(). | ||
| 413 | |||
| 409 | permission: called by the VFS to check for access rights on a POSIX-like | 414 | permission: called by the VFS to check for access rights on a POSIX-like |
| 410 | filesystem. | 415 | filesystem. |
| 411 | 416 | ||
| @@ -67,14 +67,14 @@ EXPORT_SYMBOL(inode_change_ok); | |||
| 67 | * @offset: the new size to assign to the inode | 67 | * @offset: the new size to assign to the inode |
| 68 | * @Returns: 0 on success, -ve errno on failure | 68 | * @Returns: 0 on success, -ve errno on failure |
| 69 | * | 69 | * |
| 70 | * inode_newsize_ok must be called with i_mutex held. | ||
| 71 | * | ||
| 70 | * inode_newsize_ok will check filesystem limits and ulimits to check that the | 72 | * inode_newsize_ok will check filesystem limits and ulimits to check that the |
| 71 | * new inode size is within limits. inode_newsize_ok will also send SIGXFSZ | 73 | * new inode size is within limits. inode_newsize_ok will also send SIGXFSZ |
| 72 | * when necessary. Caller must not proceed with inode size change if failure is | 74 | * when necessary. Caller must not proceed with inode size change if failure is |
| 73 | * returned. @inode must be a file (not directory), with appropriate | 75 | * returned. @inode must be a file (not directory), with appropriate |
| 74 | * permissions to allow truncate (inode_newsize_ok does NOT check these | 76 | * permissions to allow truncate (inode_newsize_ok does NOT check these |
| 75 | * conditions). | 77 | * conditions). |
| 76 | * | ||
| 77 | * inode_newsize_ok must be called with i_mutex held. | ||
| 78 | */ | 78 | */ |
| 79 | int inode_newsize_ok(const struct inode *inode, loff_t offset) | 79 | int inode_newsize_ok(const struct inode *inode, loff_t offset) |
| 80 | { | 80 | { |
| @@ -104,17 +104,25 @@ out_big: | |||
| 104 | } | 104 | } |
| 105 | EXPORT_SYMBOL(inode_newsize_ok); | 105 | EXPORT_SYMBOL(inode_newsize_ok); |
| 106 | 106 | ||
| 107 | int inode_setattr(struct inode * inode, struct iattr * attr) | 107 | /** |
| 108 | * generic_setattr - copy simple metadata updates into the generic inode | ||
| 109 | * @inode: the inode to be updated | ||
| 110 | * @attr: the new attributes | ||
| 111 | * | ||
| 112 | * generic_setattr must be called with i_mutex held. | ||
| 113 | * | ||
| 114 | * generic_setattr updates the inode's metadata with that specified | ||
| 115 | * in attr. Noticably missing is inode size update, which is more complex | ||
| 116 | * as it requires pagecache updates. See simple_setsize. | ||
| 117 | * | ||
| 118 | * The inode is not marked as dirty after this operation. The rationale is | ||
| 119 | * that for "simple" filesystems, the struct inode is the inode storage. | ||
| 120 | * The caller is free to mark the inode dirty afterwards if needed. | ||
| 121 | */ | ||
| 122 | void generic_setattr(struct inode *inode, const struct iattr *attr) | ||
| 108 | { | 123 | { |
| 109 | unsigned int ia_valid = attr->ia_valid; | 124 | unsigned int ia_valid = attr->ia_valid; |
| 110 | 125 | ||
| 111 | if (ia_valid & ATTR_SIZE && | ||
| 112 | attr->ia_size != i_size_read(inode)) { | ||
| 113 | int error = vmtruncate(inode, attr->ia_size); | ||
| 114 | if (error) | ||
| 115 | return error; | ||
| 116 | } | ||
| 117 | |||
| 118 | if (ia_valid & ATTR_UID) | 126 | if (ia_valid & ATTR_UID) |
| 119 | inode->i_uid = attr->ia_uid; | 127 | inode->i_uid = attr->ia_uid; |
| 120 | if (ia_valid & ATTR_GID) | 128 | if (ia_valid & ATTR_GID) |
| @@ -135,6 +143,28 @@ int inode_setattr(struct inode * inode, struct iattr * attr) | |||
| 135 | mode &= ~S_ISGID; | 143 | mode &= ~S_ISGID; |
| 136 | inode->i_mode = mode; | 144 | inode->i_mode = mode; |
| 137 | } | 145 | } |
| 146 | } | ||
| 147 | EXPORT_SYMBOL(generic_setattr); | ||
| 148 | |||
| 149 | /* | ||
| 150 | * note this function is deprecated, the new truncate sequence should be | ||
| 151 | * used instead -- see eg. simple_setsize, generic_setattr. | ||
| 152 | */ | ||
| 153 | int inode_setattr(struct inode *inode, const struct iattr *attr) | ||
| 154 | { | ||
| 155 | unsigned int ia_valid = attr->ia_valid; | ||
| 156 | |||
| 157 | if (ia_valid & ATTR_SIZE && | ||
| 158 | attr->ia_size != i_size_read(inode)) { | ||
| 159 | int error; | ||
| 160 | |||
| 161 | error = vmtruncate(inode, attr->ia_size); | ||
| 162 | if (error) | ||
| 163 | return error; | ||
| 164 | } | ||
| 165 | |||
| 166 | generic_setattr(inode, attr); | ||
| 167 | |||
| 138 | mark_inode_dirty(inode); | 168 | mark_inode_dirty(inode); |
| 139 | 169 | ||
| 140 | return 0; | 170 | return 0; |
diff --git a/fs/buffer.c b/fs/buffer.c index e8aa7081d25c..d54812b198e9 100644 --- a/fs/buffer.c +++ b/fs/buffer.c | |||
| @@ -1949,14 +1949,11 @@ static int __block_commit_write(struct inode *inode, struct page *page, | |||
| 1949 | } | 1949 | } |
| 1950 | 1950 | ||
| 1951 | /* | 1951 | /* |
| 1952 | * block_write_begin takes care of the basic task of block allocation and | 1952 | * Filesystems implementing the new truncate sequence should use the |
| 1953 | * bringing partial write blocks uptodate first. | 1953 | * _newtrunc postfix variant which won't incorrectly call vmtruncate. |
| 1954 | * | 1954 | * The filesystem needs to handle block truncation upon failure. |
| 1955 | * If *pagep is not NULL, then block_write_begin uses the locked page | ||
| 1956 | * at *pagep rather than allocating its own. In this case, the page will | ||
| 1957 | * not be unlocked or deallocated on failure. | ||
| 1958 | */ | 1955 | */ |
| 1959 | int block_write_begin(struct file *file, struct address_space *mapping, | 1956 | int block_write_begin_newtrunc(struct file *file, struct address_space *mapping, |
| 1960 | loff_t pos, unsigned len, unsigned flags, | 1957 | loff_t pos, unsigned len, unsigned flags, |
| 1961 | struct page **pagep, void **fsdata, | 1958 | struct page **pagep, void **fsdata, |
| 1962 | get_block_t *get_block) | 1959 | get_block_t *get_block) |
| @@ -1992,20 +1989,50 @@ int block_write_begin(struct file *file, struct address_space *mapping, | |||
| 1992 | unlock_page(page); | 1989 | unlock_page(page); |
| 1993 | page_cache_release(page); | 1990 | page_cache_release(page); |
| 1994 | *pagep = NULL; | 1991 | *pagep = NULL; |
| 1995 | |||
| 1996 | /* | ||
| 1997 | * prepare_write() may have instantiated a few blocks | ||
| 1998 | * outside i_size. Trim these off again. Don't need | ||
| 1999 | * i_size_read because we hold i_mutex. | ||
| 2000 | */ | ||
| 2001 | if (pos + len > inode->i_size) | ||
| 2002 | vmtruncate(inode, inode->i_size); | ||
| 2003 | } | 1992 | } |
| 2004 | } | 1993 | } |
| 2005 | 1994 | ||
| 2006 | out: | 1995 | out: |
| 2007 | return status; | 1996 | return status; |
| 2008 | } | 1997 | } |
| 1998 | EXPORT_SYMBOL(block_write_begin_newtrunc); | ||
| 1999 | |||
| 2000 | /* | ||
| 2001 | * block_write_begin takes care of the basic task of block allocation and | ||
| 2002 | * bringing partial write blocks uptodate first. | ||
| 2003 | * | ||
| 2004 | * If *pagep is not NULL, then block_write_begin uses the locked page | ||
| 2005 | * at *pagep rather than allocating its own. In this case, the page will | ||
| 2006 | * not be unlocked or deallocated on failure. | ||
| 2007 | */ | ||
| 2008 | int block_write_begin(struct file *file, struct address_space *mapping, | ||
| 2009 | loff_t pos, unsigned len, unsigned flags, | ||
| 2010 | struct page **pagep, void **fsdata, | ||
| 2011 | get_block_t *get_block) | ||
| 2012 | { | ||
| 2013 | int ret; | ||
| 2014 | |||
| 2015 | ret = block_write_begin_newtrunc(file, mapping, pos, len, flags, | ||
| 2016 | pagep, fsdata, get_block); | ||
| 2017 | |||
| 2018 | /* | ||
| 2019 | * prepare_write() may have instantiated a few blocks | ||
