Subject: Re: HW-assisted VLAN handling
To: Quentin Garnier <cube@cubidou.net>
From: Jaromir Dolecek <jdolecek@NetBSD.org>
List: tech-kern
Date: 01/30/2005 17:09:45
Quentin Garnier wrote:
> Here, Jaromir has a device that can select the vlan ids it will receive,
> pretty much like a multicast filter.
Exactly.
> I guess an API should be added to have vlan(4) notify the hardware
> layer, again just like multicast filter. AFAICT, no such exists at the
> moment.
What about following?
The patch:
* adds list of vlans ethernet interface is configured for to ethercom
* adds a ETHERCAP_VLAN_HWFILTER capability, if set the driver is
notified via ioctl when a vlan is attached or detached
This works fine for my needs.
Jaromir
Index: if_ether.h
===================================================================
RCS file: /cvsroot/src/sys/net/if_ether.h,v
retrieving revision 1.36
diff -u -p -r1.36 if_ether.h
--- if_ether.h 8 Jan 2005 03:18:18 -0000 1.36
+++ if_ether.h 30 Jan 2005 15:47:12 -0000
@@ -92,7 +92,7 @@ struct ether_header {
(((etype) == ETHERTYPE_VLAN) ? ETHER_VLAN_ENCAP_LEN : 0))
/*
- * Ethernet CRC32 polynomials (big- and little-endian verions).
+ * Ethernet CRC32 polynomials (big- and little-endian versions).
*/
#define ETHER_CRC_POLY_LE 0xedb88320
#define ETHER_CRC_POLY_BE 0x04c11db6
@@ -161,11 +161,13 @@ struct ethercom {
struct mowner ec_rx_mowner; /* mbufs received */
struct mowner ec_tx_mowner; /* mbufs transmitted */
#endif
+ LIST_HEAD(, ether_vlan) ec_vlanaddrs; /* list of ether VLANs */
};
#define ETHERCAP_VLAN_MTU 0x00000001 /* VLAN-compatible MTU */
#define ETHERCAP_VLAN_HWTAGGING 0x00000002 /* hardware VLAN tag support */
#define ETHERCAP_JUMBO_MTU 0x00000004 /* 9000 byte MTU supported */
+#define ETHERCAP_VLAN_HWFILTER 0x00000008 /* hardware uses VLAN filter */
#ifdef _KERNEL
extern const uint8_t etherbroadcastaddr[ETHER_ADDR_LEN];
@@ -242,7 +244,19 @@ struct ether_multistep {
ETHER_NEXT_MULTI((step), (enm)); \
}
+/*
+ * Ethernet 802.1Q VLAN structure.
+ */
+struct ether_vlan {
+ u_int16_t ev_tag;
+ LIST_ENTRY(ether_vlan) ev_list;
+};
+
#ifdef _KERNEL
+
+/* Kernel-only ioctls */
+#define SIOCSETHVLAN _IOWR('i', 141, uint16_t)
+
void ether_ifattach(struct ifnet *, const u_int8_t *);
void ether_ifdetach(struct ifnet *);
Index: if_ethersubr.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_ethersubr.c,v
retrieving revision 1.118
diff -u -p -r1.118 if_ethersubr.c
--- if_ethersubr.c 8 Jan 2005 03:18:18 -0000 1.118
+++ if_ethersubr.c 30 Jan 2005 15:47:12 -0000
@@ -1520,6 +1520,11 @@ ether_ioctl(struct ifnet *ifp, u_long cm
error = ether_delmulti(ifr, ec);
break;
+ case SIOCSETHVLAN:
+ /* ignored by default */
+ error = 0;
+ break;
+
default:
error = ENOTTY;
}
Index: if_vlan.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_vlan.c,v
retrieving revision 1.42
diff -u -p -r1.42 if_vlan.c
--- if_vlan.c 4 Dec 2004 18:31:43 -0000 1.42
+++ if_vlan.c 30 Jan 2005 15:47:13 -0000
@@ -132,14 +132,14 @@ struct ifvlan {
union {
struct ethercom ifvu_ec;
} ifv_u;
- struct ifnet *ifv_p; /* parent interface of this vlan */
+ struct ifnet *ifv_p; /* parent interface of this vlan */
struct ifv_linkmib {
const struct vlan_multisw *ifvm_msw;
int ifvm_encaplen; /* encapsulation length */
int ifvm_mtufudge; /* MTU fudged by this much */
int ifvm_mintu; /* min transmission unit */
u_int16_t ifvm_proto; /* encapsulation ethertype */
- u_int16_t ifvm_tag; /* tag to apply on packets */
+ struct ether_vlan ifvm_vlan; /* VLAN context (for parent) */
} ifv_mib;
LIST_HEAD(__vlan_mchead, vlan_mc_entry) ifv_mc_listhead;
LIST_ENTRY(ifvlan) ifv_list;
@@ -156,7 +156,8 @@ struct ifvlan {
#define ifv_encaplen ifv_mib.ifvm_encaplen
#define ifv_mtufudge ifv_mib.ifvm_mtufudge
#define ifv_mintu ifv_mib.ifvm_mintu
-#define ifv_tag ifv_mib.ifvm_tag
+#define ifv_vlan ifv_mib.ifvm_vlan
+#define ifv_tag ifv_vlan.ev_tag
struct vlan_multisw {
int (*vmsw_addmulti)(struct ifvlan *, struct ifreq *);
@@ -176,7 +177,7 @@ const struct vlan_multisw vlan_ether_mul
static int vlan_clone_create(struct if_clone *, int);
static int vlan_clone_destroy(struct ifnet *);
-static int vlan_config(struct ifvlan *, struct ifnet *);
+static int vlan_config(struct ifvlan *, struct ifnet *, struct vlanreq *);
static int vlan_ioctl(struct ifnet *, u_long, caddr_t);
static void vlan_start(struct ifnet *);
static void vlan_unconfig(struct ifnet *);
@@ -268,7 +269,7 @@ vlan_clone_destroy(struct ifnet *ifp)
* Configure a VLAN interface. Must be called at splnet().
*/
static int
-vlan_config(struct ifvlan *ifv, struct ifnet *p)
+vlan_config(struct ifvlan *ifv, struct ifnet *p, struct vlanreq *vlr)
{
struct ifnet *ifp = &ifv->ifv_if;
int error;
@@ -290,7 +291,7 @@ vlan_config(struct ifvlan *ifv, struct i
* i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
* enable it.
*/
- if (ec->ec_nvlans++ == 0 &&
+ if (++ec->ec_nvlans == 1 &&
(ec->ec_capabilities & ETHERCAP_VLAN_MTU) != 0) {
/*
* Enable Tx/Rx of VLAN-sized frames.
@@ -303,7 +304,7 @@ vlan_config(struct ifvlan *ifv, struct i
error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
(caddr_t) &ifr);
if (error) {
- if (ec->ec_nvlans-- == 1)
+ if (--ec->ec_nvlans == 0)
ec->ec_capenable &=
~ETHERCAP_VLAN_MTU;
return (error);
@@ -332,6 +333,20 @@ vlan_config(struct ifvlan *ifv, struct i
IFCAP_CSUM_UDPv4|IFCAP_CSUM_TCPv6|
IFCAP_CSUM_UDPv6);
+ ifv->ifv_tag = vlr->vlr_tag;
+
+ /* Make our VLAN ID available to parent */
+ LIST_INSERT_HEAD(&((struct ethercom *)p)->ec_vlanaddrs,
+ &ifv->ifv_vlan, ev_list);
+
+ /*
+ * Notify the driver about changed VLAN list.
+ */
+ if (ec->ec_capabilities & ETHERCAP_VLAN_HWFILTER) {
+ (void) (*p->if_ioctl)(p, SIOCSETHVLAN,
+ (caddr_t) &vlr->vlr_tag);
+ }
+
/*
* We inherit the parent's Ethernet address.
*/
@@ -347,8 +362,7 @@ vlan_config(struct ifvlan *ifv, struct i
ifv->ifv_p = p;
ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
ifv->ifv_if.if_flags = p->if_flags &
- (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
-
+ (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_RUNNING);
/*
* Inherit the if_type from the parent. This allows us
* to participate in bridges of that type.
@@ -382,7 +396,7 @@ vlan_unconfig(struct ifnet *ifp)
{
struct ethercom *ec = (void *) ifv->ifv_p;
- if (ec->ec_nvlans-- == 1) {
+ if (--ec->ec_nvlans == 0) {
/*
* Disable Tx/Rx of VLAN-sized frames.
*/
@@ -396,6 +410,17 @@ vlan_unconfig(struct ifnet *ifp)
}
}
+ /* Deassociate VLAN from parent */
+ LIST_REMOVE(&ifv->ifv_vlan, ev_list);
+
+ /*
+ * Notify the driver about changed VLAN list.
+ */
+ if (ec->ec_capabilities & ETHERCAP_VLAN_HWFILTER) {
+ (void) (*ifv->ifv_p->if_ioctl)(ifv->ifv_p,
+ SIOCSETHVLAN, (caddr_t) &ifv->ifv_tag);
+ }
+
ether_ifdetach(ifp);
vlan_reset_linkname(ifp);
break;
@@ -528,10 +553,8 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
error = ENOENT;
break;
}
- if ((error = vlan_config(ifv, pr)) != 0)
+ if ((error = vlan_config(ifv, pr, &vlr)) != 0)
break;
- ifv->ifv_tag = vlr.vlr_tag;
- ifp->if_flags |= IFF_RUNNING;
/* Update promiscuous mode, if necessary. */
vlan_set_promisc(ifp);
--
Jaromir Dolecek <jdolecek@NetBSD.org> http://www.NetBSD.cz/
-=- We can walk our road together if our goals are all the same; -=-
-=- We can run alone and free if we pursue a different aim. -=-