diff -urNp --exclude CVS --exclude BitKeeper x-ref/arch/i386/config.in x/arch/i386/config.in --- x-ref/arch/i386/config.in 2003-09-23 03:23:03.000000000 +0200 +++ x/arch/i386/config.in 2003-09-23 03:23:04.000000000 +0200 @@ -70,6 +70,7 @@ if [ "$CONFIG_M486" = "y" ]; then define_bool CONFIG_X86_F00F_WORKS_OK n fi if [ "$CONFIG_M586" = "y" ]; then + define_bool CONFIG_X86_CMPXCHG8 y define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_USE_STRING_486 y define_bool CONFIG_X86_ALIGNMENT_16 y @@ -77,6 +78,7 @@ if [ "$CONFIG_M586" = "y" ]; then define_bool CONFIG_X86_F00F_WORKS_OK n fi if [ "$CONFIG_M586TSC" = "y" ]; then + define_bool CONFIG_X86_CMPXCHG8 y define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_USE_STRING_486 y define_bool CONFIG_X86_ALIGNMENT_16 y @@ -85,6 +87,7 @@ if [ "$CONFIG_M586TSC" = "y" ]; then define_bool CONFIG_X86_F00F_WORKS_OK n fi if [ "$CONFIG_M586MMX" = "y" ]; then + define_bool CONFIG_X86_CMPXCHG8 y define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_USE_STRING_486 y define_bool CONFIG_X86_ALIGNMENT_16 y @@ -94,6 +97,7 @@ if [ "$CONFIG_M586MMX" = "y" ]; then define_bool CONFIG_X86_F00F_WORKS_OK n fi if [ "$CONFIG_M686" = "y" ]; then + define_bool CONFIG_X86_CMPXCHG8 y define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_HAS_TSC y define_bool CONFIG_X86_GOOD_APIC y @@ -103,6 +107,7 @@ if [ "$CONFIG_M686" = "y" ]; then define_bool CONFIG_X86_F00F_WORKS_OK y fi if [ "$CONFIG_MPENTIUMIII" = "y" ]; then + define_bool CONFIG_X86_CMPXCHG8 y define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_HAS_TSC y define_bool CONFIG_X86_GOOD_APIC y @@ -111,6 +116,7 @@ if [ "$CONFIG_MPENTIUMIII" = "y" ]; then define_bool CONFIG_X86_F00F_WORKS_OK y fi if [ "$CONFIG_MPENTIUM4" = "y" ]; then + define_bool CONFIG_X86_CMPXCHG8 y define_int CONFIG_X86_L1_CACHE_SHIFT 7 define_bool CONFIG_X86_HAS_TSC y define_bool CONFIG_X86_GOOD_APIC y @@ -119,6 +125,7 @@ if [ "$CONFIG_MPENTIUM4" = "y" ]; then define_bool CONFIG_X86_F00F_WORKS_OK y fi if [ "$CONFIG_MK6" = "y" ]; then + define_bool CONFIG_X86_CMPXCHG8 y define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_HAS_TSC y @@ -130,6 +137,7 @@ if [ "$CONFIG_MK8" = "y" ]; then define_bool CONFIG_MK7 y fi if [ "$CONFIG_MK7" = "y" ]; then + define_bool CONFIG_X86_CMPXCHG8 y define_int CONFIG_X86_L1_CACHE_SHIFT 6 define_bool CONFIG_X86_HAS_TSC y define_bool CONFIG_X86_GOOD_APIC y diff -urNp --exclude CVS --exclude BitKeeper x-ref/fs/inode.c x/fs/inode.c --- x-ref/fs/inode.c 2003-09-23 03:23:01.000000000 +0200 +++ x/fs/inode.c 2003-09-23 03:23:04.000000000 +0200 @@ -154,6 +154,7 @@ void _inode_init_once(struct inode *inod sema_init(&inode->i_zombie, 1); init_rwsem(&inode->i_alloc_sem); spin_lock_init(&inode->i_data.i_shared_lock); + i_size_ordered_init(inode); } void inode_init_once(struct inode *inode) diff -urNp --exclude CVS --exclude BitKeeper x-ref/include/asm-i386/bugs.h x/include/asm-i386/bugs.h --- x-ref/include/asm-i386/bugs.h 2003-02-14 07:01:57.000000000 +0100 +++ x/include/asm-i386/bugs.h 2003-09-23 03:23:04.000000000 +0200 @@ -200,6 +200,11 @@ static void __init check_config(void) && (boot_cpu_data.x86_mask < 6 || boot_cpu_data.x86_mask == 11)) panic("Kernel compiled for PMMX+, assumes a local APIC without the read-before-write bug!"); #endif + +#ifdef CONFIG_X86_CMPXCHG8 + if (!cpu_has_cx8) + panic("Kernel compiled for Pentium+, requires CMPXCHG8B feature!"); +#endif } static void __init check_bugs(void) diff -urNp --exclude CVS --exclude BitKeeper x-ref/include/asm-i386/cpufeature.h x/include/asm-i386/cpufeature.h --- x-ref/include/asm-i386/cpufeature.h 2003-09-22 08:05:01.000000000 +0200 +++ x/include/asm-i386/cpufeature.h 2003-09-23 03:26:53.000000000 +0200 @@ -99,6 +99,7 @@ #define cpu_has_k6_mtrr boot_cpu_has(X86_FEATURE_K6_MTRR) #define cpu_has_cyrix_arr boot_cpu_has(X86_FEATURE_CYRIX_ARR) #define cpu_has_centaur_mcr boot_cpu_has(X86_FEATURE_CENTAUR_MCR) +#define cpu_has_cx8 boot_cpu_has(X86_FEATURE_CX8) #define cpu_has_xstore boot_cpu_has(X86_FEATURE_XSTORE) #endif /* __ASM_I386_CPUFEATURE_H */ diff -urNp --exclude CVS --exclude BitKeeper x-ref/include/asm-i386/system.h x/include/asm-i386/system.h --- x-ref/include/asm-i386/system.h 2003-09-23 03:22:51.000000000 +0200 +++ x/include/asm-i386/system.h 2003-09-23 03:23:04.000000000 +0200 @@ -143,6 +143,8 @@ struct __xchg_dummy { unsigned long a[10 #define __xg(x) ((struct __xchg_dummy *)(x)) +#ifdef CONFIG_X86_CMPXCHG8 +#define __ARCH_HAS_GET_SET_64BIT 1 /* * The semantics of XCHGCMP8B are a bit strange, this is why * there is a loop and the loading of %%eax and %%edx has to @@ -167,7 +169,7 @@ static inline void __set_64bit (unsigned "lock cmpxchg8b (%0)\n\t" "jnz 1b" : /* no outputs */ - : "D"(ptr), + : "r"(ptr), "b"(low), "c"(high) : "ax","dx","memory"); @@ -197,6 +199,32 @@ static inline void __set_64bit_var (unsi __set_64bit(ptr, (unsigned int)(value), (unsigned int)((value)>>32ULL) ) : \ __set_64bit(ptr, ll_low(value), ll_high(value)) ) + +/* + * The memory clobber is needed in the read side only if + * there is an unsafe writer before the get_64bit, which should + * never be the case, but just to be safe. + */ +static inline unsigned long long get_64bit(unsigned long long * ptr) +{ + unsigned long low, high; + + __asm__ __volatile__ ( + "\n1:\t" + "movl (%2), %%eax\n\t" + "movl 4(%2), %%edx\n\t" + "movl %%eax, %%ebx\n\t" + "movl %%edx, %%ecx\n\t" + "lock cmpxchg8b (%2)\n\t" + "jnz 1b" + : "=&b" (low), "=&c" (high) + : "r" (ptr) + : "ax","dx","memory"); + + return low | ((unsigned long long) high << 32); +} +#endif /* CONFIG_X86_CMPXCHG */ + /* * Note: no "lock" prefix even on SMP: xchg always implies lock anyway * Note 2: xchg has side effect, so that attribute volatile is necessary, diff -urNp --exclude CVS --exclude BitKeeper x-ref/include/linux/fs.h x/include/linux/fs.h --- x-ref/include/linux/fs.h 2003-09-23 03:23:01.000000000 +0200 +++ x/include/linux/fs.h 2003-09-23 03:23:04.000000000 +0200 @@ -441,6 +441,13 @@ struct block_device { struct list_head bd_inodes; }; +#if BITS_PER_LONG==32 && defined(CONFIG_SMP) && !defined(__ARCH_HAS_GET_SET_64BIT) +#define __NEED_I_SIZE_ORDERED +#define i_size_ordered_init(inode) do { (inode)->i_size_version1 = (inode)->i_size_version2 = 0; } while (0) +#else +#define i_size_ordered_init(inode) do { } while (0) +#endif + struct inode { struct list_head i_hash; struct list_head i_list; @@ -527,8 +534,65 @@ struct inode { struct jffs2_inode_info jffs2_i; void *generic_ip; } u; +#ifdef __NEED_I_SIZE_ORDERED + volatile int i_size_version1; + volatile int i_size_version2; +#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) +#ifdef __ARCH_HAS_GET_SET_64BIT + return (loff_t) get_64bit((unsigned long long *) &inode->i_size); +#else + loff_t i_size; + int v1, v2; + + /* Retry if i_size was possibly modified while sampling. */ + do { + v1 = inode->i_size_version1; + rmb(); + i_size = inode->i_size; + rmb(); + v2 = inode->i_size_version2; + } while (v1 != v2); + + return i_size; +#endif +#elif BITS_PER_LONG==64 || !defined(CONFIG_SMP) + 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) +#ifdef __ARCH_HAS_GET_SET_64BIT + set_64bit((unsigned long long *) &inode->i_size, (unsigned long long) i_size); +#else + inode->i_size_version2++; + wmb(); + inode->i_size = i_size; + wmb(); + inode->i_size_version1++; + wmb(); /* make it visible ASAP */ +#endif +#elif BITS_PER_LONG==64 || !defined(CONFIG_SMP) + inode->i_size = i_size; +#endif +} + static inline int have_mapping_directIO(struct address_space * mapping) { return mapping->a_ops->direct_IO || mapping->a_ops->direct_fileIO;