vfs: make open_with_fake_path() not contribute to nr_files
authorMiklos Szeredi <mszeredi@redhat.com>
Wed, 18 Jul 2018 13:44:40 +0000 (15:44 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Wed, 18 Jul 2018 13:44:40 +0000 (15:44 +0200)
Stacking file operations in overlay will store an extra open file for each
overlay file opened.

The overhead is just that of "struct file" which is about 256bytes, because
overlay already pins an extra dentry and inode when the file is open, which
add up to a much larger overhead.

For fear of breaking working setups, don't start accounting the extra file.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/file_table.c
fs/internal.h
fs/open.c
include/linux/fs.h

index 9b70ed2bbc4ed6a54e758a3207f17176b67d4659..0cc7bea6b51a25ea77c1be3f4b34b62a0eaa468f 100644 (file)
@@ -52,7 +52,8 @@ static void file_free_rcu(struct rcu_head *head)
 static inline void file_free(struct file *f)
 {
        security_file_free(f);
-       percpu_counter_dec(&nr_files);
+       if (!(f->f_mode & FMODE_NOACCOUNT))
+               percpu_counter_dec(&nr_files);
        call_rcu(&f->f_u.fu_rcuhead, file_free_rcu);
 }
 
@@ -91,6 +92,34 @@ int proc_nr_files(struct ctl_table *table, int write,
 }
 #endif
 
+static struct file *__alloc_file(int flags, const struct cred *cred)
+{
+       struct file *f;
+       int error;
+
+       f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
+       if (unlikely(!f))
+               return ERR_PTR(-ENOMEM);
+
+       f->f_cred = get_cred(cred);
+       error = security_file_alloc(f);
+       if (unlikely(error)) {
+               file_free_rcu(&f->f_u.fu_rcuhead);
+               return ERR_PTR(error);
+       }
+
+       atomic_long_set(&f->f_count, 1);
+       rwlock_init(&f->f_owner.lock);
+       spin_lock_init(&f->f_lock);
+       mutex_init(&f->f_pos_lock);
+       eventpoll_init_file(f);
+       f->f_flags = flags;
+       f->f_mode = OPEN_FMODE(flags);
+       /* f->f_version: 0 */
+
+       return f;
+}
+
 /* Find an unused file structure and return a pointer to it.
  * Returns an error pointer if some error happend e.g. we over file
  * structures limit, run out of memory or operation is not permitted.
@@ -105,7 +134,6 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
 {
        static long old_max;
        struct file *f;
-       int error;
 
        /*
         * Privileged users can go above max_files
@@ -119,26 +147,10 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
                        goto over;
        }
 
-       f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
-       if (unlikely(!f))
-               return ERR_PTR(-ENOMEM);
-
-       f->f_cred = get_cred(cred);
-       error = security_file_alloc(f);
-       if (unlikely(error)) {
-               file_free_rcu(&f->f_u.fu_rcuhead);
-               return ERR_PTR(error);
-       }
+       f = __alloc_file(flags, cred);
+       if (!IS_ERR(f))
+               percpu_counter_inc(&nr_files);
 
-       atomic_long_set(&f->f_count, 1);
-       rwlock_init(&f->f_owner.lock);
-       spin_lock_init(&f->f_lock);
-       mutex_init(&f->f_pos_lock);
-       eventpoll_init_file(f);
-       f->f_flags = flags;
-       f->f_mode = OPEN_FMODE(flags);
-       /* f->f_version: 0 */
-       percpu_counter_inc(&nr_files);
        return f;
 
 over:
@@ -150,6 +162,21 @@ over:
        return ERR_PTR(-ENFILE);
 }
 
+/*
+ * Variant of alloc_empty_file() that doesn't check and modify nr_files.
+ *
+ * Should not be used unless there's a very good reason to do so.
+ */
+struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
+{
+       struct file *f = __alloc_file(flags, cred);
+
+       if (!IS_ERR(f))
+               f->f_mode |= FMODE_NOACCOUNT;
+
+       return f;
+}
+
 /**
  * alloc_file - allocate and initialize a 'struct file'
  *
index 52a346903748b6a104a0dcc8e86548a1d9963eb1..442098fa0a841fae3a8ff282f795a01a016e0b8f 100644 (file)
@@ -94,6 +94,7 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
  * file_table.c
  */
 extern struct file *alloc_empty_file(int, const struct cred *);
+extern struct file *alloc_empty_file_noaccount(int, const struct cred *);
 
 /*
  * super.c
index dd15711eb6582d123468f68399dd1d94e478a920..9c6617dbb2c06a4705b0fe2d66f7dcbc688172de 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -928,7 +928,7 @@ EXPORT_SYMBOL(dentry_open);
 struct file *open_with_fake_path(const struct path *path, int flags,
                                struct inode *inode, const struct cred *cred)
 {
-       struct file *f = alloc_empty_file(flags, cred);
+       struct file *f = alloc_empty_file_noaccount(flags, cred);
        if (!IS_ERR(f)) {
                int error;
 
index 5ce2b413abc6baf315a0dbd781e8a68d8ec27927..e1884840d556ae895b93292e5a14dda82fbcb8b6 100644 (file)
@@ -156,6 +156,9 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
 /* File is capable of returning -EAGAIN if I/O will block */
 #define FMODE_NOWAIT   ((__force fmode_t)0x8000000)
 
+/* File does not contribute to nr_files count */
+#define FMODE_NOACCOUNT        ((__force fmode_t)0x20000000)
+
 /*
  * Flag for rw_copy_check_uvector and compat_rw_copy_check_uvector
  * that indicates that they should check the contents of the iovec are