diff mbox series

[net-next] dropwatch: Support monitoring of dropped frames

Message ID 20200707171515.110818-1-izabela.bakollari@gmail.com
State Changes Requested
Delegated to: David Miller
Headers show
Series [net-next] dropwatch: Support monitoring of dropped frames | expand

Commit Message

Izabela Bakollari July 7, 2020, 5:15 p.m. UTC
From: Izabela Bakollari <izabela.bakollari@gmail.com>

Dropwatch is a utility that monitors dropped frames by having userspace
record them over the dropwatch protocol over a file. This augument
allows live monitoring of dropped frames using tools like tcpdump.

With this feature, dropwatch allows two additional commands (start and
stop interface) which allows the assignment of a net_device to the
dropwatch protocol. When assinged, dropwatch will clone dropped frames,
and receive them on the assigned interface, allowing tools like tcpdump
to monitor for them.

With this feature, create a dummy ethernet interface (ip link add dev
dummy0 type dummy), assign it to the dropwatch kernel subsystem, by using
these new commands, and then monitor dropped frames in real time by
running tcpdump -i dummy0.

Signed-off-by: Izabela Bakollari <izabela.bakollari@gmail.com>
---
 include/uapi/linux/net_dropmon.h |  3 ++
 net/core/drop_monitor.c          | 79 +++++++++++++++++++++++++++++++-
 2 files changed, 80 insertions(+), 2 deletions(-)

Comments

Eric Dumazet July 7, 2020, 5:33 p.m. UTC | #1
On 7/7/20 10:15 AM, izabela.bakollari@gmail.com wrote:
> From: Izabela Bakollari <izabela.bakollari@gmail.com>
> 
> Dropwatch is a utility that monitors dropped frames by having userspace
> record them over the dropwatch protocol over a file. This augument
> allows live monitoring of dropped frames using tools like tcpdump.
> 
> With this feature, dropwatch allows two additional commands (start and
> stop interface) which allows the assignment of a net_device to the
> dropwatch protocol. When assinged, dropwatch will clone dropped frames,
> and receive them on the assigned interface, allowing tools like tcpdump
> to monitor for them.
> 
> With this feature, create a dummy ethernet interface (ip link add dev
> dummy0 type dummy), assign it to the dropwatch kernel subsystem, by using
> these new commands, and then monitor dropped frames in real time by
> running tcpdump -i dummy0.
> 
> Signed-off-by: Izabela Bakollari <izabela.bakollari@gmail.com>
> ---
>  include/uapi/linux/net_dropmon.h |  3 ++
>  net/core/drop_monitor.c          | 79 +++++++++++++++++++++++++++++++-
>  2 files changed, 80 insertions(+), 2 deletions(-)
> 
> diff --git a/include/uapi/linux/net_dropmon.h b/include/uapi/linux/net_dropmon.h
> index 67e31f329190..e8e861e03a8a 100644
> --- a/include/uapi/linux/net_dropmon.h
> +++ b/include/uapi/linux/net_dropmon.h
> @@ -58,6 +58,8 @@ enum {
>  	NET_DM_CMD_CONFIG_NEW,
>  	NET_DM_CMD_STATS_GET,
>  	NET_DM_CMD_STATS_NEW,
> +	NET_DM_CMD_START_IFC,
> +	NET_DM_CMD_STOP_IFC,
>  	_NET_DM_CMD_MAX,
>  };
>  
> @@ -93,6 +95,7 @@ enum net_dm_attr {
>  	NET_DM_ATTR_SW_DROPS,			/* flag */
>  	NET_DM_ATTR_HW_DROPS,			/* flag */
>  	NET_DM_ATTR_FLOW_ACTION_COOKIE,		/* binary */
> +	NET_DM_ATTR_IFNAME,			/* string */
>  
>  	__NET_DM_ATTR_MAX,
>  	NET_DM_ATTR_MAX = __NET_DM_ATTR_MAX - 1
> diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
> index 8e33cec9fc4e..8049bff05abd 100644
> --- a/net/core/drop_monitor.c
> +++ b/net/core/drop_monitor.c
> @@ -30,6 +30,7 @@
>  #include <net/genetlink.h>
>  #include <net/netevent.h>
>  #include <net/flow_offload.h>
> +#include <net/sock.h>
>  
>  #include <trace/events/skb.h>
>  #include <trace/events/napi.h>
> @@ -46,6 +47,7 @@
>   */
>  static int trace_state = TRACE_OFF;
>  static bool monitor_hw;
> +struct net_device *interface;
>  
>  /* net_dm_mutex
>   *
> @@ -220,9 +222,8 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
>  	struct per_cpu_dm_data *data;
>  	unsigned long flags;
>  
> -	local_irq_save(flags);
> +	spin_lock_irqsave(&data->lock, flags);
>  	data = this_cpu_ptr(&dm_cpu_data);
> -	spin_lock(&data->lock);

This change seems unrelated ?

And also buggy, since data is essentially garbage when you call spin_lock_irqsave(&data->lock, flags);

>  	dskb = data->skb;
>  
>  	if (!dskb)
> @@ -255,6 +256,12 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
>  
>  out:
>  	spin_unlock_irqrestore(&data->lock, flags);
> +
> +	if (interface && interface != skb->dev) {
> +		skb = skb_clone(skb, GFP_ATOMIC);

skb_clone() can return NULL

> +		skb->dev = interface;
> +		netif_receive_skb(skb);
> +	}
>  }
>  
>  static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location)
> @@ -1315,6 +1322,63 @@ static int net_dm_cmd_trace(struct sk_buff *skb,
>  	return -EOPNOTSUPP;
>  }
>  
> +static int net_dm_interface_start(struct net *net, const char *ifname)
> +{
> +	struct net_device *nd;
> +
> +	nd = dev_get_by_name(net, ifname);
> +
> +	if (nd) {
> +		interface = nd;
> +		dev_hold(interface);
> +	} else {
> +		return -ENODEV;
> +	}
> +	return 0;
> +}
> +


What happens after this monitoring is started, then the admin does :

rmmod ifb
Eric Dumazet July 7, 2020, 5:36 p.m. UTC | #2
On 7/7/20 10:33 AM, Eric Dumazet wrote:
> 
> 
> What happens after this monitoring is started, then the admin does :
> 
> rmmod ifb
> 

I meant  :  rmmod dummy
Eric Dumazet July 7, 2020, 5:52 p.m. UTC | #3
On 7/7/20 10:15 AM, izabela.bakollari@gmail.com wrote:
> From: Izabela Bakollari <izabela.bakollari@gmail.com>
> 
> Dropwatch is a utility that monitors dropped frames by having userspace
> record them over the dropwatch protocol over a file. This augument
> allows live monitoring of dropped frames using tools like tcpdump.
> 
> With this feature, dropwatch allows two additional commands (start and
> stop interface) which allows the assignment of a net_device to the
> dropwatch protocol. When assinged, dropwatch will clone dropped frames,
> and receive them on the assigned interface, allowing tools like tcpdump
> to monitor for them.
> 
> With this feature, create a dummy ethernet interface (ip link add dev
> dummy0 type dummy), assign it to the dropwatch kernel subsystem, by using
> these new commands, and then monitor dropped frames in real time by
> running tcpdump -i dummy0.
> 
> Signed-off-by: Izabela Bakollari <izabela.bakollari@gmail.com>
> ---
>  include/uapi/linux/net_dropmon.h |  3 ++
>  net/core/drop_monitor.c          | 79 +++++++++++++++++++++++++++++++-
>  2 files changed, 80 insertions(+), 2 deletions(-)
> 
> diff --git a/include/uapi/linux/net_dropmon.h b/include/uapi/linux/net_dropmon.h
> index 67e31f329190..e8e861e03a8a 100644
> --- a/include/uapi/linux/net_dropmon.h
> +++ b/include/uapi/linux/net_dropmon.h
> @@ -58,6 +58,8 @@ enum {
>  	NET_DM_CMD_CONFIG_NEW,
>  	NET_DM_CMD_STATS_GET,
>  	NET_DM_CMD_STATS_NEW,
> +	NET_DM_CMD_START_IFC,
> +	NET_DM_CMD_STOP_IFC,
>  	_NET_DM_CMD_MAX,
>  };
>  
> @@ -93,6 +95,7 @@ enum net_dm_attr {
>  	NET_DM_ATTR_SW_DROPS,			/* flag */
>  	NET_DM_ATTR_HW_DROPS,			/* flag */
>  	NET_DM_ATTR_FLOW_ACTION_COOKIE,		/* binary */
> +	NET_DM_ATTR_IFNAME,			/* string */
>  
>  	__NET_DM_ATTR_MAX,
>  	NET_DM_ATTR_MAX = __NET_DM_ATTR_MAX - 1
> diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
> index 8e33cec9fc4e..8049bff05abd 100644
> --- a/net/core/drop_monitor.c
> +++ b/net/core/drop_monitor.c
> @@ -30,6 +30,7 @@
>  #include <net/genetlink.h>
>  #include <net/netevent.h>
>  #include <net/flow_offload.h>
> +#include <net/sock.h>
>  
>  #include <trace/events/skb.h>
>  #include <trace/events/napi.h>
> @@ -46,6 +47,7 @@
>   */
>  static int trace_state = TRACE_OFF;
>  static bool monitor_hw;
> +struct net_device *interface;
>  
>  /* net_dm_mutex
>   *
> @@ -220,9 +222,8 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
>  	struct per_cpu_dm_data *data;
>  	unsigned long flags;
>  
> -	local_irq_save(flags);
> +	spin_lock_irqsave(&data->lock, flags);
>  	data = this_cpu_ptr(&dm_cpu_data);
> -	spin_lock(&data->lock);
>  	dskb = data->skb;
>  
>  	if (!dskb)
> @@ -255,6 +256,12 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
>  
>  out:
>  	spin_unlock_irqrestore(&data->lock, flags);
> +

What protects interface from being changed under us by another thread/cpu ?

> +	if (interface && interface != skb->dev) {
> +		skb = skb_clone(skb, GFP_ATOMIC);
> +		skb->dev = interface;
> +		netif_receive_skb(skb);
> +	}
>  }
>  
>  static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location)
> @@ -1315,6 +1322,63 @@ static int net_dm_cmd_trace(struct sk_buff *skb,
>  	return -EOPNOTSUPP;
>  }
>  
> +static int net_dm_interface_start(struct net *net, const char *ifname)
> +{
> +	struct net_device *nd;
> +
> +	nd = dev_get_by_name(net, ifname);
> +
> +	if (nd) {
> +		interface = nd;

If interface was already set, you forgot to dev_put() it.

> +		dev_hold(interface);

Note that dev_get_by_name() already did a dev_hold()

> +	} else {
> +		return -ENODEV;
> +	}
> +	return 0;
> +}
> +
> +static int net_dm_interface_stop(struct net *net, const char *ifname)
> +{
> +	struct net_device *nd;
> +
> +	nd = dev_get_by_name(net, ifname);
> +
> +	if (nd) {



> +		interface = nd;


You probably meant : interface = NULL; ?

> +		dev_put(interface);

		and dev_put(nd);


> +	} else {
> +		return -ENODEV;
> +	}
> +	return 0;
> +}
> +
> +static int net_dm_cmd_ifc_trace(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct net *net = sock_net(skb->sk);
> +	char ifname[IFNAMSIZ];
> +	int rc;
> +
> +	memset(ifname, 0, IFNAMSIZ);
> +	nla_strlcpy(ifname, info->attrs[NET_DM_ATTR_IFNAME], IFNAMSIZ - 1);
> +
> +	switch (info->genlhdr->cmd) {
> +	case NET_DM_CMD_START_IFC:
> +		rc = net_dm_interface_start(net, ifname);
> +		if (rc)
> +			return rc;
> +		break;
> +	case NET_DM_CMD_STOP_IFC:
> +		if (interface) {
> +			rc = net_dm_interface_stop(net, interface->ifname);
> +			return rc;
> +		} else {
> +			return -ENODEV;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  static int net_dm_config_fill(struct sk_buff *msg, struct genl_info *info)
>  {
>  	void *hdr;
> @@ -1543,6 +1607,7 @@ static const struct nla_policy net_dm_nl_policy[NET_DM_ATTR_MAX + 1] = {
>  	[NET_DM_ATTR_QUEUE_LEN] = { .type = NLA_U32 },
>  	[NET_DM_ATTR_SW_DROPS]	= {. type = NLA_FLAG },
>  	[NET_DM_ATTR_HW_DROPS]	= {. type = NLA_FLAG },
> +	[NET_DM_ATTR_IFNAME] = {. type = NLA_STRING, .len = IFNAMSIZ },
>  };
>  
>  static const struct genl_ops dropmon_ops[] = {
> @@ -1570,6 +1635,16 @@ static const struct genl_ops dropmon_ops[] = {
>  		.cmd = NET_DM_CMD_STATS_GET,
>  		.doit = net_dm_cmd_stats_get,
>  	},
> +	{
> +		.cmd = NET_DM_CMD_START_IFC,
> +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
> +		.doit = net_dm_cmd_ifc_trace,
> +	},
> +	{
> +		.cmd = NET_DM_CMD_STOP_IFC,
> +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
> +		.doit = net_dm_cmd_ifc_trace,
> +	},
>  };
>  
>  static int net_dm_nl_pre_doit(const struct genl_ops *ops,
>
kernel test robot July 7, 2020, 9 p.m. UTC | #4
Hi,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/izabela-bakollari-gmail-com/dropwatch-Support-monitoring-of-dropped-frames/20200708-011806
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git d47a72152097d7be7cfc453d205196c0aa976c33
config: i386-allyesconfig (attached as .config)
compiler: gcc-9 (Debian 9.3.0-14) 9.3.0
reproduce (this is a W=1 build):
        # save the attached .config to linux build tree
        make W=1 ARCH=i386 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   net/core/drop_monitor.c: In function 'net_dm_cmd_ifc_trace':
>> net/core/drop_monitor.c:1375:47: error: 'struct net_device' has no member named 'ifname'; did you mean 'name'?
    1375 |    rc = net_dm_interface_stop(net, interface->ifname);
         |                                               ^~~~~~
         |                                               name

vim +1375 net/core/drop_monitor.c

  1357	
  1358	static int net_dm_cmd_ifc_trace(struct sk_buff *skb, struct genl_info *info)
  1359	{
  1360		struct net *net = sock_net(skb->sk);
  1361		char ifname[IFNAMSIZ];
  1362		int rc;
  1363	
  1364		memset(ifname, 0, IFNAMSIZ);
  1365		nla_strlcpy(ifname, info->attrs[NET_DM_ATTR_IFNAME], IFNAMSIZ - 1);
  1366	
  1367		switch (info->genlhdr->cmd) {
  1368		case NET_DM_CMD_START_IFC:
  1369			rc = net_dm_interface_start(net, ifname);
  1370			if (rc)
  1371				return rc;
  1372			break;
  1373		case NET_DM_CMD_STOP_IFC:
  1374			if (interface) {
> 1375				rc = net_dm_interface_stop(net, interface->ifname);
  1376				return rc;
  1377			} else {
  1378				return -ENODEV;
  1379			}
  1380		}
  1381	
  1382		return 0;
  1383	}
  1384	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot July 8, 2020, 12:27 a.m. UTC | #5
Hi,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/izabela-bakollari-gmail-com/dropwatch-Support-monitoring-of-dropped-frames/20200708-011806
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git d47a72152097d7be7cfc453d205196c0aa976c33
config: x86_64-allyesconfig (attached as .config)
compiler: clang version 11.0.0 (https://github.com/llvm/llvm-project 02946de3802d3bc65bc9f2eb9b8d4969b5a7add8)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

>> net/core/drop_monitor.c:226:21: warning: variable 'data' is uninitialized when used here [-Wuninitialized]
           spin_lock_irqsave(&data->lock, flags);
                              ^~~~
   include/linux/spinlock.h:383:39: note: expanded from macro 'spin_lock_irqsave'
           raw_spin_lock_irqsave(spinlock_check(lock), flags);     \
                                                ^~~~
   include/linux/spinlock.h:251:34: note: expanded from macro 'raw_spin_lock_irqsave'
                   flags = _raw_spin_lock_irqsave(lock);   \
                                                  ^~~~
   net/core/drop_monitor.c:223:30: note: initialize the variable 'data' to silence this warning
           struct per_cpu_dm_data *data;
                                       ^
                                        = NULL
>> net/core/drop_monitor.c:1375:47: error: no member named 'ifname' in 'struct net_device'; did you mean 'name'?
                           rc = net_dm_interface_stop(net, interface->ifname);
                                                                      ^~~~~~
                                                                      name
   include/linux/netdevice.h:1844:9: note: 'name' declared here
           char                    name[IFNAMSIZ];
                                   ^
   1 warning and 1 error generated.

vim +1375 net/core/drop_monitor.c

  1357	
  1358	static int net_dm_cmd_ifc_trace(struct sk_buff *skb, struct genl_info *info)
  1359	{
  1360		struct net *net = sock_net(skb->sk);
  1361		char ifname[IFNAMSIZ];
  1362		int rc;
  1363	
  1364		memset(ifname, 0, IFNAMSIZ);
  1365		nla_strlcpy(ifname, info->attrs[NET_DM_ATTR_IFNAME], IFNAMSIZ - 1);
  1366	
  1367		switch (info->genlhdr->cmd) {
  1368		case NET_DM_CMD_START_IFC:
  1369			rc = net_dm_interface_start(net, ifname);
  1370			if (rc)
  1371				return rc;
  1372			break;
  1373		case NET_DM_CMD_STOP_IFC:
  1374			if (interface) {
> 1375				rc = net_dm_interface_stop(net, interface->ifname);
  1376				return rc;
  1377			} else {
  1378				return -ENODEV;
  1379			}
  1380		}
  1381	
  1382		return 0;
  1383	}
  1384	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Izabela Bakollari July 8, 2020, 2:06 p.m. UTC | #6
Hi Eric,

Thank you for reviewing my patch. I understand your comments
and will be working on correcting what you pointed out.

Best,
Izabela


On Tue, Jul 7, 2020 at 7:52 PM Eric Dumazet <eric.dumazet@gmail.com> wrote:
>
>
>
> On 7/7/20 10:15 AM, izabela.bakollari@gmail.com wrote:
> > From: Izabela Bakollari <izabela.bakollari@gmail.com>
> >
> > Dropwatch is a utility that monitors dropped frames by having userspace
> > record them over the dropwatch protocol over a file. This augument
> > allows live monitoring of dropped frames using tools like tcpdump.
> >
> > With this feature, dropwatch allows two additional commands (start and
> > stop interface) which allows the assignment of a net_device to the
> > dropwatch protocol. When assinged, dropwatch will clone dropped frames,
> > and receive them on the assigned interface, allowing tools like tcpdump
> > to monitor for them.
> >
> > With this feature, create a dummy ethernet interface (ip link add dev
> > dummy0 type dummy), assign it to the dropwatch kernel subsystem, by using
> > these new commands, and then monitor dropped frames in real time by
> > running tcpdump -i dummy0.
> >
> > Signed-off-by: Izabela Bakollari <izabela.bakollari@gmail.com>
> > ---
> >  include/uapi/linux/net_dropmon.h |  3 ++
> >  net/core/drop_monitor.c          | 79 +++++++++++++++++++++++++++++++-
> >  2 files changed, 80 insertions(+), 2 deletions(-)
> >
> > diff --git a/include/uapi/linux/net_dropmon.h b/include/uapi/linux/net_dropmon.h
> > index 67e31f329190..e8e861e03a8a 100644
> > --- a/include/uapi/linux/net_dropmon.h
> > +++ b/include/uapi/linux/net_dropmon.h
> > @@ -58,6 +58,8 @@ enum {
> >       NET_DM_CMD_CONFIG_NEW,
> >       NET_DM_CMD_STATS_GET,
> >       NET_DM_CMD_STATS_NEW,
> > +     NET_DM_CMD_START_IFC,
> > +     NET_DM_CMD_STOP_IFC,
> >       _NET_DM_CMD_MAX,
> >  };
> >
> > @@ -93,6 +95,7 @@ enum net_dm_attr {
> >       NET_DM_ATTR_SW_DROPS,                   /* flag */
> >       NET_DM_ATTR_HW_DROPS,                   /* flag */
> >       NET_DM_ATTR_FLOW_ACTION_COOKIE,         /* binary */
> > +     NET_DM_ATTR_IFNAME,                     /* string */
> >
> >       __NET_DM_ATTR_MAX,
> >       NET_DM_ATTR_MAX = __NET_DM_ATTR_MAX - 1
> > diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
> > index 8e33cec9fc4e..8049bff05abd 100644
> > --- a/net/core/drop_monitor.c
> > +++ b/net/core/drop_monitor.c
> > @@ -30,6 +30,7 @@
> >  #include <net/genetlink.h>
> >  #include <net/netevent.h>
> >  #include <net/flow_offload.h>
> > +#include <net/sock.h>
> >
> >  #include <trace/events/skb.h>
> >  #include <trace/events/napi.h>
> > @@ -46,6 +47,7 @@
> >   */
> >  static int trace_state = TRACE_OFF;
> >  static bool monitor_hw;
> > +struct net_device *interface;
> >
> >  /* net_dm_mutex
> >   *
> > @@ -220,9 +222,8 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
> >       struct per_cpu_dm_data *data;
> >       unsigned long flags;
> >
> > -     local_irq_save(flags);
> > +     spin_lock_irqsave(&data->lock, flags);
> >       data = this_cpu_ptr(&dm_cpu_data);
> > -     spin_lock(&data->lock);
> >       dskb = data->skb;
> >
> >       if (!dskb)
> > @@ -255,6 +256,12 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
> >
> >  out:
> >       spin_unlock_irqrestore(&data->lock, flags);
> > +
>
> What protects interface from being changed under us by another thread/cpu ?
>
> > +     if (interface && interface != skb->dev) {
> > +             skb = skb_clone(skb, GFP_ATOMIC);
> > +             skb->dev = interface;
> > +             netif_receive_skb(skb);
> > +     }
> >  }
> >
> >  static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location)
> > @@ -1315,6 +1322,63 @@ static int net_dm_cmd_trace(struct sk_buff *skb,
> >       return -EOPNOTSUPP;
> >  }
> >
> > +static int net_dm_interface_start(struct net *net, const char *ifname)
> > +{
> > +     struct net_device *nd;
> > +
> > +     nd = dev_get_by_name(net, ifname);
> > +
> > +     if (nd) {
> > +             interface = nd;
>
> If interface was already set, you forgot to dev_put() it.
>
> > +             dev_hold(interface);
>
> Note that dev_get_by_name() already did a dev_hold()
>
> > +     } else {
> > +             return -ENODEV;
> > +     }
> > +     return 0;
> > +}
> > +
> > +static int net_dm_interface_stop(struct net *net, const char *ifname)
> > +{
> > +     struct net_device *nd;
> > +
> > +     nd = dev_get_by_name(net, ifname);
> > +
> > +     if (nd) {
>
>
>
> > +             interface = nd;
>
>
> You probably meant : interface = NULL; ?
>
> > +             dev_put(interface);
>
>                 and dev_put(nd);
>
>
> > +     } else {
> > +             return -ENODEV;
> > +     }
> > +     return 0;
> > +}
> > +
> > +static int net_dm_cmd_ifc_trace(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +     struct net *net = sock_net(skb->sk);
> > +     char ifname[IFNAMSIZ];
> > +     int rc;
> > +
> > +     memset(ifname, 0, IFNAMSIZ);
> > +     nla_strlcpy(ifname, info->attrs[NET_DM_ATTR_IFNAME], IFNAMSIZ - 1);
> > +
> > +     switch (info->genlhdr->cmd) {
> > +     case NET_DM_CMD_START_IFC:
> > +             rc = net_dm_interface_start(net, ifname);
> > +             if (rc)
> > +                     return rc;
> > +             break;
> > +     case NET_DM_CMD_STOP_IFC:
> > +             if (interface) {
> > +                     rc = net_dm_interface_stop(net, interface->ifname);
> > +                     return rc;
> > +             } else {
> > +                     return -ENODEV;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> >  static int net_dm_config_fill(struct sk_buff *msg, struct genl_info *info)
> >  {
> >       void *hdr;
> > @@ -1543,6 +1607,7 @@ static const struct nla_policy net_dm_nl_policy[NET_DM_ATTR_MAX + 1] = {
> >       [NET_DM_ATTR_QUEUE_LEN] = { .type = NLA_U32 },
> >       [NET_DM_ATTR_SW_DROPS]  = {. type = NLA_FLAG },
> >       [NET_DM_ATTR_HW_DROPS]  = {. type = NLA_FLAG },
> > +     [NET_DM_ATTR_IFNAME] = {. type = NLA_STRING, .len = IFNAMSIZ },
> >  };
> >
> >  static const struct genl_ops dropmon_ops[] = {
> > @@ -1570,6 +1635,16 @@ static const struct genl_ops dropmon_ops[] = {
> >               .cmd = NET_DM_CMD_STATS_GET,
> >               .doit = net_dm_cmd_stats_get,
> >       },
> > +     {
> > +             .cmd = NET_DM_CMD_START_IFC,
> > +             .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
> > +             .doit = net_dm_cmd_ifc_trace,
> > +     },
> > +     {
> > +             .cmd = NET_DM_CMD_STOP_IFC,
> > +             .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
> > +             .doit = net_dm_cmd_ifc_trace,
> > +     },
> >  };
> >
> >  static int net_dm_nl_pre_doit(const struct genl_ops *ops,
> >
diff mbox series

Patch

diff --git a/include/uapi/linux/net_dropmon.h b/include/uapi/linux/net_dropmon.h
index 67e31f329190..e8e861e03a8a 100644
--- a/include/uapi/linux/net_dropmon.h
+++ b/include/uapi/linux/net_dropmon.h
@@ -58,6 +58,8 @@  enum {
 	NET_DM_CMD_CONFIG_NEW,
 	NET_DM_CMD_STATS_GET,
 	NET_DM_CMD_STATS_NEW,
+	NET_DM_CMD_START_IFC,
+	NET_DM_CMD_STOP_IFC,
 	_NET_DM_CMD_MAX,
 };
 
@@ -93,6 +95,7 @@  enum net_dm_attr {
 	NET_DM_ATTR_SW_DROPS,			/* flag */
 	NET_DM_ATTR_HW_DROPS,			/* flag */
 	NET_DM_ATTR_FLOW_ACTION_COOKIE,		/* binary */
+	NET_DM_ATTR_IFNAME,			/* string */
 
 	__NET_DM_ATTR_MAX,
 	NET_DM_ATTR_MAX = __NET_DM_ATTR_MAX - 1
diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index 8e33cec9fc4e..8049bff05abd 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -30,6 +30,7 @@ 
 #include <net/genetlink.h>
 #include <net/netevent.h>
 #include <net/flow_offload.h>
+#include <net/sock.h>
 
 #include <trace/events/skb.h>
 #include <trace/events/napi.h>
@@ -46,6 +47,7 @@ 
  */
 static int trace_state = TRACE_OFF;
 static bool monitor_hw;
+struct net_device *interface;
 
 /* net_dm_mutex
  *
@@ -220,9 +222,8 @@  static void trace_drop_common(struct sk_buff *skb, void *location)
 	struct per_cpu_dm_data *data;
 	unsigned long flags;
 
-	local_irq_save(flags);
+	spin_lock_irqsave(&data->lock, flags);
 	data = this_cpu_ptr(&dm_cpu_data);
-	spin_lock(&data->lock);
 	dskb = data->skb;
 
 	if (!dskb)
@@ -255,6 +256,12 @@  static void trace_drop_common(struct sk_buff *skb, void *location)
 
 out:
 	spin_unlock_irqrestore(&data->lock, flags);
+
+	if (interface && interface != skb->dev) {
+		skb = skb_clone(skb, GFP_ATOMIC);
+		skb->dev = interface;
+		netif_receive_skb(skb);
+	}
 }
 
 static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location)
@@ -1315,6 +1322,63 @@  static int net_dm_cmd_trace(struct sk_buff *skb,
 	return -EOPNOTSUPP;
 }
 
+static int net_dm_interface_start(struct net *net, const char *ifname)
+{
+	struct net_device *nd;
+
+	nd = dev_get_by_name(net, ifname);
+
+	if (nd) {
+		interface = nd;
+		dev_hold(interface);
+	} else {
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int net_dm_interface_stop(struct net *net, const char *ifname)
+{
+	struct net_device *nd;
+
+	nd = dev_get_by_name(net, ifname);
+
+	if (nd) {
+		interface = nd;
+		dev_put(interface);
+	} else {
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int net_dm_cmd_ifc_trace(struct sk_buff *skb, struct genl_info *info)
+{
+	struct net *net = sock_net(skb->sk);
+	char ifname[IFNAMSIZ];
+	int rc;
+
+	memset(ifname, 0, IFNAMSIZ);
+	nla_strlcpy(ifname, info->attrs[NET_DM_ATTR_IFNAME], IFNAMSIZ - 1);
+
+	switch (info->genlhdr->cmd) {
+	case NET_DM_CMD_START_IFC:
+		rc = net_dm_interface_start(net, ifname);
+		if (rc)
+			return rc;
+		break;
+	case NET_DM_CMD_STOP_IFC:
+		if (interface) {
+			rc = net_dm_interface_stop(net, interface->ifname);
+			return rc;
+		} else {
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
 static int net_dm_config_fill(struct sk_buff *msg, struct genl_info *info)
 {
 	void *hdr;
@@ -1543,6 +1607,7 @@  static const struct nla_policy net_dm_nl_policy[NET_DM_ATTR_MAX + 1] = {
 	[NET_DM_ATTR_QUEUE_LEN] = { .type = NLA_U32 },
 	[NET_DM_ATTR_SW_DROPS]	= {. type = NLA_FLAG },
 	[NET_DM_ATTR_HW_DROPS]	= {. type = NLA_FLAG },
+	[NET_DM_ATTR_IFNAME] = {. type = NLA_STRING, .len = IFNAMSIZ },
 };
 
 static const struct genl_ops dropmon_ops[] = {
@@ -1570,6 +1635,16 @@  static const struct genl_ops dropmon_ops[] = {
 		.cmd = NET_DM_CMD_STATS_GET,
 		.doit = net_dm_cmd_stats_get,
 	},
+	{
+		.cmd = NET_DM_CMD_START_IFC,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = net_dm_cmd_ifc_trace,
+	},
+	{
+		.cmd = NET_DM_CMD_STOP_IFC,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = net_dm_cmd_ifc_trace,
+	},
 };
 
 static int net_dm_nl_pre_doit(const struct genl_ops *ops,