Skip to content

Commit

Permalink
sysctl: replace child with an enumeration
Browse files Browse the repository at this point in the history
This is part of the effort to remove the empty element at the end of
ctl_table structs. "child" was a deprecated elem in this struct and was
being used to differentiate between two types of ctl_tables: "normal"
and "permanently emtpy".

What changed?:
* Replace "child" with an enumeration that will have two values: the
  default (0) and the permanently empty (1). The latter is left at zero
  so when struct ctl_table is created with kzalloc or in a local
  context, it will have the zero value by default. We document the
  new enum with kdoc.
* Remove the "empty child" check from sysctl_check_table
* Remove count_subheaders function as there is no longer a need to
  calculate how many headers there are for every child
* Remove the recursive call to unregister_sysctl_table as there is no
  need to traverse down the child tree any longer
* Add a new SYSCTL_PERM_EMPTY_DIR binary flag
* Remove the last remanence of child from partport/procfs.c

Signed-off-by: Joel Granados <j.granados@samsung.com>
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
  • Loading branch information
Joel Granados authored and Luis Chamberlain committed Jun 18, 2023
1 parent 94a6490 commit 2f2665c
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 66 deletions.
1 change: 0 additions & 1 deletion drivers/parport/procfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,6 @@ parport_device_sysctl_template = {
.data = NULL,
.maxlen = 0,
.mode = 0555,
.child = NULL
},
{}
}
Expand Down
81 changes: 18 additions & 63 deletions fs/proc/proc_sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ static const struct file_operations proc_sys_dir_file_operations;
static const struct inode_operations proc_sys_dir_operations;

/* Support for permanently empty directories */

struct ctl_table sysctl_mount_point[] = {
{ }
{.type = SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY }
};

/**
Expand All @@ -48,21 +47,14 @@ struct ctl_table_header *register_sysctl_mount_point(const char *path)
}
EXPORT_SYMBOL(register_sysctl_mount_point);

static bool is_empty_dir(struct ctl_table_header *head)
{
return head->ctl_table[0].child == sysctl_mount_point;
}

static void set_empty_dir(struct ctl_dir *dir)
{
dir->header.ctl_table[0].child = sysctl_mount_point;
}

static void clear_empty_dir(struct ctl_dir *dir)

{
dir->header.ctl_table[0].child = NULL;
}
#define sysctl_is_perm_empty_ctl_table(tptr) \
(tptr[0].type == SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY)
#define sysctl_is_perm_empty_ctl_header(hptr) \
(sysctl_is_perm_empty_ctl_table(hptr->ctl_table))
#define sysctl_set_perm_empty_ctl_header(hptr) \
(hptr->ctl_table[0].type = SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY)
#define sysctl_clear_perm_empty_ctl_header(hptr) \
(hptr->ctl_table[0].type = SYSCTL_TABLE_TYPE_DEFAULT)

void proc_sys_poll_notify(struct ctl_table_poll *poll)
{
Expand Down Expand Up @@ -230,20 +222,22 @@ static void erase_header(struct ctl_table_header *head)
static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header)
{
struct ctl_table *entry;
struct ctl_table_header *dir_h = &dir->header;
int err;


/* Is this a permanently empty directory? */
if (is_empty_dir(&dir->header))
if (sysctl_is_perm_empty_ctl_header(dir_h))
return -EROFS;

/* Am I creating a permanently empty directory? */
if (header->ctl_table == sysctl_mount_point) {
if (sysctl_is_perm_empty_ctl_table(header->ctl_table)) {
if (!RB_EMPTY_ROOT(&dir->root))
return -EINVAL;
set_empty_dir(dir);
sysctl_set_perm_empty_ctl_header(dir_h);
}

dir->header.nreg++;
dir_h->nreg++;
header->parent = dir;
err = insert_links(header);
if (err)
Expand All @@ -259,9 +253,9 @@ static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header)
put_links(header);
fail_links:
if (header->ctl_table == sysctl_mount_point)
clear_empty_dir(dir);
sysctl_clear_perm_empty_ctl_header(dir_h);
header->parent = NULL;
drop_sysctl_table(&dir->header);
drop_sysctl_table(dir_h);
return err;
}

Expand Down Expand Up @@ -479,7 +473,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
inode->i_mode |= S_IFDIR;
inode->i_op = &proc_sys_dir_operations;
inode->i_fop = &proc_sys_dir_file_operations;
if (is_empty_dir(head))
if (sysctl_is_perm_empty_ctl_header(head))
make_empty_dir_inode(inode);
}

Expand Down Expand Up @@ -1136,9 +1130,6 @@ static int sysctl_check_table(const char *path, struct ctl_table *table)
struct ctl_table *entry;
int err = 0;
list_for_each_table_entry(entry, table) {
if (entry->child)
err |= sysctl_err(path, entry, "Not a file");

if ((entry->proc_handler == proc_dostring) ||
(entry->proc_handler == proc_dobool) ||
(entry->proc_handler == proc_dointvec) ||
Expand Down Expand Up @@ -1465,25 +1456,6 @@ void __init __register_sysctl_init(const char *path, struct ctl_table *table,
kmemleak_not_leak(hdr);
}

static int count_subheaders(struct ctl_table *table)
{
int has_files = 0;
int nr_subheaders = 0;
struct ctl_table *entry;

/* special case: no directory and empty directory */
if (!table || !table->procname)
return 1;

list_for_each_table_entry(entry, table) {
if (entry->child)
nr_subheaders += count_subheaders(entry->child);
else
has_files = 1;
}
return nr_subheaders + has_files;
}

static void put_links(struct ctl_table_header *header)
{
struct ctl_table_set *root_set = &sysctl_table_root.default_set;
Expand Down Expand Up @@ -1546,28 +1518,11 @@ static void drop_sysctl_table(struct ctl_table_header *header)
*/
void unregister_sysctl_table(struct ctl_table_header * header)
{
int nr_subheaders;
might_sleep();

if (header == NULL)
return;

nr_subheaders = count_subheaders(header->ctl_table_arg);
if (unlikely(nr_subheaders > 1)) {
struct ctl_table_header **subheaders;
int i;

subheaders = (struct ctl_table_header **)(header + 1);
for (i = nr_subheaders -1; i >= 0; i--) {
struct ctl_table_header *subh = subheaders[i];
struct ctl_table *table = subh->ctl_table_arg;
unregister_sysctl_table(subh);
kfree(table);
}
kfree(header);
return;
}

spin_lock(&sysctl_lock);
drop_sysctl_table(header);
spin_unlock(&sysctl_lock);
Expand Down
14 changes: 12 additions & 2 deletions include/linux/sysctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,17 @@ struct ctl_table {
void *data;
int maxlen;
umode_t mode;
struct ctl_table *child; /* Deprecated */
/**
* enum type - Enumeration to differentiate between ctl target types
* @SYSCTL_TABLE_TYPE_DEFAULT: ctl target with no special considerations
* @SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY: Used to identify a permanently
* empty directory target to serve
* as mount point.
*/
enum {
SYSCTL_TABLE_TYPE_DEFAULT,
SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY
} type;
proc_handler *proc_handler; /* Callback for text formatting */
struct ctl_table_poll *poll;
void *extra1;
Expand Down Expand Up @@ -229,7 +239,7 @@ extern int unaligned_enabled;
extern int unaligned_dump_stack;
extern int no_unaligned_warning;

extern struct ctl_table sysctl_mount_point[];
#define SYSCTL_PERM_EMPTY_DIR (1 << 0)

#else /* CONFIG_SYSCTL */

Expand Down

0 comments on commit 2f2665c

Please sign in to comment.