summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2003-07-10 10:02:02 -0700
committerLinus Torvalds <torvalds@home.osdl.org>2003-07-10 10:02:02 -0700
commiteafe591605deb294a315026372ff1fe6fc572eea (patch)
treef8c48e1e286d05281d52cfaffeef481dc174403e /include
parente9b94f6af1eca7294fdea13b3a289993852b6397 (diff)
[PATCH] i_size atomic access
From: Daniel McNeil <daniel@osdl.org> 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.
Diffstat (limited to 'include')
-rw-r--r--include/linux/fs.h63
1 files changed, 63 insertions, 0 deletions
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 <linux/seqlock.h>
+#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 */