861 lines · 20.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* -*- linux-c -*- * sysctl_net_core.c: sysctl interface to net core subsystem. * * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net/core directory entry (empty =) ). [MS] */ #include <linux/filter.h>#include <linux/mm.h>#include <linux/sysctl.h>#include <linux/module.h>#include <linux/socket.h>#include <linux/netdevice.h>#include <linux/ratelimit.h>#include <linux/vmalloc.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/sched/isolation.h>#include <linux/hex.h> #include <net/ip.h>#include <net/sock.h>#include <net/net_ratelimit.h>#include <net/busy_poll.h>#include <net/pkt_sched.h>#include <net/hotdata.h>#include <net/proto_memory.h>#include <net/rps.h> #include "dev.h"#include "net-sysfs.h" static int int_3600 = 3600;static int min_sndbuf = SOCK_MIN_SNDBUF;static int min_rcvbuf = SOCK_MIN_RCVBUF;static int max_skb_frags = MAX_SKB_FRAGS;static int min_mem_pcpu_rsv = SK_MEMORY_PCPU_RESERVE;static int netdev_budget_usecs_min = 2 * USEC_PER_SEC / HZ; static int net_msg_warn;	/* Unused, but still a sysctl */ int sysctl_fb_tunnels_only_for_init_net __read_mostly = 0;EXPORT_SYMBOL(sysctl_fb_tunnels_only_for_init_net); /* 0 - Keep current behavior: *     IPv4: inherit all current settings from init_net *     IPv6: reset all settings to default * 1 - Both inherit all current settings from init_net * 2 - Both reset all settings to default * 3 - Both inherit all settings from current netns */int sysctl_devconf_inherit_init_net __read_mostly;EXPORT_SYMBOL(sysctl_devconf_inherit_init_net); #if IS_ENABLED(CONFIG_NET_FLOW_LIMIT) || IS_ENABLED(CONFIG_RPS)static int dump_cpumask(void *buffer, size_t *lenp, loff_t *ppos,			struct cpumask *mask){	char *kbuf;	int len; 	if (*ppos || !*lenp) {		*lenp = 0;		return 0;	} 	/* CPUs are displayed as a hex bitmap + a comma between each groups of 8	 * nibbles (except the last one which has a newline instead).	 * Guesstimate the buffer size at the group granularity level.	 */	len = min(DIV_ROUND_UP(nr_cpumask_bits, 32) * (8 + 1), *lenp);	kbuf = kmalloc(len, GFP_KERNEL);	if (!kbuf) {		*lenp = 0;		return -ENOMEM;	} 	len = scnprintf(kbuf, len, "%*pb", cpumask_pr_args(mask));	if (!len) {		*lenp = 0;		goto free_buf;	} 	/* scnprintf writes a trailing null char not counted in the returned	 * length, override it with a newline.	 */	kbuf[len++] = '\n';	memcpy(buffer, kbuf, len);	*lenp = len;	*ppos += len; free_buf:	kfree(kbuf);	return 0;}#endif #ifdef CONFIG_RPS DEFINE_MUTEX(rps_default_mask_mutex); static int rps_default_mask_sysctl(const struct ctl_table *table, int write,				   void *buffer, size_t *lenp, loff_t *ppos){	struct net *net = (struct net *)table->data;	struct cpumask *mask;	int err = 0; 	mutex_lock(&rps_default_mask_mutex);	mask = net->core.rps_default_mask;	if (write) {		if (!mask) {			mask = kzalloc(cpumask_size(), GFP_KERNEL);			net->core.rps_default_mask = mask;		}		err = -ENOMEM;		if (!mask)			goto done; 		err = cpumask_parse(buffer, mask);		if (err)			goto done; 		err = rps_cpumask_housekeeping(mask);		if (err)			goto done;	} else {		err = dump_cpumask(buffer, lenp, ppos,				   mask ?: cpu_none_mask);	} done:	mutex_unlock(&rps_default_mask_mutex);	return err;} static int rps_sock_flow_sysctl(const struct ctl_table *table, int write,				void *buffer, size_t *lenp, loff_t *ppos){	struct rps_sock_flow_table *o_sock_table, *sock_table;	static DEFINE_MUTEX(sock_flow_mutex);	rps_tag_ptr o_tag_ptr, tag_ptr;	unsigned int orig_size, size;	struct ctl_table tmp = {		.data = &size,		.maxlen = sizeof(size),		.mode = table->mode	};	void *tofree = NULL;	int ret, i;	u8 log; 	mutex_lock(&sock_flow_mutex); 	o_tag_ptr = tag_ptr = net_hotdata.rps_sock_flow_table; 	size = o_tag_ptr ? rps_tag_to_mask(o_tag_ptr) + 1 : 0;	o_sock_table = rps_tag_to_table(o_tag_ptr);	orig_size = size; 	ret = proc_dointvec(&tmp, write, buffer, lenp, ppos); 	if (!write)		goto unlock; 	if (size) {		if (size > 1<<29) {			/* Enforce limit to prevent overflow */			mutex_unlock(&sock_flow_mutex);			return -EINVAL;		}		sock_table = o_sock_table;		size = roundup_pow_of_two(size);		if (size != orig_size) {			sock_table = vmalloc_huge(size * sizeof(*sock_table),						  GFP_KERNEL);			if (!sock_table) {				mutex_unlock(&sock_flow_mutex);				return -ENOMEM;			}			net_hotdata.rps_cpu_mask =				roundup_pow_of_two(nr_cpu_ids) - 1;			log = ilog2(size);			tag_ptr = (rps_tag_ptr)sock_table | log;		} 		for (i = 0; i < size; i++)			sock_table[i].ent = RPS_NO_CPU;	} else {		sock_table = NULL;		tag_ptr = 0UL;	}	if (tag_ptr != o_tag_ptr) {		smp_store_release(&net_hotdata.rps_sock_flow_table, tag_ptr);		if (sock_table) {			static_branch_inc(&rps_needed);			static_branch_inc(&rfs_needed);		}		if (o_sock_table) {			static_branch_dec(&rps_needed);			static_branch_dec(&rfs_needed);			tofree = o_sock_table;		}	} unlock:	mutex_unlock(&sock_flow_mutex); 	kvfree_rcu_mightsleep(tofree);	return ret;}#endif /* CONFIG_RPS */ #ifdef CONFIG_NET_FLOW_LIMITstatic DEFINE_MUTEX(flow_limit_update_mutex); static int flow_limit_cpu_sysctl(const struct ctl_table *table, int write,				 void *buffer, size_t *lenp, loff_t *ppos){	struct sd_flow_limit *cur;	struct softnet_data *sd;	cpumask_var_t mask;	int i, len, ret = 0; 	if (!alloc_cpumask_var(&mask, GFP_KERNEL))		return -ENOMEM; 	if (write) {		ret = cpumask_parse(buffer, mask);		if (ret)			goto done; 		mutex_lock(&flow_limit_update_mutex);		len = sizeof(*cur) + netdev_flow_limit_table_len;		for_each_possible_cpu(i) {			sd = &per_cpu(softnet_data, i);			cur = rcu_dereference_protected(sd->flow_limit,				     lockdep_is_held(&flow_limit_update_mutex));			if (cur && !cpumask_test_cpu(i, mask)) {				RCU_INIT_POINTER(sd->flow_limit, NULL);				kfree_rcu(cur, rcu);			} else if (!cur && cpumask_test_cpu(i, mask)) {				cur = kzalloc_node(len, GFP_KERNEL,						   cpu_to_node(i));				if (!cur) {					/* not unwinding previous changes */					ret = -ENOMEM;					goto write_unlock;				}				cur->log_buckets = ilog2(netdev_flow_limit_table_len);				rcu_assign_pointer(sd->flow_limit, cur);			}		}write_unlock:		mutex_unlock(&flow_limit_update_mutex);	} else {		cpumask_clear(mask);		rcu_read_lock();		for_each_possible_cpu(i) {			sd = &per_cpu(softnet_data, i);			if (rcu_dereference(sd->flow_limit))				cpumask_set_cpu(i, mask);		}		rcu_read_unlock(); 		ret = dump_cpumask(buffer, lenp, ppos, mask);	} done:	free_cpumask_var(mask);	return ret;} static int flow_limit_table_len_sysctl(const struct ctl_table *table, int write,				       void *buffer, size_t *lenp, loff_t *ppos){	unsigned int old, *ptr;	int ret; 	mutex_lock(&flow_limit_update_mutex); 	ptr = table->data;	old = *ptr;	ret = proc_dointvec(table, write, buffer, lenp, ppos);	if (!ret && write && !is_power_of_2(*ptr)) {		*ptr = old;		ret = -EINVAL;	} 	mutex_unlock(&flow_limit_update_mutex);	return ret;}#endif /* CONFIG_NET_FLOW_LIMIT */ #ifdef CONFIG_NET_SCHEDstatic int set_default_qdisc(const struct ctl_table *table, int write,			     void *buffer, size_t *lenp, loff_t *ppos){	char id[IFNAMSIZ];	struct ctl_table tbl = {		.data = id,		.maxlen = IFNAMSIZ,	};	int ret; 	qdisc_get_default(id, IFNAMSIZ); 	ret = proc_dostring(&tbl, write, buffer, lenp, ppos);	if (write && ret == 0)		ret = qdisc_set_default(id);	return ret;}#endif static int proc_do_dev_weight(const struct ctl_table *table, int write,			   void *buffer, size_t *lenp, loff_t *ppos){	static DEFINE_MUTEX(dev_weight_mutex);	int ret, weight; 	mutex_lock(&dev_weight_mutex);	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);	if (!ret && write) {		weight = READ_ONCE(weight_p);		WRITE_ONCE(net_hotdata.dev_rx_weight, weight * dev_weight_rx_bias);		WRITE_ONCE(net_hotdata.dev_tx_weight, weight * dev_weight_tx_bias);	}	mutex_unlock(&dev_weight_mutex); 	return ret;} static int proc_do_rss_key(const struct ctl_table *table, int write,			   void *buffer, size_t *lenp, loff_t *ppos){	char buf[NETDEV_RSS_KEY_LEN * 3];	struct ctl_table fake_table;	char *pos = buf; 	for (int i = 0; i < NETDEV_RSS_KEY_LEN; i++) {		pos = hex_byte_pack(pos, netdev_rss_key[i]);		*pos++ = ':';	}	*(--pos) = 0; 	fake_table.data = buf;	fake_table.maxlen = sizeof(buf);	return proc_dostring(&fake_table, write, buffer, lenp, ppos);} static int proc_do_skb_defer_max(const struct ctl_table *table, int write,		 void *buffer, size_t *lenp, loff_t *ppos){	static DEFINE_MUTEX(skb_defer_max_mutex);	int ret, oval, nval; 	mutex_lock(&skb_defer_max_mutex); 	oval = !net_hotdata.sysctl_skb_defer_max;	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);	nval = !net_hotdata.sysctl_skb_defer_max; 	if (nval != oval) {		if (nval)			static_branch_enable(&skb_defer_disable_key);		else			static_branch_disable(&skb_defer_disable_key);	} 	mutex_unlock(&skb_defer_max_mutex);	return ret;} #ifdef CONFIG_BPF_JITstatic int proc_dointvec_minmax_bpf_enable(const struct ctl_table *table, int write,					   void *buffer, size_t *lenp,					   loff_t *ppos){	int ret, jit_enable = *(int *)table->data;	int min = *(int *)table->extra1;	int max = *(int *)table->extra2;	struct ctl_table tmp = *table; 	if (write && !capable(CAP_SYS_ADMIN))		return -EPERM; 	tmp.data = &jit_enable;	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);	if (write && !ret) {		if (jit_enable < 2 ||		    (jit_enable == 2 && bpf_dump_raw_ok(current_cred()))) {			*(int *)table->data = jit_enable;			if (jit_enable == 2)				pr_warn("bpf_jit_enable = 2 was set! NEVER use this in production, only for JIT debugging!\n");		} else {			ret = -EPERM;		}	} 	if (write && ret && min == max)		pr_info_once("CONFIG_BPF_JIT_ALWAYS_ON is enabled, bpf_jit_enable is permanently set to 1.\n"); 	return ret;} # ifdef CONFIG_HAVE_EBPF_JITstatic intproc_dointvec_minmax_bpf_restricted(const struct ctl_table *table, int write,				    void *buffer, size_t *lenp, loff_t *ppos){	if (!capable(CAP_SYS_ADMIN))		return -EPERM; 	return proc_dointvec_minmax(table, write, buffer, lenp, ppos);}# endif /* CONFIG_HAVE_EBPF_JIT */ static intproc_dolongvec_minmax_bpf_restricted(const struct ctl_table *table, int write,				     void *buffer, size_t *lenp, loff_t *ppos){	if (!capable(CAP_SYS_ADMIN))		return -EPERM; 	return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);}#endif static struct ctl_table net_core_table[] = {	{		.procname	= "mem_pcpu_rsv",		.data		= &net_hotdata.sysctl_mem_pcpu_rsv,		.maxlen		= sizeof(int),		.mode		= 0644,		.proc_handler	= proc_dointvec_minmax,		.extra1		= &min_mem_pcpu_rsv,	},	{		.procname	= "dev_weight",		.data		= &weight_p,		.maxlen		= sizeof(int),		.mode		= 0644,		.proc_handler	= proc_do_dev_weight,		.extra1         = SYSCTL_ONE,	},	{		.procname	= "dev_weight_rx_bias",		.data		= &dev_weight_rx_bias,		.maxlen		= sizeof(int),		.mode		= 0644,		.proc_handler	= proc_do_dev_weight,		.extra1         = SYSCTL_ONE,	},	{		.procname	= "dev_weight_tx_bias",		.data		= &dev_weight_tx_bias,		.maxlen		= sizeof(int),		.mode		= 0644,		.proc_handler	= proc_do_dev_weight,		.extra1         = SYSCTL_ONE,	},	{		.procname	= "netdev_max_backlog",		.data		= &net_hotdata.max_backlog,		.maxlen		= sizeof(int),		.mode		= 0644,		.proc_handler	= proc_dointvec	},	{		.procname	= "qdisc_max_burst",		.data		= &net_hotdata.qdisc_max_burst,		.maxlen		= sizeof(int),		.mode		= 0644,		.proc_handler	= proc_dointvec	},	{		.procname	= "netdev_rss_key",		.data		= &netdev_rss_key,		.maxlen		= sizeof(int),		.mode		= 0444,		.proc_handler	= proc_do_rss_key,	},#ifdef CONFIG_BPF_JIT	{		.procname	= "bpf_jit_enable",		.data		= &bpf_jit_enable,		.maxlen		= sizeof(int),		.mode		= 0644,		.proc_handler	= proc_dointvec_minmax_bpf_enable,# ifdef CONFIG_BPF_JIT_ALWAYS_ON		.extra1		= SYSCTL_ONE,		.extra2		= SYSCTL_ONE,# else		.extra1		= SYSCTL_ZERO,		.extra2		= SYSCTL_TWO,# endif	},# ifdef CONFIG_HAVE_EBPF_JIT	{

Lines 1–500 of 861.