LCOV - code coverage report
Current view: top level - net/core - netprio_cgroup.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_real_modules_combined.info Lines: 14 82 17.1 %
Date: 2020-09-30 20:25:40 Functions: 4 12 33.3 %
Branches: 4 62 6.5 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  * net/core/netprio_cgroup.c    Priority Control Group
       4                 :            :  *
       5                 :            :  * Authors:     Neil Horman <nhorman@tuxdriver.com>
       6                 :            :  */
       7                 :            : 
       8                 :            : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
       9                 :            : 
      10                 :            : #include <linux/module.h>
      11                 :            : #include <linux/slab.h>
      12                 :            : #include <linux/types.h>
      13                 :            : #include <linux/string.h>
      14                 :            : #include <linux/errno.h>
      15                 :            : #include <linux/skbuff.h>
      16                 :            : #include <linux/cgroup.h>
      17                 :            : #include <linux/rcupdate.h>
      18                 :            : #include <linux/atomic.h>
      19                 :            : #include <linux/sched/task.h>
      20                 :            : 
      21                 :            : #include <net/rtnetlink.h>
      22                 :            : #include <net/pkt_cls.h>
      23                 :            : #include <net/sock.h>
      24                 :            : #include <net/netprio_cgroup.h>
      25                 :            : 
      26                 :            : #include <linux/fdtable.h>
      27                 :            : 
      28                 :            : /*
      29                 :            :  * netprio allocates per-net_device priomap array which is indexed by
      30                 :            :  * css->id.  Limiting css ID to 16bits doesn't lose anything.
      31                 :            :  */
      32                 :            : #define NETPRIO_ID_MAX          USHRT_MAX
      33                 :            : 
      34                 :            : #define PRIOMAP_MIN_SZ          128
      35                 :            : 
      36                 :            : /*
      37                 :            :  * Extend @dev->priomap so that it's large enough to accommodate
      38                 :            :  * @target_idx.  @dev->priomap.priomap_len > @target_idx after successful
      39                 :            :  * return.  Must be called under rtnl lock.
      40                 :            :  */
      41                 :          0 : static int extend_netdev_table(struct net_device *dev, u32 target_idx)
      42                 :            : {
      43                 :            :         struct netprio_map *old, *new;
      44                 :            :         size_t new_sz, new_len;
      45                 :            : 
      46                 :            :         /* is the existing priomap large enough? */
      47                 :          0 :         old = rtnl_dereference(dev->priomap);
      48   [ #  #  #  # ]:          0 :         if (old && old->priomap_len > target_idx)
      49                 :            :                 return 0;
      50                 :            : 
      51                 :            :         /*
      52                 :            :          * Determine the new size.  Let's keep it power-of-two.  We start
      53                 :            :          * from PRIOMAP_MIN_SZ and double it until it's large enough to
      54                 :            :          * accommodate @target_idx.
      55                 :            :          */
      56                 :            :         new_sz = PRIOMAP_MIN_SZ;
      57                 :            :         while (true) {
      58                 :          0 :                 new_len = (new_sz - offsetof(struct netprio_map, priomap)) /
      59                 :            :                         sizeof(new->priomap[0]);
      60         [ #  # ]:          0 :                 if (new_len > target_idx)
      61                 :            :                         break;
      62                 :          0 :                 new_sz *= 2;
      63                 :            :                 /* overflowed? */
      64   [ #  #  #  # ]:          0 :                 if (WARN_ON(new_sz < PRIOMAP_MIN_SZ))
      65                 :            :                         return -ENOSPC;
      66                 :            :         }
      67                 :            : 
      68                 :            :         /* allocate & copy */
      69                 :          0 :         new = kzalloc(new_sz, GFP_KERNEL);
      70         [ #  # ]:          0 :         if (!new)
      71                 :            :                 return -ENOMEM;
      72                 :            : 
      73         [ #  # ]:          0 :         if (old)
      74                 :          0 :                 memcpy(new->priomap, old->priomap,
      75                 :          0 :                        old->priomap_len * sizeof(old->priomap[0]));
      76                 :            : 
      77                 :          0 :         new->priomap_len = new_len;
      78                 :            : 
      79                 :            :         /* install the new priomap */
      80                 :          0 :         rcu_assign_pointer(dev->priomap, new);
      81         [ #  # ]:          0 :         if (old)
      82         [ #  # ]:          0 :                 kfree_rcu(old, rcu);
      83                 :            :         return 0;
      84                 :            : }
      85                 :            : 
      86                 :            : /**
      87                 :            :  * netprio_prio - return the effective netprio of a cgroup-net_device pair
      88                 :            :  * @css: css part of the target pair
      89                 :            :  * @dev: net_device part of the target pair
      90                 :            :  *
      91                 :            :  * Should be called under RCU read or rtnl lock.
      92                 :            :  */
      93                 :            : static u32 netprio_prio(struct cgroup_subsys_state *css, struct net_device *dev)
      94                 :            : {
      95                 :          0 :         struct netprio_map *map = rcu_dereference_rtnl(dev->priomap);
      96                 :          0 :         int id = css->cgroup->id;
      97                 :            : 
      98   [ #  #  #  #  :          0 :         if (map && id < map->priomap_len)
             #  #  #  # ]
      99                 :          0 :                 return map->priomap[id];
     100                 :            :         return 0;
     101                 :            : }
     102                 :            : 
     103                 :            : /**
     104                 :            :  * netprio_set_prio - set netprio on a cgroup-net_device pair
     105                 :            :  * @css: css part of the target pair
     106                 :            :  * @dev: net_device part of the target pair
     107                 :            :  * @prio: prio to set
     108                 :            :  *
     109                 :            :  * Set netprio to @prio on @css-@dev pair.  Should be called under rtnl
     110                 :            :  * lock and may fail under memory pressure for non-zero @prio.
     111                 :            :  */
     112                 :          0 : static int netprio_set_prio(struct cgroup_subsys_state *css,
     113                 :            :                             struct net_device *dev, u32 prio)
     114                 :            : {
     115                 :            :         struct netprio_map *map;
     116                 :          0 :         int id = css->cgroup->id;
     117                 :            :         int ret;
     118                 :            : 
     119                 :            :         /* avoid extending priomap for zero writes */
     120                 :          0 :         map = rtnl_dereference(dev->priomap);
     121   [ #  #  #  #  :          0 :         if (!prio && (!map || map->priomap_len <= id))
                   #  # ]
     122                 :            :                 return 0;
     123                 :            : 
     124                 :          0 :         ret = extend_netdev_table(dev, id);
     125         [ #  # ]:          0 :         if (ret)
     126                 :            :                 return ret;
     127                 :            : 
     128                 :          0 :         map = rtnl_dereference(dev->priomap);
     129                 :          0 :         map->priomap[id] = prio;
     130                 :          0 :         return 0;
     131                 :            : }
     132                 :            : 
     133                 :            : static struct cgroup_subsys_state *
     134                 :        207 : cgrp_css_alloc(struct cgroup_subsys_state *parent_css)
     135                 :            : {
     136                 :            :         struct cgroup_subsys_state *css;
     137                 :            : 
     138                 :        207 :         css = kzalloc(sizeof(*css), GFP_KERNEL);
     139         [ +  - ]:        207 :         if (!css)
     140                 :            :                 return ERR_PTR(-ENOMEM);
     141                 :            : 
     142                 :        207 :         return css;
     143                 :            : }
     144                 :            : 
     145                 :        207 : static int cgrp_css_online(struct cgroup_subsys_state *css)
     146                 :            : {
     147                 :        207 :         struct cgroup_subsys_state *parent_css = css->parent;
     148                 :            :         struct net_device *dev;
     149                 :            :         int ret = 0;
     150                 :            : 
     151         [ +  - ]:        207 :         if (css->id > NETPRIO_ID_MAX)
     152                 :            :                 return -ENOSPC;
     153                 :            : 
     154         [ -  + ]:        207 :         if (!parent_css)
     155                 :            :                 return 0;
     156                 :            : 
     157                 :          0 :         rtnl_lock();
     158                 :            :         /*
     159                 :            :          * Inherit prios from the parent.  As all prios are set during
     160                 :            :          * onlining, there is no need to clear them on offline.
     161                 :            :          */
     162         [ #  # ]:          0 :         for_each_netdev(&init_net, dev) {
     163                 :            :                 u32 prio = netprio_prio(parent_css, dev);
     164                 :            : 
     165                 :          0 :                 ret = netprio_set_prio(css, dev, prio);
     166         [ #  # ]:          0 :                 if (ret)
     167                 :            :                         break;
     168                 :            :         }
     169                 :          0 :         rtnl_unlock();
     170                 :          0 :         return ret;
     171                 :            : }
     172                 :            : 
     173                 :          0 : static void cgrp_css_free(struct cgroup_subsys_state *css)
     174                 :            : {
     175                 :          0 :         kfree(css);
     176                 :          0 : }
     177                 :            : 
     178                 :          0 : static u64 read_prioidx(struct cgroup_subsys_state *css, struct cftype *cft)
     179                 :            : {
     180                 :          0 :         return css->cgroup->id;
     181                 :            : }
     182                 :            : 
     183                 :          0 : static int read_priomap(struct seq_file *sf, void *v)
     184                 :            : {
     185                 :            :         struct net_device *dev;
     186                 :            : 
     187                 :            :         rcu_read_lock();
     188         [ #  # ]:          0 :         for_each_netdev_rcu(&init_net, dev)
     189                 :          0 :                 seq_printf(sf, "%s %u\n", dev->name,
     190                 :            :                            netprio_prio(seq_css(sf), dev));
     191                 :            :         rcu_read_unlock();
     192                 :          0 :         return 0;
     193                 :            : }
     194                 :            : 
     195                 :          0 : static ssize_t write_priomap(struct kernfs_open_file *of,
     196                 :            :                              char *buf, size_t nbytes, loff_t off)
     197                 :            : {
     198                 :            :         char devname[IFNAMSIZ + 1];
     199                 :            :         struct net_device *dev;
     200                 :            :         u32 prio;
     201                 :            :         int ret;
     202                 :            : 
     203         [ #  # ]:          0 :         if (sscanf(buf, "%"__stringify(IFNAMSIZ)"s %u", devname, &prio) != 2)
     204                 :            :                 return -EINVAL;
     205                 :            : 
     206                 :          0 :         dev = dev_get_by_name(&init_net, devname);
     207         [ #  # ]:          0 :         if (!dev)
     208                 :            :                 return -ENODEV;
     209                 :            : 
     210                 :          0 :         cgroup_sk_alloc_disable();
     211                 :            : 
     212                 :          0 :         rtnl_lock();
     213                 :            : 
     214                 :          0 :         ret = netprio_set_prio(of_css(of), dev, prio);
     215                 :            : 
     216                 :          0 :         rtnl_unlock();
     217                 :          0 :         dev_put(dev);
     218         [ #  # ]:          0 :         return ret ?: nbytes;
     219                 :            : }
     220                 :            : 
     221                 :          0 : static int update_netprio(const void *v, struct file *file, unsigned n)
     222                 :            : {
     223                 :            :         int err;
     224                 :          0 :         struct socket *sock = sock_from_file(file, &err);
     225         [ #  # ]:          0 :         if (sock) {
     226                 :            :                 spin_lock(&cgroup_sk_update_lock);
     227                 :          0 :                 sock_cgroup_set_prioidx(&sock->sk->sk_cgrp_data,
     228                 :            :                                         (unsigned long)v);
     229                 :            :                 spin_unlock(&cgroup_sk_update_lock);
     230                 :            :         }
     231                 :          0 :         return 0;
     232                 :            : }
     233                 :            : 
     234                 :          0 : static void net_prio_attach(struct cgroup_taskset *tset)
     235                 :            : {
     236                 :            :         struct task_struct *p;
     237                 :            :         struct cgroup_subsys_state *css;
     238                 :            : 
     239                 :          0 :         cgroup_sk_alloc_disable();
     240                 :            : 
     241         [ #  # ]:          0 :         cgroup_taskset_for_each(p, css, tset) {
     242                 :          0 :                 void *v = (void *)(unsigned long)css->cgroup->id;
     243                 :            : 
     244                 :            :                 task_lock(p);
     245                 :          0 :                 iterate_fd(p->files, 0, update_netprio, v);
     246                 :            :                 task_unlock(p);
     247                 :            :         }
     248                 :          0 : }
     249                 :            : 
     250                 :            : static struct cftype ss_files[] = {
     251                 :            :         {
     252                 :            :                 .name = "prioidx",
     253                 :            :                 .read_u64 = read_prioidx,
     254                 :            :         },
     255                 :            :         {
     256                 :            :                 .name = "ifpriomap",
     257                 :            :                 .seq_show = read_priomap,
     258                 :            :                 .write = write_priomap,
     259                 :            :         },
     260                 :            :         { }     /* terminate */
     261                 :            : };
     262                 :            : 
     263                 :            : struct cgroup_subsys net_prio_cgrp_subsys = {
     264                 :            :         .css_alloc      = cgrp_css_alloc,
     265                 :            :         .css_online     = cgrp_css_online,
     266                 :            :         .css_free       = cgrp_css_free,
     267                 :            :         .attach         = net_prio_attach,
     268                 :            :         .legacy_cftypes = ss_files,
     269                 :            : };
     270                 :            : 
     271                 :       2277 : static int netprio_device_event(struct notifier_block *unused,
     272                 :            :                                 unsigned long event, void *ptr)
     273                 :            : {
     274                 :            :         struct net_device *dev = netdev_notifier_info_to_dev(ptr);
     275                 :            :         struct netprio_map *old;
     276                 :            : 
     277                 :            :         /*
     278                 :            :          * Note this is called with rtnl_lock held so we have update side
     279                 :            :          * protection on our rcu assignments
     280                 :            :          */
     281                 :            : 
     282         [ -  + ]:       2277 :         switch (event) {
     283                 :            :         case NETDEV_UNREGISTER:
     284                 :          0 :                 old = rtnl_dereference(dev->priomap);
     285                 :            :                 RCU_INIT_POINTER(dev->priomap, NULL);
     286         [ #  # ]:          0 :                 if (old)
     287         [ #  # ]:          0 :                         kfree_rcu(old, rcu);
     288                 :            :                 break;
     289                 :            :         }
     290                 :       2277 :         return NOTIFY_DONE;
     291                 :            : }
     292                 :            : 
     293                 :            : static struct notifier_block netprio_device_notifier = {
     294                 :            :         .notifier_call = netprio_device_event
     295                 :            : };
     296                 :            : 
     297                 :        207 : static int __init init_cgroup_netprio(void)
     298                 :            : {
     299                 :        207 :         register_netdevice_notifier(&netprio_device_notifier);
     300                 :        207 :         return 0;
     301                 :            : }
     302                 :            : subsys_initcall(init_cgroup_netprio);

Generated by: LCOV version 1.14