Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

proc: decouple proc from VFS with "struct proc_ops"

Currently core /proc code uses "struct file_operations" for custom hooks,
however, VFS doesn't directly call them. Every time VFS expands
file_operations hook set, /proc code bloats for no reason.

Introduce "struct proc_ops" which contains only those hooks which /proc
allows to call into (open, release, read, write, ioctl, mmap, poll). It
doesn't contain module pointer as well.

Save ~184 bytes per usage:

add/remove: 26/26 grow/shrink: 1/4 up/down: 1922/-6674 (-4752)
Function old new delta
sysvipc_proc_ops - 72 +72
...
config_gz_proc_ops - 72 +72
proc_get_inode 289 339 +50
proc_reg_get_unmapped_area 110 107 -3
close_pdeo 227 224 -3
proc_reg_open 289 284 -5
proc_create_data 60 53 -7
rt_cpu_seq_fops 256 - -256
...
default_affinity_proc_fops 256 - -256
Total: Before=5430095, After=5425343, chg -0.09%

Link: http://lkml.kernel.org/r/20191225172228.GA13378@avx2
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Alexey Dobriyan and committed by
Linus Torvalds
d56c0d45 0d6e24d4

+98 -80
+18 -20
fs/proc/generic.c
··· 473 473 ent = __proc_create(&parent, name, S_IFDIR | mode, 2); 474 474 if (ent) { 475 475 ent->data = data; 476 - ent->proc_fops = &proc_dir_operations; 476 + ent->proc_dir_ops = &proc_dir_operations; 477 477 ent->proc_iops = &proc_dir_inode_operations; 478 478 ent = proc_register(parent, ent); 479 479 } ··· 503 503 ent = __proc_create(&parent, name, mode, 2); 504 504 if (ent) { 505 505 ent->data = NULL; 506 - ent->proc_fops = NULL; 506 + ent->proc_dir_ops = NULL; 507 507 ent->proc_iops = NULL; 508 508 ent = proc_register(parent, ent); 509 509 } ··· 533 533 534 534 struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, 535 535 struct proc_dir_entry *parent, 536 - const struct file_operations *proc_fops, void *data) 536 + const struct proc_ops *proc_ops, void *data) 537 537 { 538 538 struct proc_dir_entry *p; 539 - 540 - BUG_ON(proc_fops == NULL); 541 539 542 540 p = proc_create_reg(name, mode, &parent, data); 543 541 if (!p) 544 542 return NULL; 545 - p->proc_fops = proc_fops; 543 + p->proc_ops = proc_ops; 546 544 return proc_register(parent, p); 547 545 } 548 546 EXPORT_SYMBOL(proc_create_data); 549 547 550 548 struct proc_dir_entry *proc_create(const char *name, umode_t mode, 551 549 struct proc_dir_entry *parent, 552 - const struct file_operations *proc_fops) 550 + const struct proc_ops *proc_ops) 553 551 { 554 - return proc_create_data(name, mode, parent, proc_fops, NULL); 552 + return proc_create_data(name, mode, parent, proc_ops, NULL); 555 553 } 556 554 EXPORT_SYMBOL(proc_create); 557 555 ··· 571 573 return seq_release(inode, file); 572 574 } 573 575 574 - static const struct file_operations proc_seq_fops = { 575 - .open = proc_seq_open, 576 - .read = seq_read, 577 - .llseek = seq_lseek, 578 - .release = proc_seq_release, 576 + static const struct proc_ops proc_seq_ops = { 577 + .proc_open = proc_seq_open, 578 + .proc_read = seq_read, 579 + .proc_lseek = seq_lseek, 580 + .proc_release = proc_seq_release, 579 581 }; 580 582 581 583 struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode, ··· 587 589 p = proc_create_reg(name, mode, &parent, data); 588 590 if (!p) 589 591 return NULL; 590 - p->proc_fops = &proc_seq_fops; 592 + p->proc_ops = &proc_seq_ops; 591 593 p->seq_ops = ops; 592 594 p->state_size = state_size; 593 595 return proc_register(parent, p); ··· 601 603 return single_open(file, de->single_show, de->data); 602 604 } 603 605 604 - static const struct file_operations proc_single_fops = { 605 - .open = proc_single_open, 606 - .read = seq_read, 607 - .llseek = seq_lseek, 608 - .release = single_release, 606 + static const struct proc_ops proc_single_ops = { 607 + .proc_open = proc_single_open, 608 + .proc_read = seq_read, 609 + .proc_lseek = seq_lseek, 610 + .proc_release = single_release, 609 611 }; 610 612 611 613 struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode, ··· 617 619 p = proc_create_reg(name, mode, &parent, data); 618 620 if (!p) 619 621 return NULL; 620 - p->proc_fops = &proc_single_fops; 622 + p->proc_ops = &proc_single_ops; 621 623 p->single_show = show; 622 624 return proc_register(parent, p); 623 625 }
+39 -37
fs/proc/inode.c
··· 163 163 pdeo->closing = true; 164 164 spin_unlock(&pde->pde_unload_lock); 165 165 file = pdeo->file; 166 - pde->proc_fops->release(file_inode(file), file); 166 + pde->proc_ops->proc_release(file_inode(file), file); 167 167 spin_lock(&pde->pde_unload_lock); 168 168 /* After ->release. */ 169 169 list_del(&pdeo->lh); ··· 200 200 struct proc_dir_entry *pde = PDE(file_inode(file)); 201 201 loff_t rv = -EINVAL; 202 202 if (use_pde(pde)) { 203 - typeof_member(struct file_operations, llseek) llseek; 203 + typeof_member(struct proc_ops, proc_lseek) lseek; 204 204 205 - llseek = pde->proc_fops->llseek; 206 - if (!llseek) 207 - llseek = default_llseek; 208 - rv = llseek(file, offset, whence); 205 + lseek = pde->proc_ops->proc_lseek; 206 + if (!lseek) 207 + lseek = default_llseek; 208 + rv = lseek(file, offset, whence); 209 209 unuse_pde(pde); 210 210 } 211 211 return rv; ··· 216 216 struct proc_dir_entry *pde = PDE(file_inode(file)); 217 217 ssize_t rv = -EIO; 218 218 if (use_pde(pde)) { 219 - typeof_member(struct file_operations, read) read; 219 + typeof_member(struct proc_ops, proc_read) read; 220 220 221 - read = pde->proc_fops->read; 221 + read = pde->proc_ops->proc_read; 222 222 if (read) 223 223 rv = read(file, buf, count, ppos); 224 224 unuse_pde(pde); ··· 231 231 struct proc_dir_entry *pde = PDE(file_inode(file)); 232 232 ssize_t rv = -EIO; 233 233 if (use_pde(pde)) { 234 - typeof_member(struct file_operations, write) write; 234 + typeof_member(struct proc_ops, proc_write) write; 235 235 236 - write = pde->proc_fops->write; 236 + write = pde->proc_ops->proc_write; 237 237 if (write) 238 238 rv = write(file, buf, count, ppos); 239 239 unuse_pde(pde); ··· 246 246 struct proc_dir_entry *pde = PDE(file_inode(file)); 247 247 __poll_t rv = DEFAULT_POLLMASK; 248 248 if (use_pde(pde)) { 249 - typeof_member(struct file_operations, poll) poll; 249 + typeof_member(struct proc_ops, proc_poll) poll; 250 250 251 - poll = pde->proc_fops->poll; 251 + poll = pde->proc_ops->proc_poll; 252 252 if (poll) 253 253 rv = poll(file, pts); 254 254 unuse_pde(pde); ··· 261 261 struct proc_dir_entry *pde = PDE(file_inode(file)); 262 262 long rv = -ENOTTY; 263 263 if (use_pde(pde)) { 264 - typeof_member(struct file_operations, unlocked_ioctl) ioctl; 264 + typeof_member(struct proc_ops, proc_ioctl) ioctl; 265 265 266 - ioctl = pde->proc_fops->unlocked_ioctl; 266 + ioctl = pde->proc_ops->proc_ioctl; 267 267 if (ioctl) 268 268 rv = ioctl(file, cmd, arg); 269 269 unuse_pde(pde); ··· 277 277 struct proc_dir_entry *pde = PDE(file_inode(file)); 278 278 long rv = -ENOTTY; 279 279 if (use_pde(pde)) { 280 - typeof_member(struct file_operations, compat_ioctl) compat_ioctl; 280 + typeof_member(struct proc_ops, proc_compat_ioctl) compat_ioctl; 281 281 282 - compat_ioctl = pde->proc_fops->compat_ioctl; 282 + compat_ioctl = pde->proc_ops->proc_compat_ioctl; 283 283 if (compat_ioctl) 284 284 rv = compat_ioctl(file, cmd, arg); 285 285 unuse_pde(pde); ··· 293 293 struct proc_dir_entry *pde = PDE(file_inode(file)); 294 294 int rv = -EIO; 295 295 if (use_pde(pde)) { 296 - typeof_member(struct file_operations, mmap) mmap; 296 + typeof_member(struct proc_ops, proc_mmap) mmap; 297 297 298 - mmap = pde->proc_fops->mmap; 298 + mmap = pde->proc_ops->proc_mmap; 299 299 if (mmap) 300 300 rv = mmap(file, vma); 301 301 unuse_pde(pde); ··· 312 312 unsigned long rv = -EIO; 313 313 314 314 if (use_pde(pde)) { 315 - typeof_member(struct file_operations, get_unmapped_area) get_area; 315 + typeof_member(struct proc_ops, proc_get_unmapped_area) get_area; 316 316 317 - get_area = pde->proc_fops->get_unmapped_area; 317 + get_area = pde->proc_ops->proc_get_unmapped_area; 318 318 #ifdef CONFIG_MMU 319 319 if (!get_area) 320 320 get_area = current->mm->get_unmapped_area; ··· 333 333 { 334 334 struct proc_dir_entry *pde = PDE(inode); 335 335 int rv = 0; 336 - typeof_member(struct file_operations, open) open; 337 - typeof_member(struct file_operations, release) release; 336 + typeof_member(struct proc_ops, proc_open) open; 337 + typeof_member(struct proc_ops, proc_release) release; 338 338 struct pde_opener *pdeo; 339 339 340 340 /* ··· 351 351 if (!use_pde(pde)) 352 352 return -ENOENT; 353 353 354 - release = pde->proc_fops->release; 354 + release = pde->proc_ops->proc_release; 355 355 if (release) { 356 356 pdeo = kmem_cache_alloc(pde_opener_cache, GFP_KERNEL); 357 357 if (!pdeo) { ··· 360 360 } 361 361 } 362 362 363 - open = pde->proc_fops->open; 363 + open = pde->proc_ops->proc_open; 364 364 if (open) 365 365 rv = open(inode, file); 366 366 ··· 468 468 inode->i_size = de->size; 469 469 if (de->nlink) 470 470 set_nlink(inode, de->nlink); 471 - WARN_ON(!de->proc_iops); 472 - inode->i_op = de->proc_iops; 473 - if (de->proc_fops) { 474 - if (S_ISREG(inode->i_mode)) { 471 + 472 + if (S_ISREG(inode->i_mode)) { 473 + inode->i_op = de->proc_iops; 474 + inode->i_fop = &proc_reg_file_ops; 475 475 #ifdef CONFIG_COMPAT 476 - if (!de->proc_fops->compat_ioctl) 477 - inode->i_fop = 478 - &proc_reg_file_ops_no_compat; 479 - else 480 - #endif 481 - inode->i_fop = &proc_reg_file_ops; 482 - } else { 483 - inode->i_fop = de->proc_fops; 476 + if (!de->proc_ops->proc_compat_ioctl) { 477 + inode->i_fop = &proc_reg_file_ops_no_compat; 484 478 } 485 - } 479 + #endif 480 + } else if (S_ISDIR(inode->i_mode)) { 481 + inode->i_op = de->proc_iops; 482 + inode->i_fop = de->proc_dir_ops; 483 + } else if (S_ISLNK(inode->i_mode)) { 484 + inode->i_op = de->proc_iops; 485 + inode->i_fop = NULL; 486 + } else 487 + BUG(); 486 488 } else 487 489 pde_put(de); 488 490 return inode;
+4 -1
fs/proc/internal.h
··· 39 39 spinlock_t pde_unload_lock; 40 40 struct completion *pde_unload_completion; 41 41 const struct inode_operations *proc_iops; 42 - const struct file_operations *proc_fops; 42 + union { 43 + const struct proc_ops *proc_ops; 44 + const struct file_operations *proc_dir_ops; 45 + }; 43 46 const struct dentry_operations *proc_dops; 44 47 union { 45 48 const struct seq_operations *seq_ops;
+16 -16
fs/proc/proc_net.c
··· 90 90 return 0; 91 91 } 92 92 93 - static const struct file_operations proc_net_seq_fops = { 94 - .open = seq_open_net, 95 - .read = seq_read, 96 - .write = proc_simple_write, 97 - .llseek = seq_lseek, 98 - .release = seq_release_net, 93 + static const struct proc_ops proc_net_seq_ops = { 94 + .proc_open = seq_open_net, 95 + .proc_read = seq_read, 96 + .proc_write = proc_simple_write, 97 + .proc_lseek = seq_lseek, 98 + .proc_release = seq_release_net, 99 99 }; 100 100 101 101 struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode, ··· 108 108 if (!p) 109 109 return NULL; 110 110 pde_force_lookup(p); 111 - p->proc_fops = &proc_net_seq_fops; 111 + p->proc_ops = &proc_net_seq_ops; 112 112 p->seq_ops = ops; 113 113 p->state_size = state_size; 114 114 return proc_register(parent, p); ··· 152 152 if (!p) 153 153 return NULL; 154 154 pde_force_lookup(p); 155 - p->proc_fops = &proc_net_seq_fops; 155 + p->proc_ops = &proc_net_seq_ops; 156 156 p->seq_ops = ops; 157 157 p->state_size = state_size; 158 158 p->write = write; ··· 183 183 return single_release(ino, f); 184 184 } 185 185 186 - static const struct file_operations proc_net_single_fops = { 187 - .open = single_open_net, 188 - .read = seq_read, 189 - .write = proc_simple_write, 190 - .llseek = seq_lseek, 191 - .release = single_release_net, 186 + static const struct proc_ops proc_net_single_ops = { 187 + .proc_open = single_open_net, 188 + .proc_read = seq_read, 189 + .proc_write = proc_simple_write, 190 + .proc_lseek = seq_lseek, 191 + .proc_release = single_release_net, 192 192 }; 193 193 194 194 struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode, ··· 201 201 if (!p) 202 202 return NULL; 203 203 pde_force_lookup(p); 204 - p->proc_fops = &proc_net_single_fops; 204 + p->proc_ops = &proc_net_single_ops; 205 205 p->single_show = show; 206 206 return proc_register(parent, p); 207 207 } ··· 244 244 if (!p) 245 245 return NULL; 246 246 pde_force_lookup(p); 247 - p->proc_fops = &proc_net_single_fops; 247 + p->proc_ops = &proc_net_single_ops; 248 248 p->single_show = show; 249 249 p->write = write; 250 250 return proc_register(parent, p);
+1 -1
fs/proc/proc_sysctl.c
··· 1720 1720 1721 1721 proc_sys_root = proc_mkdir("sys", NULL); 1722 1722 proc_sys_root->proc_iops = &proc_sys_dir_operations; 1723 - proc_sys_root->proc_fops = &proc_sys_dir_file_operations; 1723 + proc_sys_root->proc_dir_ops = &proc_sys_dir_file_operations; 1724 1724 proc_sys_root->nlink = 0; 1725 1725 1726 1726 return sysctl_init();
+1 -1
fs/proc/root.c
··· 292 292 .nlink = 2, 293 293 .refcnt = REFCOUNT_INIT(1), 294 294 .proc_iops = &proc_root_inode_operations, 295 - .proc_fops = &proc_root_operations, 295 + .proc_dir_ops = &proc_root_operations, 296 296 .parent = &proc_root, 297 297 .subdir = RB_ROOT, 298 298 .name = "/proc",
+19 -4
include/linux/proc_fs.h
··· 12 12 struct seq_file; 13 13 struct seq_operations; 14 14 15 + struct proc_ops { 16 + int (*proc_open)(struct inode *, struct file *); 17 + ssize_t (*proc_read)(struct file *, char __user *, size_t, loff_t *); 18 + ssize_t (*proc_write)(struct file *, const char __user *, size_t, loff_t *); 19 + loff_t (*proc_lseek)(struct file *, loff_t, int); 20 + int (*proc_release)(struct inode *, struct file *); 21 + __poll_t (*proc_poll)(struct file *, struct poll_table_struct *); 22 + long (*proc_ioctl)(struct file *, unsigned int, unsigned long); 23 + #ifdef CONFIG_COMPAT 24 + long (*proc_compat_ioctl)(struct file *, unsigned int, unsigned long); 25 + #endif 26 + int (*proc_mmap)(struct file *, struct vm_area_struct *); 27 + unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); 28 + }; 29 + 15 30 #ifdef CONFIG_PROC_FS 16 31 17 32 typedef int (*proc_write_t)(struct file *, char *, size_t); ··· 58 43 59 44 extern struct proc_dir_entry *proc_create_data(const char *, umode_t, 60 45 struct proc_dir_entry *, 61 - const struct file_operations *, 46 + const struct proc_ops *, 62 47 void *); 63 48 64 - struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops); 49 + struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct proc_ops *proc_ops); 65 50 extern void proc_set_size(struct proc_dir_entry *, loff_t); 66 51 extern void proc_set_user(struct proc_dir_entry *, kuid_t, kgid_t); 67 52 extern void *PDE_DATA(const struct inode *); ··· 123 108 #define proc_create_seq(name, mode, parent, ops) ({NULL;}) 124 109 #define proc_create_single(name, mode, parent, show) ({NULL;}) 125 110 #define proc_create_single_data(name, mode, parent, show, data) ({NULL;}) 126 - #define proc_create(name, mode, parent, proc_fops) ({NULL;}) 127 - #define proc_create_data(name, mode, parent, proc_fops, data) ({NULL;}) 111 + #define proc_create(name, mode, parent, proc_ops) ({NULL;}) 112 + #define proc_create_data(name, mode, parent, proc_ops, data) ({NULL;}) 128 113 129 114 static inline void proc_set_size(struct proc_dir_entry *de, loff_t size) {} 130 115 static inline void proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid) {}