From: Kan Liang <redacted>
It is a big challenge to get good network performance. First, the network
performance is not good with default system settings. Second, it is too
difficult to do automatic tuning for all possible workloads, since workloads
have different requirements. Some workloads may want high throughput. Some may
need low latency. Last but not least, there are lots of manual configurations.
Fine grained configuration is too difficult for users.
NET policy intends to simplify the network configuration and get a good network
performance according to the hints(policy) which is applied by user. It
provides some typical "policies" for user which can be set per-socket, per-task
or per-device. The kernel will automatically figures out how to merge different
requests to get good network performance.
NET policy is designed for multiqueue network devices. This implementation is
only for Intel NICs using i40e driver. But the concepts and generic code should
apply to other multiqueue NICs too.
NET policy is also a combination of generic policy manager code and some
ethtool callbacks (per queue coalesce setting, flow classification rules) to
configure the driver.
This series also supports CPU hotplug and device hotplug.
Here are some common questions about NET policy.
1. Why userspace tool cannot do the same thing?
A: Kernel is more suitable for NET policy.
- User space code would be far more complicated to get right and perform
well . It always need to work with out of date state compared to the
latest, because it cannot do any locking with the kernel state.
- User space code is less efficient than kernel code, because of the
additional context switches needed.
- Kernel is in the right position to coordinate requests from multiple
users.
2. Is NET policy looking for optimal settings?
A: No. The NET policy intends to get a good network performance according
to user's specific request. Our target for good performance is ~90% of
the optimal settings.
3. How's the configuration impact the connection rates?
A: There are two places to acquire rtnl mutex to configure the device.
- One is to do device policy setting. It happens on initalization stage,
hotplug or queue number changes. The device policy will be set to
NET_POLICY_NONE. If so, it "falls back" to the system default way to
direct the packets. It doesn't block the connection.
- The other is to set Rx network flow classification options or rules.
It uses work queue to do asynchronized setting. It avoid destroying
the connection rates.
4. About disabling IRQ balance?
A: Disabling IRQ balance is a common way (recommend way for some devices) to
tune network performance. NET policy provides an option for driver to choose
to disable IRQ balance and set IRQ affinity.
Here are some key Interfaces/APIs for NET policy.
Interfaces which export to user space
/proc/net/netpolicy/$DEV/policy
User can set/get per device policy from /proc
/proc/$PID/net_policy
User can set/get per task policy from /proc
prctl(PR_SET_NETPOLICY, POLICY_NAME, NULL, NULL, NULL)
An alternative way to set/get per task policy is from prctl.
setsockopt(sockfd,SOL_SOCKET,SO_NETPOLICY,&policy,sizeof(int))
User can set/get per socket policy by setsockopt
New ndo opt
int (*ndo_netpolicy_init)(struct net_device *dev,
struct netpolicy_info *info);
Initialize device driver for NET policy
int (*ndo_get_irq_info)(struct net_device *dev,
struct netpolicy_dev_info *info);
Collect device information. Currently, only collecting IRQ
informance should be enough.
int (*ndo_set_net_policy)(struct net_device *dev,
enum netpolicy_name name);
This interface is used to set device NET policy by name. It is device driver's
responsibility to set driver specific configuration for the given policy.
NET policy subsystem APIs
netpolicy_register(struct netpolicy_instance *instance,
enum netpolicy_name policy)
netpolicy_unregister(struct netpolicy_instance *instance)
Register/unregister per task/socket NET policy.
The socket/task can only be benefited when it register itself with
specific policy. After registeration, an record will be created and inserted
into a RCU hash table, which include all the NET policy related information
for the socket/task.
netpolicy_pick_queue(struct netpolicy_instance *instance, bool is_rx);
Find the proper queue according to policy for packet receiving and
transmitting
netpolicy_set_rules(struct netpolicy_instance *instance);
Configure Rx network flow classification rules
For using NET policy, the per-device policy must be set in advance. It will
automatically configure the system and re-organize the resource of the system
accordingly. For system configuration, in this series, it will disable irq
balance, set device queue irq affinity, and modify interrupt moderation. For
re-organizing the resource, current implementation forces that CPU and queue
irq are 1:1 mapping. An 1:1 mapping group is also called NET policy object.
For each device policy, it maintains a policy list. Once the device policy is
applied, the objects will be insert and tracked in that device policy list. The
policy list only be updated when CPU/device hotplug, queue number changes or
device policy changes.
The user can use /proc, prctl and setsockopt to set per-task and per-socket
NET policy. Once the policy is set, an related record will be inserted into RCU
hash table. The record includes ptr, policy and NET policy object. The ptr is
the pointer address of task/socket. The object will not be assigned until the
first package receive/transmit. The object is picked by round-robin from object
list. Once the object is determined, the following packets will be set to
redirect to the queue(object).
The object can be shared. The per-task or per-socket policy can be inherited.
Now NET policy supports four per device policies and three per task/socket
policies.
- BULK policy: This policy is designed for high throughput. It can be
applied to either per device policy or per task/socket policy.
- CPU policy: This policy is designed for high throughput but lower CPU
utilization (power saving). It can be applied to either per device policy
or per task/socket policy.
- LATENCY policy: This policy is designed for low latency. It can be
applied to either per device policy or per task/socket policy.
- MIX policy: This policy can only be applied to per device policy. This
is designed for the case which miscellaneous types of workload running
on the device.
Lots of tests are done for NET policy on platforms with Intel Xeon E5 V2
and XL710 40G NIC. The baseline test is with Linux 4.6.0 kernel.
Netperf is used to evaluate the throughput and latency performance.
- "netperf -f m -t TCP_RR -H server_IP -c -C -l 60 -- -r buffersize
-b burst -D" is used to evaluate throughput performance, which is
called throughput-first workload.
- "netperf -t TCP_RR -H server_IP -c -C -l 60 -- -r buffersize" is
used to evaluate latency performance, which is called latency-first
workload.
- Different loads are also evaluated by running 1, 12, 24, 48 or 96
throughput-first workloads/latency-first workload simultaneously.
For "BULK" policy, the throughput performance is on average ~1.22X than
baseline.
For "CPU" policy, the throughput performance is on average ~1.19X than
baseline, and has lower CPU% (on average ~5% lower than "BULK" policy).
For "LATENCY" policy, the latency is on average 49.8% less than the baseline.
For "MIX" policy, mixed workloads performance is evaluated.
The mixed workloads are combination of throughput-first workload and
latency-first workload. Five different types of combinations are evaluated
(pure throughput-first workload, pure latency-first workloads,
2/3 throughput-first workload + 1/3 latency-first workloads,
1/3 throughput-first workload + 2/3 latency-first workloads and
1/2 throughput-first workload + 1/2 latency-first workloads).
For caculating the performance of mixed workloads, a weighted sum system
is introduced.
Score = normalized_latency * Weight + normalized_throughput * (1 - Weight).
If we assume that the user has an equal interest in latency and throughput
performance, the Score for "MIX" policy is on average ~1.63X than baseline.
Changes since V2:
- Set default to n for NET policy subsystem
- Modify the queue selection algorism. The new algorism will consider
CPU loads and ref number
- Extends the netpolicy to support tc bpf when selecting Tx queue
- Provides an option irq_affinity for driver to choose to disable IRQ balance
and set IRQ affinity
- Make the netpolicy_sys_map_version per device not global
- Modify the changelog accordingly
Changes since V1:
- Using work queue to set Rx network flow classification rules and search
available NET policy object asynchronously.
- Using RCU lock to replace read-write lock
- Redo performance test and update performance results.
- Some minor modification for codes and documents.
- Remove i40e related patches which will be submitted in separate thread.
Kan Liang (26):
net: introduce NET policy
net/netpolicy: init NET policy
net/netpolicy: get device queue irq information
net/netpolicy: get CPU information
net/netpolicy: create CPU and queue mapping
net/netpolicy: set and remove IRQ affinity
net/netpolicy: enable and disable NET policy
net/netpolicy: introduce NET policy object
net/netpolicy: set NET policy by policy name
net/netpolicy: add three new NET policies
net/netpolicy: add MIX policy
net/netpolicy: NET device hotplug
net/netpolicy: support CPU hotplug
net/netpolicy: handle channel changes
net/netpolicy: implement netpolicy register
net/netpolicy: introduce per socket netpolicy
net/netpolicy: introduce netpolicy_pick_queue
net/netpolicy: set tx queues according to policy
net/netpolicy: tc bpf extension to pick Tx queue
net/netpolicy: set Rx queues according to policy
net/netpolicy: introduce per task net policy
net/netpolicy: set per task policy by proc
net/netpolicy: fast path for finding the queues
net/netpolicy: optimize for queue pair
net/netpolicy: limit the total record number
Documentation/networking: Document NET policy
Documentation/networking/netpolicy.txt | 157 ++++
arch/alpha/include/uapi/asm/socket.h | 2 +
arch/avr32/include/uapi/asm/socket.h | 2 +
arch/frv/include/uapi/asm/socket.h | 2 +
arch/ia64/include/uapi/asm/socket.h | 2 +
arch/m32r/include/uapi/asm/socket.h | 2 +
arch/mips/include/uapi/asm/socket.h | 2 +
arch/mn10300/include/uapi/asm/socket.h | 2 +
arch/parisc/include/uapi/asm/socket.h | 2 +
arch/powerpc/include/uapi/asm/socket.h | 2 +
arch/s390/include/uapi/asm/socket.h | 2 +
arch/sparc/include/uapi/asm/socket.h | 2 +
arch/xtensa/include/uapi/asm/socket.h | 2 +
fs/proc/base.c | 64 ++
include/linux/init_task.h | 9 +
include/linux/netdevice.h | 31 +
include/linux/netpolicy.h | 177 ++++
include/linux/sched.h | 8 +
include/net/net_namespace.h | 3 +
include/net/request_sock.h | 4 +-
include/net/sock.h | 28 +
include/uapi/asm-generic/socket.h | 2 +
include/uapi/linux/bpf.h | 8 +
include/uapi/linux/prctl.h | 4 +
kernel/exit.c | 4 +
kernel/fork.c | 6 +
kernel/sched/fair.c | 8 +-
kernel/sys.c | 31 +
net/Kconfig | 7 +
net/core/Makefile | 1 +
net/core/dev.c | 20 +-
net/core/ethtool.c | 8 +-
net/core/filter.c | 36 +
net/core/netpolicy.c | 1571 ++++++++++++++++++++++++++++++++
net/core/sock.c | 36 +
net/ipv4/af_inet.c | 71 ++
net/ipv4/udp.c | 4 +
samples/bpf/Makefile | 1 +
samples/bpf/bpf_helpers.h | 2 +
39 files changed, 2317 insertions(+), 8 deletions(-)
create mode 100644 Documentation/networking/netpolicy.txt
create mode 100644 include/linux/netpolicy.h
create mode 100644 net/core/netpolicy.c
--
2.5.5
From: Kan Liang <redacted>
This patch tries to initialize NET policy for all the devices in the
system. However, not all device drivers have NET policy support. For
those drivers who does not have NET policy support, the node will not be
showed in /proc/net/netpolicy/.
The device driver who has NET policy support must implement the
interface ndo_netpolicy_init, which is used to do necessory
initialization and collect information (E.g. supported policies) from
driver.
The user can check /proc/net/netpolicy/ and
/proc/net/netpolicy/$DEV/policy to know the available device and its
supported policy.
np_lock is also introduced to protect the state of NET policy.
Device hotplug will be handled later in this series.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netdevice.h | 12 +++++++
include/linux/netpolicy.h | 31 ++++++++++++++++
net/core/netpolicy.c | 91 +++++++++++++++++++++++++++++++++++++++++------
3 files changed, 123 insertions(+), 11 deletions(-)
create mode 100644 include/linux/netpolicy.h
@@ -34,14 +34,31 @@#include<linux/uaccess.h>#include<linux/netdevice.h>#include<net/net_namespace.h>+#include<net/rtnetlink.h>+constchar*policy_name[NET_POLICY_MAX]={+"NONE"+};#ifdef CONFIG_PROC_FSstaticintnet_policy_proc_show(structseq_file*m,void*v){structnet_device*dev=(structnet_device*)m->private;--seq_printf(m,"%s doesn't support net policy manager\n",dev->name);+inti;++if(WARN_ON(!dev->netpolicy))+return-EINVAL;++if(dev->netpolicy->cur_policy==NET_POLICY_NONE){+seq_printf(m,"%s: There is no policy applied\n",dev->name);+seq_printf(m,"%s: The available policy include:",dev->name);+for_each_set_bit(i,dev->netpolicy->avail_policy,NET_POLICY_MAX)+seq_printf(m," %s",policy_name[i]);+seq_printf(m,"\n");+}else{+seq_printf(m,"%s: POLICY %s is running on the system\n",+dev->name,policy_name[dev->netpolicy->cur_policy]);+}return0;}
@@ -73,33 +90,85 @@ static int netpolicy_proc_dev_init(struct net *net, struct net_device *dev)}return0;}+#endif /* CONFIG_PROC_FS */++intinit_netpolicy(structnet_device*dev)+{+intret;++spin_lock(&dev->np_lock);+ret=0;++if(!dev->netdev_ops->ndo_netpolicy_init){+ret=-ENOTSUPP;+gotounlock;+}++if(dev->netpolicy)+gotounlock;++dev->netpolicy=kzalloc(sizeof(*dev->netpolicy),GFP_ATOMIC);+if(!dev->netpolicy){+ret=-ENOMEM;+gotounlock;+}++ret=dev->netdev_ops->ndo_netpolicy_init(dev,dev->netpolicy);+if(ret){+kfree(dev->netpolicy);+dev->netpolicy=NULL;+}++unlock:+spin_unlock(&dev->np_lock);+returnret;+}++voiduninit_netpolicy(structnet_device*dev)+{+spin_lock(&dev->np_lock);+if(dev->netpolicy){+kfree(dev->netpolicy);+dev->netpolicy=NULL;+}+spin_unlock(&dev->np_lock);+}staticint__net_initnetpolicy_net_init(structnet*net){structnet_device*dev,*aux;+#ifdef CONFIG_PROC_FSnet->proc_netpolicy=proc_net_mkdir(net,"netpolicy",net->proc_net);if(!net->proc_netpolicy)return-ENOMEM;+#endif /* CONFIG_PROC_FS */+rtnl_lock();for_each_netdev_safe(net,dev,aux){-netpolicy_proc_dev_init(net,dev);+if(!init_netpolicy(dev)){+#ifdef CONFIG_PROC_FS+if(netpolicy_proc_dev_init(net,dev))+uninit_netpolicy(dev);+else+#endif /* CONFIG_PROC_FS */+pr_info("NETPOLICY: Init net policy for %s\n",dev->name);+}}+rtnl_unlock();return0;}-#else /* CONFIG_PROC_FS */--staticint__net_initnetpolicy_net_init(structnet*net)-{-return0;-}-#endif /* CONFIG_PROC_FS */-staticvoid__net_exitnetpolicy_net_exit(structnet*net){+structnet_device*dev,*aux;++rtnl_lock();+for_each_netdev_safe(net,dev,aux)+uninit_netpolicy(dev);+rtnl_unlock();#ifdef CONFIG_PROC_FSremove_proc_subtree("netpolicy",net->proc_net);#endif /* CONFIG_PROC_FS */
From: Kan Liang <redacted>
This patch introduces functions to enable and disable NET policy.
For enabling, it collects device and CPU information and setup CPU/queue
mapping. Also, for some drivers like i40e driver, it will get better
performance if setting IRQ affinity. This patch provides an option
irq_affinity for driver to set IRQ affinity in NET policy enabling.
For disabling, it removes the IRQ affinity (if set) and mapping
information.
np_lock should protect the enable and disable state. It will be done
later in this series.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netpolicy.h | 1 +
net/core/netpolicy.c | 41 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 42 insertions(+)
@@ -49,6 +49,7 @@ struct netpolicy_sys_info {structnetpolicy_info{enumnetpolicy_namecur_policy;unsignedlongavail_policy[BITS_TO_LONGS(NET_POLICY_MAX)];+boolirq_affinity;/* cpu and queue mapping information */structnetpolicy_sys_infosys_info;};
@@ -162,6 +162,47 @@ static void netpolicy_set_affinity(struct net_device *dev)}}+staticintnetpolicy_disable(structnet_device*dev)+{+if(dev->netpolicy->irq_affinity)+netpolicy_clear_affinity(dev);+netpolicy_free_sys_map(dev);++return0;+}++staticintnetpolicy_enable(structnet_device*dev)+{+intret;+structnetpolicy_dev_infod_info;+u32cpu;++if(WARN_ON(!dev->netpolicy))+return-EINVAL;++/* get driver information */+ret=netpolicy_get_dev_info(dev,&d_info);+if(ret)+returnret;++/* get cpu information */+cpu=netpolicy_get_cpu_information();++/* create sys map */+ret=netpolicy_update_sys_map(dev,&d_info,cpu);+if(ret){+netpolicy_free_dev_info(&d_info);+returnret;+}++/* set irq affinity */+if(dev->netpolicy->irq_affinity)+netpolicy_set_affinity(dev);++netpolicy_free_dev_info(&d_info);+return0;+}+constchar*policy_name[NET_POLICY_MAX]={"NONE"};
From: Kan Liang <redacted>
This patches introduces functions to set and remove IRQ affinity
according to cpu and queue mapping.
The functions will not record the previous affinity status. After a
set/remove cycles, it will set the affinity on all online CPU with IRQ
balance enabling.
Signed-off-by: Kan Liang <redacted>
---
net/core/netpolicy.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
From: Kan Liang <redacted>
Current implementation forces CPU and queue 1:1 mapping. This patch
introduces the function netpolicy_update_sys_map to create this mapping.
The result is stored in netpolicy_sys_info.
If the CPU count and queue count are different, the remaining
CPUs/queues are not used for now.
CPU hotplug, device hotplug or ethtool may change the CPU count or
queue count. For these cases, this function can also be called to
reconstruct the mapping. These cases will be handled later in this
series.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netpolicy.h | 18 ++++++++++++
net/core/netpolicy.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+)
@@ -30,9 +30,27 @@ struct netpolicy_dev_info {u32*tx_irq;};+structnetpolicy_sys_map{+u32cpu;+u32queue;+u32irq;+};++structnetpolicy_sys_info{+/*+*Recordthecpuandqueue1:1mapping+*/+u32avail_rx_num;+structnetpolicy_sys_map*rx;+u32avail_tx_num;+structnetpolicy_sys_map*tx;+};+structnetpolicy_info{enumnetpolicy_namecur_policy;unsignedlongavail_policy[BITS_TO_LONGS(NET_POLICY_MAX)];+/* cpu and queue mapping information */+structnetpolicy_sys_infosys_info;};#endif /*__LINUX_NETPOLICY_H*/
From: Kan Liang <redacted>
This patch introduces the concept of NET policy object and policy object
list.
The NET policy object is the instance of CPU/queue mapping. The object
can be shared between different tasks/sockets. So besides CPU and queue
information, the object also maintains a reference counter.
Each policy will have a dedicated object list. If the policy is set as
device policy, all objects will be inserted into the related policy
object list. The user will search and pickup the available objects from
the list later.
The network performance for objects could be different because of the
queue and CPU topology. To generate a proper object list, dev location,
HT and CPU topology have to be considered. The high performance objects
are in the front of the list.
The object lists will be regenerated if sys mapping changes or device
net policy changes.
Lock np_ob_list_lock is used to protect the object list.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netdevice.h | 2 +
include/linux/netpolicy.h | 15 +++
net/core/netpolicy.c | 238 +++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 254 insertions(+), 1 deletion(-)
@@ -46,12 +52,21 @@ struct netpolicy_sys_info {structnetpolicy_sys_map*tx;};+structnetpolicy_object{+structlist_headlist;+u32cpu;+u32queue;+atomic_trefcnt;+};+structnetpolicy_info{enumnetpolicy_namecur_policy;unsignedlongavail_policy[BITS_TO_LONGS(NET_POLICY_MAX)];boolirq_affinity;/* cpu and queue mapping information */structnetpolicy_sys_infosys_info;+/* List of policy objects 0 rx 1 tx */+structlist_headobj_list[NETPOLICY_RXTX][NET_POLICY_MAX];};#endif /*__LINUX_NETPOLICY_H*/
@@ -206,6 +227,213 @@ static int netpolicy_enable(struct net_device *dev)constchar*policy_name[NET_POLICY_MAX]={"NONE"};++staticu32cpu_to_queue(structnet_device*dev,+u32cpu,boolis_rx)+{+structnetpolicy_sys_info*s_info=&dev->netpolicy->sys_info;+inti;++if(is_rx){+for(i=0;i<s_info->avail_rx_num;i++){+if(s_info->rx[i].cpu==cpu)+returns_info->rx[i].queue;+}+}else{+for(i=0;i<s_info->avail_tx_num;i++){+if(s_info->tx[i].cpu==cpu)+returns_info->tx[i].queue;+}+}++return~0;+}++staticintnetpolicy_add_obj(structnet_device*dev,+u32cpu,boolis_rx,+enumnetpolicy_namepolicy)+{+structnetpolicy_object*obj;+intdir=is_rx?NETPOLICY_RX:NETPOLICY_TX;++obj=kzalloc(sizeof(*obj),GFP_ATOMIC);+if(!obj)+return-ENOMEM;+obj->cpu=cpu;+obj->queue=cpu_to_queue(dev,cpu,is_rx);+list_add_tail(&obj->list,&dev->netpolicy->obj_list[dir][policy]);++return0;+}++structsort_node{+intnode;+intdistance;+};++staticinlineintnode_distance_cmp(constvoid*a,constvoid*b)+{+conststructsort_node*_a=a;+conststructsort_node*_b=b;++return_a->distance-_b->distance;+}++staticint_netpolicy_gen_obj_list(structnet_device*dev,boolis_rx,+enumnetpolicy_namepolicy,+structsort_node*nodes,intnum_node,+structcpumask*node_avail_cpumask)+{+cpumask_var_tnode_tmp_cpumask,sibling_tmp_cpumask;+structcpumask*node_assigned_cpumask;+inti,ret=-ENOMEM;+u32cpu;++if(!alloc_cpumask_var(&node_tmp_cpumask,GFP_ATOMIC))+returnret;+if(!alloc_cpumask_var(&sibling_tmp_cpumask,GFP_ATOMIC))+gotoalloc_fail1;++node_assigned_cpumask=kcalloc(num_node,sizeof(structcpumask),GFP_ATOMIC);+if(!node_assigned_cpumask)+gotoalloc_fail2;++/* Don't share physical core */+for(i=0;i<num_node;i++){+if(cpumask_weight(&node_avail_cpumask[nodes[i].node])==0)+continue;+spin_lock(&dev->np_ob_list_lock);+cpumask_copy(node_tmp_cpumask,&node_avail_cpumask[nodes[i].node]);+while(cpumask_weight(node_tmp_cpumask)){+cpu=cpumask_first(node_tmp_cpumask);++/* push to obj list */+ret=netpolicy_add_obj(dev,cpu,is_rx,policy);+if(ret){+spin_unlock(&dev->np_ob_list_lock);+gotoerr;+}++cpumask_set_cpu(cpu,&node_assigned_cpumask[nodes[i].node]);+cpumask_and(sibling_tmp_cpumask,node_tmp_cpumask,topology_sibling_cpumask(cpu));+cpumask_xor(node_tmp_cpumask,node_tmp_cpumask,sibling_tmp_cpumask);+}+spin_unlock(&dev->np_ob_list_lock);+}++for(i=0;i<num_node;i++){+cpumask_xor(node_tmp_cpumask,&node_avail_cpumask[nodes[i].node],&node_assigned_cpumask[nodes[i].node]);+if(cpumask_weight(node_tmp_cpumask)==0)+continue;+spin_lock(&dev->np_ob_list_lock);+for_each_cpu(cpu,node_tmp_cpumask){+/* push to obj list */+ret=netpolicy_add_obj(dev,cpu,is_rx,policy);+if(ret){+spin_unlock(&dev->np_ob_list_lock);+gotoerr;+}+cpumask_set_cpu(cpu,&node_assigned_cpumask[nodes[i].node]);+}+spin_unlock(&dev->np_ob_list_lock);+}++err:+kfree(node_assigned_cpumask);+alloc_fail2:+free_cpumask_var(sibling_tmp_cpumask);+alloc_fail1:+free_cpumask_var(node_tmp_cpumask);++returnret;+}++staticintnetpolicy_gen_obj_list(structnet_device*dev,+enumnetpolicy_namepolicy)+{+structnetpolicy_sys_info*s_info=&dev->netpolicy->sys_info;+structcpumask*node_avail_cpumask;+structsort_node*nodes;+inti,ret,node=0;+intnum_nodes=1;+u32cpu;+#ifdef CONFIG_NUMA+intdev_node=0;+intval;+#endif+/* The network performance for objects could be different+*becauseofthequeueandcputopology.+*Theobjectswillbeorderedaccordingly,+*andputhighperformanceobjectinthefront.+*+*Thepriorityrulesasbelow,+*-Thelocalobject.(Localmeanscpuandqueueareinthesamenode.)+*-Thecpuintheobjectistheonlylogicalcoreinphysicalcore.+*Thesibilingcore'sobjecthasnotbeenaddedintheobjectlistyet.+*-Therestofobjects+*+*Sotheorderofobjectlistisasbelow:+*1.Localcore+theonlylogicalcore+*2.Remotecore+theonlylogicalcore+*3.Localcore+thecore'ssiblingisalreadyintheobjectlist+*4.Remotecore+thecore'ssiblingisalreadyintheobjectlist+*/+#ifdef CONFIG_NUMA+dev_node=dev_to_node(dev->dev.parent);+num_nodes=num_online_nodes();+#endif++nodes=kcalloc(num_nodes,sizeof(*nodes),GFP_ATOMIC);+if(!nodes)+return-ENOMEM;++node_avail_cpumask=kcalloc(num_nodes,sizeof(structcpumask),GFP_ATOMIC);+if(!node_avail_cpumask){+kfree(nodes);+return-ENOMEM;+}++#ifdef CONFIG_NUMA+/* order the node from near to far */+for_each_node_mask(i,node_online_map){+val=node_distance(dev_node,i);+nodes[node].node=i;+nodes[node].distance=val;+node++;+}+sort(nodes,num_nodes,sizeof(*nodes),+node_distance_cmp,NULL);+#else+nodes[0].node=0;+#endif++for(i=0;i<s_info->avail_rx_num;i++){+cpu=s_info->rx[i].cpu;+cpumask_set_cpu(cpu,&node_avail_cpumask[cpu_to_node(cpu)]);+}+ret=_netpolicy_gen_obj_list(dev,true,policy,nodes,+node,node_avail_cpumask);+if(ret)+gotoerr;++for(i=0;i<node;i++)+cpumask_clear(&node_avail_cpumask[nodes[i].node]);++for(i=0;i<s_info->avail_tx_num;i++){+cpu=s_info->tx[i].cpu;+cpumask_set_cpu(cpu,&node_avail_cpumask[cpu_to_node(cpu)]);+}+ret=_netpolicy_gen_obj_list(dev,false,policy,nodes,+node,node_avail_cpumask);+if(ret)+gotoerr;++err:+kfree(nodes);+kfree(node_avail_cpumask);+returnret;+}+#ifdef CONFIG_PROC_FSstaticintnet_policy_proc_show(structseq_file*m,void*v)
@@ -261,7 +489,7 @@ static int netpolicy_proc_dev_init(struct net *net, struct net_device *dev)intinit_netpolicy(structnet_device*dev){-intret;+intret,i,j;spin_lock(&dev->np_lock);ret=0;
@@ -284,7 +512,15 @@ int init_netpolicy(struct net_device *dev)if(ret){kfree(dev->netpolicy);dev->netpolicy=NULL;+gotounlock;+}++spin_lock(&dev->np_ob_list_lock);+for(i=0;i<NETPOLICY_RXTX;i++){+for(j=NET_POLICY_NONE;j<NET_POLICY_MAX;j++)+INIT_LIST_HEAD(&dev->netpolicy->obj_list[i][j]);}+spin_unlock(&dev->np_ob_list_lock);unlock:spin_unlock(&dev->np_lock);
From: Kan Liang <redacted>
The network socket is the most basic unit which control the network
traffic. A socket option is needed for user to set their own policy on
socket to improve the network performance.
There is no existing SOCKET options which can be reused. For socket
options, SO_MARK or may be SO_PRIORITY is close to NET policy's
requirement. But they can not be reused for NET policy. SO_MARK can be
used for routing and packet filtering. But the NET policy doesn't intend
to change the routing. It only redirects the packet to the specific
device queue. Also, the target queue is assigned by NET policy subsystem
at run time. It should not be set in advance. SO_PRIORITY can set
protocol-defined priority for all packets on the socket. But the NET
policies don't have priority yet.
This patch introduces a new socket option SO_NETPOLICY to
set/get net policy for socket. so that the application can set its own
policy on socket to improve the network performance.
Per socket net policy can also be inherited by new socket.
The usage of SO_NETPOLICY socket option is as below.
setsockopt(sockfd,SOL_SOCKET,SO_NETPOLICY,&policy,sizeof(int))
getsockopt(sockfd,SOL_SOCKET,SO_NETPOLICY,&policy,sizeof(int))
The policy set by SO_NETPOLICY socket option must be valid and
compatible with current device policy. Othrewise, it will error out. The
socket policy will be set to NET_POLICY_INVALID.
Signed-off-by: Kan Liang <redacted>
---
arch/alpha/include/uapi/asm/socket.h | 2 ++
arch/avr32/include/uapi/asm/socket.h | 2 ++
arch/frv/include/uapi/asm/socket.h | 2 ++
arch/ia64/include/uapi/asm/socket.h | 2 ++
arch/m32r/include/uapi/asm/socket.h | 2 ++
arch/mips/include/uapi/asm/socket.h | 2 ++
arch/mn10300/include/uapi/asm/socket.h | 2 ++
arch/parisc/include/uapi/asm/socket.h | 2 ++
arch/powerpc/include/uapi/asm/socket.h | 2 ++
arch/s390/include/uapi/asm/socket.h | 2 ++
arch/sparc/include/uapi/asm/socket.h | 2 ++
arch/xtensa/include/uapi/asm/socket.h | 2 ++
include/net/request_sock.h | 4 +++-
include/net/sock.h | 9 +++++++++
include/uapi/asm-generic/socket.h | 2 ++
net/core/sock.c | 28 ++++++++++++++++++++++++++++
16 files changed, 66 insertions(+), 1 deletion(-)
@@ -1263,6 +1269,11 @@ int sock_getsockopt(struct socket *sock, int level, int optname,v.val=sk->sk_incoming_cpu;break;+#ifdef CONFIG_NETPOLICY+caseSO_NETPOLICY:+v.val=sk->sk_netpolicy.policy;+break;+#endifdefault:/* We implement the SO_SNDLOWAT etc to not be settable*(1003.1g7).
@@ -1402,6 +1413,12 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,sock_update_classid(&sk->sk_cgrp_data);sock_update_netprioidx(&sk->sk_cgrp_data);++#ifdef CONFIG_NETPOLICY+sk->sk_netpolicy.dev=NULL;+sk->sk_netpolicy.ptr=(void*)sk;+sk->sk_netpolicy.policy=NET_POLICY_INVALID;+#endif}returnsk;
From: Kan Liang <redacted>
Some drivers like i40e driver does not support separate Tx and Rx queues
as channels. Using Rx queue to stand for the channels, if queue_pair is
set by driver.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netpolicy.h | 1 +
net/core/netpolicy.c | 3 +++
2 files changed, 4 insertions(+)
@@ -83,6 +83,7 @@ struct netpolicy_info {unsignedlongavail_policy[BITS_TO_LONGS(NET_POLICY_MAX)];boolirq_affinity;boolhas_mix_policy;+boolqueue_pair;/* cpu and queue mapping information */structnetpolicy_sys_infosys_info;/* List of policy objects 0 rx 1 tx */
@@ -0,0 +1,157 @@+What is Linux Net Policy?++It is a big challenge to get good network performance. First, the network+performance is not good with default system settings. Second, it is too+difficult to do automatic tuning for all possible workloads, since workloads+have different requirements. Some workloads may want high throughput. Some may+need low latency. Last but not least, there are lots of manual configurations.+Fine grained configuration is too difficult for users.++"NET policy" intends to simplify the network configuration and get a+good network performance according to the hints(policy) which is applied by+user. It provides some typical "policies" for user which can be set+per-socket, per-task or per-device. The kernel automatically figures out+how to merge different requests to get good network performance.++"Net policy" is designed for multiqueue network devices. This document+describes the concepts and APIs of "net policy" support.++NET POLICY CONCEPTS++Scope of Net Policies++ Device net policy: this policy applies to the whole device. Once the+ device net policy is set, it automatically configures the system+ according to the applied policy. The configuration usually includes IRQ+ affinity, IRQ balance disable, interrupt moderation, and so on. But the+ device net policy does not change the packet direction.++ Task net policy: this is a per-task policy. When it is applied to specific+ task, all packet transmissions of the task will be redirected to the+ assigned queues accordingly. If a task does not define a task policy,+ it "falls back" to the system default way to direct the packets. The+ per-task policy must be compatible with device net policy.++ Socket net policy: this is a per-socket policy. When it is applied to+ specific socket, all packet transmissions of the socket will be redirected+ to the assigned queues accordingly. If a socket does not define a socket+ policy, it "falls back" to the system default way to direct the packets.+ The per-socket policy must be compatible with both device net policy and+ per-task policy.++Components of Net Policies++ Net policy object: it is a combination of CPU and queue. The queue IRQ has+ to set affinity with the CPU. It can be shared between sockets and tasks.+ A reference counter is used to track the sharing number.++ Net policy object list: each device policy has an object list. Once the+ device policy is determined, the net policy object will be inserted into+ the net policy object list. The net policy object list does not change+ unless the CPU/queue number is changed, the netpolicy is disabled or+ the device policy is changed.+ The network performance for objects could be different because of the+ CPU/queue topology and dev location. The objects which can bring high+ performance are in the front of the list.++ RCU hash table: an RCU hash table to maintain the relationship between+ the task/socket and the assigned object. The task/socket can get the+ assigned object by searching the table.+ If it is the first time, there is no assigned object in the table. It will+ go through the object list to find the available object based on position+ and reference number.+ If the net policy object list changes, all the assigned objects will become+ invalid.++NET POLICY APIs++Interfaces between net policy and device driver++ int (*ndo_netpolicy_init)(struct net_device *dev,+ struct netpolicy_info *info);++ The device driver who has NET policy support must implement this interface.+ In this interface, the device driver does necessory initialization, and fill+ the info for net policy module. The information could include supported+ policy, MIX policy support, queue pair support and so on.++ int (*ndo_get_irq_info)(struct net_device *dev,+ struct netpolicy_dev_info *info);++ This interface is used to get more accurate device IRQ information.++ int (*ndo_set_net_policy)(struct net_device *dev,+ enum netpolicy_name name);++ This interface is used to set device net policy by name. It is device driver's+ responsibility to set driver specific configuration for the given policy.++Interfaces between net policy and kernel++ int netpolicy_register(struct netpolicy_instance *instance);+ void netpolicy_unregister(struct netpolicy_instance *instance);++ This interface is used to register per task/socket net policy.+ The socket/task can only be benefited when it register itself with specific+ policy. After registeration, a record will be created and inserted into RCU+ hash table, which include all the NET policy related information for the+ socket/task, such as pointor, policy, object and so on.++ int netpolicy_pick_queue(struct netpolicy_instance *instance, bool is_rx);++ This interface is used to find the proper queue(object) for packet+ receiving and transmitting. The proper queue is picked from object list+ according to policy, reference, location and so on.+++ int netpolicy_set_rules(struct netpolicy_instance *instance);++ This interface is used to add device specific rules. Once the rule is+ applied, the packet from specific IP and port will be redirected to the+ given queue. This interface is usually used on receive side.++NET POLICY INTERFACE++Device net policy setting++ /proc/net/netpolicy/$DEV/policy++ Concatenating(cat) the "policy" file can show the available device+ policies, if there is no device policy applied. Otherwise, the device+ policy name will be printed out. If it is MIX policy, the policy for each+ queue will also be printed out.+ User can set device net policy by writing policy name.++Task policy setting++ /proc/$PID/net_policy++ Concatenating(cat) the "net_policy" file can show the applied per task+ policy.+ User can set per task net policy by writing policy name.++ OR++ prctl(PR_SET_NETPOLICY, POLICY_NAME, NULL, NULL, NULL)++ "prctl" is an alternative way to set/get per task policy.++Socket policy setting++ setsockopt(sockfd,SOL_SOCKET,SO_NETPOLICY,&policy,sizeof(int))++ The socket net policy can be set by option SO_NETPOLICY of setsockopt.++AVAILABLE NET POLICIES++ The available net policies are defined as below:+ - CPU: intends to get higher throughput and lower CPU% (power saving).+ This policy can be applied as either device net policy or+ task/socket net policy.+ - BULK: intends to get highest throughput. This policy can be applied as+ either device net policy or task/socket net policy.+ - LATENCY: intends to get lowest latency. This policy can be applied as+ either device net policy or task/socket net policy.+ - MIX: combination of other policies, which allows each queue to have a+ different policy. This policy can only be set as device net policy.+
From: Kan Liang <redacted>
NET policy can not fulfill users request without limit, because of the
security consideration and device limitation. For security
consideration, the attacker may fake millions of per task/socket request
to crash the system. For device limitation, the flow director rules
number is limited on i40e driver. NET policy should not run out the
rules, otherwise it cannot guarantee the good performance.
This patch limits the total record number in RCU hash table to fix the
cases as above. The max total record number could vary for different
device. For i40e driver, it limits the record number according to flow
director rules number. If it exceeds the limitation, the registeration
and new object request will be denied.
Since the dev may not be aware in registeration, the cur_rec_num may not
be updated on time. So the actual registered record may exceeds the
max_rec_num. But it will not bring any problems. Because the patch also
check the limitation on object request. It guarantees that the device
resource will not run out.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netpolicy.h | 4 ++++
net/core/netpolicy.c | 23 +++++++++++++++++++++--
2 files changed, 25 insertions(+), 2 deletions(-)
@@ -88,6 +89,9 @@ struct netpolicy_info {structnetpolicy_sys_infosys_info;/* List of policy objects 0 rx 1 tx */structlist_headobj_list[NETPOLICY_RXTX][NET_POLICY_MAX];+/* for record number limitation */+intmax_rec_num;+atomic_tcur_rec_num;};structnetpolicy_tcpudpip4_spec{
@@ -685,6 +692,7 @@ int netpolicy_register(struct netpolicy_instance *instance,enumnetpolicy_namepolicy){unsignedlongptr_id=(uintptr_t)instance->ptr;+structnet_device*dev=instance->dev;structnetpolicy_record*new,*old;if(!is_net_policy_valid(policy)){
@@ -692,6 +700,10 @@ int netpolicy_register(struct netpolicy_instance *instance,return-EINVAL;}+if(dev&&dev->netpolicy&&+(atomic_read(&dev->netpolicy->cur_rec_num)>dev->netpolicy->max_rec_num))+return-ENOSPC;+new=kzalloc(sizeof(*new),GFP_KERNEL);if(!new){instance->policy=NET_POLICY_INVALID;
@@ -715,6 +727,8 @@ int netpolicy_register(struct netpolicy_instance *instance,new->dev=instance->dev;new->policy=policy;hash_add_rcu(np_record_hash,&new->hash_node,ptr_id);+if(dev&&dev->netpolicy)+atomic_inc(&dev->netpolicy->cur_rec_num);}instance->policy=policy;spin_unlock_bh(&np_hashtable_lock);
@@ -761,6 +775,8 @@ void netpolicy_unregister(struct netpolicy_instance *instance)/* The record cannot be share. It can be safely free. */put_queue(record->dev,record->rx_obj,record->tx_obj);kfree(record);+if(dev&&dev->netpolicy)+atomic_dec(&dev->netpolicy->cur_rec_num);}instance->policy=NET_POLICY_INVALID;spin_unlock_bh(&np_hashtable_lock);
@@ -1298,6 +1314,9 @@ int init_netpolicy(struct net_device *dev)gotounlock;}+if(!dev->netpolicy->max_rec_num)+dev->netpolicy->max_rec_num=NETPOLICY_MAX_RECORD_NUM;+spin_lock(&dev->np_ob_list_lock);for(i=0;i<NETPOLICY_RXTX;i++){for(j=NET_POLICY_NONE;j<NET_POLICY_MAX;j++)
From: Kan Liang <redacted>
Current implementation searches the hash table to get assigned object
for each transmit/receive packet. It's not necessory, because the
assigned object usually remain unchanged. This patch store the assigned
queue to speed up the searching process.
But under certain situations, the assigned objects has to be changed,
especially when system cpu and queue mapping changed, such as CPU
hotplug, device hotplug, queue number changes and so on. In this patch,
the netpolicy_sys_map_version is used to track the system cpu and queue
mapping changes. If the netpolicy_sys_map_version doesn't match with the
instance's version, the stored queue will be dropped. The
netpolicy_sys_map_version is protected by RCU lock.
Also, to reduce the overhead, this patch asynchronously find the
available object by work queue. So the first several packets may not be
benefited.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netpolicy.h | 23 +++++++---
net/core/netpolicy.c | 106 +++++++++++++++++++++++++++++++++++++++++++++-
net/ipv4/af_inet.c | 7 +--
3 files changed, 125 insertions(+), 11 deletions(-)
@@ -110,7 +116,14 @@ struct netpolicy_instance {structwork_structfc_wk;/* flow classification work */atomic_tfc_wk_cnt;/* flow classification work number */structnetpolicy_flow_specflow;/* flow information */-+/* For fast path */+atomic_trx_queue;+atomic_ttx_queue;+structwork_structget_rx_wk;+atomic_tget_rx_wk_cnt;+structwork_structget_tx_wk;+atomic_tget_tx_wk_cnt;+intsys_map_version;};structnetpolicy_cpu_load{
@@ -1379,6 +1463,19 @@ void update_netpolicy_sys_map(void)}dev->netpolicy->cur_policy=cur_policy;++old_version=rcu_dereference_protected(dev->netpolicy->sys_info.version,1);+new_version=kzalloc(sizeof(*new_version),GFP_ATOMIC);+if(new_version){+new_version->major=old_version->major+1;+if(new_version->major<0)+new_version->major=0;+rcu_assign_pointer(dev->netpolicy->sys_info.version,new_version);+kfree_rcu(old_version,rcu);+}else{+pr_warn("NETPOLICY: Failed to update sys map version for dev %s\n",+dev->name);+}unlock:spin_unlock(&dev->np_lock);}
@@ -1418,6 +1515,12 @@ static int __init netpolicy_init(void)if(!np_fc_wq)return-ENOMEM;+np_fast_path_wq=create_workqueue("np_fast_path");+if(!np_fast_path_wq){+destroy_workqueue(np_fc_wq);+return-ENOMEM;+}+ret=register_pernet_subsys(&netpolicy_net_ops);if(!ret)register_netdevice_notifier(&netpolicy_dev_notf);
@@ -1432,6 +1535,7 @@ static int __init netpolicy_init(void)staticvoid__exitnetpolicy_exit(void){destroy_workqueue(np_fc_wq);+destroy_workqueue(np_fast_path_wq);unregister_netdevice_notifier(&netpolicy_dev_notf);unregister_pernet_subsys(&netpolicy_net_ops);
From: Kan Liang <redacted>
Users may not want to change the source code to add per task net polic
support. Or they may want to change a running task's net policy. prctl
does not work for both cases.
This patch adds an interface in /proc, which can be used to set and
retrieve policy of already running tasks. User can write the policy name
into /proc/$PID/net_policy to set per task net policy.
Signed-off-by: Kan Liang <redacted>
---
fs/proc/base.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
From: Kan Liang <redacted>
Usually, application as a whole has specific requirement. Applying the
net policy to all sockets one by one in the application is too complex.
This patch introduces per task net policy to address this case.
Once the per task net policy is applied, all the sockets in the
application will apply the same net policy. Also, per task net policy
can be inherited by all children.
The usage of PR_SET_NETPOLICY option is as below.
prctl(PR_SET_NETPOLICY, POLICY_NAME, NULL, NULL, NULL).
It applies per task policy. The policy name must be valid and compatible
with current device policy. Othrewise, it will error out. The task
policy will be set to NET_POLICY_INVALID.
Signed-off-by: Kan Liang <redacted>
---
include/linux/init_task.h | 9 +++++++++
include/linux/sched.h | 5 +++++
include/net/sock.h | 12 +++++++++++-
include/uapi/linux/prctl.h | 4 ++++
kernel/exit.c | 4 ++++
kernel/fork.c | 6 ++++++
kernel/sys.c | 31 +++++++++++++++++++++++++++++++
net/core/netpolicy.c | 35 +++++++++++++++++++++++++++++++++++
net/core/sock.c | 10 +++++++++-
net/ipv4/af_inet.c | 7 +++++--
10 files changed, 119 insertions(+), 4 deletions(-)
@@ -62,6 +62,8 @@ struct sched_param {#include<asm/processor.h>+#include<linux/netpolicy.h>+#define SCHED_ATTR_SIZE_VER0 48 /* sizeof first published struct *//*
@@ -1923,6 +1925,9 @@ struct task_struct {#ifdef CONFIG_MMUstructtask_struct*oom_reaper_list;#endif+#ifdef CONFIG_NETPOLICY+structnetpolicy_instancetask_netpolicy;+#endif/* CPU-specific state of this task */structthread_structthread;/*
@@ -2280,10 +2281,19 @@ extern int sysctl_optmem_max;extern__u32sysctl_wmem_default;extern__u32sysctl_rmem_default;-/* Return netpolicy instance information from socket. */+/* Return netpolicy instance information from either task or socket.+*Ifbothtaskandsockethavenetpolicyinstanceinformation,+*usingtask'sandunregisteringsocket's.Becausetaskpolicyis+*dominantpolicy+*/staticinlinestructnetpolicy_instance*netpolicy_find_instance(structsock*sk){#ifdef CONFIG_NETPOLICY+if(is_net_policy_valid(current->task_netpolicy.policy)){+if(is_net_policy_valid(sk->sk_netpolicy.policy))+netpolicy_unregister(&sk->sk_netpolicy);+return¤t->task_netpolicy;+}if(is_net_policy_valid(sk->sk_netpolicy.policy))return&sk->sk_netpolicy;#endif
@@ -1451,6 +1451,12 @@ static struct task_struct *copy_process(unsigned long clone_flags,p->sequential_io_avg=0;#endif+#ifdef CONFIG_NETPOLICY+p->task_netpolicy.ptr=(void*)p;+if(is_net_policy_valid(p->task_netpolicy.policy))+netpolicy_register(&p->task_netpolicy,p->task_netpolicy.policy);+#endif+/* Perform scheduler related setup. Assign this task to a CPU. */retval=sched_fork(clone_flags,p);if(retval)
@@ -1006,7 +1006,13 @@ set_rcvbuf:#ifdef CONFIG_NETPOLICYcaseSO_NETPOLICY:-ret=netpolicy_register(&sk->sk_netpolicy,val);+if(is_net_policy_valid(current->task_netpolicy.policy)&&+(current->task_netpolicy.policy!=val)){+printk_ratelimited(KERN_WARNING"NETPOLICY: new policy is not compatible with task netpolicy\n");+ret=-EINVAL;+}else{+ret=netpolicy_register(&sk->sk_netpolicy,val);+}break;#endifdefault:
@@ -766,8 +766,11 @@ static void sock_netpolicy_manage_flow(struct sock *sk, struct msghdr *msg)if(!instance)return;-if(!instance->dev)-return;+if(!instance->dev){+if(!sk->sk_netpolicy.dev)+return;+instance->dev=sk->sk_netpolicy.dev;+}flow=&instance->flow;/* TODO: need to change here and add more protocol support */
From: Kan Liang <redacted>
For setting Rx queues, this patch configure Rx network flow
classification rules to redirect the packets to the assigned queue.
Since we may not get all the information required for rule until the
first packet arrived, it will add the rule after recvmsg. Also, to
avoid destroying the connection rates, the configuration will be done
asynchronized by work queue. So the first several packets may not use
the assigned queue.
The dev information will be discarded in udp_queue_rcv_skb, so we record
it in netpolicy struct in advance.
This patch only support INET tcp4 and udp4. It can be extend to other
socket type and V6 later shortly.
For each sk, it only supports one rule. If the port/address changed, the
previos rule will be replaced.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netpolicy.h | 30 +++++++++++
net/core/netpolicy.c | 132 +++++++++++++++++++++++++++++++++++++++++++++-
net/ipv4/af_inet.c | 71 +++++++++++++++++++++++++
net/ipv4/udp.c | 4 ++
4 files changed, 236 insertions(+), 1 deletion(-)
@@ -81,11 +83,34 @@ struct netpolicy_info {structlist_headobj_list[NETPOLICY_RXTX][NET_POLICY_MAX];};+structnetpolicy_tcpudpip4_spec{+/* source and Destination host and port */+__be32ip4src;+__be32ip4dst;+__be16psrc;+__be16pdst;+};++unionnetpolicy_flow_union{+structnetpolicy_tcpudpip4_spectcp_udp_ip4_spec;+};++structnetpolicy_flow_spec{+__u32flow_type;+unionnetpolicy_flow_unionspec;+};+structnetpolicy_instance{structnet_device*dev;enumnetpolicy_namepolicy;/* required policy */void*ptr;/* pointers */structtask_struct*task;+intlocation;/* rule location */+atomic_trule_queue;/* queue set by rule */+structwork_structfc_wk;/* flow classification work */+atomic_tfc_wk_cnt;/* flow classification work number */+structnetpolicy_flow_specflow;/* flow information */+};structnetpolicy_cpu_load{
@@ -106,6 +131,7 @@ extern int netpolicy_register(struct netpolicy_instance *instance,enumnetpolicy_namepolicy);externvoidnetpolicy_unregister(structnetpolicy_instance*instance);externintnetpolicy_pick_queue(structnetpolicy_instance*instance,boolis_rx);+externvoidnetpolicy_set_rules(structnetpolicy_instance*instance);#elsestaticinlinevoidupdate_netpolicy_sys_map(void){
@@ -472,6 +474,90 @@ int netpolicy_pick_queue(struct netpolicy_instance *instance, bool is_rx)}EXPORT_SYMBOL(netpolicy_pick_queue);+voidnp_flow_rule_set(structwork_struct*wk)+{+structnetpolicy_instance*instance;+structnetpolicy_flow_spec*flow;+structethtool_rxnfccmd;+structnet_device*dev;+intqueue,ret;++instance=container_of(wk,structnetpolicy_instance,+fc_wk);+if(!instance)+gotodone;++flow=&instance->flow;+if(WARN_ON(!flow))+gotodone;+dev=instance->dev;+if(WARN_ON(!dev))+gotodone;++/* Check if ntuple is supported */+if(!dev->ethtool_ops->set_rxnfc)+gotodone;++/* Only support TCP/UDP V4 by now */+if((flow->flow_type!=TCP_V4_FLOW)&&+(flow->flow_type!=UDP_V4_FLOW))+gotodone;++queue=get_avail_queue(instance,true);+if(queue<0)+gotodone;++/* using ethtool flow-type to configure+*Rxnetworkflowclassificationoptionsorrules+*RX_CLS_LOC_ANYmustbesupportedbythedriver+*/+memset(&cmd,0,sizeof(cmd));+cmd.cmd=ETHTOOL_SRXCLSRLINS;+cmd.fs.flow_type=flow->flow_type;+cmd.fs.h_u.tcp_ip4_spec.ip4src=flow->spec.tcp_udp_ip4_spec.ip4src;+cmd.fs.h_u.tcp_ip4_spec.psrc=flow->spec.tcp_udp_ip4_spec.psrc;+cmd.fs.h_u.tcp_ip4_spec.ip4dst=flow->spec.tcp_udp_ip4_spec.ip4dst;+cmd.fs.h_u.tcp_ip4_spec.pdst=flow->spec.tcp_udp_ip4_spec.pdst;+cmd.fs.ring_cookie=queue;+cmd.fs.location=RX_CLS_LOC_ANY;+rtnl_lock();+ret=dev->ethtool_ops->set_rxnfc(dev,&cmd);+rtnl_unlock();+if(ret<0){+pr_warn("Failed to set rules ret %d\n",ret);+atomic_set(&instance->rule_queue,NETPOLICY_INVALID_QUEUE);+gotodone;+}++/* TODO: now one sk only has one rule */+if(instance->location!=NETPOLICY_INVALID_LOC){+/* delete the old rule */+structethtool_rxnfcdel_cmd;++del_cmd.cmd=ETHTOOL_SRXCLSRLDEL;+del_cmd.fs.location=instance->location;+rtnl_lock();+ret=dev->ethtool_ops->set_rxnfc(dev,&del_cmd);+rtnl_unlock();+if(ret<0)+pr_warn("Failed to delete rules ret %d\n",ret);+}++/* record rule location */+instance->location=cmd.fs.location;+atomic_set(&instance->rule_queue,queue);+done:+atomic_set(&instance->fc_wk_cnt,0);+}++staticvoidinit_instance(structnetpolicy_instance*instance)+{+instance->location=NETPOLICY_INVALID_LOC;+atomic_set(&instance->rule_queue,NETPOLICY_INVALID_QUEUE);+atomic_set(&instance->fc_wk_cnt,0);+INIT_WORK(&instance->fc_wk,np_flow_rule_set);+}+/***netpolicy_register()-Registerpersocket/taskpolicyrequest*@instance:NETpolicypersocket/taskinstanceinfo
@@ -516,6 +602,7 @@ int netpolicy_register(struct netpolicy_instance *instance,}kfree(new);}else{+init_instance(instance);new->ptr_id=ptr_id;new->dev=instance->dev;new->policy=policy;
@@ -538,8 +625,23 @@ EXPORT_SYMBOL(netpolicy_register);*/voidnetpolicy_unregister(structnetpolicy_instance*instance){-structnetpolicy_record*record;unsignedlongptr_id=(uintptr_t)instance->ptr;+structnet_device*dev=instance->dev;+structnetpolicy_record*record;++cancel_work_sync(&instance->fc_wk);+/* remove FD rules */+if(dev&&instance->location!=NETPOLICY_INVALID_LOC){+structethtool_rxnfcdel_cmd;++del_cmd.cmd=ETHTOOL_SRXCLSRLDEL;+del_cmd.fs.location=instance->location;+rtnl_lock();+dev->ethtool_ops->set_rxnfc(dev,&del_cmd);+rtnl_unlock();+instance->location=NETPOLICY_INVALID_LOC;+atomic_set(&instance->rule_queue,NETPOLICY_INVALID_QUEUE);+}spin_lock_bh(&np_hashtable_lock);/* del from hash table */
@@ -555,6 +657,28 @@ void netpolicy_unregister(struct netpolicy_instance *instance)}EXPORT_SYMBOL(netpolicy_unregister);+/**+*netpolicy_set_rules()-ConfigureRxnetworkflowclassificationrules+*@instance:NETpolicypersocket/taskinstanceinfo+*+*ThisfunctionintendstoconfigureRxnetworkflowclassificationrules+*accordingtoipandportinformation.Theconfigurationwillbedone+*asynchronizedbyworkqueue.Itavoidstodestroytheconnectionrates.+*+*Currently,itonlysupportsTCPandUDPV4.Otherprotocolswillbe+*supportedlater.+*+*/+voidnetpolicy_set_rules(structnetpolicy_instance*instance)+{+/* There should be only one work to run at the same time */+if(!atomic_cmpxchg(&instance->fc_wk_cnt,0,1)){+instance->task=current;+queue_work(np_fc_wq,&instance->fc_wk);+}+}+EXPORT_SYMBOL(netpolicy_set_rules);+constchar*policy_name[NET_POLICY_MAX]={"NONE","CPU",
@@ -1255,6 +1379,10 @@ static int __init netpolicy_init(void){intret;+np_fc_wq=create_workqueue("np_fc");+if(!np_fc_wq)+return-ENOMEM;+ret=register_pernet_subsys(&netpolicy_net_ops);if(!ret)register_netdevice_notifier(&netpolicy_dev_notf);
@@ -1268,6 +1396,8 @@ static int __init netpolicy_init(void)staticvoid__exitnetpolicy_exit(void){+destroy_workqueue(np_fc_wq);+unregister_netdevice_notifier(&netpolicy_dev_notf);unregister_pernet_subsys(&netpolicy_net_ops);
@@ -754,6 +754,71 @@ ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset,}EXPORT_SYMBOL(inet_sendpage);+staticvoidsock_netpolicy_manage_flow(structsock*sk,structmsghdr*msg)+{+#ifdef CONFIG_NETPOLICY+structnetpolicy_instance*instance;+structnetpolicy_flow_spec*flow;+boolchange=false;+intqueue;++instance=netpolicy_find_instance(sk);+if(!instance)+return;++if(!instance->dev)+return;++flow=&instance->flow;+/* TODO: need to change here and add more protocol support */+if(sk->sk_family!=AF_INET)+return;+if((sk->sk_protocol==IPPROTO_TCP)&&+(sk->sk_type==SOCK_STREAM)){+if((flow->flow_type!=TCP_V4_FLOW)||+(flow->spec.tcp_udp_ip4_spec.ip4src!=sk->sk_daddr)||+(flow->spec.tcp_udp_ip4_spec.psrc!=sk->sk_dport)||+(flow->spec.tcp_udp_ip4_spec.ip4dst!=sk->sk_rcv_saddr)||+(flow->spec.tcp_udp_ip4_spec.pdst!=htons(sk->sk_num)))+change=true;+if(change){+flow->flow_type=TCP_V4_FLOW;+flow->spec.tcp_udp_ip4_spec.ip4src=sk->sk_daddr;+flow->spec.tcp_udp_ip4_spec.psrc=sk->sk_dport;+flow->spec.tcp_udp_ip4_spec.ip4dst=sk->sk_rcv_saddr;+flow->spec.tcp_udp_ip4_spec.pdst=htons(sk->sk_num);+}+}elseif((sk->sk_protocol==IPPROTO_UDP)&&+(sk->sk_type==SOCK_DGRAM)){+DECLARE_SOCKADDR(structsockaddr_in*,sin,msg->msg_name);++if(!sin||!sin->sin_addr.s_addr||!sin->sin_port)+return;+if((flow->flow_type!=UDP_V4_FLOW)||+(flow->spec.tcp_udp_ip4_spec.ip4src!=sin->sin_addr.s_addr)||+(flow->spec.tcp_udp_ip4_spec.psrc!=sin->sin_port)||+(flow->spec.tcp_udp_ip4_spec.ip4dst!=sk->sk_rcv_saddr)||+(flow->spec.tcp_udp_ip4_spec.pdst!=htons(sk->sk_num)))+change=true;+if(change){+flow->flow_type=UDP_V4_FLOW;+flow->spec.tcp_udp_ip4_spec.ip4src=sin->sin_addr.s_addr;+flow->spec.tcp_udp_ip4_spec.psrc=sin->sin_port;+flow->spec.tcp_udp_ip4_spec.ip4dst=sk->sk_rcv_saddr;+flow->spec.tcp_udp_ip4_spec.pdst=htons(sk->sk_num);+}+}else{+return;+}++queue=netpolicy_pick_queue(instance,true);+if(queue<0)+return;+if((queue!=atomic_read(&instance->rule_queue))||change)+netpolicy_set_rules(instance);+#endif+}+intinet_recvmsg(structsocket*sock,structmsghdr*msg,size_tsize,intflags){
@@ -767,6 +832,12 @@ int inet_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,flags&~MSG_DONTWAIT,&addr_len);if(err>=0)msg->msg_namelen=addr_len;++/* The dev info, src address and port information for UDP+*canonlyberetrievedafterprocessingthemsg.+*/+sock_netpolicy_manage_flow(sk,msg);+returnerr;}EXPORT_SYMBOL(inet_recvmsg);
@@ -1786,6 +1786,10 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,if(sk){intret;+#ifdef CONFIG_NETPOLICY+/* Record dev info before it's discarded in udp_queue_rcv_skb */+sk->sk_netpolicy.dev=skb->dev;+#endifif(inet_get_convert_csum(sk)&&uh->check&&!IS_UDPLITE(sk))skb_checksum_try_convert(skb,IPPROTO_UDP,uh->check,inet_compute_pseudo);
From: Kan Liang <redacted>
This patch extends the netpolicy to support tc bpf when selecting Tx
queue. It implements a bpf classifier for clsact qdisc. The classifier
will pick up the proper queue from net policy subsystem. This queue
selection from tc is not compatible with XPS. So XPS will be invalid.
Currently, tc bpf extension only supports the queue selection on egress.
To enable the extension, the following command must be applied.
# ./tc qdisc add dev $DEVNAME clsact
# ./tc filter add dev $DEVNAME egress bpf obj netpolicy_kern.o
Signed-off-by: Kan Liang <redacted>
---
include/uapi/linux/bpf.h | 8 ++++++++
net/core/dev.c | 4 ++--
net/core/filter.c | 36 ++++++++++++++++++++++++++++++++++++
samples/bpf/Makefile | 1 +
samples/bpf/bpf_helpers.h | 2 ++
5 files changed, 49 insertions(+), 2 deletions(-)
@@ -88,6 +88,8 @@ static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flag(void*)BPF_FUNC_l4_csum_replace;staticint(*bpf_skb_under_cgroup)(void*ctx,void*map,intindex)=(void*)BPF_FUNC_skb_under_cgroup;+staticint(*bpf_netpolicy)(void*ctx)=+(void*)BPF_FUNC_netpolicy;#if defined(__x86_64__)
From: Kan Liang <redacted>
When the device tries to transmit a packet, netdev_pick_tx is called to
find the available tx queues. If the net policy is applied, it picks up
the assigned tx queue from net policy subsystem, and redirect the
traffic to the assigned queue.
Signed-off-by: Kan Liang <redacted>
---
include/net/sock.h | 9 +++++++++
net/core/dev.c | 20 ++++++++++++++++++--
2 files changed, 27 insertions(+), 2 deletions(-)
From: Kan Liang <redacted>
To achieve better network performance, the key step is to distribute the
packets to dedicated queues according to policy and system run time
status.
This patch provides an interface which can return the proper dedicated
queue for socket/task. Then the packets of the socket/task will be
redirect to the dedicated queue for better network performance.
For selecting the proper queue, currently it checks the CPU loads and
ref number. The object which has lowest CPU loads and ref number will be
chosen.
The selected object will be stored in hashtable. So it does not need to
go through the whole object list every time.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netpolicy.h | 12 ++++
include/linux/sched.h | 3 +
kernel/sched/fair.c | 8 +--
net/core/netpolicy.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 198 insertions(+), 4 deletions(-)
@@ -1257,10 +1257,10 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page,group_faults_cpu(ng,src_nid)*group_faults(p,dst_nid)*4;}-staticunsignedlongweighted_cpuload(constintcpu);+unsignedlongweighted_cpuload(constintcpu);staticunsignedlongsource_load(intcpu,inttype);staticunsignedlongtarget_load(intcpu,inttype);-staticunsignedlongcapacity_of(intcpu);+unsignedlongcapacity_of(intcpu);staticlongeffective_load(structtask_group*tg,intcpu,longwl,longwg);/* Cached statistics for all CPUs within a node */
@@ -4752,7 +4752,7 @@ static void cpu_load_update(struct rq *this_rq, unsigned long this_load,}/* Used instead of source_load when we know the type == 0 */-staticunsignedlongweighted_cpuload(constintcpu)+unsignedlongweighted_cpuload(constintcpu){returncfs_rq_runnable_load_avg(&cpu_rq(cpu)->cfs);}
@@ -4902,7 +4902,7 @@ static unsigned long target_load(int cpu, int type)returnmax(rq->cpu_load[type-1],total);}-staticunsignedlongcapacity_of(intcpu)+unsignedlongcapacity_of(intcpu){returncpu_rq(cpu)->cpu_capacity;}
@@ -293,6 +294,184 @@ static void netpolicy_record_clear_dev_node(struct net_device *dev)spin_unlock_bh(&np_hashtable_lock);}+staticstructnetpolicy_object*get_avail_object(structnet_device*dev,+enumnetpolicy_namepolicy,+structnetpolicy_instance*instance,+boolis_rx)+{+intavail_cpu_num=cpumask_weight(tsk_cpus_allowed(instance->task));+intdir=is_rx?NETPOLICY_RX:NETPOLICY_TX;+structnetpolicy_object*tmp,*obj=NULL;+unsignedlongload=0,min_load=-1;+structnetpolicy_cpu_load*cpu_load;+inti=0,val=-1;++/* Check if net policy is supported */+if(!dev||!dev->netpolicy)+gotoexit;++/* The system should have queues which support the request policy. */+if((policy!=dev->netpolicy->cur_policy)&&+(dev->netpolicy->cur_policy!=NET_POLICY_MIX))+gotoexit;++if(!avail_cpu_num)+gotoexit;++cpu_load=kcalloc(avail_cpu_num,sizeof(*cpu_load),GFP_KERNEL);+if(!cpu_load)+gotoexit;++spin_lock_bh(&dev->np_ob_list_lock);++/* find the lowest load and remove obvious high load objects */+list_for_each_entry(tmp,&dev->netpolicy->obj_list[dir][policy],list){+if(!cpumask_test_cpu(tmp->cpu,tsk_cpus_allowed(instance->task)))+continue;++#ifdef CONFIG_SMP+/* normalized load */+load=weighted_cpuload(tmp->cpu)*100/capacity_of(tmp->cpu);++if((min_load!=-1)&&+load>(min_load+LOAD_TOLERANCE))+continue;+#endif+cpu_load[i].load=load;+cpu_load[i].obj=tmp;+if((min_load==-1)||+(load<min_load))+min_load=load;+i++;+}+avail_cpu_num=i;+spin_unlock_bh(&dev->np_ob_list_lock);++for(i=0;i<avail_cpu_num;i++){+if(cpu_load[i].load>(min_load+LOAD_TOLERANCE))+continue;++tmp=cpu_load[i].obj;+if((val>atomic_read(&tmp->refcnt))||+(val==-1)){+val=atomic_read(&tmp->refcnt);+obj=tmp;+}+}++if(!obj)+gotofree_load;++atomic_inc(&obj->refcnt);++free_load:+kfree(cpu_load);+exit:+returnobj;+}++staticintget_avail_queue(structnetpolicy_instance*instance,boolis_rx)+{+structnetpolicy_record*old_record,*new_record;+structnet_device*dev=instance->dev;+unsignedlongptr_id=(uintptr_t)instance->ptr;+intqueue=-1;++spin_lock_bh(&np_hashtable_lock);+old_record=netpolicy_record_search(ptr_id);+if(!old_record){+pr_warn("NETPOLICY: doesn't registered. Remove net policy settings!\n");+instance->policy=NET_POLICY_INVALID;+gotoerr;+}++if(is_rx&&old_record->rx_obj){+queue=old_record->rx_obj->queue;+}elseif(!is_rx&&old_record->tx_obj){+queue=old_record->tx_obj->queue;+}else{+new_record=kzalloc(sizeof(*new_record),GFP_KERNEL);+if(!new_record)+gotoerr;+memcpy(new_record,old_record,sizeof(*new_record));++if(is_rx){+new_record->rx_obj=get_avail_object(dev,new_record->policy,+instance,is_rx);+if(!new_record->dev)+new_record->dev=dev;+if(!new_record->rx_obj){+kfree(new_record);+gotoerr;+}+queue=new_record->rx_obj->queue;+}else{+new_record->tx_obj=get_avail_object(dev,new_record->policy,+instance,is_rx);+if(!new_record->dev)+new_record->dev=dev;+if(!new_record->tx_obj){+kfree(new_record);+gotoerr;+}+queue=new_record->tx_obj->queue;+}+/* update record */+hlist_replace_rcu(&old_record->hash_node,&new_record->hash_node);+kfree(old_record);+}+err:+spin_unlock_bh(&np_hashtable_lock);+returnqueue;+}++staticinlineboolpolicy_validate(structnetpolicy_instance*instance)+{+structnet_device*dev=instance->dev;+enumnetpolicy_namecur_policy;++cur_policy=dev->netpolicy->cur_policy;+if((instance->policy==NET_POLICY_NONE)||+(cur_policy==NET_POLICY_NONE))+returnfalse;++if(((cur_policy!=NET_POLICY_MIX)&&(cur_policy!=instance->policy))||+((cur_policy==NET_POLICY_MIX)&&(instance->policy==NET_POLICY_CPU))){+pr_warn("NETPOLICY: %s current device policy %s doesn't support required policy %s! Remove net policy settings!\n",+dev->name,policy_name[cur_policy],+policy_name[instance->policy]);+returnfalse;+}+returntrue;+}++/**+*netpolicy_pick_queue()-Findproperqueue+*@instance:NETpolicypersocket/taskinstanceinfo+*@is_rx:RXqueueorTXqueue+*+*Thisfunctionintendstofindtheproperqueueaccordingtopolicy.+*Forselectingtheproperqueue,currentlyitusesround-robinalgorithm+*tofindtheavailableobjectfromthegivenpolicyobjectlist.+*Theselectedobjectwillbestoredinhashtable.Soitdoesnotneedto+*gothroughthewholeobjectlisteverytime.+*+*Return:negativeonfailure,otherwiseontheassignedqueue+*/+intnetpolicy_pick_queue(structnetpolicy_instance*instance,boolis_rx)+{+structnet_device*dev=instance->dev;++if(!dev||!dev->netpolicy)+return-EINVAL;++if(!policy_validate(instance))+return-EINVAL;++returnget_avail_queue(instance,is_rx);+}+EXPORT_SYMBOL(netpolicy_pick_queue);+/***netpolicy_register()-Registerpersocket/taskpolicyrequest*@instance:NETpolicypersocket/taskinstanceinfo
From: Kan Liang <redacted>
The socket/task can only be benefited when it register itself with
specific policy. If it's the first time to register, a record will be
created and inserted into RCU hash table. The record includes ptr,
policy and object information. ptr is the socket/task's pointer which is
used as key to search the record in hash table. Object will be assigned
later.
This patch also introduces a new type NET_POLICY_INVALID, which
indicates that the task/socket are not registered.
np_hashtable_lock is introduced to protect the hash table.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netpolicy.h | 26 ++++++++
net/core/netpolicy.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 179 insertions(+)
@@ -226,6 +239,143 @@ static int netpolicy_enable(struct net_device *dev)return0;}+staticstructnetpolicy_record*netpolicy_record_search(unsignedlongptr_id)+{+structnetpolicy_record*rec=NULL;++hash_for_each_possible_rcu(np_record_hash,rec,hash_node,ptr_id){+if(rec->ptr_id==ptr_id)+break;+}++returnrec;+}++staticvoidput_queue(structnet_device*dev,+structnetpolicy_object*rx_obj,+structnetpolicy_object*tx_obj)+{+if(!dev||!dev->netpolicy)+return;++if(rx_obj)+atomic_dec(&rx_obj->refcnt);+if(tx_obj)+atomic_dec(&tx_obj->refcnt);+}++staticvoidnetpolicy_record_clear_obj(void)+{+structnetpolicy_record*rec;+inti;++spin_lock(&np_hashtable_lock);+hash_for_each_rcu(np_record_hash,i,rec,hash_node){+put_queue(rec->dev,rec->rx_obj,rec->tx_obj);+rec->rx_obj=NULL;+rec->tx_obj=NULL;+}+spin_unlock(&np_hashtable_lock);+}++staticvoidnetpolicy_record_clear_dev_node(structnet_device*dev)+{+structnetpolicy_record*rec;+inti;++spin_lock_bh(&np_hashtable_lock);+hash_for_each_rcu(np_record_hash,i,rec,hash_node){+if(rec->dev==dev){+hash_del_rcu(&rec->hash_node);+kfree(rec);+}+}+spin_unlock_bh(&np_hashtable_lock);+}++/**+*netpolicy_register()-Registerpersocket/taskpolicyrequest+*@instance:NETpolicypersocket/taskinstanceinfo+*@policy:requestNETpolicy+*+*Thisfunctionintendstoregisterpersocket/taskpolicyrequest.+*Ifit'sthefirsttimetoregister,anrecordwillbecreatedand+*insertedintoRCUhashtable.+*+*Therecordincludesptr,policyandobjectinfo.ptrofthesocket/task+*isthekeytosearchtherecordinhashtable.Objectwillbeassigned+*untilthefirstpacketisreceived/transmitted.+*+*Return:0onsuccess,othersonfailure+*/+intnetpolicy_register(structnetpolicy_instance*instance,+enumnetpolicy_namepolicy)+{+unsignedlongptr_id=(uintptr_t)instance->ptr;+structnetpolicy_record*new,*old;++if(!is_net_policy_valid(policy)){+instance->policy=NET_POLICY_INVALID;+return-EINVAL;+}++new=kzalloc(sizeof(*new),GFP_KERNEL);+if(!new){+instance->policy=NET_POLICY_INVALID;+return-ENOMEM;+}++spin_lock_bh(&np_hashtable_lock);+/* Check it in mapping table */+old=netpolicy_record_search(ptr_id);+if(old){+if(old->policy!=policy){+put_queue(old->dev,old->rx_obj,old->tx_obj);+old->rx_obj=NULL;+old->tx_obj=NULL;+old->policy=policy;+}+kfree(new);+}else{+new->ptr_id=ptr_id;+new->dev=instance->dev;+new->policy=policy;+hash_add_rcu(np_record_hash,&new->hash_node,ptr_id);+}+instance->policy=policy;+spin_unlock_bh(&np_hashtable_lock);++return0;+}+EXPORT_SYMBOL(netpolicy_register);++/**+*netpolicy_unregister()-Unregisterpersocket/taskpolicyrequest+*@instance:NETpolicypersocket/taskinstanceinfo+*+*Thisfunctionintendstounregisterpolicyrequestbydelrelatedrecord+*fromhashtable.+*+*/+voidnetpolicy_unregister(structnetpolicy_instance*instance)+{+structnetpolicy_record*record;+unsignedlongptr_id=(uintptr_t)instance->ptr;++spin_lock_bh(&np_hashtable_lock);+/* del from hash table */+record=netpolicy_record_search(ptr_id);+if(record){+hash_del_rcu(&record->hash_node);+/* The record cannot be share. It can be safely free. */+put_queue(record->dev,record->rx_obj,record->tx_obj);+kfree(record);+}+instance->policy=NET_POLICY_INVALID;+spin_unlock_bh(&np_hashtable_lock);+}+EXPORT_SYMBOL(netpolicy_unregister);+constchar*policy_name[NET_POLICY_MAX]={"NONE","CPU",
@@ -833,6 +983,7 @@ static int netpolicy_notify(struct notifier_block *this,break;caseNETDEV_GOING_DOWN:uninit_netpolicy(dev);+netpolicy_record_clear_dev_node(dev);#ifdef CONFIG_PROC_FSproc_remove(dev->proc_dev);dev->proc_dev=NULL;
From: Kan Liang <redacted>
User can uses ethtool to set the channel number. This patch handles the
channel changes by rebuilding the object list.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netpolicy.h | 8 ++++++++
net/core/ethtool.c | 8 +++++++-
net/core/netpolicy.c | 1 +
3 files changed, 16 insertions(+), 1 deletion(-)
From: Kan Liang <redacted>
For CPU hotplug, the NET policy subsystem will rebuild the sys map and
object list.
Signed-off-by: Kan Liang <redacted>
---
net/core/netpolicy.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
@@ -846,6 +847,77 @@ static struct notifier_block netpolicy_dev_notf = {.notifier_call=netpolicy_notify,};+/**+*update_netpolicy_sys_map()-rebuildthesysmapandobjectlist+*+*Thisfunctiongothroughalltheavailablenetpolicysupporteddevice,+*andrebuildsysmapandobjectlist.+*+*/+voidupdate_netpolicy_sys_map(void)+{+structnet*net;+structnet_device*dev,*aux;+enumnetpolicy_namecur_policy;++for_each_net(net){+for_each_netdev_safe(net,dev,aux){+spin_lock(&dev->np_lock);+if(!dev->netpolicy)+gotounlock;+cur_policy=dev->netpolicy->cur_policy;+if(cur_policy==NET_POLICY_NONE)+gotounlock;++dev->netpolicy->cur_policy=NET_POLICY_NONE;++/* rebuild everything */+netpolicy_disable(dev);+netpolicy_enable(dev);+if(netpolicy_gen_obj_list(dev,cur_policy)){+pr_warn("NETPOLICY: Failed to generate netpolicy object list for dev %s\n",+dev->name);+netpolicy_disable(dev);+gotounlock;+}+if(dev->netdev_ops->ndo_set_net_policy(dev,cur_policy)){+pr_warn("NETPOLICY: Failed to set netpolicy for dev %s\n",+dev->name);+netpolicy_disable(dev);+gotounlock;+}++dev->netpolicy->cur_policy=cur_policy;+unlock:+spin_unlock(&dev->np_lock);+}+}+}++staticintnetpolicy_cpu_callback(structnotifier_block*nfb,+unsignedlongaction,void*hcpu)+{+switch(action&~CPU_TASKS_FROZEN){+caseCPU_ONLINE:+rtnl_lock();+update_netpolicy_sys_map();+rtnl_unlock();+break;+caseCPU_DYING:+rtnl_lock();+update_netpolicy_sys_map();+rtnl_unlock();+break;+}+returnNOTIFY_OK;+}++staticstructnotifier_blocknetpolicy_cpu_notifier={+&netpolicy_cpu_callback,+NULL,+0+};+staticint__initnetpolicy_init(void){intret;
@@ -854,6 +926,10 @@ static int __init netpolicy_init(void)if(!ret)register_netdevice_notifier(&netpolicy_dev_notf);+cpu_notifier_register_begin();+__register_cpu_notifier(&netpolicy_cpu_notifier);+cpu_notifier_register_done();+returnret;}
From: Kan Liang <redacted>
Support NET device up/down/namechange in the NET policy code.
Signed-off-by: Kan Liang <redacted>
---
net/core/netpolicy.c | 66 +++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 58 insertions(+), 8 deletions(-)
@@ -754,6 +757,19 @@ void uninit_netpolicy(struct net_device *dev)spin_unlock(&dev->np_lock);}+staticvoidnetpolicy_dev_init(structnet*net,+structnet_device*dev)+{+if(!init_netpolicy(dev)){+#ifdef CONFIG_PROC_FS+if(netpolicy_proc_dev_init(net,dev))+uninit_netpolicy(dev);+else+#endif /* CONFIG_PROC_FS */+pr_info("NETPOLICY: Init net policy for %s\n",dev->name);+}+}+staticint__net_initnetpolicy_net_init(structnet*net){structnet_device*dev,*aux;
@@ -767,14 +783,7 @@ static int __net_init netpolicy_net_init(struct net *net)rtnl_lock();for_each_netdev_safe(net,dev,aux){-if(!init_netpolicy(dev)){-#ifdef CONFIG_PROC_FS-if(netpolicy_proc_dev_init(net,dev))-uninit_netpolicy(dev);-else-#endif /* CONFIG_PROC_FS */-pr_info("NETPOLICY: Init net policy for %s\n",dev->name);-}+netpolicy_dev_init(net,dev);}rtnl_unlock();
From: Kan Liang <redacted>
MIX policy is combine of other policies. It allows different queue has
different policy. If MIX policy is applied,
/proc/net/netpolicy/$DEV/policy shows per queue policy.
Usually, the workloads requires either high throughput or low latency.
So for current implementation, MIX policy is combine of LATENCY policy
and BULK policy.
The workloads which requires high throughput are usually utilize more
CPU resources compared to the workloads which requires low latency. This
means that if there is an equal interest in latency and throughput
performance, it is better to reserve more BULK queues than LATENCY
queues. In this patch, MIX policy is forced to include 1/3 LATENCY
policy queues and 2/3 BULK policy queues.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netpolicy.h | 7 +++
net/core/netpolicy.c | 139 ++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 136 insertions(+), 10 deletions(-)
@@ -67,6 +73,7 @@ struct netpolicy_info {enumnetpolicy_namecur_policy;unsignedlongavail_policy[BITS_TO_LONGS(NET_POLICY_MAX)];boolirq_affinity;+boolhas_mix_policy;/* cpu and queue mapping information */structnetpolicy_sys_infosys_info;/* List of policy objects 0 rx 1 tx */
@@ -506,17 +607,35 @@ unlock:staticintnet_policy_proc_show(structseq_file*m,void*v){structnet_device*dev=(structnet_device*)m->private;+enumnetpolicy_namecur;+structnetpolicy_object*obj,*tmp;inti;if(WARN_ON(!dev->netpolicy))return-EINVAL;-if(dev->netpolicy->cur_policy==NET_POLICY_NONE){+cur=dev->netpolicy->cur_policy;+if(cur==NET_POLICY_NONE){seq_printf(m,"%s: There is no policy applied\n",dev->name);seq_printf(m,"%s: The available policy include:",dev->name);for_each_set_bit(i,dev->netpolicy->avail_policy,NET_POLICY_MAX)seq_printf(m," %s",policy_name[i]);+if(dev->netpolicy->has_mix_policy)+seq_printf(m," MIX");seq_printf(m,"\n");+}elseif(cur==NET_POLICY_MIX){+seq_printf(m,"%s: MIX policy is running on the system\n",dev->name);+spin_lock(&dev->np_ob_list_lock);+for(i=NET_POLICY_NONE;i<NET_POLICY_MAX;i++){+seq_printf(m,"%s: queues for %s policy\n",dev->name,policy_name[i]);+list_for_each_entry_safe(obj,tmp,&dev->netpolicy->obj_list[NETPOLICY_RX][i],list){+seq_printf(m,"%s: rx queue %d\n",dev->name,obj->queue);+}+list_for_each_entry_safe(obj,tmp,&dev->netpolicy->obj_list[NETPOLICY_TX][i],list){+seq_printf(m,"%s: tx queue %d\n",dev->name,obj->queue);+}+}+spin_unlock(&dev->np_ob_list_lock);}else{seq_printf(m,"%s: POLICY %s is running on the system\n",dev->name,policy_name[dev->netpolicy->cur_policy]);
From: Kan Liang <redacted>
User can write policy name to /proc/net/netpolicy/$DEV/policy to enable
net policy for specific device.
When the policy is enabled, the subsystem automatically disables IRQ
balance and set IRQ affinity. The object list is also generated
accordingly.
It is device driver's responsibility to set driver specific
configuration for the given policy.
np_lock will be used to protect the state.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netdevice.h | 5 +++
include/linux/netpolicy.h | 1 +
net/core/netpolicy.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 101 insertions(+)
@@ -434,6 +435,69 @@ err:returnret;}+staticintnet_policy_set_by_name(char*name,structnet_device*dev)+{+inti,ret;++spin_lock(&dev->np_lock);+ret=0;++if(!dev->netpolicy||+!dev->netdev_ops->ndo_set_net_policy){+ret=-ENOTSUPP;+gotounlock;+}++for(i=0;i<NET_POLICY_MAX;i++){+if(!strncmp(name,policy_name[i],strlen(policy_name[i])))+break;+}++if(!test_bit(i,dev->netpolicy->avail_policy)){+ret=-ENOTSUPP;+gotounlock;+}++if(i==dev->netpolicy->cur_policy)+gotounlock;++/* If there is no policy applied yet, need to do enable first . */+if(dev->netpolicy->cur_policy==NET_POLICY_NONE){+ret=netpolicy_enable(dev);+if(ret)+gotounlock;+}++netpolicy_free_obj_list(dev);++/* Generate object list according to policy name */+ret=netpolicy_gen_obj_list(dev,i);+if(ret)+gotoerr;++/* set policy */+ret=dev->netdev_ops->ndo_set_net_policy(dev,i);+if(ret)+gotoerr;++/* If removing policy, need to do disable. */+if(i==NET_POLICY_NONE)+netpolicy_disable(dev);++dev->netpolicy->cur_policy=i;++spin_unlock(&dev->np_lock);+return0;++err:+netpolicy_free_obj_list(dev);+if(dev->netpolicy->cur_policy==NET_POLICY_NONE)+netpolicy_disable(dev);+unlock:+spin_unlock(&dev->np_lock);+returnret;+}+#ifdef CONFIG_PROC_FSstaticintnet_policy_proc_show(structseq_file*m,void*v)
From: Kan Liang <redacted>
Net policy also needs to know CPU information. Currently, online
CPU count is enough.
Signed-off-by: Kan Liang <redacted>
---
net/core/netpolicy.c | 5 +++++
1 file changed, 5 insertions(+)
From: Kan Liang <redacted>
Net policy needs to know device information. Currently, it's enough to
only get irq information of rx and tx queues.
This patch introduces ndo ops to do so, not ethtool ops.
Because there are already several ways to get irq information in
userspace. It's not necessory to extend the ethtool.
Signed-off-by: Kan Liang <redacted>
---
include/linux/netdevice.h | 5 +++++
include/linux/netpolicy.h | 7 +++++++
net/core/netpolicy.c | 14 ++++++++++++++
3 files changed, 26 insertions(+)
From: Kan Liang <redacted>
It is a big challenge to get good network performance. First, the network
performance is not good with default system settings. Second, it is too
[..]
I ask to be dropped from CC list of further submissions of this series,
I've said all I have say about this ('do it in userspace') and
its very unlikely I will change my opinion.
Thanks.
From: Eric Dumazet <hidden> Date: 2016-09-12 15:52:19
On Mon, 2016-09-12 at 07:55 -0700, kan.liang@intel.com wrote:
From: Kan Liang <redacted>
Documentation/networking/netpolicy.txt | 157 ++++
I find this patch series very suspect, as
Documentation/networking/scaling.txt is untouched.
I highly recommend you present your ideas at next netdev conference.
I really doubt the mailing lists are the best place to present your
work, given the huge amount of code/layers you want to add in linux
kernel.
From: Sergei Shtylyov <hidden> Date: 2016-09-12 16:48:54
Hello.
On 09/12/2016 05:55 PM, kan.liang@intel.com wrote:
From: Kan Liang <redacted>
Net policy needs to know device information. Currently, it's enough to
only get irq information of rx and tx queues.
This patch introduces ndo ops to do so, not ethtool ops.
Because there are already several ways to get irq information in
userspace. It's not necessory to extend the ethtool.
From: Sergei Shtylyov <hidden> Date: 2016-09-12 17:02:01
On 09/12/2016 05:55 PM, kan.liang@intel.com wrote:
From: Kan Liang <redacted>
Users may not want to change the source code to add per task net polic
Policy?
support. Or they may want to change a running task's net policy. prctl
does not work for both cases.
This patch adds an interface in /proc, which can be used to set and
retrieve policy of already running tasks. User can write the policy name
into /proc/$PID/net_policy to set per task net policy.
Signed-off-by: Kan Liang <redacted>
From: Cong Wang <hidden> Date: 2016-09-12 17:22:30
On Mon, Sep 12, 2016 at 8:38 AM, Florian Westphal [off-list ref] wrote:
kan.liang@intel.com [off-list ref] wrote:
quoted
From: Kan Liang <redacted>
It is a big challenge to get good network performance. First, the network
performance is not good with default system settings. Second, it is too
[..]
I ask to be dropped from CC list of further submissions of this series,
I've said all I have say about this ('do it in userspace') and
its very unlikely I will change my opinion.
From: Tom Herbert <hidden> Date: 2016-09-12 20:23:32
On Mon, Sep 12, 2016 at 7:55 AM, [off-list ref] wrote:
quoted hunk
From: Kan Liang <redacted>
When the device tries to transmit a packet, netdev_pick_tx is called to
find the available tx queues. If the net policy is applied, it picks up
the assigned tx queue from net policy subsystem, and redirect the
traffic to the assigned queue.
Signed-off-by: Kan Liang <redacted>
---
include/net/sock.h | 9 +++++++++
net/core/dev.c | 20 ++++++++++++++++++--
2 files changed, 27 insertions(+), 2 deletions(-)
I doubt this produces the intended effect. Several drivers use
ndo_select_queue (such as mlx4) where there might do something special
for a few packets but end up called the default handler which
__netdev_pick_tx for most packets. So in such cases the netpolicy path
would be routinely bypassed. Maybe this code should be in
__netdev_pick_tx.
Tom
From: Liang, Kan <hidden> Date: 2016-09-13 12:23:00
-----Original Message-----
From: Tom Herbert [mailto:tom@herbertland.com]
Sent: Monday, September 12, 2016 4:23 PM
To: Liang, Kan <redacted>
Cc: David S. Miller <davem@davemloft.net>; LKML <linux-
kernel@vger.kernel.org>; Linux Kernel Network Developers
[off-list ref]; Kirsher, Jeffrey T [off-list ref];
Ingo Molnar [off-list ref]; peterz@infradead.org; Alexey Kuznetsov
[off-list ref]; James Morris [off-list ref]; Hideaki
YOSHIFUJI [off-list ref]; Patrick McHardy [off-list ref];
akpm@linux-foundation.org; Kees Cook [off-list ref];
viro@zeniv.linux.org.uk; gorcunov@openvz.org; John Stultz
[off-list ref]; Alexander Duyck [off-list ref]; Ben
Hutchings [off-list ref]; David Decotigny [off-list ref];
Florian Westphal [off-list ref]; Alexander Duyck
[off-list ref]; Daniel Borkmann [off-list ref];
rdunlap@infradead.org; Cong Wang [off-list ref]; Hannes
Frederic Sowa [off-list ref]; Stephen Hemminger
[off-list ref]; Alexei Starovoitov
[off-list ref]; Brandeburg, Jesse
[off-list ref]; Andi Kleen [off-list ref]
Subject: Re: [RFC V3 PATCH 18/26] net/netpolicy: set tx queues according to
policy
On Mon, Sep 12, 2016 at 7:55 AM, [off-list ref] wrote:
quoted
From: Kan Liang <redacted>
When the device tries to transmit a packet, netdev_pick_tx is called
to find the available tx queues. If the net policy is applied, it
picks up the assigned tx queue from net policy subsystem, and redirect
the traffic to the assigned queue.
Signed-off-by: Kan Liang <redacted>
---
include/net/sock.h | 9 +++++++++
net/core/dev.c | 20 ++++++++++++++++++--
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/include/net/sock.h b/include/net/sock.h index
e1e9e3d..ca97f35 100644
--- a/include/net/sock.h+++ b/include/net/sock.h
@@ -2280,4 +2280,13 @@ extern int sysctl_optmem_max; extern __u32
I doubt this produces the intended effect. Several drivers use
ndo_select_queue (such as mlx4) where there might do something special
for a few packets but end up called the default handler which
__netdev_pick_tx for most packets. So in such cases the netpolicy path would
be routinely bypassed. Maybe this code should be in __netdev_pick_tx.
I will move the code to __netdev_pick_tx in next version.
Thanks,
Kan
From: Liang, Kan <hidden> Date: 2016-09-13 12:23:17
Hello.
On 09/12/2016 05:55 PM, kan.liang@intel.com wrote:
quoted
From: Kan Liang <redacted>
Net policy needs to know device information. Currently, it's enough to
only get irq information of rx and tx queues.
This patch introduces ndo ops to do so, not ethtool ops.
Because there are already several ways to get irq information in
userspace. It's not necessory to extend the ethtool.
Necessary.
OK. I will extend the ethtool in next version.
Thanks,
Kan
From: Alexander Duyck <hidden> Date: 2016-09-13 13:14:19
On Tue, Sep 13, 2016 at 5:23 AM, Liang, Kan [off-list ref] wrote:
quoted
Hello.
On 09/12/2016 05:55 PM, kan.liang@intel.com wrote:
quoted
From: Kan Liang <redacted>
Net policy needs to know device information. Currently, it's enough to
only get irq information of rx and tx queues.
This patch introduces ndo ops to do so, not ethtool ops.
Because there are already several ways to get irq information in
userspace. It's not necessory to extend the ethtool.
Necessary.
OK. I will extend the ethtool in next version.
Thanks,
Kan
Kan, I don't think Sergei was saying you have to extend the ethtool.
Your spelling of necessary was incorrect in your patch description.
Sergei, please feel free to tell me I am wrong if my assumption on
that is incorrect.
- Alex
From: Liang, Kan <hidden> Date: 2016-09-13 13:23:07
On Tue, Sep 13, 2016 at 5:23 AM, Liang, Kan [off-list ref] wrote:
quoted
quoted
Hello.
On 09/12/2016 05:55 PM, kan.liang@intel.com wrote:
quoted
From: Kan Liang <redacted>
Net policy needs to know device information. Currently, it's enough
to only get irq information of rx and tx queues.
This patch introduces ndo ops to do so, not ethtool ops.
Because there are already several ways to get irq information in
userspace. It's not necessory to extend the ethtool.
Necessary.
OK. I will extend the ethtool in next version.
Thanks,
Kan
Kan, I don't think Sergei was saying you have to extend the ethtool.
Your spelling of necessary was incorrect in your patch description.
Sergei, please feel free to tell me I am wrong if my assumption on that is
incorrect.
- Alex
From: Stephen Hemminger <stephen@networkplumber.org> Date: 2016-09-19 20:39:18
On Mon, 12 Sep 2016 08:52:14 -0700
Eric Dumazet [off-list ref] wrote:
On Mon, 2016-09-12 at 07:55 -0700, kan.liang@intel.com wrote:
quoted
From: Kan Liang <redacted>
quoted
Documentation/networking/netpolicy.txt | 157 ++++
I find this patch series very suspect, as
Documentation/networking/scaling.txt is untouched.
I highly recommend you present your ideas at next netdev conference.
I really doubt the mailing lists are the best place to present your
work, given the huge amount of code/layers you want to add in linux
kernel.