#include #include #include #include #include #include #include #define RH_FLAG_NAME_LEN 32 #define MAX_RH_FLAGS 128 #define MAX_RH_FLAG_NAME_LEN (MAX_RH_FLAGS * RH_FLAG_NAME_LEN) struct rh_flag { struct list_head list; char name[RH_FLAG_NAME_LEN]; }; static LIST_HEAD(rh_flag_list); static DEFINE_SPINLOCK(rh_flag_lock); bool __rh_add_flag(const char *flag_name) { struct rh_flag *feat, *iter; BUG_ON(in_interrupt()); feat = kzalloc(sizeof(*feat), GFP_ATOMIC); if (WARN(!feat, "Adding Red Hat flag %s.\n", flag_name)) return false; strscpy(feat->name, flag_name, RH_FLAG_NAME_LEN); spin_lock(&rh_flag_lock); list_for_each_entry_rcu(iter, &rh_flag_list, list) { if (!strcmp(iter->name, flag_name)) { kfree(feat); feat = NULL; break; } } if (feat) list_add_rcu(&feat->list, &rh_flag_list); spin_unlock(&rh_flag_lock); if (feat) pr_info("Adding Red Hat flag %s.\n", flag_name); return true; } EXPORT_SYMBOL(__rh_add_flag); void rh_print_flags(void) { struct rh_flag *feat; /* * This function cannot do any locking, we're oopsing. Traversing * rh_flag_list is okay, though, even without the rcu_read_lock * taken: we never delete from that list and thus don't need the * delayed free. All we need are the smp barriers invoked by the rcu * list manipulation routines. */ if (list_empty(&rh_flag_list)) return; printk(KERN_DEFAULT "Red Hat flags:"); list_for_each_entry_lockless(feat, &rh_flag_list, list) { pr_cont(" %s", feat->name); } pr_cont("\n"); } EXPORT_SYMBOL(rh_print_flags); #ifdef CONFIG_SYSCTL static int rh_flags_show(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table tbl = { .maxlen = MAX_RH_FLAG_NAME_LEN, }; struct rh_flag *feat; size_t offs = 0; int ret; tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL); if (!tbl.data) return -ENOMEM; ((char *)tbl.data)[0] = '\0'; rcu_read_lock(); list_for_each_entry_rcu(feat, &rh_flag_list, list) { offs += scnprintf(tbl.data + offs, tbl.maxlen - offs, "%s%s", offs == 0 ? "" : " ", feat->name); } rcu_read_unlock(); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); kfree(tbl.data); return ret; } static struct ctl_table rh_flags_table[] = { { .procname = "rh_flags", .data = &rh_flag_list, .maxlen = MAX_RH_FLAG_NAME_LEN, .mode = 0444, .proc_handler = rh_flags_show, }, { } }; #endif static __init int rh_flags_init(void) { #ifdef CONFIG_SYSCTL register_sysctl_init("kernel", rh_flags_table); #endif return 0; } subsys_initcall(rh_flags_init);