Skip to content

Commit

Permalink
net: qualcomm: rmnet: Update rmnet device MTU based on real device
Browse files Browse the repository at this point in the history
[ Upstream commit b7f5eb6 ]

Packets sent by rmnet to the real device have variable MAP header
lengths based on the data format configured. This patch adds checks
to ensure that the real device MTU is sufficient to transmit the MAP
packet comprising of the MAP header and the IP packet. This check
is enforced when rmnet devices are created and updated and during
MTU updates of both the rmnet and real device.

Additionally, rmnet devices now have a default MTU configured which
accounts for the real device MTU and the headroom based on the data
format.

Signed-off-by: Sean Tranchetti <stranche@codeaurora.org>
Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
Tested-by: Loic Poulain <loic.poulain@linaro.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Subash Abhinov Kasiviswanathan authored and gregkh committed Jun 23, 2021
1 parent 4abfd59 commit e357777
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 3 deletions.
15 changes: 13 additions & 2 deletions drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
Expand Up @@ -26,7 +26,7 @@ static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
}

/* Needs rtnl lock */
static struct rmnet_port*
struct rmnet_port*
rmnet_get_port_rtnl(const struct net_device *real_dev)
{
return rtnl_dereference(real_dev->rx_handler_data);
Expand Down Expand Up @@ -253,7 +253,10 @@ static int rmnet_config_notify_cb(struct notifier_block *nb,
netdev_dbg(real_dev, "Kernel unregister\n");
rmnet_force_unassociate_device(real_dev);
break;

case NETDEV_CHANGEMTU:
if (rmnet_vnd_validate_real_dev_mtu(real_dev))
return NOTIFY_BAD;
break;
default:
break;
}
Expand Down Expand Up @@ -329,9 +332,17 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],

if (data[IFLA_RMNET_FLAGS]) {
struct ifla_rmnet_flags *flags;
u32 old_data_format;

old_data_format = port->data_format;
flags = nla_data(data[IFLA_RMNET_FLAGS]);
port->data_format = flags->flags & flags->mask;

if (rmnet_vnd_update_dev_mtu(port, real_dev)) {
port->data_format = old_data_format;
NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev");
return -EINVAL;
}
}

return 0;
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
Expand Up @@ -73,4 +73,6 @@ int rmnet_add_bridge(struct net_device *rmnet_dev,
struct netlink_ext_ack *extack);
int rmnet_del_bridge(struct net_device *rmnet_dev,
struct net_device *slave_dev);
struct rmnet_port*
rmnet_get_port_rtnl(const struct net_device *real_dev);
#endif /* _RMNET_CONFIG_H_ */
73 changes: 72 additions & 1 deletion drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
Expand Up @@ -58,9 +58,30 @@ static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}

static int rmnet_vnd_headroom(struct rmnet_port *port)
{
u32 headroom;

headroom = sizeof(struct rmnet_map_header);

if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4)
headroom += sizeof(struct rmnet_map_ul_csum_header);

return headroom;
}

static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)
{
if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)
struct rmnet_priv *priv = netdev_priv(rmnet_dev);
struct rmnet_port *port;
u32 headroom;

port = rmnet_get_port_rtnl(priv->real_dev);

headroom = rmnet_vnd_headroom(port);

if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE ||
new_mtu > (priv->real_dev->mtu - headroom))
return -EINVAL;

rmnet_dev->mtu = new_mtu;
Expand Down Expand Up @@ -229,6 +250,7 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,

{
struct rmnet_priv *priv = netdev_priv(rmnet_dev);
u32 headroom;
int rc;

if (rmnet_get_endpoint(port, id)) {
Expand All @@ -242,6 +264,13 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,

priv->real_dev = real_dev;

headroom = rmnet_vnd_headroom(port);

if (rmnet_vnd_change_mtu(rmnet_dev, real_dev->mtu - headroom)) {
NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev");
return -EINVAL;
}

rc = register_netdevice(rmnet_dev);
if (!rc) {
ep->egress_dev = rmnet_dev;
Expand Down Expand Up @@ -283,3 +312,45 @@ int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable)

return 0;
}

int rmnet_vnd_validate_real_dev_mtu(struct net_device *real_dev)
{
struct hlist_node *tmp_ep;
struct rmnet_endpoint *ep;
struct rmnet_port *port;
unsigned long bkt_ep;
u32 headroom;

port = rmnet_get_port_rtnl(real_dev);

headroom = rmnet_vnd_headroom(port);

hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {
if (ep->egress_dev->mtu > (real_dev->mtu - headroom))
return -1;
}

return 0;
}

int rmnet_vnd_update_dev_mtu(struct rmnet_port *port,
struct net_device *real_dev)
{
struct hlist_node *tmp_ep;
struct rmnet_endpoint *ep;
unsigned long bkt_ep;
u32 headroom;

headroom = rmnet_vnd_headroom(port);

hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {
if (ep->egress_dev->mtu <= (real_dev->mtu - headroom))
continue;

if (rmnet_vnd_change_mtu(ep->egress_dev,
real_dev->mtu - headroom))
return -1;
}

return 0;
}
3 changes: 3 additions & 0 deletions drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h
Expand Up @@ -18,4 +18,7 @@ int rmnet_vnd_dellink(u8 id, struct rmnet_port *port,
void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev);
void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev);
void rmnet_vnd_setup(struct net_device *dev);
int rmnet_vnd_validate_real_dev_mtu(struct net_device *real_dev);
int rmnet_vnd_update_dev_mtu(struct rmnet_port *port,
struct net_device *real_dev);
#endif /* _RMNET_VND_H_ */

0 comments on commit e357777

Please sign in to comment.