History

proc: faster open/read/close with "permanent" files

Now that "struct proc_ops" exist we can start putting there stuff which could not fly with VFS "struct file_operations"... Most of fs/proc/inode.c file is dedicated to make open/read/.../close reliable in the event of disappearing /proc entries which usually happens if module is getting removed. Files like /proc/cpuinfo which never disappear simply do not need such protection. Save 2 atomic ops, 1 allocation, 1 free per open/read/close sequence for such "permanent" files. Enable "permanent" flag for /proc/cpuinfo /proc/kmsg /proc/modules /proc/slabinfo /proc/stat /proc/sysvipc/* /proc/swaps More will come once I figure out foolproof way to prevent out module authors from marking their stuff "permanent" for performance reasons when it is not. This should help with scalability: benchmark is "read /proc/cpuinfo R times by N threads scattered over the system". N R t, s (before) t, s (after) ----------------------------------------------------- 64 4096 1.582458 1.530502 -3.2% 256 4096 6.371926 6.125168 -3.9% 1024 4096 25.64888 24.47528 -4.6% Benchmark source: #include <chrono> #include <iostream> #include <thread> #include <vector> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> const int NR_CPUS = sysconf(_SC_NPROCESSORS_ONLN); int N; const char *filename; int R; int xxx = 0; int glue(int n) { cpu_set_t m; CPU_ZERO(&m); CPU_SET(n, &m); return sched_setaffinity(0, sizeof(cpu_set_t), &m); } void f(int n) { glue(n % NR_CPUS); while (*(volatile int *)&xxx == 0) { } for (int i = 0; i < R; i++) { int fd = open(filename, O_RDONLY); char buf[4096]; ssize_t rv = read(fd, buf, sizeof(buf)); asm volatile ("" :: "g" (rv)); close(fd); } } int main(int argc, char *argv[]) { if (argc < 4) { std::cerr << "usage: " << argv[0] << ' ' << "N /proc/filename R "; return 1; } N = atoi(argv[1]); filename = argv[2]; R = atoi(argv[3]); for (int i = 0; i < NR_CPUS; i++) { if (glue(i) == 0) break; } std::vector<std::thread> T; T.reserve(N); for (int i = 0; i < N; i++) { T.emplace_back(f, i); } auto t0 = std::chrono::system_clock::now(); { *(volatile int *)&xxx = 1; for (auto& t: T) { t.join(); } } auto t1 = std::chrono::system_clock::now(); std::chrono::duration<double> dt = t1 - t0; std::cout << dt.count() << ' '; return 0; } P.S.: Explicit randomization marker is added because adding non-function pointer will silently disable structure layout randomization. [akpm@linux-foundation.org: coding style fixes] Reported-by: kbuild test robot <lkp@intel.com> Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Joe Perches <joe@perches.com> Link: http://lkml.kernel.org/r/20200222201539.GA22576@avx2 Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Alexey Dobriyan committed 6 years ago

Commit d919b331 parent 904f39411 files · +194 −54Browse files

Unified · Split

11 files
  1. fs/proc/cpuinfo.c +1 −0
  2. fs/proc/generic.c +28 −3
  3. fs/proc/inode.c +137 −50
  4. fs/proc/internal.h +6 −0
  5. fs/proc/kmsg.c +1 −0
  6. fs/proc/stat.c +1 −0
  7. include/linux/proc_fs.h +16 −1
  8. ipc/util.c +1 −0
  9. kernel/module.c +1 −0
  10. mm/slab_common.c +1 −0
  11. mm/swapfile.c +1 −0
fs/proc/cpuinfo.c+1−0
@@ -17,6 +17,7 @@ static int cpuinfo_open(struct inode *inode, struct file *file) · 16 unmodified lines
1717 }
1818
1919 static const struct proc_ops cpuinfo_proc_ops = {
20+ .proc_flags = PROC_ENTRY_PERMANENT,
2021 .proc_open = cpuinfo_open,
2122 .proc_read = seq_read,
2223 .proc_lseek = seq_lseek,
fs/proc/generic.c+28−3
@@ -531,6 +531,12 @@ struct proc_dir_entry *proc_create_reg(const char *name, umode_t mode, · 530 unmodified lines
531531 return p;
532532 }
533533
534+static inline void pde_set_flags(struct proc_dir_entry *pde)
535+{
536+ if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
537+ pde->flags |= PROC_ENTRY_PERMANENT;
538+}
539+
534540 struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
535541 struct proc_dir_entry *parent,
536542 const struct proc_ops *proc_ops, void *data)
@@ -541,6 +547,7 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, · 4 unmodified lines
541547 if (!p)
542548 return NULL;
543549 p->proc_ops = proc_ops;
550+ pde_set_flags(p);
544551 return proc_register(parent, p);
545552 }
546553 EXPORT_SYMBOL(proc_create_data);
@@ -572,6 +579,7 @@ static int proc_seq_release(struct inode *inode, struct file *file) · 25 unmodified lines
572579 }
573580
574581 static const struct proc_ops proc_seq_ops = {
582+ /* not permanent -- can call into arbitrary seq_operations */
575583 .proc_open = proc_seq_open,
576584 .proc_read = seq_read,
577585 .proc_lseek = seq_lseek,
@@ -602,6 +610,7 @@ static int proc_single_open(struct inode *inode, struct file *file) · 24 unmodified lines
602610 }
603611
604612 static const struct proc_ops proc_single_ops = {
613+ /* not permanent -- can call into arbitrary ->single_show */
605614 .proc_open = proc_single_open,
606615 .proc_read = seq_read,
607616 .proc_lseek = seq_lseek,
@@ -662,9 +671,13 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) · 54 unmodified lines
662671
663672 de = pde_subdir_find(parent, fn, len);
664673 if (de) {
665− rb_erase(&de->subdir_node, &parent->subdir);
666− if (S_ISDIR(de->mode)) {
667− parent->nlink--;
674+ if (unlikely(pde_is_permanent(de))) {
675+ WARN(1, "removing permanent /proc entry '%s'", de->name);
676+ de = NULL;
677+ } else {
678+ rb_erase(&de->subdir_node, &parent->subdir);
679+ if (S_ISDIR(de->mode))
680+ parent->nlink--;
668681 }
669682 }
670683 write_unlock(&proc_subdir_lock);
@@ -700,12 +713,24 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) · 29 unmodified lines
700713 write_unlock(&proc_subdir_lock);
701714 return -ENOENT;
702715 }
716+ if (unlikely(pde_is_permanent(root))) {
717+ write_unlock(&proc_subdir_lock);
718+ WARN(1, "removing permanent /proc entry '%s/%s'",
719+ root->parent->name, root->name);
720+ return -EINVAL;
721+ }
703722 rb_erase(&root->subdir_node, &parent->subdir);
704723
705724 de = root;
706725 while (1) {
707726 next = pde_subdir_first(de);
708727 if (next) {
728+ if (unlikely(pde_is_permanent(root))) {
729+ write_unlock(&proc_subdir_lock);
730+ WARN(1, "removing permanent /proc entry '%s/%s'",
731+ next->parent->name, next->name);
732+ return -EINVAL;
733+ }
709734 rb_erase(&next->subdir_node, &de->subdir);
710735 de = next;
711736 continue;
fs/proc/inode.c+137−50
@@ -259,135 +259,204 @@ void proc_entry_rundown(struct proc_dir_entry *de) · 258 unmodified lines
259259 spin_unlock(&de->pde_unload_lock);
260260 }
261261
262+static loff_t pde_lseek(struct proc_dir_entry *pde, struct file *file, loff_t offset, int whence)
263+{
264+ typeof_member(struct proc_ops, proc_lseek) lseek;
265+
266+ lseek = pde->proc_ops->proc_lseek;
267+ if (!lseek)
268+ lseek = default_llseek;
269+ return lseek(file, offset, whence);
270+}
271+
262272 static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence)
263273 {
264274 struct proc_dir_entry *pde = PDE(file_inode(file));
265275 loff_t rv = -EINVAL;
266− if (use_pde(pde)) {
267− typeof_member(struct proc_ops, proc_lseek) lseek;
268276
269− lseek = pde->proc_ops->proc_lseek;
270− if (!lseek)
271− lseek = default_llseek;
272− rv = lseek(file, offset, whence);
277+ if (pde_is_permanent(pde)) {
278+ return pde_lseek(pde, file, offset, whence);
279+ } else if (use_pde(pde)) {
280+ rv = pde_lseek(pde, file, offset, whence);
273281 unuse_pde(pde);
274282 }
275283 return rv;
276284 }
277285
286+static ssize_t pde_read(struct proc_dir_entry *pde, struct file *file, char __user *buf, size_t count, loff_t *ppos)
287+{
288+ typeof_member(struct proc_ops, proc_read) read;
289+
290+ read = pde->proc_ops->proc_read;
291+ if (read)
292+ return read(file, buf, count, ppos);
293+ return -EIO;
294+}
295+
278296 static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
279297 {
280298 struct proc_dir_entry *pde = PDE(file_inode(file));
281299 ssize_t rv = -EIO;
282− if (use_pde(pde)) {
283− typeof_member(struct proc_ops, proc_read) read;
284300
285− read = pde->proc_ops->proc_read;
286− if (read)
287− rv = read(file, buf, count, ppos);
301+ if (pde_is_permanent(pde)) {
302+ return pde_read(pde, file, buf, count, ppos);
303+ } else if (use_pde(pde)) {
304+ rv = pde_read(pde, file, buf, count, ppos);
288305 unuse_pde(pde);
289306 }
290307 return rv;
291308 }
292309
310+static ssize_t pde_write(struct proc_dir_entry *pde, struct file *file, const char __user *buf, size_t count, loff_t *ppos)
311+{
312+ typeof_member(struct proc_ops, proc_write) write;
313+
314+ write = pde->proc_ops->proc_write;
315+ if (write)
316+ return write(file, buf, count, ppos);
317+ return -EIO;
318+}
319+
293320 static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
294321 {
295322 struct proc_dir_entry *pde = PDE(file_inode(file));
296323 ssize_t rv = -EIO;
297− if (use_pde(pde)) {
298− typeof_member(struct proc_ops, proc_write) write;
299324
300− write = pde->proc_ops->proc_write;
301− if (write)
302− rv = write(file, buf, count, ppos);
325+ if (pde_is_permanent(pde)) {
326+ return pde_write(pde, file, buf, count, ppos);
327+ } else if (use_pde(pde)) {
328+ rv = pde_write(pde, file, buf, count, ppos);
303329 unuse_pde(pde);
304330 }
305331 return rv;
306332 }
307333
334+static __poll_t pde_poll(struct proc_dir_entry *pde, struct file *file, struct poll_table_struct *pts)
335+{
336+ typeof_member(struct proc_ops, proc_poll) poll;
337+
338+ poll = pde->proc_ops->proc_poll;
339+ if (poll)
340+ return poll(file, pts);
341+ return DEFAULT_POLLMASK;
342+}
343+
308344 static __poll_t proc_reg_poll(struct file *file, struct poll_table_struct *pts)
309345 {
310346 struct proc_dir_entry *pde = PDE(file_inode(file));
311347 __poll_t rv = DEFAULT_POLLMASK;
312− if (use_pde(pde)) {
313− typeof_member(struct proc_ops, proc_poll) poll;
314348
315− poll = pde->proc_ops->proc_poll;
316− if (poll)
317− rv = poll(file, pts);
349+ if (pde_is_permanent(pde)) {
350+ return pde_poll(pde, file, pts);
351+ } else if (use_pde(pde)) {
352+ rv = pde_poll(pde, file, pts);
318353 unuse_pde(pde);
319354 }
320355 return rv;
321356 }
322357
358+static long pde_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg)
359+{
360+ typeof_member(struct proc_ops, proc_ioctl) ioctl;
361+
362+ ioctl = pde->proc_ops->proc_ioctl;
363+ if (ioctl)
364+ return ioctl(file, cmd, arg);
365+ return -ENOTTY;
366+}
367+
323368 static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
324369 {
325370 struct proc_dir_entry *pde = PDE(file_inode(file));
326371 long rv = -ENOTTY;
327− if (use_pde(pde)) {
328− typeof_member(struct proc_ops, proc_ioctl) ioctl;
329372
330− ioctl = pde->proc_ops->proc_ioctl;
331− if (ioctl)
332− rv = ioctl(file, cmd, arg);
373+ if (pde_is_permanent(pde)) {
374+ return pde_ioctl(pde, file, cmd, arg);
375+ } else if (use_pde(pde)) {
376+ rv = pde_ioctl(pde, file, cmd, arg);
333377 unuse_pde(pde);
334378 }
335379 return rv;
336380 }
337381
338382 #ifdef CONFIG_COMPAT
383+static long pde_compat_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg)
384+{
385+ typeof_member(struct proc_ops, proc_compat_ioctl) compat_ioctl;
386+
387+ compat_ioctl = pde->proc_ops->proc_compat_ioctl;
388+ if (compat_ioctl)
389+ return compat_ioctl(file, cmd, arg);
390+ return -ENOTTY;
391+}
392+
339393 static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
340394 {
341395 struct proc_dir_entry *pde = PDE(file_inode(file));
342396 long rv = -ENOTTY;
343− if (use_pde(pde)) {
344− typeof_member(struct proc_ops, proc_compat_ioctl) compat_ioctl;
345−
346− compat_ioctl = pde->proc_ops->proc_compat_ioctl;
347− if (compat_ioctl)
348− rv = compat_ioctl(file, cmd, arg);
397+ if (pde_is_permanent(pde)) {
398+ return pde_compat_ioctl(pde, file, cmd, arg);
399+ } else if (use_pde(pde)) {
400+ rv = pde_compat_ioctl(pde, file, cmd, arg);
349401 unuse_pde(pde);
350402 }
351403 return rv;
352404 }
353405 #endif
354406
407+static int pde_mmap(struct proc_dir_entry *pde, struct file *file, struct vm_area_struct *vma)
408+{
409+ typeof_member(struct proc_ops, proc_mmap) mmap;
410+
411+ mmap = pde->proc_ops->proc_mmap;
412+ if (mmap)
413+ return mmap(file, vma);
414+ return -EIO;
415+}
416+
355417 static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma)
356418 {
357419 struct proc_dir_entry *pde = PDE(file_inode(file));
358420 int rv = -EIO;
359− if (use_pde(pde)) {
360− typeof_member(struct proc_ops, proc_mmap) mmap;
361421
362− mmap = pde->proc_ops->proc_mmap;
363− if (mmap)
364− rv = mmap(file, vma);
422+ if (pde_is_permanent(pde)) {
423+ return pde_mmap(pde, file, vma);
424+ } else if (use_pde(pde)) {
425+ rv = pde_mmap(pde, file, vma);
365426 unuse_pde(pde);
366427 }
367428 return rv;
368429 }
369430
370431 static unsigned long
371−proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr,
432+pde_get_unmapped_area(struct proc_dir_entry *pde, struct file *file, unsigned long orig_addr,
372433 unsigned long len, unsigned long pgoff,
373434 unsigned long flags)
374435 {
375− struct proc_dir_entry *pde = PDE(file_inode(file));
376− unsigned long rv = -EIO;
377−
378− if (use_pde(pde)) {
379− typeof_member(struct proc_ops, proc_get_unmapped_area) get_area;
436+ typeof_member(struct proc_ops, proc_get_unmapped_area) get_area;
380437
381− get_area = pde->proc_ops->proc_get_unmapped_area;
438+ get_area = pde->proc_ops->proc_get_unmapped_area;
382439 #ifdef CONFIG_MMU
383− if (!get_area)
384− get_area = current->mm->get_unmapped_area;
440+ if (!get_area)
441+ get_area = current->mm->get_unmapped_area;
385442 #endif
443+ if (get_area)
444+ return get_area(file, orig_addr, len, pgoff, flags);
445+ return orig_addr;
446+}
447+
448+static unsigned long
449+proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr,
450+ unsigned long len, unsigned long pgoff,
451+ unsigned long flags)
452+{
453+ struct proc_dir_entry *pde = PDE(file_inode(file));
454+ unsigned long rv = -EIO;
386455
387− if (get_area)
388− rv = get_area(file, orig_addr, len, pgoff, flags);
389− else
390− rv = orig_addr;
456+ if (pde_is_permanent(pde)) {
457+ return pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags);
458+ } else if (use_pde(pde)) {
459+ rv = pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags);
391460 unuse_pde(pde);
392461 }
393462 return rv;
@@ -401,6 +470,13 @@ static int proc_reg_open(struct inode *inode, struct file *file) · 7 unmodified lines
401470 typeof_member(struct proc_ops, proc_release) release;
402471 struct pde_opener *pdeo;
403472
473+ if (pde_is_permanent(pde)) {
474+ open = pde->proc_ops->proc_open;
475+ if (open)
476+ rv = open(inode, file);
477+ return rv;
478+ }
479+
404480 /*
405481 * Ensure that
406482 * 1) PDE's ->release hook will be called no matter what
@@ -450,6 +526,17 @@ static int proc_reg_release(struct inode *inode, struct file *file) · 43 unmodified lines
450526 {
451527 struct proc_dir_entry *pde = PDE(inode);
452528 struct pde_opener *pdeo;
529+
530+ if (pde_is_permanent(pde)) {
531+ typeof_member(struct proc_ops, proc_release) release;
532+
533+ release = pde->proc_ops->proc_release;
534+ if (release) {
535+ return release(inode, file);
536+ }
537+ return 0;
538+ }
539+
453540 spin_lock(&pde->pde_unload_lock);
454541 list_for_each_entry(pdeo, &pde->pde_openers, lh) {
455542 if (pdeo->file == file) {
fs/proc/internal.h+6−0
@@ -61,6 +61,7 @@ struct proc_dir_entry { · 60 unmodified lines
6161 struct rb_node subdir_node;
6262 char *name;
6363 umode_t mode;
64+ u8 flags;
6465 u8 namelen;
6566 char inline_name[];
6667 } __randomize_layout;
@@ -73,6 +74,11 @@ struct proc_dir_entry { · 6 unmodified lines
7374 0)
7475 #define SIZEOF_PDE_INLINE_NAME (SIZEOF_PDE - sizeof(struct proc_dir_entry))
7576
77+static inline bool pde_is_permanent(const struct proc_dir_entry *pde)
78+{
79+ return pde->flags & PROC_ENTRY_PERMANENT;
80+}
81+
7682 extern struct kmem_cache *proc_dir_entry_cache;
7783 void pde_free(struct proc_dir_entry *pde);
7884
fs/proc/kmsg.c+1−0
@@ -50,6 +50,7 @@ static __poll_t kmsg_poll(struct file *file, poll_table *wait) · 49 unmodified lines
5050
5151
5252 static const struct proc_ops kmsg_proc_ops = {
53+ .proc_flags = PROC_ENTRY_PERMANENT,
5354 .proc_read = kmsg_read,
5455 .proc_poll = kmsg_poll,
5556 .proc_open = kmsg_open,
fs/proc/stat.c+1−0
@@ -224,6 +224,7 @@ static int stat_open(struct inode *inode, struct file *file) · 223 unmodified lines
224224 }
225225
226226 static const struct proc_ops stat_proc_ops = {
227+ .proc_flags = PROC_ENTRY_PERMANENT,
227228 .proc_open = stat_open,
228229 .proc_read = seq_read,
229230 .proc_lseek = seq_lseek,
include/linux/proc_fs.h+16−1
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 #ifndef _LINUX_PROC_FS_H
66 #define _LINUX_PROC_FS_H
77
8+#include <linux/compiler.h>
89 #include <linux/types.h>
910 #include <linux/fs.h>
1011
@@ -12,7 +13,21 @@ struct proc_dir_entry; · 1 unmodified line
1213 struct seq_file;
1314 struct seq_operations;
1415
16+enum {
17+ /*
18+ * All /proc entries using this ->proc_ops instance are never removed.
19+ *
20+ * If in doubt, ignore this flag.
21+ */
22+#ifdef MODULE
23+ PROC_ENTRY_PERMANENT = 0U,
24+#else
25+ PROC_ENTRY_PERMANENT = 1U << 0,
26+#endif
27+};
28+
1529 struct proc_ops {
30+ unsigned int proc_flags;
1631 int (*proc_open)(struct inode *, struct file *);
1732 ssize_t (*proc_read)(struct file *, char __user *, size_t, loff_t *);
1833 ssize_t (*proc_write)(struct file *, const char __user *, size_t, loff_t *);
@@ -25,7 +40,7 @@ struct proc_ops { · 6 unmodified lines
2540 #endif
2641 int (*proc_mmap)(struct file *, struct vm_area_struct *);
2742 unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
28−};
43+} __randomize_layout;
2944
3045 #ifdef CONFIG_PROC_FS
3146
ipc/util.c+1−0
@@ -885,6 +885,7 @@ static int sysvipc_proc_release(struct inode *inode, struct file *file) · 884 unmodified lines
885885 }
886886
887887 static const struct proc_ops sysvipc_proc_ops = {
888+ .proc_flags = PROC_ENTRY_PERMANENT,
888889 .proc_open = sysvipc_proc_open,
889890 .proc_read = seq_read,
890891 .proc_lseek = seq_lseek,
kernel/module.c+1−0
@@ -4355,6 +4355,7 @@ static int modules_open(struct inode *inode, struct file *file) · 4354 unmodified lines
43554355 }
43564356
43574357 static const struct proc_ops modules_proc_ops = {
4358+ .proc_flags = PROC_ENTRY_PERMANENT,
43584359 .proc_open = modules_open,
43594360 .proc_read = seq_read,
43604361 .proc_lseek = seq_lseek,
mm/slab_common.c+1−0
@@ -1581,6 +1581,7 @@ static int slabinfo_open(struct inode *inode, struct file *file) · 1580 unmodified lines
15811581 }
15821582
15831583 static const struct proc_ops slabinfo_proc_ops = {
1584+ .proc_flags = PROC_ENTRY_PERMANENT,
15841585 .proc_open = slabinfo_open,
15851586 .proc_read = seq_read,
15861587 .proc_write = slabinfo_write,
mm/swapfile.c+1−0
@@ -2797,6 +2797,7 @@ static int swaps_open(struct inode *inode, struct file *file) · 2796 unmodified lines
27972797 }
27982798
27992799 static const struct proc_ops swaps_proc_ops = {
2800+ .proc_flags = PROC_ENTRY_PERMANENT,
28002801 .proc_open = swaps_open,
28012802 .proc_read = seq_read,
28022803 .proc_lseek = seq_lseek,