[PATCH 10/13] vfs: Replace close_on_exec bitmap with an IDR tag

From: Sandhya Bankar
Date: Sat Apr 29 2017 - 03:47:57 EST


Replace close_on_exec with idr_(get,set,clear)_tag().
Through this patch, added new IDR tag FD_TAG_CLOEXEC
which is passing to idr_(get,set,clear)_tag() to
achieve close_on_exec functionality.
Also removed get_close_on_exec() and using close_on_exec() instead of that.

Signed-off-by: Sandhya Bankar <bankarsandhya512@xxxxxxxxx>
Signed-off-by: Matthew Wilcox <mawilcox@xxxxxxxxxxxxx>
---
fs/exec.c | 2 +-
fs/fcntl.c | 2 +-
fs/file.c | 85 +++++++++++++++----------------------------------
fs/proc/fd.c | 4 +--
include/linux/fdtable.h | 19 +++++++----
include/linux/file.h | 1 -
6 files changed, 41 insertions(+), 72 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index 65145a3..2070bc6 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1728,7 +1728,7 @@ static int do_execveat_common(int fd, struct filename *filename,
* inaccessible after exec. Relies on having exclusive access to
* current->files (due to unshare_files above).
*/
- if (close_on_exec(fd, rcu_dereference_raw(current->files->fdt)))
+ if (close_on_exec(fd, current->files))
bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
bprm->filename = pathbuf;
}
diff --git a/fs/fcntl.c b/fs/fcntl.c
index be8fbe2..9c2061b 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -255,7 +255,7 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
err = f_dupfd(arg, filp, O_CLOEXEC);
break;
case F_GETFD:
- err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;
+ err = close_on_exec(fd, current->files) ? FD_CLOEXEC : 0;
break;
case F_SETFD:
err = 0;
diff --git a/fs/file.c b/fs/file.c
index 8cd77c5..56c5731 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -70,8 +70,6 @@ static void copy_fd_bitmaps(struct fdtable *nfdt, struct fdtable *ofdt,
set = (nfdt->max_fds - count) / BITS_PER_BYTE;
memcpy(nfdt->open_fds, ofdt->open_fds, cpy);
memset((char *)nfdt->open_fds + cpy, 0, set);
- memcpy(nfdt->close_on_exec, ofdt->close_on_exec, cpy);
- memset((char *)nfdt->close_on_exec + cpy, 0, set);
}

/*
@@ -115,13 +113,10 @@ static struct fdtable * alloc_fdtable(unsigned int nr)
goto out;
fdt->max_fds = nr;

- data = alloc_fdmem(max_t(size_t,
- 2 * nr / BITS_PER_BYTE, L1_CACHE_BYTES));
+ data = alloc_fdmem(max_t(size_t, nr / BITS_PER_BYTE, L1_CACHE_BYTES));
if (!data)
goto out_fdt;
fdt->open_fds = data;
- data += nr / BITS_PER_BYTE;
- fdt->close_on_exec = data;

return fdt;

@@ -227,15 +222,16 @@ static inline bool fd_is_open(unsigned int fd, struct files_struct *files)
return !idr_tag_get(&files->fd_idr, fd, IDR_FREE);
}

-static inline void __set_close_on_exec(unsigned int fd, struct fdtable *fdt)
+static inline void __set_close_on_exec(unsigned int fd,
+ struct files_struct *files)
{
- __set_bit(fd, fdt->close_on_exec);
+ idr_tag_set(&files->fd_idr, fd, FD_TAG_CLOEXEC);
}

-static inline void __clear_close_on_exec(unsigned int fd, struct fdtable *fdt)
+static inline void __clear_close_on_exec(unsigned int fd,
+ struct files_struct *files)
{
- if (test_bit(fd, fdt->close_on_exec))
- __clear_bit(fd, fdt->close_on_exec);
+ idr_tag_clear(&files->fd_idr, fd, FD_TAG_CLOEXEC);
}

static inline void __set_open_fd(unsigned int fd, struct fdtable *fdt)
@@ -287,7 +283,6 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
init_waitqueue_head(&newf->resize_wait);
new_fdt = &newf->fdtab;
new_fdt->max_fds = NR_OPEN_DEFAULT;
- new_fdt->close_on_exec = newf->close_on_exec_init;
new_fdt->open_fds = newf->open_fds_init;

restart:
@@ -347,6 +342,9 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
spin_unlock(&oldf->file_lock);
goto out;
}
+
+ if (idr_tag_get(&oldf->fd_idr, i, FD_TAG_CLOEXEC))
+ idr_tag_set(&newf->fd_idr, i, FD_TAG_CLOEXEC);
}

spin_unlock(&oldf->file_lock);
@@ -445,7 +443,6 @@ struct files_struct init_files = {
.fdt = &init_files.fdtab,
.fdtab = {
.max_fds = NR_OPEN_DEFAULT,
- .close_on_exec = init_files.close_on_exec_init,
.open_fds = init_files.open_fds_init,
},
.file_lock = __SPIN_LOCK_UNLOCKED(init_files.file_lock),
@@ -482,9 +479,9 @@ int __alloc_fd(struct files_struct *files,
fdt = files_fdtable(files);
__set_open_fd(fd, fdt);
if (flags & O_CLOEXEC)
- __set_close_on_exec(fd, fdt);
+ __set_close_on_exec(fd, files);
else
- __clear_close_on_exec(fd, fdt);
+ __clear_close_on_exec(fd, files);
error = fd;

out:
@@ -571,7 +568,7 @@ int __close_fd(struct files_struct *files, unsigned fd)
file = idr_remove(&files->fd_idr, fd);
if (!file)
goto out_unlock;
- __clear_close_on_exec(fd, fdt);
+ __clear_close_on_exec(fd, files);
__put_unused_fd(files, fd);
spin_unlock(&files->file_lock);
return filp_close(file, files);
@@ -583,35 +580,19 @@ int __close_fd(struct files_struct *files, unsigned fd)

void do_close_on_exec(struct files_struct *files)
{
- unsigned i;
- struct fdtable *fdt;
+ struct file *file;
+ unsigned int fd;

/* exec unshares first */
spin_lock(&files->file_lock);
- for (i = 0; ; i++) {
- unsigned long set;
- unsigned fd = i * BITS_PER_LONG;
- fdt = files_fdtable(files);
- if (fd >= fdt->max_fds)
- break;
- set = fdt->close_on_exec[i];
- if (!set)
- continue;
- fdt->close_on_exec[i] = 0;
- for ( ; set ; fd++, set >>= 1) {
- struct file *file;
- if (!(set & 1))
- continue;
- file = idr_remove(&files->fd_idr, fd);
- if (!file)
- continue;
- __put_unused_fd(files, fd);
- spin_unlock(&files->file_lock);
- filp_close(file, files);
- cond_resched();
- spin_lock(&files->file_lock);
- }

+ idr_for_each_entry_tagged(&files->fd_idr, file, fd, FD_TAG_CLOEXEC) {
+ idr_remove(&files->fd_idr, fd);
+ __put_unused_fd(files, fd);
+ spin_unlock(&files->file_lock);
+ filp_close(file, files);
+ cond_resched();
+ spin_lock(&files->file_lock);
}
spin_unlock(&files->file_lock);
}
@@ -723,28 +704,14 @@ void __f_unlock_pos(struct file *f)
void set_close_on_exec(unsigned int fd, int flag)
{
struct files_struct *files = current->files;
- struct fdtable *fdt;
spin_lock(&files->file_lock);
- fdt = files_fdtable(files);
if (flag)
- __set_close_on_exec(fd, fdt);
+ __set_close_on_exec(fd, files);
else
- __clear_close_on_exec(fd, fdt);
+ __clear_close_on_exec(fd, files);
spin_unlock(&files->file_lock);
}

-bool get_close_on_exec(unsigned int fd)
-{
- struct files_struct *files = current->files;
- struct fdtable *fdt;
- bool res;
- rcu_read_lock();
- fdt = files_fdtable(files);
- res = close_on_exec(fd, fdt);
- rcu_read_unlock();
- return res;
-}
-
static int do_dup2(struct files_struct *files,
struct file *file, unsigned fd, unsigned flags)
__releases(&files->file_lock)
@@ -783,9 +750,9 @@ static int do_dup2(struct files_struct *files,
}
__set_open_fd(fd, fdt);
if (flags & O_CLOEXEC)
- __set_close_on_exec(fd, fdt);
+ __set_close_on_exec(fd, files);
else
- __clear_close_on_exec(fd, fdt);
+ __clear_close_on_exec(fd, files);
spin_unlock(&files->file_lock);
idr_preload_end();

diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index c330495..2735ccc 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -36,10 +36,8 @@ static int seq_show(struct seq_file *m, void *v)
spin_lock(&files->file_lock);
file = fcheck_files(files, fd);
if (file) {
- struct fdtable *fdt = files_fdtable(files);
-
f_flags = file->f_flags;
- if (close_on_exec(fd, fdt))
+ if (close_on_exec(fd, files))
f_flags |= O_CLOEXEC;

get_file(file);
diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h
index 67259f4..7f1ab82 100644
--- a/include/linux/fdtable.h
+++ b/include/linux/fdtable.h
@@ -22,18 +22,14 @@
*/
#define NR_OPEN_DEFAULT BITS_PER_LONG

+#define FD_TAG_CLOEXEC 1
+
struct fdtable {
unsigned int max_fds;
- unsigned long *close_on_exec;
unsigned long *open_fds;
struct rcu_head rcu;
};

-static inline bool close_on_exec(unsigned int fd, const struct fdtable *fdt)
-{
- return test_bit(fd, fdt->close_on_exec);
-}
-
/*
* Open file table structure
*/
@@ -52,7 +48,6 @@ struct files_struct {
* written part on a separate cache line in SMP
*/
spinlock_t file_lock ____cacheline_aligned_in_smp;
- unsigned long close_on_exec_init[1];
unsigned long open_fds_init[1];
};

@@ -82,6 +77,16 @@ static inline struct file *fcheck_files(struct files_struct *files, unsigned int
return __fcheck_files(files, fd);
}

+static inline bool close_on_exec(unsigned int fd, struct files_struct *files)
+{
+ bool res;
+
+ rcu_read_lock();
+ res = idr_tag_get(&files->fd_idr, fd, FD_TAG_CLOEXEC);
+ rcu_read_unlock();
+ return res;
+}
+
/*
* Check whether the specified fd has an open file.
*/
diff --git a/include/linux/file.h b/include/linux/file.h
index 61eb82c..1856bbf 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -76,7 +76,6 @@ static inline void fdput_pos(struct fd f)
extern int f_dupfd(unsigned int from, struct file *file, unsigned flags);
extern int replace_fd(unsigned fd, struct file *file, unsigned flags);
extern void set_close_on_exec(unsigned int fd, int flag);
-extern bool get_close_on_exec(unsigned int fd);
extern void put_filp(struct file *);
extern int get_unused_fd_flags(unsigned flags);
extern void put_unused_fd(unsigned int fd);
--
1.8.3.1