Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 Apr 2012 03:16:02 +0000 (20:16 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 Apr 2012 03:16:02 +0000 (20:16 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
  security: fix compile error in commoncap.c

17 files changed:
arch/s390/Kconfig
arch/s390/defconfig
arch/s390/include/asm/facility.h
arch/s390/include/asm/pgalloc.h
arch/s390/include/asm/swab.h
arch/s390/include/asm/tlb.h
arch/s390/kernel/head.S
arch/s390/kernel/irq.c
arch/s390/kernel/perf_cpum_cf.c
arch/s390/mm/maccess.c
arch/s390/mm/pgtable.c
drivers/s390/block/dasd_eckd.c
drivers/s390/char/vmur.c
fs/fuse/dir.c
fs/fuse/file.c
fs/fuse/inode.c
include/linux/fuse.h

index 2b7c0fb..9015060 100644 (file)
@@ -90,7 +90,6 @@ config S390
        select HAVE_KERNEL_XZ
        select HAVE_ARCH_MUTEX_CPU_RELAX
        select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
-       select HAVE_RCU_TABLE_FREE if SMP
        select ARCH_SAVE_PAGE_KEYS if HIBERNATION
        select HAVE_MEMBLOCK
        select HAVE_MEMBLOCK_NODE_MAP
index 6cf8e26..1957a9d 100644 (file)
@@ -1,8 +1,12 @@
 CONFIG_EXPERIMENTAL=y
 CONFIG_SYSVIPC=y
 CONFIG_POSIX_MQUEUE=y
+CONFIG_FHANDLE=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
 CONFIG_AUDIT=y
-CONFIG_RCU_TRACE=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_CGROUPS=y
@@ -14,16 +18,22 @@ CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
 CONFIG_CGROUP_SCHED=y
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_BLK_CGROUP=y
+CONFIG_NAMESPACES=y
 CONFIG_BLK_DEV_INITRD=y
-# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_RD_XZ=y
+CONFIG_RD_LZO=y
+CONFIG_EXPERT=y
 # CONFIG_COMPAT_BRK is not set
-CONFIG_SLAB=y
 CONFIG_PROFILING=y
 CONFIG_OPROFILE=y
 CONFIG_KPROBES=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_IBM_PARTITION=y
 CONFIG_DEFAULT_DEADLINE=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
@@ -34,18 +44,15 @@ CONFIG_KSM=y
 CONFIG_BINFMT_MISC=m
 CONFIG_CMM=m
 CONFIG_HZ_100=y
-CONFIG_KEXEC=y
-CONFIG_PM=y
+CONFIG_CRASH_DUMP=y
 CONFIG_HIBERNATION=y
 CONFIG_PACKET=y
 CONFIG_UNIX=y
 CONFIG_NET_KEY=y
-CONFIG_AFIUCV=m
 CONFIG_INET=y
 CONFIG_IP_MULTICAST=y
 # CONFIG_INET_LRO is not set
 CONFIG_IPV6=y
-CONFIG_NET_SCTPPROBE=m
 CONFIG_L2TP=m
 CONFIG_L2TP_DEBUGFS=m
 CONFIG_VLAN_8021Q=y
@@ -84,15 +91,14 @@ CONFIG_SCSI_CONSTANTS=y
 CONFIG_SCSI_LOGGING=y
 CONFIG_SCSI_SCAN_ASYNC=y
 CONFIG_ZFCP=y
-CONFIG_ZFCP_DIF=y
 CONFIG_NETDEVICES=y
-CONFIG_DUMMY=m
 CONFIG_BONDING=m
+CONFIG_DUMMY=m
 CONFIG_EQUALIZER=m
 CONFIG_TUN=m
-CONFIG_NET_ETHERNET=y
 CONFIG_VIRTIO_NET=y
 CONFIG_RAW_DRIVER=m
+CONFIG_VIRTIO_BALLOON=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
 # CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
@@ -103,27 +109,21 @@ CONFIG_PROC_KCORE=y
 CONFIG_TMPFS=y
 CONFIG_TMPFS_POSIX_ACL=y
 # CONFIG_NETWORK_FILESYSTEMS is not set
-CONFIG_PARTITION_ADVANCED=y
-CONFIG_IBM_PARTITION=y
-CONFIG_DLM=m
 CONFIG_MAGIC_SYSRQ=y
-CONFIG_DEBUG_KERNEL=y
 CONFIG_TIMER_STATS=y
 CONFIG_PROVE_LOCKING=y
 CONFIG_PROVE_RCU=y
 CONFIG_LOCK_STAT=y
 CONFIG_DEBUG_LOCKDEP=y
-CONFIG_DEBUG_SPINLOCK_SLEEP=y
 CONFIG_DEBUG_LIST=y
 CONFIG_DEBUG_NOTIFIERS=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_RCU_TRACE=y
 CONFIG_KPROBES_SANITY_TEST=y
 CONFIG_DEBUG_FORCE_WEAK_PER_CPU=y
 CONFIG_CPU_NOTIFIER_ERROR_INJECT=m
 CONFIG_LATENCYTOP=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
 CONFIG_DEBUG_PAGEALLOC=y
-# CONFIG_FTRACE is not set
+CONFIG_BLK_DEV_IO_TRACE=y
 # CONFIG_STRICT_DEVMEM is not set
 CONFIG_CRYPTO_NULL=m
 CONFIG_CRYPTO_CRYPTD=m
@@ -173,4 +173,3 @@ CONFIG_CRYPTO_SHA512_S390=m
 CONFIG_CRYPTO_DES_S390=m
 CONFIG_CRYPTO_AES_S390=m
 CONFIG_CRC7=m
-CONFIG_VIRTIO_BALLOON=y
index 1e5b27e..2ee66a6 100644 (file)
@@ -38,12 +38,11 @@ static inline void stfle(u64 *stfle_fac_list, int size)
        unsigned long nr;
 
        preempt_disable();
-       S390_lowcore.stfl_fac_list = 0;
        asm volatile(
                "       .insn s,0xb2b10000,0(0)\n" /* stfl */
                "0:\n"
                EX_TABLE(0b, 0b)
-               : "=m" (S390_lowcore.stfl_fac_list));
+               : "+m" (S390_lowcore.stfl_fac_list));
        nr = 4; /* bytes stored by stfl */
        memcpy(stfle_fac_list, &S390_lowcore.stfl_fac_list, 4);
        if (S390_lowcore.stfl_fac_list & 0x01000000) {
index 8eef9b5..78e3041 100644 (file)
@@ -22,10 +22,7 @@ void crst_table_free(struct mm_struct *, unsigned long *);
 
 unsigned long *page_table_alloc(struct mm_struct *, unsigned long);
 void page_table_free(struct mm_struct *, unsigned long *);
-#ifdef CONFIG_HAVE_RCU_TABLE_FREE
 void page_table_free_rcu(struct mmu_gather *, unsigned long *);
-void __tlb_remove_table(void *_table);
-#endif
 
 static inline void clear_table(unsigned long *s, unsigned long val, size_t n)
 {
index 6bdee21..a3e4ebb 100644 (file)
@@ -77,7 +77,7 @@ static inline __u16 __arch_swab16p(const __u16 *x)
        
        asm volatile(
 #ifndef __s390x__
-               "       icm     %0,2,%O+1(%R1)\n"
+               "       icm     %0,2,%O1+1(%R1)\n"
                "       ic      %0,%1\n"
                : "=&d" (result) : "Q" (*x) : "cc");
 #else /* __s390x__ */
index c687a2c..775a5ee 100644 (file)
 
 struct mmu_gather {
        struct mm_struct *mm;
-#ifdef CONFIG_HAVE_RCU_TABLE_FREE
        struct mmu_table_batch *batch;
-#endif
        unsigned int fullmm;
-       unsigned int need_flush;
 };
 
-#ifdef CONFIG_HAVE_RCU_TABLE_FREE
 struct mmu_table_batch {
        struct rcu_head         rcu;
        unsigned int            nr;
@@ -49,7 +45,6 @@ struct mmu_table_batch {
 
 extern void tlb_table_flush(struct mmu_gather *tlb);
 extern void tlb_remove_table(struct mmu_gather *tlb, void *table);
-#endif
 
 static inline void tlb_gather_mmu(struct mmu_gather *tlb,
                                  struct mm_struct *mm,
@@ -57,29 +52,20 @@ static inline void tlb_gather_mmu(struct mmu_gather *tlb,
 {
        tlb->mm = mm;
        tlb->fullmm = full_mm_flush;
-       tlb->need_flush = 0;
-#ifdef CONFIG_HAVE_RCU_TABLE_FREE
        tlb->batch = NULL;
-#endif
        if (tlb->fullmm)
                __tlb_flush_mm(mm);
 }
 
 static inline void tlb_flush_mmu(struct mmu_gather *tlb)
 {
-       if (!tlb->need_flush)
-               return;
-       tlb->need_flush = 0;
-       __tlb_flush_mm(tlb->mm);
-#ifdef CONFIG_HAVE_RCU_TABLE_FREE
        tlb_table_flush(tlb);
-#endif
 }
 
 static inline void tlb_finish_mmu(struct mmu_gather *tlb,
                                  unsigned long start, unsigned long end)
 {
-       tlb_flush_mmu(tlb);
+       tlb_table_flush(tlb);
 }
 
 /*
@@ -105,10 +91,8 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
 static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
                                unsigned long address)
 {
-#ifdef CONFIG_HAVE_RCU_TABLE_FREE
        if (!tlb->fullmm)
                return page_table_free_rcu(tlb, (unsigned long *) pte);
-#endif
        page_table_free(tlb->mm, (unsigned long *) pte);
 }
 
@@ -125,10 +109,8 @@ static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
 #ifdef __s390x__
        if (tlb->mm->context.asce_limit <= (1UL << 31))
                return;
-#ifdef CONFIG_HAVE_RCU_TABLE_FREE
        if (!tlb->fullmm)
                return tlb_remove_table(tlb, pmd);
-#endif
        crst_table_free(tlb->mm, (unsigned long *) pmd);
 #endif
 }
@@ -146,10 +128,8 @@ static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud,
 #ifdef __s390x__
        if (tlb->mm->context.asce_limit <= (1UL << 42))
                return;
-#ifdef CONFIG_HAVE_RCU_TABLE_FREE
        if (!tlb->fullmm)
                return tlb_remove_table(tlb, pud);
-#endif
        crst_table_free(tlb->mm, (unsigned long *) pud);
 #endif
 }
index c27a072..adccd90 100644 (file)
@@ -474,9 +474,9 @@ ENTRY(startup_kdump)
        stck    __LC_LAST_UPDATE_CLOCK
        spt     5f-.LPG0(%r13)
        mvc     __LC_LAST_UPDATE_TIMER(8),5f-.LPG0(%r13)
+       xc      __LC_STFL_FAC_LIST(8),__LC_STFL_FAC_LIST
 #ifndef CONFIG_MARCH_G5
        # check capabilities against MARCH_{G5,Z900,Z990,Z9_109,Z10}
-       xc      __LC_STFL_FAC_LIST(8),__LC_STFL_FAC_LIST
        .insn   s,0xb2b10000,__LC_STFL_FAC_LIST # store facility list
        tm      __LC_STFL_FAC_LIST,0x01 # stfle available ?
        jz      0f
index 1c2cdd5..8a22c27 100644 (file)
@@ -118,9 +118,10 @@ asmlinkage void do_softirq(void)
                                         "a" (__do_softirq)
                                     : "0", "1", "2", "3", "4", "5", "14",
                                       "cc", "memory" );
-               } else
+               } else {
                        /* We are already on the async stack. */
                        __do_softirq();
+               }
        }
 
        local_irq_restore(flags);
@@ -192,11 +193,12 @@ int unregister_external_interrupt(u16 code, ext_int_handler_t handler)
        int index = ext_hash(code);
 
        spin_lock_irqsave(&ext_int_hash_lock, flags);
-       list_for_each_entry_rcu(p, &ext_int_hash[index], entry)
+       list_for_each_entry_rcu(p, &ext_int_hash[index], entry) {
                if (p->code == code && p->handler == handler) {
                        list_del_rcu(&p->entry);
                        kfree_rcu(p, rcu);
                }
+       }
        spin_unlock_irqrestore(&ext_int_hash_lock, flags);
        return 0;
 }
@@ -211,9 +213,10 @@ void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code,
 
        old_regs = set_irq_regs(regs);
        irq_enter();
-       if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator)
+       if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) {
                /* Serve timer interrupts first. */
                clock_comparator_work();
+       }
        kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++;
        if (ext_code.code != 0x1004)
                __get_cpu_var(s390_idle).nohz_delay = 1;
index 4640508..cb019f4 100644 (file)
@@ -178,7 +178,7 @@ static void cpumf_pmu_enable(struct pmu *pmu)
        err = lcctl(cpuhw->state);
        if (err) {
                pr_err("Enabling the performance measuring unit "
-                      "failed with rc=%lx\n", err);
+                      "failed with rc=%x\n", err);
                return;
        }
 
@@ -203,7 +203,7 @@ static void cpumf_pmu_disable(struct pmu *pmu)
        err = lcctl(inactive);
        if (err) {
                pr_err("Disabling the performance measuring unit "
-                      "failed with rc=%lx\n", err);
+                      "failed with rc=%x\n", err);
                return;
        }
 
index 7bb15fc..e1335dc 100644 (file)
@@ -61,21 +61,14 @@ long probe_kernel_write(void *dst, const void *src, size_t size)
        return copied < 0 ? -EFAULT : 0;
 }
 
-/*
- * Copy memory in real mode (kernel to kernel)
- */
-int memcpy_real(void *dest, void *src, size_t count)
+static int __memcpy_real(void *dest, void *src, size_t count)
 {
        register unsigned long _dest asm("2") = (unsigned long) dest;
        register unsigned long _len1 asm("3") = (unsigned long) count;
        register unsigned long _src  asm("4") = (unsigned long) src;
        register unsigned long _len2 asm("5") = (unsigned long) count;
-       unsigned long flags;
        int rc = -EFAULT;
 
-       if (!count)
-               return 0;
-       flags = __arch_local_irq_stnsm(0xf8UL);
        asm volatile (
                "0:     mvcle   %1,%2,0x0\n"
                "1:     jo      0b\n"
@@ -86,7 +79,23 @@ int memcpy_real(void *dest, void *src, size_t count)
                  "+d" (_len2), "=m" (*((long *) dest))
                : "m" (*((long *) src))
                : "cc", "memory");
-       arch_local_irq_restore(flags);
+       return rc;
+}
+
+/*
+ * Copy memory in real mode (kernel to kernel)
+ */
+int memcpy_real(void *dest, void *src, size_t count)
+{
+       unsigned long flags;
+       int rc;
+
+       if (!count)
+               return 0;
+       local_irq_save(flags);
+       __arch_local_irq_stnsm(0xfbUL);
+       rc = __memcpy_real(dest, src, count);
+       local_irq_restore(flags);
        return rc;
 }
 
index 373adf6..6e765bf 100644 (file)
@@ -678,8 +678,6 @@ void page_table_free(struct mm_struct *mm, unsigned long *table)
        }
 }
 
-#ifdef CONFIG_HAVE_RCU_TABLE_FREE
-
 static void __page_table_free_rcu(void *table, unsigned bit)
 {
        struct page *page;
@@ -733,7 +731,66 @@ void __tlb_remove_table(void *_table)
                free_pages((unsigned long) table, ALLOC_ORDER);
 }
 
-#endif
+static void tlb_remove_table_smp_sync(void *arg)
+{
+       /* Simply deliver the interrupt */
+}
+
+static void tlb_remove_table_one(void *table)
+{
+       /*
+        * This isn't an RCU grace period and hence the page-tables cannot be
+        * assumed to be actually RCU-freed.
+        *
+        * It is however sufficient for software page-table walkers that rely
+        * on IRQ disabling. See the comment near struct mmu_table_batch.
+        */
+       smp_call_function(tlb_remove_table_smp_sync, NULL, 1);
+       __tlb_remove_table(table);
+}
+
+static void tlb_remove_table_rcu(struct rcu_head *head)
+{
+       struct mmu_table_batch *batch;
+       int i;
+
+       batch = container_of(head, struct mmu_table_batch, rcu);
+
+       for (i = 0; i < batch->nr; i++)
+               __tlb_remove_table(batch->tables[i]);
+
+       free_page((unsigned long)batch);
+}
+
+void tlb_table_flush(struct mmu_gather *tlb)
+{
+       struct mmu_table_batch **batch = &tlb->batch;
+
+       if (*batch) {
+               __tlb_flush_mm(tlb->mm);
+               call_rcu_sched(&(*batch)->rcu, tlb_remove_table_rcu);
+               *batch = NULL;
+       }
+}
+
+void tlb_remove_table(struct mmu_gather *tlb, void *table)
+{
+       struct mmu_table_batch **batch = &tlb->batch;
+
+       if (*batch == NULL) {
+               *batch = (struct mmu_table_batch *)
+                       __get_free_page(GFP_NOWAIT | __GFP_NOWARN);
+               if (*batch == NULL) {
+                       __tlb_flush_mm(tlb->mm);
+                       tlb_remove_table_one(table);
+                       return;
+               }
+               (*batch)->nr = 0;
+       }
+       (*batch)->tables[(*batch)->nr++] = table;
+       if ((*batch)->nr == MAX_TABLE_BATCH)
+               tlb_table_flush(tlb);
+}
 
 /*
  * switch on pgstes for its userspace process (for kvm)
index c21871a..bc2e8a7 100644 (file)
@@ -2844,6 +2844,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
        sector_t recid, trkid;
        unsigned int offs;
        unsigned int count, count_to_trk_end;
+       int ret;
 
        basedev = block->base;
        if (rq_data_dir(req) == READ) {
@@ -2884,8 +2885,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
 
        itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0);
        if (IS_ERR(itcw)) {
-               dasd_sfree_request(cqr, startdev);
-               return ERR_PTR(-EINVAL);
+               ret = -EINVAL;
+               goto out_error;
        }
        cqr->cpaddr = itcw_get_tcw(itcw);
        if (prepare_itcw(itcw, first_trk, last_trk,
@@ -2897,8 +2898,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
                /* Clock not in sync and XRC is enabled.
                 * Try again later.
                 */
-               dasd_sfree_request(cqr, startdev);
-               return ERR_PTR(-EAGAIN);
+               ret = -EAGAIN;
+               goto out_error;
        }
        len_to_track_end = 0;
        /*
@@ -2937,8 +2938,10 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
                                        tidaw_flags = 0;
                                last_tidaw = itcw_add_tidaw(itcw, tidaw_flags,
                                                            dst, part_len);
-                               if (IS_ERR(last_tidaw))
-                                       return ERR_PTR(-EINVAL);
+                               if (IS_ERR(last_tidaw)) {
+                                       ret = -EINVAL;
+                                       goto out_error;
+                               }
                                dst += part_len;
                        }
                }
@@ -2947,8 +2950,10 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
                        dst = page_address(bv->bv_page) + bv->bv_offset;
                        last_tidaw = itcw_add_tidaw(itcw, 0x00,
                                                    dst, bv->bv_len);
-                       if (IS_ERR(last_tidaw))
-                               return ERR_PTR(-EINVAL);
+                       if (IS_ERR(last_tidaw)) {
+                               ret = -EINVAL;
+                               goto out_error;
+                       }
                }
        }
        last_tidaw->flags |= TIDAW_FLAGS_LAST;
@@ -2968,6 +2973,9 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
        return cqr;
+out_error:
+       dasd_sfree_request(cqr, startdev);
+       return ERR_PTR(ret);
 }
 
 static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
index 85f4a9a..73bef0b 100644 (file)
@@ -903,7 +903,7 @@ static int ur_set_online(struct ccw_device *cdev)
                goto fail_urdev_put;
        }
 
-       cdev_init(urd->char_device, &ur_fops);
+       urd->char_device->ops = &ur_fops;
        urd->char_device->dev = MKDEV(major, minor);
        urd->char_device->owner = ur_fops.owner;
 
index 2066328..df5ac04 100644 (file)
@@ -387,9 +387,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
        if (fc->no_create)
                return -ENOSYS;
 
-       if (flags & O_DIRECT)
-               return -EINVAL;
-
        forget = fuse_alloc_forget();
        if (!forget)
                return -ENOMEM;
@@ -644,13 +641,12 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
        fuse_put_request(fc, req);
        if (!err) {
                struct inode *inode = entry->d_inode;
+               struct fuse_inode *fi = get_fuse_inode(inode);
 
-               /*
-                * Set nlink to zero so the inode can be cleared, if the inode
-                * does have more links this will be discovered at the next
-                * lookup/getattr.
-                */
-               clear_nlink(inode);
+               spin_lock(&fc->lock);
+               fi->attr_version = ++fc->attr_version;
+               drop_nlink(inode);
+               spin_unlock(&fc->lock);
                fuse_invalidate_attr(inode);
                fuse_invalidate_attr(dir);
                fuse_invalidate_entry_cache(entry);
@@ -762,8 +758,17 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
           will reflect changes in the backing inode (link count,
           etc.)
        */
-       if (!err || err == -EINTR)
+       if (!err) {
+               struct fuse_inode *fi = get_fuse_inode(inode);
+
+               spin_lock(&fc->lock);
+               fi->attr_version = ++fc->attr_version;
+               inc_nlink(inode);
+               spin_unlock(&fc->lock);
+               fuse_invalidate_attr(inode);
+       } else if (err == -EINTR) {
                fuse_invalidate_attr(inode);
+       }
        return err;
 }
 
index a841868..504e61b 100644 (file)
@@ -194,10 +194,6 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
        struct fuse_conn *fc = get_fuse_conn(inode);
        int err;
 
-       /* VFS checks this, but only _after_ ->open() */
-       if (file->f_flags & O_DIRECT)
-               return -EINVAL;
-
        err = generic_file_open(inode, file);
        if (err)
                return err;
@@ -932,17 +928,23 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
        size_t count = 0;
+       size_t ocount = 0;
        ssize_t written = 0;
+       ssize_t written_buffered = 0;
        struct inode *inode = mapping->host;
        ssize_t err;
        struct iov_iter i;
+       loff_t endbyte = 0;
 
        WARN_ON(iocb->ki_pos != pos);
 
-       err = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ);
+       ocount = 0;
+       err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ);
        if (err)
                return err;
 
+       count = ocount;
+
        mutex_lock(&inode->i_mutex);
        vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
 
@@ -962,11 +964,41 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
 
        file_update_time(file);
 
-       iov_iter_init(&i, iov, nr_segs, count, 0);
-       written = fuse_perform_write(file, mapping, &i, pos);
-       if (written >= 0)
-               iocb->ki_pos = pos + written;
+       if (file->f_flags & O_DIRECT) {
+               written = generic_file_direct_write(iocb, iov, &nr_segs,
+                                                   pos, &iocb->ki_pos,
+                                                   count, ocount);
+               if (written < 0 || written == count)
+                       goto out;
+
+               pos += written;
+               count -= written;
 
+               iov_iter_init(&i, iov, nr_segs, count, written);
+               written_buffered = fuse_perform_write(file, mapping, &i, pos);
+               if (written_buffered < 0) {
+                       err = written_buffered;
+                       goto out;
+               }
+               endbyte = pos + written_buffered - 1;
+
+               err = filemap_write_and_wait_range(file->f_mapping, pos,
+                                                  endbyte);
+               if (err)
+                       goto out;
+
+               invalidate_mapping_pages(file->f_mapping,
+                                        pos >> PAGE_CACHE_SHIFT,
+                                        endbyte >> PAGE_CACHE_SHIFT);
+
+               written += written_buffered;
+               iocb->ki_pos = pos + written_buffered;
+       } else {
+               iov_iter_init(&i, iov, nr_segs, count, 0);
+               written = fuse_perform_write(file, mapping, &i, pos);
+               if (written >= 0)
+                       iocb->ki_pos = pos + written;
+       }
 out:
        current->backing_dev_info = NULL;
        mutex_unlock(&inode->i_mutex);
@@ -1101,30 +1133,41 @@ static ssize_t fuse_direct_read(struct file *file, char __user *buf,
        return res;
 }
 
-static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
-                                size_t count, loff_t *ppos)
+static ssize_t __fuse_direct_write(struct file *file, const char __user *buf,
+                                  size_t count, loff_t *ppos)
 {
        struct inode *inode = file->f_path.dentry->d_inode;
        ssize_t res;
 
-       if (is_bad_inode(inode))
-               return -EIO;
-
-       /* Don't allow parallel writes to the same file */
-       mutex_lock(&inode->i_mutex);
        res = generic_write_checks(file, ppos, &count, 0);
        if (!res) {
                res = fuse_direct_io(file, buf, count, ppos, 1);
                if (res > 0)
                        fuse_write_update_size(inode, *ppos);
        }
-       mutex_unlock(&inode->i_mutex);
 
        fuse_invalidate_attr(inode);
 
        return res;
 }
 
+static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+       struct inode *inode = file->f_path.dentry->d_inode;
+       ssize_t res;
+
+       if (is_bad_inode(inode))
+               return -EIO;
+
+       /* Don't allow parallel writes to the same file */
+       mutex_lock(&inode->i_mutex);
+       res = __fuse_direct_write(file, buf, count, ppos);
+       mutex_unlock(&inode->i_mutex);
+
+       return res;
+}
+
 static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req)
 {
        __free_page(req->pages[0]);
@@ -2077,6 +2120,57 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc,
        return 0;
 }
 
+static ssize_t fuse_loop_dio(struct file *filp, const struct iovec *iov,
+                            unsigned long nr_segs, loff_t *ppos, int rw)
+{
+       const struct iovec *vector = iov;
+       ssize_t ret = 0;
+
+       while (nr_segs > 0) {
+               void __user *base;
+               size_t len;
+               ssize_t nr;
+
+               base = vector->iov_base;
+               len = vector->iov_len;
+               vector++;
+               nr_segs--;
+
+               if (rw == WRITE)
+                       nr = __fuse_direct_write(filp, base, len, ppos);
+               else
+                       nr = fuse_direct_read(filp, base, len, ppos);
+
+               if (nr < 0) {
+                       if (!ret)
+                               ret = nr;
+                       break;
+               }
+               ret += nr;
+               if (nr != len)
+                       break;
+       }
+
+       return ret;
+}
+
+
+static ssize_t
+fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
+                       loff_t offset, unsigned long nr_segs)
+{
+       ssize_t ret = 0;
+       struct file *file = NULL;
+       loff_t pos = 0;
+
+       file = iocb->ki_filp;
+       pos = offset;
+
+       ret = fuse_loop_dio(file, iov, nr_segs, &pos, rw);
+
+       return ret;
+}
+
 static const struct file_operations fuse_file_operations = {
        .llseek         = fuse_file_llseek,
        .read           = do_sync_read,
@@ -2120,6 +2214,7 @@ static const struct address_space_operations fuse_file_aops  = {
        .readpages      = fuse_readpages,
        .set_page_dirty = __set_page_dirty_nobuffers,
        .bmap           = fuse_bmap,
+       .direct_IO      = fuse_direct_IO,
 };
 
 void fuse_init_file_inode(struct inode *inode)
index 4aec599..26783eb 100644 (file)
@@ -947,6 +947,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_magic = FUSE_SUPER_MAGIC;
        sb->s_op = &fuse_super_operations;
        sb->s_maxbytes = MAX_LFS_FILESIZE;
+       sb->s_time_gran = 1;
        sb->s_export_op = &fuse_export_operations;
 
        file = fget(d.fd);
index 8ba2c94..8f2ab8f 100644 (file)
@@ -593,7 +593,7 @@ struct fuse_dirent {
        __u64   off;
        __u32   namelen;
        __u32   type;
-       char name[0];
+       char name[];
 };
 
 #define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)