diff options
| author | Andrew Morton <akpm@digeo.com> | 2003-02-02 06:06:42 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2003-02-02 06:06:42 -0800 |
| commit | 7619fd2bb706279edf839d8caa8a80e63bb93b7a (patch) | |
| tree | d9ccee1dbe2a8b8f92fc88adb0040f988639717a /include | |
| parent | 7c0f82da343024b1ac260c8e2ee458464eb19de4 (diff) | |
[PATCH] Fix inode size accounting race
Since Jan removed the lock_kernel()s in inode_add_bytes() and
inode_sub_bytes(), these functions have been racy.
One problematic workload has been discovered in which concurrent writepage
and truncate on SMP quickly causes i_blocks to go negative. writepage() does
not take i_sem, and it seems that for ext2, there are no other locks in
force when inode_add_bytes() is called.
Putting the BKL back in there is not acceptable. To fix this race I have
added a new spinlock "i_lock" to the inode.
That lock is presently used to protect i_bytes and i_blocks. We could use it
to protect i_size as well.
The splitting of the used disk space into i_blocks and i_bytes is silly - we
should nuke all that and just have a bare loff_t i_usedbytes. Later.
Diffstat (limited to 'include')
| -rw-r--r-- | include/linux/fs.h | 42 |
1 files changed, 7 insertions, 35 deletions
diff --git a/include/linux/fs.h b/include/linux/fs.h index 595ea1af33fd..76b32526394f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -371,9 +371,10 @@ struct inode { struct timespec i_ctime; unsigned int i_blkbits; unsigned long i_blksize; - unsigned long i_blocks; unsigned long i_version; + unsigned long i_blocks; unsigned short i_bytes; + spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ struct semaphore i_sem; struct inode_operations *i_op; struct file_operations *i_fop; /* former ->i_op->default_file_ops */ @@ -400,7 +401,7 @@ struct inode { void *i_security; __u32 i_generation; union { - void *generic_ip; + void *generic_ip; } u; }; @@ -412,39 +413,6 @@ struct fown_struct { void *security; }; -static inline void inode_add_bytes(struct inode *inode, loff_t bytes) -{ - inode->i_blocks += bytes >> 9; - bytes &= 511; - inode->i_bytes += bytes; - if (inode->i_bytes >= 512) { - inode->i_blocks++; - inode->i_bytes -= 512; - } -} - -static inline void inode_sub_bytes(struct inode *inode, loff_t bytes) -{ - inode->i_blocks -= bytes >> 9; - bytes &= 511; - if (inode->i_bytes < bytes) { - inode->i_blocks--; - inode->i_bytes += 512; - } - inode->i_bytes -= bytes; -} - -static inline loff_t inode_get_bytes(struct inode *inode) -{ - return (((loff_t)inode->i_blocks) << 9) + inode->i_bytes; -} - -static inline void inode_set_bytes(struct inode *inode, loff_t bytes) -{ - inode->i_blocks = bytes >> 9; - inode->i_bytes = bytes & 511; -} - /* * Track a single file's readahead state */ @@ -1277,6 +1245,10 @@ extern int page_symlink(struct inode *inode, const char *symname, int len); extern struct inode_operations page_symlink_inode_operations; extern void generic_fillattr(struct inode *, struct kstat *); extern int vfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); +void inode_add_bytes(struct inode *inode, loff_t bytes); +void inode_sub_bytes(struct inode *inode, loff_t bytes); +loff_t inode_get_bytes(struct inode *inode); +void inode_set_bytes(struct inode *inode, loff_t bytes); extern int vfs_readdir(struct file *, filldir_t, void *); |
