summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorHarout Hedeshian <harouth@codeaurora.org>2014-01-06 18:23:28 +0200
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:05:01 -0700
commit20bcb202e3a6f5dccfa4d8e2552e14d202a53f86 (patch)
treedbd5bee41cc431846b103cad681a713ca4439a6f /net
parente618b5e2a8225412e1871d81e54d12503093e59c (diff)
net: rmnet_data: Enhancements to support SSR
Cleaned up refcount on get_dev_by_name. Added new APIs to support cleanup of configuration and virtual devices. Added explicit reference managment in association/un-association and when setting/unsetting logical EP. CRs-Fixed: 596227 Change-Id: Ic67bb649b0f0420d9a1e4bf5664ed63c0ff7d8bf Signed-off-by: Harout Hadeshian <harouth@codeaurora.org>
Diffstat (limited to 'net')
-rw-r--r--net/rmnet_data/rmnet_data_config.c210
-rw-r--r--net/rmnet_data/rmnet_data_config.h7
-rw-r--r--net/rmnet_data/rmnet_data_handlers.c16
-rw-r--r--net/rmnet_data/rmnet_data_vnd.c38
-rw-r--r--net/rmnet_data/rmnet_data_vnd.h3
5 files changed, 259 insertions, 15 deletions
diff --git a/net/rmnet_data/rmnet_data_config.c b/net/rmnet_data/rmnet_data_config.c
index e9561647fd74..2544d5d05aeb 100644
--- a/net/rmnet_data/rmnet_data_config.c
+++ b/net/rmnet_data/rmnet_data_config.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -125,6 +125,40 @@ static inline struct rmnet_phys_ep_conf_s *_rmnet_get_phys_ep_config
return 0;
}
+/**
+ * _rmnet_get_logical_ep() - Gets the logical end point configuration
+ * structure for a network device
+ * @dev: Device to get endpoint configuration from
+ * @config_id: Logical endpoint id on device
+ * Retrieves the logical_endpoint_config structure.
+ *
+ * Return:
+ * - End point configuration structure
+ * - NULL in case of an error
+ */
+struct rmnet_logical_ep_conf_s *_rmnet_get_logical_ep(struct net_device *dev,
+ int config_id)
+{
+ struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_logical_ep_conf_s *epconfig_l;
+
+ if (rmnet_vnd_is_vnd(dev))
+ epconfig_l = rmnet_vnd_get_le_config(dev);
+ else {
+ config = _rmnet_get_phys_ep_config(dev);
+
+ if (!config)
+ return NULL;
+
+ if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)
+ epconfig_l = &config->local_ep;
+ else
+ epconfig_l = &config->muxed_ep[config_id];
+ }
+
+ return epconfig_l;
+}
+
/* ***************** Netlink Handler **************************************** */
#define _RMNET_NETLINK_NULL_CHECKS() do { if (!rmnet_header || !resp_rmnet) \
BUG(); \
@@ -151,6 +185,7 @@ static void _rmnet_netlink_set_link_egress_data_format
rmnet_header->data_format.agg_size,
rmnet_header->data_format.agg_count
);
+ dev_put(dev);
}
static void _rmnet_netlink_set_link_ingress_data_format
@@ -171,6 +206,7 @@ static void _rmnet_netlink_set_link_ingress_data_format
resp_rmnet->return_code =
rmnet_set_ingress_data_format(dev,
rmnet_header->data_format.flags);
+ dev_put(dev);
}
static void _rmnet_netlink_set_logical_ep_config
@@ -203,6 +239,39 @@ static void _rmnet_netlink_set_logical_ep_config
dev2);
else
resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ if (dev != 0)
+ dev_put(dev);
+ if (dev2 != 0)
+ dev_put(dev2);
+}
+
+static void _rmnet_netlink_unset_logical_ep_config
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev;
+ _RMNET_NETLINK_NULL_CHECKS();
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ if (rmnet_header->local_ep_config.ep_id < -1
+ || rmnet_header->local_ep_config.ep_id > 254) {
+ resp_rmnet->return_code = RMNET_CONFIG_BAD_ARGUMENTS;
+ return;
+ }
+
+ dev = dev_get_by_name(&init_net,
+ rmnet_header->local_ep_config.dev);
+
+ if (dev != 0) {
+ resp_rmnet->return_code =
+ rmnet_unset_logical_endpoint_config(
+ dev,
+ rmnet_header->local_ep_config.ep_id);
+ dev_put(dev);
+ } else {
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+ }
}
static void _rmnet_netlink_associate_network_device
@@ -220,6 +289,7 @@ static void _rmnet_netlink_associate_network_device
}
resp_rmnet->return_code = rmnet_associate_network_device(dev);
+ dev_put(dev);
}
static void _rmnet_netlink_unassociate_network_device
@@ -237,6 +307,7 @@ static void _rmnet_netlink_unassociate_network_device
}
resp_rmnet->return_code = rmnet_unassociate_network_device(dev);
+ dev_put(dev);
}
static void _rmnet_netlink_get_link_egress_data_format
@@ -258,6 +329,7 @@ static void _rmnet_netlink_get_link_egress_data_format
config = _rmnet_get_phys_ep_config(dev);
if (!config) {
resp_rmnet->return_code = RMNET_CONFIG_INVALID_REQUEST;
+ dev_put(dev);
return;
}
@@ -267,6 +339,7 @@ static void _rmnet_netlink_get_link_egress_data_format
resp_rmnet->data_format.flags = config->egress_data_format;
resp_rmnet->data_format.agg_count = config->egress_agg_count;
resp_rmnet->data_format.agg_size = config->egress_agg_size;
+ dev_put(dev);
}
static void _rmnet_netlink_get_link_ingress_data_format
@@ -288,6 +361,7 @@ static void _rmnet_netlink_get_link_ingress_data_format
config = _rmnet_get_phys_ep_config(dev);
if (!config) {
resp_rmnet->return_code = RMNET_CONFIG_INVALID_REQUEST;
+ dev_put(dev);
return;
}
@@ -295,6 +369,7 @@ static void _rmnet_netlink_get_link_ingress_data_format
resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA;
resp_rmnet->arg_length = RMNET_NL_MSG_SIZE(data_format);
resp_rmnet->data_format.flags = config->ingress_data_format;
+ dev_put(dev);
}
static void _rmnet_netlink_get_vnd_name
@@ -438,6 +513,11 @@ void rmnet_config_netlink_msg_handler(struct sk_buff *skb)
_rmnet_netlink_set_logical_ep_config(rmnet_header, resp_rmnet);
break;
+ case RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG:
+ _rmnet_netlink_unset_logical_ep_config(rmnet_header,
+ resp_rmnet);
+ break;
+
case RMNET_NETLINK_NEW_VND:
resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
resp_rmnet->return_code =
@@ -451,6 +531,16 @@ void rmnet_config_netlink_msg_handler(struct sk_buff *skb)
rmnet_header->vnd.vnd_name);
break;
+ case RMNET_NETLINK_FREE_VND:
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ /* Please check rmnet_vnd_free_dev documentation regarding
+ the below locking sequence
+ */
+ rtnl_unlock();
+ resp_rmnet->return_code = rmnet_free_vnd(rmnet_header->vnd.id);
+ rtnl_lock();
+ break;
+
case RMNET_NETLINK_GET_VND_NAME:
_rmnet_netlink_get_vnd_name(rmnet_header, resp_rmnet);
break;
@@ -486,11 +576,14 @@ void rmnet_config_netlink_msg_handler(struct sk_buff *skb)
* - RMNET_CONFIG_OK if successful
* - RMNET_CONFIG_NO_SUCH_DEVICE dev is null
* - RMNET_CONFIG_INVALID_REQUEST if device is not already associated
+ * - RMNET_CONFIG_DEVICE_IN_USE if device has logical ep that wasn't unset
* - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
*/
int rmnet_unassociate_network_device(struct net_device *dev)
{
struct rmnet_phys_ep_conf_s *config;
+ int config_id = RMNET_LOCAL_LOGICAL_ENDPOINT;
+ struct rmnet_logical_ep_conf_s *epconfig_l;
ASSERT_RTNL();
LOGL("%s(%s);", __func__, dev->name);
@@ -501,6 +594,12 @@ int rmnet_unassociate_network_device(struct net_device *dev)
if (!_rmnet_is_physical_endpoint_associated(dev))
return RMNET_CONFIG_INVALID_REQUEST;
+ for (; config_id < RMNET_DATA_MAX_LOGICAL_EP; config_id++) {
+ epconfig_l = _rmnet_get_logical_ep(dev, config_id);
+ if (epconfig_l && epconfig_l->refcount)
+ return RMNET_CONFIG_DEVICE_IN_USE;
+ }
+
config = (struct rmnet_phys_ep_conf_s *)
rcu_dereference(dev->rx_handler_data);
@@ -511,6 +610,8 @@ int rmnet_unassociate_network_device(struct net_device *dev)
netdev_rx_handler_unregister(dev);
+ /* Explicitly release the reference from the device */
+ dev_put(dev);
return RMNET_CONFIG_OK;
}
@@ -596,6 +697,7 @@ int rmnet_set_egress_data_format(struct net_device *dev,
* Return:
* - RMNET_CONFIG_OK if successful
* - RMNET_CONFIG_NO_SUCH_DEVICE dev is null
+ * - RMNET_CONFIG_INVALID_REQUEST if the device to be associated is a vnd
* - RMNET_CONFIG_DEVICE_IN_USE if dev rx_handler is already filled
* - RMNET_CONFIG_DEVICE_IN_USE if netdev_rx_handler_register() fails
*/
@@ -615,6 +717,11 @@ int rmnet_associate_network_device(struct net_device *dev)
return RMNET_CONFIG_DEVICE_IN_USE;
}
+ if (rmnet_vnd_is_vnd(dev)) {
+ LOGM("%s(): %s is a vnd\n", __func__, dev->name);
+ return RMNET_CONFIG_INVALID_REQUEST;
+ }
+
config = (struct rmnet_phys_ep_conf_s *)
kmalloc(sizeof(struct rmnet_phys_ep_conf_s), GFP_ATOMIC);
@@ -634,6 +741,8 @@ int rmnet_associate_network_device(struct net_device *dev)
return RMNET_CONFIG_DEVICE_IN_USE;
}
+ /* Explicitly hold a reference to the device */
+ dev_hold(dev);
return RMNET_CONFIG_OK;
}
@@ -647,13 +756,13 @@ int rmnet_associate_network_device(struct net_device *dev)
* - RMNET_CONFIG_OK if successful
* - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
* - RMNET_CONFIG_NO_SUCH_DEVICE if device to set config on is null
+ * - RMNET_CONFIG_DEVICE_IN_USE if device already has a logical ep
* - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range
*/
int _rmnet_set_logical_endpoint_config(struct net_device *dev,
int config_id,
struct rmnet_logical_ep_conf_s *epconfig)
{
- struct rmnet_phys_ep_conf_s *config;
struct rmnet_logical_ep_conf_s *epconfig_l;
ASSERT_RTNL();
@@ -665,19 +774,13 @@ int _rmnet_set_logical_endpoint_config(struct net_device *dev,
|| config_id >= RMNET_DATA_MAX_LOGICAL_EP)
return RMNET_CONFIG_BAD_ARGUMENTS;
- if (rmnet_vnd_is_vnd(dev))
- epconfig_l = rmnet_vnd_get_le_config(dev);
- else {
- config = _rmnet_get_phys_ep_config(dev);
+ epconfig_l = _rmnet_get_logical_ep(dev, config_id);
- if (!config)
+ if (!epconfig_l)
return RMNET_CONFIG_UNKNOWN_ERROR;
- if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)
- epconfig_l = &config->local_ep;
- else
- epconfig_l = &config->muxed_ep[config_id];
- }
+ if (epconfig_l->refcount)
+ return RMNET_CONFIG_DEVICE_IN_USE;
memcpy(epconfig_l, epconfig, sizeof(struct rmnet_logical_ep_conf_s));
if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)
@@ -685,6 +788,46 @@ int _rmnet_set_logical_endpoint_config(struct net_device *dev,
else
epconfig_l->mux_id = config_id;
+ /* Explicitly hold a reference to the egress device */
+ dev_hold(epconfig_l->egress_dev);
+ return RMNET_CONFIG_OK;
+}
+
+/**
+ * _rmnet_unset_logical_endpoint_config() - Un-set the logical endpoing config
+ * on device
+ * @dev: Device to set endpoint configuration on
+ * @config_id: logical endpoint id on device
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
+ * - RMNET_CONFIG_NO_SUCH_DEVICE if device to set config on is null
+ * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range
+ */
+int _rmnet_unset_logical_endpoint_config(struct net_device *dev,
+ int config_id)
+{
+ struct rmnet_logical_ep_conf_s *epconfig_l = 0;
+
+ ASSERT_RTNL();
+
+ if (!dev)
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ if (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT
+ || config_id >= RMNET_DATA_MAX_LOGICAL_EP)
+ return RMNET_CONFIG_BAD_ARGUMENTS;
+
+ epconfig_l = _rmnet_get_logical_ep(dev, config_id);
+
+ if (!epconfig_l || !epconfig_l->refcount)
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ /* Explicitly release the reference from the egress device */
+ dev_put(epconfig_l->egress_dev);
+ memset(epconfig_l, 0, sizeof(struct rmnet_logical_ep_conf_s));
+
return RMNET_CONFIG_OK;
}
@@ -735,6 +878,36 @@ int rmnet_set_logical_endpoint_config(struct net_device *dev,
}
/**
+ * rmnet_unset_logical_endpoint_config() - Un-set logical endpoing configuration
+ * on a device
+ * @dev: Device to set endpoint configuration on
+ * @config_id: logical endpoint id on device
+ *
+ * Retrieves the logical_endpoint_config structure and frees the egress device.
+ * Network device must already have association with RmNet Data driver
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
+ * - RMNET_CONFIG_NO_SUCH_DEVICE device is not associated
+ * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range
+ */
+int rmnet_unset_logical_endpoint_config(struct net_device *dev,
+ int config_id)
+{
+ LOGL("%s(%s, %d);",
+ __func__, dev->name, config_id);
+
+ if (!dev
+ || ((!_rmnet_is_physical_endpoint_associated(dev))
+ && (!rmnet_vnd_is_vnd(dev)))) {
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+ }
+
+ return _rmnet_unset_logical_endpoint_config(dev, config_id);
+}
+
+/**
* rmnet_create_vnd() - Create virtual network device node
* @id: RmNet virtual device node id
*
@@ -764,3 +937,16 @@ int rmnet_create_vnd_prefix(int id, const char *prefix)
LOGL("%s(%d, \"%s\");", __func__, id, prefix);
return rmnet_vnd_create_dev(id, &dev, prefix);
}
+
+/**
+ * rmnet_free_vnd() - Free virtual network device node
+ * @id: RmNet virtual device node id
+ *
+ * Return:
+ * - result of rmnet_vnd_free_dev()
+ */
+int rmnet_free_vnd(int id)
+{
+ LOGL("%s(%d);", __func__, id);
+ return rmnet_vnd_free_dev(id);
+}
diff --git a/net/rmnet_data/rmnet_data_config.h b/net/rmnet_data/rmnet_data_config.h
index 2eac471fc83c..778a148f659c 100644
--- a/net/rmnet_data/rmnet_data_config.h
+++ b/net/rmnet_data/rmnet_data_config.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -63,8 +63,13 @@ int rmnet_set_logical_endpoint_config(struct net_device *dev,
int config_id,
uint8_t rmnet_mode,
struct net_device *egress_dev);
+int _rmnet_unset_logical_endpoint_config(struct net_device *dev,
+ int config_id);
+int rmnet_unset_logical_endpoint_config(struct net_device *dev,
+ int config_id);
void rmnet_config_netlink_msg_handler (struct sk_buff *skb);
int rmnet_create_vnd(int id);
int rmnet_create_vnd_prefix(int id, const char *name);
+int rmnet_free_vnd(int id);
#endif /* _RMNET_DATA_CONFIG_H_ */
diff --git a/net/rmnet_data/rmnet_data_handlers.c b/net/rmnet_data/rmnet_data_handlers.c
index db7c464996b8..f302ce681162 100644
--- a/net/rmnet_data/rmnet_data_handlers.c
+++ b/net/rmnet_data/rmnet_data_handlers.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -400,6 +400,13 @@ rx_handler_result_t rmnet_ingress_handler(struct sk_buff *skb)
config = (struct rmnet_phys_ep_conf_s *)
rcu_dereference(skb->dev->rx_handler_data);
+ if (!config) {
+ LOGD("%s(): %s is not associated with rmnet_data",
+ __func__, skb->dev->name);
+ kfree_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
+
/* Sometimes devices operate in ethernet mode even thouth there is no
* ethernet header. This causes the skb->protocol to contain a bogus
* value and the skb->data pointer to be off by 14 bytes. Fix it if
@@ -486,6 +493,13 @@ void rmnet_egress_handler(struct sk_buff *skb,
config = (struct rmnet_phys_ep_conf_s *)
rcu_dereference(skb->dev->rx_handler_data);
+ if (!config) {
+ LOGD("%s(): %s is not associated with rmnet_data",
+ __func__, skb->dev->name);
+ kfree_skb(skb);
+ return;
+ }
+
LOGD("%s(): Packet going out on %s with egress format 0x%08X\n",
__func__, skb->dev->name, config->egress_data_format);
diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c
index 1367071bca44..3fd583fedf12 100644
--- a/net/rmnet_data/rmnet_data_vnd.c
+++ b/net/rmnet_data/rmnet_data_vnd.c
@@ -27,6 +27,7 @@
#include "rmnet_data_handlers.h"
#include "rmnet_data_private.h"
#include "rmnet_map.h"
+#include "rmnet_data_vnd.h"
RMNET_LOG_MODULE(RMNET_DATA_LOGMASK_VND);
@@ -584,6 +585,43 @@ int rmnet_vnd_create_dev(int id, struct net_device **new_device,
}
/**
+ * rmnet_vnd_free_dev() - free a virtual network device node.
+ * @id: Virtual device node id
+ *
+ * Unregisters the virtual network device node and frees it.
+ * unregister_netdev locks the rtnl mutex, so the mutex must not be locked
+ * by the caller of the function. unregister_netdev enqueues the request to
+ * unregister the device into a TODO queue. The requests in the TODO queue
+ * are only done after rtnl mutex is unlocked, therefore free_netdev has to
+ * called after unlocking rtnl mutex.
+ *
+ * Return:
+ * - 0 if successful
+ * - RMNET_CONFIG_NO_SUCH_DEVICE if id is invalid or not in range
+ * - RMNET_CONFIG_DEVICE_IN_USE if device has logical ep that wasn't unset
+ */
+int rmnet_vnd_free_dev(int id)
+{
+ struct rmnet_logical_ep_conf_s *epconfig_l;
+
+ if ((id < 0) || (id >= RMNET_DATA_MAX_VND) || !rmnet_devices[id]) {
+ LOGM("%s(): Invalid id [%d]\n", __func__, id);
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+ }
+
+ epconfig_l = rmnet_vnd_get_le_config(rmnet_devices[id]);
+ if (epconfig_l && epconfig_l->refcount)
+ return RMNET_CONFIG_DEVICE_IN_USE;
+
+ unregister_netdev(rmnet_devices[id]);
+ free_netdev(rmnet_devices[id]);
+ rtnl_lock();
+ rmnet_devices[id] = 0;
+ rtnl_unlock();
+ return 0;
+}
+
+/**
* rmnet_vnd_get_name() - Gets the string name of a VND based on ID
* @id: Virtual device node id
* @name: Buffer to store name of virtual device node
diff --git a/net/rmnet_data/rmnet_data_vnd.h b/net/rmnet_data/rmnet_data_vnd.h
index 8bd478515df2..a124b4567fcc 100644
--- a/net/rmnet_data/rmnet_data_vnd.h
+++ b/net/rmnet_data/rmnet_data_vnd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -28,6 +28,7 @@ struct rmnet_logical_ep_conf_s *rmnet_vnd_get_le_config(struct net_device *dev);
int rmnet_vnd_get_name(int id, char *name, int name_len);
int rmnet_vnd_create_dev(int id, struct net_device **new_device,
const char *prefix);
+int rmnet_vnd_free_dev(int id);
int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev);
int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev);
int rmnet_vnd_is_vnd(struct net_device *dev);