From eafe591605deb294a315026372ff1fe6fc572eea Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 10 Jul 2003 10:02:02 -0700 Subject: [PATCH] i_size atomic access From: Daniel McNeil This adds i_seqcount to the inode structure and then uses i_size_read() and i_size_write() to provide atomic access to i_size. This is a port of Andrea Arcangeli's i_size atomic access patch from 2.4. This only uses the generic reader/writer consistent mechanism. Before: mnm:/usr/src/25> size vmlinux text data bss dec hex filename 2229582 1027683 162436 3419701 342e35 vmlinux After: mnm:/usr/src/25> size vmlinux text data bss dec hex filename 2225642 1027655 162436 3415733 341eb5 vmlinux 3.9k more text, a lot of it fastpath :( It's a very minor bug, and the fix has a fairly non-minor cost. The most compelling reason for fixing this is that writepage() checks i_size. If it sees a transient value it may decide that page is outside i_size and will refuse to write it. Lost user data. --- include/linux/fs.h | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'include/linux') diff --git a/include/linux/fs.h b/include/linux/fs.h index 77dd4b13dc43..2a79214753c0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -349,6 +349,17 @@ struct block_device { struct gendisk * bd_disk; }; +/* + * Use sequence counter to get consistent i_size on 32-bit processors. + */ +#if BITS_PER_LONG==32 && defined(CONFIG_SMP) +#include +#define __NEED_I_SIZE_ORDERED +#define i_size_ordered_init(inode) seqcount_init(&inode->i_size_seqcount) +#else +#define i_size_ordered_init(inode) do { } while (0) +#endif + struct inode { struct hlist_node i_hash; struct list_head i_list; @@ -399,8 +410,60 @@ struct inode { union { void *generic_ip; } u; +#ifdef __NEED_I_SIZE_ORDERED + seqcount_t i_size_seqcount; +#endif }; +/* + * NOTE: in a 32bit arch with a preemptable kernel and + * an UP compile the i_size_read/write must be atomic + * with respect to the local cpu (unlike with preempt disabled), + * but they don't need to be atomic with respect to other cpus like in + * true SMP (so they need either to either locally disable irq around + * the read or for example on x86 they can be still implemented as a + * cmpxchg8b without the need of the lock prefix). For SMP compiles + * and 64bit archs it makes no difference if preempt is enabled or not. + */ +static inline loff_t i_size_read(struct inode *inode) +{ +#if BITS_PER_LONG==32 && defined(CONFIG_SMP) + loff_t i_size; + unsigned int seq; + + do { + seq = read_seqcount_begin(&inode->i_size_seqcount); + i_size = inode->i_size; + } while (read_seqcount_retry(&inode->i_size_seqcount, seq)); + return i_size; +#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT) + loff_t i_size; + + preempt_disable(); + i_size = inode->i_size; + preempt_enable(); + return i_size; +#else + return inode->i_size; +#endif +} + + +static inline void i_size_write(struct inode *inode, loff_t i_size) +{ +#if BITS_PER_LONG==32 && defined(CONFIG_SMP) + write_seqcount_begin(&inode->i_size_seqcount); + inode->i_size = i_size; + write_seqcount_end(&inode->i_size_seqcount); +#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT) + preempt_disable(); + inode->i_size = i_size; + preempt_enable(); +#else + inode->i_size = i_size; +#endif +} + struct fown_struct { rwlock_t lock; /* protects pid, uid, euid fields */ int pid; /* pid or -pgrp where SIGIO should be sent */ -- cgit v1.2.3