Commit 1e757011 authored by David S. Miller's avatar David S. Miller

Merge branch 'rmnet-Configuration-options'

Subash Abhinov Kasiviswanathan says:

====================
net: qualcomm: rmnet: Configuration options

This series adds support for configuring features on rmnet devices.
The rmnet specific features to be configured here are aggregation and
control commands.

Patch 1 is a cleanup of return codes in the transmit path.
Patch 2 removes some redundant ingress and egress macros.
Patch 3 restricts the creation of rmnet dev to one dev per mux id for a
given real dev.
Patch 4 adds ethernet data path support.
Patches 5-6 add support for configuring features on new and existing
rmnet devices.

v1->v2:
The memory leak fixed as part of patch 1 is merged seperately as
a896d94abd2c ("net: qualcomm: rmnet: Fix leak on transmit failure").
Fix a use after free in patch 4 if a packet with headroom lesser than ethernet
header length is received.

v2->v3:
Fix formatting problem in patch 5 in the return statement.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b4f70c3d 23790ef1
...@@ -143,11 +143,7 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, ...@@ -143,11 +143,7 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[], struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
int ingress_format = RMNET_INGRESS_FORMAT_DEMUXING | int ingress_format = RMNET_INGRESS_FORMAT_DEAGGREGATION;
RMNET_INGRESS_FORMAT_DEAGGREGATION |
RMNET_INGRESS_FORMAT_MAP;
int egress_format = RMNET_EGRESS_FORMAT_MUXING |
RMNET_EGRESS_FORMAT_MAP;
struct net_device *real_dev; struct net_device *real_dev;
int mode = RMNET_EPMODE_VND; int mode = RMNET_EPMODE_VND;
struct rmnet_endpoint *ep; struct rmnet_endpoint *ep;
...@@ -181,13 +177,20 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, ...@@ -181,13 +177,20 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev,
if (err) if (err)
goto err2; goto err2;
netdev_dbg(dev, "data format [ingress 0x%08X] [egress 0x%08X]\n",
ingress_format, egress_format);
port->egress_data_format = egress_format;
port->ingress_data_format = ingress_format;
port->rmnet_mode = mode; port->rmnet_mode = mode;
hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]); hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
if (data[IFLA_VLAN_FLAGS]) {
struct ifla_vlan_flags *flags;
flags = nla_data(data[IFLA_VLAN_FLAGS]);
ingress_format = flags->flags & flags->mask;
}
netdev_dbg(dev, "data format [ingress 0x%08X]\n", ingress_format);
port->ingress_data_format = ingress_format;
return 0; return 0;
err2: err2:
...@@ -317,9 +320,49 @@ static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[], ...@@ -317,9 +320,49 @@ static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
return 0; return 0;
} }
static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct rmnet_priv *priv = netdev_priv(dev);
struct net_device *real_dev;
struct rmnet_endpoint *ep;
struct rmnet_port *port;
u16 mux_id;
real_dev = __dev_get_by_index(dev_net(dev),
nla_get_u32(tb[IFLA_LINK]));
if (!real_dev || !dev || !rmnet_is_real_dev_registered(real_dev))
return -ENODEV;
port = rmnet_get_port_rtnl(real_dev);
if (data[IFLA_VLAN_ID]) {
mux_id = nla_get_u16(data[IFLA_VLAN_ID]);
ep = rmnet_get_endpoint(port, priv->mux_id);
hlist_del_init_rcu(&ep->hlnode);
hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
ep->mux_id = mux_id;
priv->mux_id = mux_id;
}
if (data[IFLA_VLAN_FLAGS]) {
struct ifla_vlan_flags *flags;
flags = nla_data(data[IFLA_VLAN_FLAGS]);
port->ingress_data_format = flags->flags & flags->mask;
}
return 0;
}
static size_t rmnet_get_size(const struct net_device *dev) static size_t rmnet_get_size(const struct net_device *dev)
{ {
return nla_total_size(2); /* IFLA_VLAN_ID */ return nla_total_size(2) /* IFLA_VLAN_ID */ +
nla_total_size(sizeof(struct ifla_vlan_flags)); /* IFLA_VLAN_FLAGS */
} }
struct rtnl_link_ops rmnet_link_ops __read_mostly = { struct rtnl_link_ops rmnet_link_ops __read_mostly = {
...@@ -331,6 +374,7 @@ struct rtnl_link_ops rmnet_link_ops __read_mostly = { ...@@ -331,6 +374,7 @@ struct rtnl_link_ops rmnet_link_ops __read_mostly = {
.newlink = rmnet_newlink, .newlink = rmnet_newlink,
.dellink = rmnet_dellink, .dellink = rmnet_dellink,
.get_size = rmnet_get_size, .get_size = rmnet_get_size,
.changelink = rmnet_changelink,
}; };
/* Needs either rcu_read_lock() or rtnl lock */ /* Needs either rcu_read_lock() or rtnl lock */
......
...@@ -33,7 +33,6 @@ struct rmnet_endpoint { ...@@ -33,7 +33,6 @@ struct rmnet_endpoint {
struct rmnet_port { struct rmnet_port {
struct net_device *dev; struct net_device *dev;
u32 ingress_data_format; u32 ingress_data_format;
u32 egress_data_format;
u8 nr_rmnet_devs; u8 nr_rmnet_devs;
u8 rmnet_mode; u8 rmnet_mode;
struct hlist_head muxed_ep[RMNET_MAX_LOGICAL_EP]; struct hlist_head muxed_ep[RMNET_MAX_LOGICAL_EP];
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/netdev_features.h> #include <linux/netdev_features.h>
#include <linux/if_arp.h>
#include "rmnet_private.h" #include "rmnet_private.h"
#include "rmnet_config.h" #include "rmnet_config.h"
#include "rmnet_vnd.h" #include "rmnet_vnd.h"
...@@ -104,6 +105,15 @@ rmnet_map_ingress_handler(struct sk_buff *skb, ...@@ -104,6 +105,15 @@ rmnet_map_ingress_handler(struct sk_buff *skb,
{ {
struct sk_buff *skbn; struct sk_buff *skbn;
if (skb->dev->type == ARPHRD_ETHER) {
if (pskb_expand_head(skb, ETH_HLEN, 0, GFP_KERNEL)) {
kfree_skb(skb);
return;
}
skb_push(skb, ETH_HLEN);
}
if (port->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) { if (port->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) {
while ((skbn = rmnet_map_deaggregate(skb)) != NULL) while ((skbn = rmnet_map_deaggregate(skb)) != NULL)
__rmnet_map_ingress_handler(skbn, port); __rmnet_map_ingress_handler(skbn, port);
...@@ -133,20 +143,18 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, ...@@ -133,20 +143,18 @@ static int rmnet_map_egress_handler(struct sk_buff *skb,
if (!map_header) if (!map_header)
goto fail; goto fail;
if (port->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) {
if (mux_id == 0xff) if (mux_id == 0xff)
map_header->mux_id = 0; map_header->mux_id = 0;
else else
map_header->mux_id = mux_id; map_header->mux_id = mux_id;
}
skb->protocol = htons(ETH_P_MAP); skb->protocol = htons(ETH_P_MAP);
return RMNET_MAP_SUCCESS; return 0;
fail: fail:
kfree_skb(skb); kfree_skb(skb);
return RMNET_MAP_CONSUMED; return -ENOMEM;
} }
static void static void
...@@ -178,7 +186,6 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) ...@@ -178,7 +186,6 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)
switch (port->rmnet_mode) { switch (port->rmnet_mode) {
case RMNET_EPMODE_VND: case RMNET_EPMODE_VND:
if (port->ingress_data_format & RMNET_INGRESS_FORMAT_MAP)
rmnet_map_ingress_handler(skb, port); rmnet_map_ingress_handler(skb, port);
break; break;
case RMNET_EPMODE_BRIDGE: case RMNET_EPMODE_BRIDGE:
...@@ -212,20 +219,9 @@ void rmnet_egress_handler(struct sk_buff *skb) ...@@ -212,20 +219,9 @@ void rmnet_egress_handler(struct sk_buff *skb)
return; return;
} }
if (port->egress_data_format & RMNET_EGRESS_FORMAT_MAP) { if (rmnet_map_egress_handler(skb, port, mux_id, orig_dev))
switch (rmnet_map_egress_handler(skb, port, mux_id, orig_dev)) {
case RMNET_MAP_CONSUMED:
return; return;
case RMNET_MAP_SUCCESS:
break;
default:
kfree_skb(skb);
return;
}
}
rmnet_vnd_tx_fixup(skb, orig_dev); rmnet_vnd_tx_fixup(skb, orig_dev);
dev_queue_xmit(skb); dev_queue_xmit(skb);
......
...@@ -30,15 +30,6 @@ struct rmnet_map_control_command { ...@@ -30,15 +30,6 @@ struct rmnet_map_control_command {
}; };
} __aligned(1); } __aligned(1);
enum rmnet_map_results {
RMNET_MAP_SUCCESS,
RMNET_MAP_CONSUMED,
RMNET_MAP_GENERAL_FAILURE,
RMNET_MAP_NOT_ENABLED,
RMNET_MAP_FAILED_AGGREGATION,
RMNET_MAP_FAILED_MUX
};
enum rmnet_map_commands { enum rmnet_map_commands {
RMNET_MAP_COMMAND_NONE, RMNET_MAP_COMMAND_NONE,
RMNET_MAP_COMMAND_FLOW_DISABLE, RMNET_MAP_COMMAND_FLOW_DISABLE,
......
...@@ -19,14 +19,8 @@ ...@@ -19,14 +19,8 @@
#define RMNET_TX_QUEUE_LEN 1000 #define RMNET_TX_QUEUE_LEN 1000
/* Constants */ /* Constants */
#define RMNET_EGRESS_FORMAT_MAP BIT(1) #define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(0)
#define RMNET_EGRESS_FORMAT_AGGREGATION BIT(2) #define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(1)
#define RMNET_EGRESS_FORMAT_MUXING BIT(3)
#define RMNET_INGRESS_FORMAT_MAP BIT(1)
#define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(2)
#define RMNET_INGRESS_FORMAT_DEMUXING BIT(3)
#define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(4)
/* Replace skb->dev to a virtual rmnet device and pass up the stack */ /* Replace skb->dev to a virtual rmnet device and pass up the stack */
#define RMNET_EPMODE_VND (1) #define RMNET_EPMODE_VND (1)
......
...@@ -185,6 +185,9 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, ...@@ -185,6 +185,9 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
if (ep->egress_dev) if (ep->egress_dev)
return -EINVAL; return -EINVAL;
if (rmnet_get_endpoint(port, id))
return -EBUSY;
rc = register_netdevice(rmnet_dev); rc = register_netdevice(rmnet_dev);
if (!rc) { if (!rc) {
ep->egress_dev = rmnet_dev; ep->egress_dev = rmnet_dev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment