Introduce deferred if_start framework
The framework provides a means to schedule if_start that will be executed in softint later. It intends to be used to avoid calling if_start, especially bpf_mtap, in hardware interrupt. It adds a dedicated softint to a driver if the driver requests to use the framework via if_deferred_start_init. The driver can schedule deferred if_start by if_schedule_deferred_start. Proposed and discussed on tech-kern and tech-net
This commit is contained in:
parent
9d8a67c608
commit
0cfa5e16fd
108
sys/net/if.c
108
sys/net/if.c
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: if.c,v 1.363 2016/12/06 01:23:01 ozaki-r Exp $ */
|
||||
/* $NetBSD: if.c,v 1.364 2016/12/08 01:06:35 ozaki-r Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1999, 2000, 2001, 2008 The NetBSD Foundation, Inc.
|
||||
|
@ -90,7 +90,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.363 2016/12/06 01:23:01 ozaki-r Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.364 2016/12/08 01:06:35 ozaki-r Exp $");
|
||||
|
||||
#if defined(_KERNEL_OPT)
|
||||
#include "opt_inet.h"
|
||||
|
@ -223,6 +223,16 @@ static int sysctl_percpuq_drops_handler(SYSCTLFN_PROTO);
|
|||
static void sysctl_percpuq_setup(struct sysctllog **, const char *,
|
||||
struct if_percpuq *);
|
||||
|
||||
struct if_deferred_start {
|
||||
struct ifnet *ids_ifp;
|
||||
void (*ids_if_start)(struct ifnet *);
|
||||
void *ids_si;
|
||||
};
|
||||
|
||||
static void if_deferred_start_softint(void *);
|
||||
static void if_deferred_start_common(struct ifnet *);
|
||||
static void if_deferred_start_destroy(struct ifnet *);
|
||||
|
||||
#if defined(INET) || defined(INET6)
|
||||
static void sysctl_net_pktq_setup(struct sysctllog **, int);
|
||||
#endif
|
||||
|
@ -953,6 +963,99 @@ bad:
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The deferred if_start framework
|
||||
*
|
||||
* The common APIs to defer if_start to softint when if_start is requested
|
||||
* from a device driver running in hardware interrupt context.
|
||||
*/
|
||||
/*
|
||||
* Call ifp->if_start (or equivalent) in a dedicated softint for
|
||||
* deferred if_start.
|
||||
*/
|
||||
static void
|
||||
if_deferred_start_softint(void *arg)
|
||||
{
|
||||
struct if_deferred_start *ids = arg;
|
||||
struct ifnet *ifp = ids->ids_ifp;
|
||||
|
||||
#ifdef DEBUG
|
||||
log(LOG_DEBUG, "%s: deferred start on %s\n", __func__,
|
||||
ifp->if_xname);
|
||||
#endif
|
||||
|
||||
ids->ids_if_start(ifp);
|
||||
}
|
||||
|
||||
/*
|
||||
* The default callback function for deferred if_start.
|
||||
*/
|
||||
static void
|
||||
if_deferred_start_common(struct ifnet *ifp)
|
||||
{
|
||||
|
||||
if_start_lock(ifp);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
if_snd_is_used(struct ifnet *ifp)
|
||||
{
|
||||
|
||||
return ifp->if_transmit == NULL || ifp->if_transmit == if_nulltransmit ||
|
||||
ALTQ_IS_ENABLED(&ifp->if_snd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Schedule deferred if_start.
|
||||
*/
|
||||
void
|
||||
if_schedule_deferred_start(struct ifnet *ifp)
|
||||
{
|
||||
|
||||
KASSERT(ifp->if_deferred_start != NULL);
|
||||
|
||||
if (if_snd_is_used(ifp) && IFQ_IS_EMPTY(&ifp->if_snd))
|
||||
return;
|
||||
|
||||
softint_schedule(ifp->if_deferred_start->ids_si);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an instance of deferred if_start. A driver should call the function
|
||||
* only if the driver needs deferred if_start. Drivers can setup their own
|
||||
* deferred if_start function via 2nd argument.
|
||||
*/
|
||||
void
|
||||
if_deferred_start_init(struct ifnet *ifp, void (*func)(struct ifnet *))
|
||||
{
|
||||
struct if_deferred_start *ids;
|
||||
|
||||
ids = kmem_zalloc(sizeof(*ids), KM_SLEEP);
|
||||
if (ids == NULL)
|
||||
panic("kmem_zalloc failed");
|
||||
|
||||
ids->ids_ifp = ifp;
|
||||
ids->ids_si = softint_establish(SOFTINT_NET|SOFTINT_MPSAFE,
|
||||
if_deferred_start_softint, ids);
|
||||
if (func != NULL)
|
||||
ids->ids_if_start = func;
|
||||
else
|
||||
ids->ids_if_start = if_deferred_start_common;
|
||||
|
||||
ifp->if_deferred_start = ids;
|
||||
}
|
||||
|
||||
static void
|
||||
if_deferred_start_destroy(struct ifnet *ifp)
|
||||
{
|
||||
|
||||
if (ifp->if_deferred_start == NULL)
|
||||
return;
|
||||
|
||||
softint_disestablish(ifp->if_deferred_start->ids_si);
|
||||
kmem_free(ifp->if_deferred_start, sizeof(*ifp->if_deferred_start));
|
||||
ifp->if_deferred_start = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The common interface input routine that is called by device drivers,
|
||||
|
@ -1194,6 +1297,7 @@ if_detach(struct ifnet *ifp)
|
|||
callout_destroy(ifp->if_slowtimo_ch);
|
||||
kmem_free(ifp->if_slowtimo_ch, sizeof(*ifp->if_slowtimo_ch));
|
||||
}
|
||||
if_deferred_start_destroy(ifp);
|
||||
|
||||
/*
|
||||
* Do an if_down() to give protocols a chance to do something.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: if.h,v 1.229 2016/11/22 02:06:00 ozaki-r Exp $ */
|
||||
/* $NetBSD: if.h,v 1.230 2016/12/08 01:06:35 ozaki-r Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
|
||||
|
@ -230,6 +230,7 @@ struct bridge_iflist;
|
|||
struct callout;
|
||||
struct krwlock;
|
||||
struct if_percpuq;
|
||||
struct if_deferred_start;
|
||||
|
||||
typedef unsigned short if_index_t;
|
||||
|
||||
|
@ -342,6 +343,7 @@ typedef struct ifnet {
|
|||
struct pslist_entry if_pslist_entry;
|
||||
struct psref_target if_psref;
|
||||
struct pslist_head if_addr_pslist;
|
||||
struct if_deferred_start *if_deferred_start;
|
||||
#endif
|
||||
} ifnet_t;
|
||||
|
||||
|
@ -989,6 +991,9 @@ void if_percpuq_destroy(struct if_percpuq *);
|
|||
void
|
||||
if_percpuq_enqueue(struct if_percpuq *, struct mbuf *);
|
||||
|
||||
void if_deferred_start_init(struct ifnet *, void (*)(struct ifnet *));
|
||||
void if_schedule_deferred_start(struct ifnet *);
|
||||
|
||||
void ifa_insert(struct ifnet *, struct ifaddr *);
|
||||
void ifa_remove(struct ifnet *, struct ifaddr *);
|
||||
|
||||
|
|
Loading…
Reference in New Issue