Currently most NICs support Q-tagged frames[1], i.e. 1522 bytes frames
to handle 4 bytes VLAN header. But some encapsulation protocols like
802.1ad requires them to handle larger frames.
This change introduces dev_set_env_hdr_len() and corresponding drivers'
operation .ndo_set_env_hdr_len(), which notifies drivers of needed
encapsulation header length. This enables devices to accept longer
frames with encapsulation headers, i.e. envelope frames[2], without
expanding MTU size for non-encapsulated frames.

Note 1:
Envelope frames are not jumbo frames. See IEEE 802.3as[3] for detail.
IEEE 802.3-2012 3.2.7 says:
          The envelope frame is intended to allow inclusion of additional
          prefixes and suffixes required by higher layer encapusulation
          protocols such as those defined by the IEEE 802.1 working
          group (such as Provider Bridges and MAC Security), ITU-T or
          IETF (such as MPLS). The original MAC Client Data field
          maximum remains 1500 octets while the encapsulation protocols
          may add up to an additional 482 octets. Use of these extra
          octets for other purposes is not recommended, and may result
          in MAC frames being dropped or corrupted as they may violate
          maximum MAC frame size restrictions if encapsulation protocols
          are required to operate on them.

Note 2:
Envelope frames in IEEE 802.3 defines the max size of envelope frames
as 2000 bytes. This change is more flexible than 802.3 in terms of max
allowed frame length.

[1] IEEE 802.3-2012, 1.4.334.
[2] IEEE 802.3-2012, 1.4.184.
[3] http://www.ieee802.org/3/as/public/0607/802.3as_overview.pdf

Signed-off-by: Toshiaki Makita <makita.toshi...@lab.ntt.co.jp>
---
 include/linux/netdevice.h | 21 +++++++++++++++++++++
 net/core/dev.c            | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 136ae6bb..a0ac76a 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1132,6 +1132,10 @@ struct netdev_xdp {
  * int (*ndo_xdp)(struct net_device *dev, struct netdev_xdp *xdp);
  *     This function is used to set or query state related to XDP on the
  *     netdevice. See definition of enum xdp_netdev_command for details.
+ * int (*ndo_set_env_hdr_len)(struct net_device *dev, int hdr_len);
+ *     This function is used to set the maximum header size of envelope
+ *     frames. The device must accept the size of MTU + envelope header
+ *     size on packet reception.
  *
  */
 struct net_device_ops {
@@ -1323,6 +1327,8 @@ struct net_device_ops {
                                                       int needed_headroom);
        int                     (*ndo_xdp)(struct net_device *dev,
                                           struct netdev_xdp *xdp);
+       int                     (*ndo_set_env_hdr_len)(struct net_device *dev,
+                                                      int hdr_len);
 };
 
 /**
@@ -1506,6 +1512,7 @@ enum netdev_priv_flags {
  *     @if_port:       Selectable AUI, TP, ...
  *     @dma:           DMA channel
  *     @mtu:           Interface MTU value
+ *     @env_hdr_len:   Additional encapsulation header length to MTU
  *     @type:          Interface hardware type
  *     @hard_header_len: Maximum hardware header length.
  *
@@ -1726,6 +1733,7 @@ struct net_device {
        unsigned char           dma;
 
        unsigned int            mtu;
+       unsigned int            env_hdr_len;
        unsigned short          type;
        unsigned short          hard_header_len;
 
@@ -3300,6 +3308,7 @@ int dev_change_name(struct net_device *, const char *);
 int dev_set_alias(struct net_device *, const char *, size_t);
 int dev_change_net_namespace(struct net_device *, struct net *, const char *);
 int dev_set_mtu(struct net_device *, int);
+int dev_set_env_hdr_len(struct net_device *, int);
 void dev_set_group(struct net_device *, int);
 int dev_set_mac_address(struct net_device *, struct sockaddr *);
 int dev_change_carrier(struct net_device *, bool new_carrier);
@@ -4233,6 +4242,18 @@ static inline bool netif_reduces_vlan_mtu(struct 
net_device *dev)
        return dev->priv_flags & IFF_MACSEC;
 }
 
+/* return envelope header length */
+static inline int netif_get_env_hdr_len(struct net_device *dev)
+{
+       if (dev->netdev_ops->ndo_set_env_hdr_len)
+               return dev->env_hdr_len;
+
+       if (netif_reduces_vlan_mtu(dev))
+               return 0;
+
+       return 4; /* VLAN_HLEN */
+}
+
 extern struct pernet_operations __net_initdata loopback_net_ops;
 
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
diff --git a/net/core/dev.c b/net/core/dev.c
index c0c291f..df75aaa 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6524,6 +6524,38 @@ int dev_set_mtu(struct net_device *dev, int new_mtu)
 EXPORT_SYMBOL(dev_set_mtu);
 
 /**
+ *     dev_set_env_hdr_len - Set max envelope header length
+ *     @dev: device
+ *     @new_len: new length
+ */
+int dev_set_env_hdr_len(struct net_device *dev, int new_len)
+{
+       const struct net_device_ops *ops = dev->netdev_ops;
+       int err;
+
+       if (!ops->ndo_set_env_hdr_len)
+               return -EOPNOTSUPP;
+
+       if (new_len < 0)
+               return -EINVAL;
+
+       if (!netif_device_present(dev))
+               return -ENODEV;
+
+       if (new_len == dev->env_hdr_len)
+               return 0;
+
+       err = ops->ndo_set_env_hdr_len(dev, new_len);
+       if (err)
+               return err;
+
+       dev->env_hdr_len = new_len;
+
+       return 0;
+}
+EXPORT_SYMBOL(dev_set_env_hdr_len);
+
+/**
  *     dev_set_group - Change group this device belongs to
  *     @dev: device
  *     @new_group: group this device should belong to
-- 
1.8.3.1



Reply via email to