[qca-nss-drv] Migrate virt_if to dynamic interface
Migrate virt_if to dynamic interfaces and add stats support.
Change-Id: I310fad03a09c496d627b65797d8d9bc4a4d263ba
Signed-off-by: Sundarajan Srinivasan <sundaraj@codeaurora.org>
diff --git a/exports/nss_virt_if.h b/exports/nss_virt_if.h
index f0b51f6..6ea97fd 100644
--- a/exports/nss_virt_if.h
+++ b/exports/nss_virt_if.h
@@ -46,40 +46,107 @@
NSS_VIRT_IF_BSHAPER_CONFIG = NSS_IF_BSHAPER_CONFIG,
NSS_VIRT_IF_TX_CREATE_MSG = NSS_IF_MAX_MSG_TYPES + 1,
NSS_VIRT_IF_TX_DESTROY_MSG,
+ NSS_VIRT_IF_STATS_SYNC_MSG,
NSS_VIRT_IF_MAX_MSG_TYPES,
};
/**
+ * virt_if error types
+ */
+enum nss_virt_if_error_types {
+ NSS_VIRT_IF_SUCCESS, /*< Success */
+ NSS_VIRT_IF_CORE_FAILURE, /*< nss core failure */
+ NSS_VIRT_IF_ALLOC_FAILURE, /*< Memory allocation failure */
+ NSS_VIRT_IF_DYNAMIC_IF_FAILURE, /*< Dynamic interface failure */
+ NSS_VIRT_IF_MSG_TX_FAILURE, /*< Message transmission failure */
+ NSS_VIRT_IF_REG_FAILURE, /*< Registration failure */
+ NSS_VIRT_IF_CORE_NOT_INITIALIZED, /*< NSS core not intialized */
+};
+
+/**
+ * Structure which contains stats received from NSS.
+ */
+struct nss_virt_if_stats {
+ struct nss_if_stats node_stats; /**< common stats */
+ uint32_t tx_enqueue_failed; /**< tx enqueue failures in the FW */
+ uint32_t shaper_enqueue_failed; /**< shaper enqueue failures in the FW */
+};
+
+/**
* The NSS virtual interface creation structure.
*/
-struct nss_virt_if_create {
- uint32_t flags; /**> Interface flags */
- uint8_t mac_addr[ETH_ALEN]; /**> MAC address */
+struct nss_virt_if_create_msg {
+ uint32_t flags; /**< Interface flags */
+ uint8_t mac_addr[ETH_ALEN]; /**< MAC address */
};
/**
* The NSS virtual interface destruction structure.
*/
-struct nss_virt_if_destroy {
- int32_t reserved; /**> place holder */
+struct nss_virt_if_destroy_msg {
+ int32_t reserved; /**< place holder */
};
/**
* Message structure to send/receive virtual interface commands
*/
struct nss_virt_if_msg {
- struct nss_cmn_msg cm; /**> Message Header */
+ struct nss_cmn_msg cm;
+ /**< Message Header */
union {
union nss_if_msgs if_msgs;
- struct nss_virt_if_create if_create; /**> Message: create virt if rule */
- struct nss_virt_if_destroy if_destroy; /**> Message: destroy virt if rule */
+ struct nss_virt_if_create_msg if_create;
+ /**< Message: create virt if rule */
+ struct nss_virt_if_destroy_msg if_destroy;
+ /**< Message: destroy virt if rule */
+ struct nss_virt_if_stats stats;
+ /**< Message: stats */
} msg;
};
+/*
+ * Private data structure for virt_if interface
+ */
+struct nss_virt_if_pvt {
+ struct semaphore sem;
+ struct completion complete;
+ int response;
+ int sem_init_done;
+};
+
typedef void (*nss_virt_if_data_callback_t)(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi);
typedef void (*nss_virt_if_msg_callback_t)(void *app_data, struct nss_cmn_msg *msg);
/**
+ * Context for WLAN to NSS communication
+ */
+struct nss_virt_if_handle {
+ struct nss_ctx_instance *nss_ctx; /*< NSS context */
+ int32_t if_num; /*< interface number */
+ struct nss_virt_if_pvt *pvt; /*< Private data structure */
+ struct nss_virt_if_stats stats; /*< virt_if stats */
+ atomic_t refcnt; /*< Reference count */
+ nss_virt_if_msg_callback_t cb; /*< callback */
+ void *app_data; /*< app_data to be passed to callback */
+};
+
+/**
+ * @brief Create a virtual interface
+ *
+ * @param netdev net device associated with Wifi
+ * @return Pointer to nss_virt_if_handle struct
+ */
+extern struct nss_virt_if_handle *nss_virt_if_create(struct net_device *netdev);
+
+/**
+ * @brief Destroy the virtual interface associated with the if_num
+ *
+ * @param handle virtual interface handle (provided during dynamic_interface allocation)
+ * @return command Tx status
+ */
+extern nss_tx_status_t nss_virt_if_destroy(struct nss_virt_if_handle *handle);
+
+/**
* @brief Send message to virtual interface
*
* @param nss_ctx NSS context (provided during registration)
@@ -90,35 +157,46 @@
extern nss_tx_status_t nss_virt_if_tx_msg(struct nss_ctx_instance *nss_ctx, struct nss_virt_if_msg *nvim);
/**
- * @brief Forward virtual interface packets
+ * @brief Forward virtual interface packets to NSS
*
- * @param nss_ctx NSS context (provided during registration)
- * @param if_num Interface number (provided during dynamic interface allocation)
+ * @param handle virtual interface handle (provided during registration)
* @param skb HLOS data buffer (sk_buff in Linux)
*
* @return command Tx status
*/
-extern nss_tx_status_t nss_virt_if_tx_rxbuf(struct nss_ctx_instance *nss_ctx, int32_t if_num, struct sk_buff *skb);
-
+extern nss_tx_status_t nss_virt_if_tx_buf(struct nss_virt_if_handle *handle,
+ struct sk_buff *skb);
/**
* @brief Register Virtual Interface with NSS driver
*
- * @param if_num Interface number (provied during dynamic_interface allocation)
+ * @param handle virtual interface handle(provided during dynamic_interface allocation)
* @param data_callback Callback handler for virtual data packets
- * @param msg_callback Callback handler for virtual interface messages
- * @param if_ctx netdevice structure of virtual interface
+ * @param netdev netdevice structure associated with WiFi
*
* @return command Tx status
*/
-extern struct nss_ctx_instance *nss_virt_if_register(uint32_t if_num, nss_virt_if_data_callback_t data_callback,
- nss_virt_if_msg_callback_t msg_callback, struct net_device *if_ctx);
+extern void nss_virt_if_register(struct nss_virt_if_handle *handle,
+ nss_virt_if_data_callback_t data_callback,
+ struct net_device *netdev);
/**
* @brief Unregister virtual interface from NSS driver
*
- * @param if_num Interface number (provied during dynamic_interface allocation)
+ * @param handle virtual interface handle
*/
-void nss_virt_if_unregister(uint32_t if_num);
+extern void nss_virt_if_unregister(struct nss_virt_if_handle *handle);
+
+/**
+ * @brief Get stats for virtual interface from NSS driver
+ *
+ * @param if_num Interface number (provided during dynamic_interface allocation)
+ * @param i index of stats
+ * @param line buffer into which the stats will be copied.
+ *
+ * @return int32_t Returns 0 if if_num is not in range or the number of bytes copied.
+ */
+extern int32_t nss_virt_if_copy_stats(int32_t if_num, int i, char *line);
+
#endif /* __NSS_VIRT_IF_H */
diff --git a/nss_core.h b/nss_core.h
index eaaf67b..35f57ac 100755
--- a/nss_core.h
+++ b/nss_core.h
@@ -670,6 +670,7 @@
struct dentry *logs_dentry; /* NSS FW logs directory */
struct dentry *core_log_dentry; /* NSS Core's FW log file */
struct dentry *wifi_if_dentry; /* wifi_if stats dentry */
+ struct dentry *virt_if_dentry; /* virt_if stats dentry */
struct nss_ctx_instance nss[NSS_MAX_CORES];
/* NSS contexts */
/*
diff --git a/nss_stats.c b/nss_stats.c
index 8672b76..71fd9ac 100644
--- a/nss_stats.c
+++ b/nss_stats.c
@@ -1702,6 +1702,87 @@
}
/*
+ * nss_stats_virt_if_read()
+ * Read virt_if statistics
+ */
+static ssize_t nss_stats_virt_if_read(struct file *fp, char __user *ubuf,
+ size_t sz, loff_t *ppos)
+{
+ struct nss_stats_data *data = fp->private_data;
+ int32_t if_num = NSS_DYNAMIC_IF_START;
+ int32_t max_if_num = if_num + NSS_MAX_DYNAMIC_INTERFACES;
+ size_t bytes = 0;
+ ssize_t bytes_read = 0;
+ char line[80];
+ int start, end;
+
+ if (data) {
+ if_num = data->if_num;
+ }
+
+ if (if_num > max_if_num) {
+ return 0;
+ }
+
+ for (; if_num < max_if_num; if_num++) {
+ if (nss_dynamic_interface_get_type(if_num) != NSS_DYNAMIC_INTERFACE_TYPE_802_3_REDIR)
+ continue;
+
+ bytes = scnprintf(line, sizeof(line), "if_num %d stats start:\n\n", if_num);
+ if ((bytes_read + bytes) > sz)
+ break;
+
+ if (copy_to_user(ubuf + bytes_read, line, bytes) != 0) {
+ bytes_read = -EFAULT;
+ goto end;
+ }
+
+ bytes_read += bytes;
+
+ start = 0;
+ end = 7;
+ while (bytes_read < sz && start < end) {
+ bytes = nss_virt_if_copy_stats(if_num, start, line);
+ if (!bytes)
+ break;
+
+ if ((bytes_read + bytes) > sz)
+ break;
+
+ if (copy_to_user(ubuf + bytes_read, line, bytes) != 0) {
+ bytes_read = -EFAULT;
+ goto end;
+ }
+
+ bytes_read += bytes;
+ start++;
+ }
+
+ bytes = scnprintf(line, sizeof(line), "if_num %d stats end:\n\n", if_num);
+ if (bytes_read > (sz - bytes))
+ break;
+
+ if (copy_to_user(ubuf + bytes_read, line, bytes) != 0) {
+ bytes_read = -EFAULT;
+ goto end;
+ }
+
+ bytes_read += bytes;
+ }
+
+ if (bytes_read > 0) {
+ *ppos = bytes_read;
+ }
+
+ if (data) {
+ data->if_num = if_num;
+ }
+
+end:
+ return bytes_read;
+}
+
+/*
* nss_stats_open()
*/
static int nss_stats_open(struct inode *inode, struct file *filp)
@@ -1810,6 +1891,8 @@
NSS_STATS_DECLARE_FILE_OPERATIONS(wifi_if)
+NSS_STATS_DECLARE_FILE_OPERATIONS(virt_if)
+
/*
* wifi_stats_ops
*/
@@ -2007,6 +2090,13 @@
return;
}
+ nss_top_main.virt_if_dentry = debugfs_create_file("virt_if", 0400,
+ nss_top_main.stats_dentry, &nss_top_main, &nss_stats_virt_if_ops);
+ if (unlikely(nss_top_main.virt_if_dentry == NULL)) {
+ nss_warning("Failed to create qca-nss-drv/stats/virt_if file in debugfs");
+ return;
+ }
+
nss_log_init();
}
diff --git a/nss_tx_rx_common.h b/nss_tx_rx_common.h
index 7beb8e3..94a3c93 100644
--- a/nss_tx_rx_common.h
+++ b/nss_tx_rx_common.h
@@ -60,10 +60,18 @@
#endif
/*
+ * Handles associated with redir interfaces(virt_if & wifi_if).
+ * TODO: Once wifi moves to using the new interfaces, this will be deprecated.
+ */
+struct nss_redir_handle {
+ struct nss_wifi_if_handle *whandle;
+ struct nss_virt_if_handle *vhandle;
+};
+
+/*
* CB handlers for variour interfaces
*/
void nss_phys_if_register_handler(uint32_t if_num);
-extern void nss_virt_if_register_handler(void);
extern void nss_crypto_register_handler(void);
extern void nss_ipsec_register_handler(void);
extern void nss_ipv4_register_handler(void);
diff --git a/nss_tx_rx_virt_if.c b/nss_tx_rx_virt_if.c
index 3779fa2..fc3eb2f 100644
--- a/nss_tx_rx_virt_if.c
+++ b/nss_tx_rx_virt_if.c
@@ -31,17 +31,12 @@
nss_virt_if_rx_callback_t rx_callback,
struct net_device *netdev)
{
- struct nss_ctx_instance *nss_ctx = &nss_top_main.nss[nss_top_main.ipv4_handler_id];
- int32_t if_num = (int32_t)ctx;
+ struct nss_redir_handle *handle = (struct nss_redir_handle *)ctx;
- nss_assert(NSS_IS_IF_TYPE(VIRTUAL, if_num));
+ nss_wifi_if_register(handle->whandle, rx_callback, netdev);
+ nss_virt_if_register(handle->vhandle, rx_callback, netdev);
- nss_top_main.subsys_dp_register[if_num].ndev = netdev;
- nss_top_main.subsys_dp_register[if_num].cb = rx_callback;
- nss_top_main.subsys_dp_register[if_num].app_data = NULL;
- nss_top_main.subsys_dp_register[if_num].features = (uint32_t)netdev->features;
-
- return nss_ctx;
+ return ctx;
}
/*
@@ -49,102 +44,25 @@
*/
void nss_unregister_virt_if(void *ctx)
{
- int32_t if_num = (int32_t)ctx;
+ struct nss_redir_handle *handle = (struct nss_redir_handle *)ctx;
- nss_assert(NSS_IS_IF_TYPE(VIRTUAL, if_num));
-
- nss_top_main.subsys_dp_register[if_num].ndev = NULL;
- nss_top_main.subsys_dp_register[if_num].cb = NULL;
- nss_top_main.subsys_dp_register[if_num].app_data = NULL;
- nss_top_main.subsys_dp_register[if_num].features = 0;
+ nss_wifi_if_unregister(handle->whandle);
+ nss_virt_if_unregister(handle->vhandle);
}
/*
* nss_tx_virt_if_recvbuf()
* HLOS interface has received a packet which we redirect to the NSS, if appropriate to do so.
*/
-nss_tx_status_t nss_tx_virt_if_recvbuf(void *ctx, struct sk_buff *os_buf, uint32_t nwifi)
+nss_tx_status_t nss_tx_virt_if_recvbuf(void *ctx, struct sk_buff *skb, uint32_t nwifi)
{
- int32_t status;
- struct nss_ctx_instance *nss_ctx = &nss_top_main.nss[nss_top_main.ipv4_handler_id];
- int32_t if_num = (int32_t)ctx;
- uint32_t bufftype;
- int32_t push_mac_header = 0;
-
- if (unlikely(nss_ctl_redirect == 0)) {
- return NSS_TX_FAILURE_NOT_ENABLED;
- }
-
- if (unlikely(os_buf->vlan_tci)) {
- return NSS_TX_FAILURE_NOT_SUPPORTED;
- }
-
- nss_assert(NSS_IS_IF_TYPE(VIRTUAL, if_num));
- nss_trace("%p: Virtual Rx packet, if_num:%d, skb:%p", nss_ctx, if_num, os_buf);
-
- /*
- * Get the NSS context that will handle this packet and check that it is initialised and ready
- */
- NSS_VERIFY_CTX_MAGIC(nss_ctx);
- if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
- nss_warning("%p: Virtual Rx packet dropped as core not ready", nss_ctx);
- return NSS_TX_FAILURE_NOT_READY;
- }
-
- /*
- * Sanity check the SKB to ensure that it's suitable for us
- */
- if (unlikely(os_buf->len <= ETH_HLEN)) {
- nss_warning("%p: Virtual Rx packet: %p too short", nss_ctx, os_buf);
- return NSS_TX_FAILURE_TOO_SHORT;
- }
-
- if (unlikely(skb_shinfo(os_buf)->nr_frags != 0)) {
- /*
- * TODO: If we have a connection matching rule for this skbuff,
- * do we need to flush it??
- */
- nss_warning("%p: Delivering the packet to Linux because of fragmented skb: %p\n", nss_ctx, os_buf);
- return NSS_TX_FAILURE_NOT_SUPPORTED;
- }
+ struct nss_redir_handle *handle = (struct nss_redir_handle *)ctx;
if (nwifi) {
- bufftype = H2N_BUFFER_NATIVE_WIFI;
+ return nss_wifi_if_tx_buf(handle->whandle, skb);
} else {
- bufftype = H2N_BUFFER_PACKET;
-
- /*
- * NSS expects to see buffer from Ethernet header onwards.
- * If the wireless driver has called eth_type_trans to remove
- * the ethernet header, we need to push back the header
- */
- if (unlikely((os_buf->data - skb_mac_header(os_buf)) == ETH_HLEN)) {
- skb_push(os_buf, ETH_HLEN);
- push_mac_header = 1;
- }
+ return nss_virt_if_tx_buf(handle->vhandle, skb);
}
-
- /*
- * Direct the buffer to the NSS
- */
- status = nss_core_send_buffer(nss_ctx, if_num, os_buf, NSS_IF_DATA_QUEUE_0, bufftype, H2N_BIT_FLAG_VIRTUAL_BUFFER);
- if (unlikely(status != NSS_CORE_STATUS_SUCCESS)) {
- nss_warning("%p: Virtual Rx packet unable to enqueue\n", nss_ctx);
-
- if(unlikely(push_mac_header)) {
- skb_pull(os_buf, ETH_HLEN);
- }
-
- return NSS_TX_FAILURE_QUEUE;
- }
-
- /*
- * Kick the NSS awake so it can process our new entry.
- */
- nss_hal_send_interrupt(nss_ctx->nmap, nss_ctx->h2n_desc_rings[NSS_IF_DATA_QUEUE_0].desc_ring.int_bit,
- NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE);
- NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_PACKET]);
- return NSS_TX_SUCCESS;
}
/*
@@ -181,67 +99,33 @@
*/
void *nss_create_virt_if(struct net_device *netdev)
{
- struct nss_virt_if_msg nvim;
- struct nss_virt_if_create *nvic;
- struct nss_ctx_instance *nss_ctx = &nss_top_main.nss[nss_top_main.ipv4_handler_id];
- int32_t if_num;
+ struct nss_redir_handle *handles;
- if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
- nss_warning("Interface could not be created as core not ready");
- return NULL;
+ handles = (struct nss_redir_handle *)kzalloc(sizeof(struct nss_redir_handle), GFP_KERNEL);
+ if (!handles) {
+ nss_warning("%s: kzalloc failed\n", __func__);
+ goto error1;
}
- /*
- * Check if net_device is Ethernet type
- */
- if (netdev->type != ARPHRD_ETHER) {
- nss_warning("%p:Register virtual interface %p: type incorrect: %d ", nss_ctx, netdev, netdev->type);
- return NULL;
+ handles->whandle = nss_wifi_if_create(netdev);
+ if (!handles->whandle) {
+ nss_warning("%s: nss_wifi_if creation failed\n", __func__);
+ goto error2;
}
- /*
- * Find a free virtual interface
- */
- spin_lock_bh(&nss_top_main.lock);
- for (if_num = NSS_VIRTUAL_IF_START; if_num < NSS_VIRTUAL_IF_START + NSS_MAX_VIRTUAL_INTERFACES; ++if_num) {
- if (!nss_top_main.subsys_dp_register[if_num].ndev) {
- /*
- * Use this redirection interface
- */
- nss_top_main.subsys_dp_register[if_num].ndev = netdev;
- break;
- }
+ handles->vhandle = nss_virt_if_create(netdev);
+ if (!handles->vhandle) {
+ nss_warning("%s: nss_virt_if creation failed\n", __func__);
+ goto error3;
}
- spin_unlock_bh(&nss_top_main.lock);
- if (if_num == NSS_VIRTUAL_IF_START + NSS_MAX_VIRTUAL_INTERFACES) {
- /*
- * No available virtual contexts
- */
- nss_warning("%p:Register virtual interface %p: no contexts available:", nss_ctx, netdev);
- return NULL;
- }
-
- nss_cmn_msg_init(&nvim.cm, if_num, NSS_VIRT_IF_TX_CREATE_MSG,
- sizeof(struct nss_virt_if_create), NULL, NULL);
-
- nvic = &nvim.msg.if_create;
- nvic->flags = 0;
- memcpy(nvic->mac_addr, netdev->dev_addr, ETH_ALEN);
-
- (void)nss_virt_if_tx_msg(nss_ctx, &nvim);
-
- /*
- * Hold a reference to the net_device
- */
- dev_hold(netdev);
- nss_info("%p:Registered virtual interface %d: context %p", nss_ctx, if_num, netdev);
-
- /*
- * The context returned is the virtual interface # which is, essentially, the index into the if_ctx
- * array that is holding the net_device pointer
- */
- return (void *)if_num;
+ return (void *)handles;
+error3:
+ nss_wifi_if_destroy(handles->whandle);
+error2:
+ kfree(handles);
+error1:
+ return NULL;
}
/*
@@ -249,39 +133,12 @@
*/
nss_tx_status_t nss_destroy_virt_if(void *ctx)
{
- int32_t if_num;
- struct nss_virt_if_msg nvim;
- struct net_device *dev;
- struct nss_ctx_instance *nss_ctx = &nss_top_main.nss[nss_top_main.ipv4_handler_id];
+ struct nss_redir_handle *handle = (struct nss_redir_handle *)ctx;
- if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
- nss_warning("Interface could not be destroyed as core not ready");
- return NSS_TX_FAILURE_NOT_READY;
- }
+ nss_wifi_if_destroy(handle->whandle);
+ nss_virt_if_destroy(handle->vhandle);
- if_num = (int32_t)ctx;
- nss_assert(NSS_IS_IF_TYPE(VIRTUAL, if_num));
-
- spin_lock_bh(&nss_top_main.lock);
- if (!nss_top_main.subsys_dp_register[if_num].ndev) {
- spin_unlock_bh(&nss_top_main.lock);
- nss_warning("%p: Unregister virtual interface %d: no context", nss_ctx, if_num);
- return NSS_TX_FAILURE_BAD_PARAM;
- }
-
- /*
- * Set this context to NULL
- */
- dev = nss_top_main.subsys_dp_register[if_num].ndev;
- nss_top_main.subsys_dp_register[if_num].ndev = NULL;
- spin_unlock_bh(&nss_top_main.lock);
- nss_info("%p:Unregister virtual interface %d (%p)", nss_ctx, if_num, dev);
- dev_put(dev);
-
- nss_cmn_msg_init(&nvim.cm, if_num, NSS_VIRT_IF_TX_DESTROY_MSG,
- sizeof(struct nss_virt_if_destroy), NULL, NULL);
-
- return nss_virt_if_tx_msg(nss_ctx, &nvim);
+ return NSS_TX_SUCCESS;
}
EXPORT_SYMBOL(nss_tx_virt_if_rxbuf);
diff --git a/nss_virt_if.c b/nss_virt_if.c
index fa11efb..9b53f8d 100644
--- a/nss_virt_if.c
+++ b/nss_virt_if.c
@@ -22,15 +22,52 @@
#include "nss_tx_rx_common.h"
#include <net/arp.h>
+#define NSS_VIRT_IF_TX_TIMEOUT 3000 /* 3 Seconds */
+#define NSS_VIRT_IF_INDEX_OFFSET(if_num) (if_num-NSS_DYNAMIC_IF_START)
+
extern int nss_ctl_redirect;
/*
+ * Data structure that holds the virtual interface context.
+ */
+static struct nss_virt_if_handle *virt_handle[NSS_MAX_DYNAMIC_INTERFACES];
+
+/*
+ * Spinlock to protect the global data structure virt_handle.
+ */
+DEFINE_SPINLOCK(virt_if_lock);
+
+/*
+ * nss_virt_if_stats_sync()
+ * Sync stats from the NSS FW
+ */
+static void nss_virt_if_stats_sync(struct nss_virt_if_handle *handle,
+ struct nss_virt_if_stats *nwis)
+{
+ struct nss_virt_if_stats *stats = &handle->stats;
+
+ stats->node_stats.rx_packets += nwis->node_stats.rx_packets;
+ stats->node_stats.rx_bytes += nwis->node_stats.rx_bytes;
+ stats->node_stats.rx_dropped += nwis->node_stats.rx_dropped;
+ stats->node_stats.tx_packets += nwis->node_stats.tx_packets;
+ stats->node_stats.tx_bytes += nwis->node_stats.tx_bytes;
+ stats->tx_enqueue_failed += nwis->tx_enqueue_failed;
+ stats->shaper_enqueue_failed += nwis->shaper_enqueue_failed;
+}
+
+/*
* nss_virt_if_msg_handler()
* Handle msg responses from the FW on virtual interfaces
*/
-static void nss_virt_if_msg_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data)
+static void nss_virt_if_msg_handler(struct nss_ctx_instance *nss_ctx,
+ struct nss_cmn_msg *ncm,
+ __attribute__((unused))void *app_data)
{
+ struct nss_virt_if_msg *nvim = (struct nss_virt_if_msg *)ncm;
+ int32_t if_num;
+
nss_virt_if_msg_callback_t cb;
+ struct nss_virt_if_handle *handle = NULL;
/*
* Sanity check the message type
@@ -40,16 +77,6 @@
return;
}
- if (((!NSS_IS_IF_TYPE(DYNAMIC, ncm->interface)) && (!NSS_IS_IF_TYPE(VIRTUAL, ncm->interface)))) {
- nss_warning("%p: response for another interface: %d", nss_ctx, ncm->interface);
- return;
- }
-
- if (ncm->len > sizeof(struct nss_virt_if_msg)) {
- nss_warning("%p: message length too big: %d", nss_ctx, ncm->len);
- return;
- }
-
/*
* Messages value that are within the base class are handled by the base class.
*/
@@ -57,6 +84,30 @@
return nss_if_msg_handler(nss_ctx, ncm, app_data);
}
+ if (((!NSS_IS_IF_TYPE(DYNAMIC, ncm->interface)) &&
+ (!NSS_IS_IF_TYPE(VIRTUAL, ncm->interface)))) {
+ nss_warning("%p: response for another interface: %d", nss_ctx, ncm->interface);
+ return;
+ }
+
+ if_num = NSS_VIRT_IF_INDEX_OFFSET(ncm->interface);
+
+ spin_lock_bh(&virt_if_lock);
+ if (!virt_handle[if_num]) {
+ spin_unlock_bh(&virt_if_lock);
+ nss_warning("%p: virt_if handle is NULL\n", nss_ctx);
+ return;
+ }
+
+ handle = virt_handle[if_num];
+ spin_unlock_bh(&virt_if_lock);
+
+ switch (nvim->cm.type) {
+ case NSS_VIRT_IF_STATS_SYNC_MSG:
+ nss_virt_if_stats_sync(handle, &nvim->msg.stats);
+ break;
+ }
+
/*
* Log failures
*/
@@ -86,12 +137,35 @@
}
/*
- * nss_virt_if_tx_rxbuf()
+ * nss_virt_if_callback
+ * Callback to handle the completion of NSS ->HLOS messages.
+ */
+static void nss_virt_if_callback(void *app_data, struct nss_cmn_msg *ncm)
+{
+ struct nss_virt_if_handle *handle = (struct nss_virt_if_handle *)app_data;
+ struct nss_virt_if_pvt *nvip = handle->pvt;
+
+ if (ncm->response != NSS_CMN_RESPONSE_ACK) {
+ nss_warning("%p: virt_if Error response %d\n", handle->nss_ctx, ncm->response);
+ nvip->response = NSS_TX_FAILURE;
+ complete(&nvip->complete);
+ return;
+ }
+
+ nvip->response = NSS_TX_SUCCESS;
+ complete(&nvip->complete);
+}
+
+/*
+ * nss_virt_if_tx_buf()
* HLOS interface has received a packet which we redirect to the NSS, if appropriate to do so.
*/
-nss_tx_status_t nss_virt_if_tx_rxbuf(struct nss_ctx_instance *nss_ctx, int32_t if_num, struct sk_buff *skb)
+nss_tx_status_t nss_virt_if_tx_buf(struct nss_virt_if_handle *handle,
+ struct sk_buff *skb)
{
int32_t status;
+ int32_t if_num = handle->if_num;
+ struct nss_ctx_instance *nss_ctx = handle->nss_ctx;
if (unlikely(nss_ctl_redirect == 0)) {
return NSS_TX_FAILURE_NOT_ENABLED;
@@ -133,7 +207,8 @@
/*
* Direct the buffer to the NSS
*/
- status = nss_core_send_buffer(nss_ctx, if_num, skb, NSS_IF_DATA_QUEUE_0, H2N_BUFFER_PACKET, H2N_BIT_FLAG_VIRTUAL_BUFFER);
+ status = nss_core_send_buffer(nss_ctx, if_num, skb, NSS_IF_DATA_QUEUE_0,
+ H2N_BUFFER_PACKET, H2N_BIT_FLAG_VIRTUAL_BUFFER);
if (unlikely(status != NSS_CORE_STATUS_SUCCESS)) {
nss_warning("%p: Virtual Rx packet unable to enqueue\n", nss_ctx);
return NSS_TX_FAILURE_QUEUE;
@@ -142,13 +217,277 @@
/*
* Kick the NSS awake so it can process our new entry.
*/
- nss_hal_send_interrupt(nss_ctx->nmap, nss_ctx->h2n_desc_rings[NSS_IF_DATA_QUEUE_0].desc_ring.int_bit,
- NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE);
+ nss_hal_send_interrupt(nss_ctx->nmap,
+ nss_ctx->h2n_desc_rings[NSS_IF_DATA_QUEUE_0].desc_ring.int_bit,
+ NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE);
NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_PACKET]);
return NSS_TX_SUCCESS;
}
+EXPORT_SYMBOL(nss_virt_if_tx_buf);
/*
+ * nss_virt_if_tx_msg_sync
+ * Send a message from HLOS to NSS synchronously.
+ */
+static nss_tx_status_t nss_virt_if_tx_msg_sync(struct nss_virt_if_handle *handle,
+ struct nss_virt_if_msg *nvim)
+{
+ nss_tx_status_t status;
+ int ret = 0;
+ struct nss_virt_if_pvt *nwip = handle->pvt;
+ struct nss_ctx_instance *nss_ctx = handle->nss_ctx;
+
+ down(&nwip->sem);
+
+ status = nss_virt_if_tx_msg(nss_ctx, nvim);
+ if (status != NSS_TX_SUCCESS) {
+ nss_warning("%p: nss_virt_if_msg failed\n", nss_ctx);
+ up(&nwip->sem);
+ return status;
+ }
+
+ ret = wait_for_completion_timeout(&nwip->complete,
+ msecs_to_jiffies(NSS_VIRT_IF_TX_TIMEOUT));
+ if (!ret) {
+ nss_warning("%p: virt_if tx failed due to timeout\n", nss_ctx);
+ nwip->response = NSS_TX_FAILURE;
+ }
+
+ status = nwip->response;
+ up(&nwip->sem);
+
+ return status;
+}
+
+/*
+ * nss_virt_if_handle_destroy()
+ * Destroy the virt handle either due to request from WLAN or due to error.
+ */
+static int nss_virt_if_handle_destroy(struct nss_virt_if_handle *handle)
+{
+ nss_tx_status_t status;
+ int32_t if_num = handle->if_num;
+ int32_t index = NSS_VIRT_IF_INDEX_OFFSET(if_num);
+ struct nss_ctx_instance *nss_ctx = handle->nss_ctx;
+
+ spin_lock_bh(&virt_if_lock);
+ virt_handle[index] = NULL;
+ spin_unlock_bh(&virt_if_lock);
+
+ status = nss_dynamic_interface_dealloc_node(if_num, NSS_DYNAMIC_INTERFACE_TYPE_802_3_REDIR);
+ if (status != NSS_TX_SUCCESS) {
+ nss_warning("%p: Dynamic interface destroy failed status %d\n", nss_ctx, status);
+ return status;
+ }
+
+ kfree(handle->pvt);
+ kfree(handle);
+
+ return status;
+}
+
+/*
+ * nss_virt_if_handle_init()
+ * Initialize virt handle which holds the if_num and stats per interface.
+ */
+static struct nss_virt_if_handle *nss_virt_if_handle_create(struct nss_ctx_instance *nss_ctx, int32_t *cmd_rsp)
+{
+ int32_t index;
+ int32_t if_num = 0;
+ struct nss_virt_if_handle *handle;
+
+ if_num = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_802_3_REDIR);
+ if (if_num < 0) {
+ nss_warning("%p: failure allocating virt if\n", nss_ctx);
+ *cmd_rsp = NSS_VIRT_IF_DYNAMIC_IF_FAILURE;
+ return NULL;
+ }
+
+ index = NSS_VIRT_IF_INDEX_OFFSET(if_num);
+
+ handle = (struct nss_virt_if_handle *)kzalloc(sizeof(struct nss_virt_if_handle),
+ GFP_KERNEL);
+ if (!handle) {
+ nss_warning("%p: handle memory alloc failed\n", nss_ctx);
+ *cmd_rsp = NSS_VIRT_IF_ALLOC_FAILURE;
+ goto error1;
+ }
+
+ handle->nss_ctx = nss_ctx;
+ handle->if_num = if_num;
+ handle->pvt = (struct nss_virt_if_pvt *)kzalloc(sizeof(struct nss_virt_if_pvt),
+ GFP_KERNEL);
+ if (!handle->pvt) {
+ nss_warning("%p: failure allocating memory for nss_virt_if_pvt\n", nss_ctx);
+ *cmd_rsp = NSS_VIRT_IF_ALLOC_FAILURE;
+ goto error2;
+ }
+
+ handle->cb = NULL;
+ handle->app_data = NULL;
+
+ spin_lock_bh(&virt_if_lock);
+ virt_handle[index] = handle;
+ spin_unlock_bh(&virt_if_lock);
+
+ *cmd_rsp = NSS_VIRT_IF_SUCCESS;
+
+ return handle;
+
+error2:
+ kfree(handle);
+error1:
+ nss_dynamic_interface_dealloc_node(if_num, NSS_DYNAMIC_INTERFACE_TYPE_802_3_REDIR);
+ return NULL;
+}
+
+/* nss_virt_if_msg_init()
+ * Initialize virt specific message structure.
+ */
+static void nss_virt_if_msg_init(struct nss_virt_if_msg *nvim,
+ uint16_t if_num,
+ uint32_t type,
+ uint32_t len,
+ nss_virt_if_msg_callback_t cb,
+ struct nss_virt_if_handle *app_data)
+{
+ nss_cmn_msg_init(&nvim->cm, if_num, type, len, (void *)cb, (void *)app_data);
+}
+
+/*
+ * nss_virt_if_register_handler()
+ * register handler for statically allocated virtual interface on NSS with NSS core.
+ */
+static uint32_t nss_virt_if_register_handler(struct nss_virt_if_handle *handle)
+{
+ uint32_t ret;
+ struct nss_virt_if_pvt *nvip = NULL;
+ int32_t if_num = handle->if_num;
+
+ ret = nss_core_register_handler(if_num, nss_virt_if_msg_handler, NULL);
+ if (ret != NSS_CORE_STATUS_SUCCESS) {
+ nss_warning("%d: Message handler failed to be registered for interface\n", if_num);
+ return NSS_VIRT_IF_CORE_FAILURE;
+ }
+
+ nvip = handle->pvt;
+ if (!nvip->sem_init_done) {
+ sema_init(&nvip->sem, 1);
+ init_completion(&nvip->complete);
+ nvip->sem_init_done = 1;
+ }
+
+ return NSS_VIRT_IF_SUCCESS;
+}
+
+/*
+ * nss_virt_if_create()
+ * Create a virt interface and associate it with the netdev
+ */
+struct nss_virt_if_handle *nss_virt_if_create(struct net_device *netdev)
+{
+ struct nss_ctx_instance *nss_ctx = &nss_top_main.nss[nss_top_main.wlan_handler_id];
+ struct nss_virt_if_msg nvim;
+ struct nss_virt_if_create_msg *nvcm;
+ uint32_t ret;
+ struct nss_virt_if_handle *handle = NULL;
+
+ if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
+ nss_warning("%p: Interface could not be created as core not ready\n", nss_ctx);
+ return NULL;
+ }
+
+ handle = nss_virt_if_handle_create(nss_ctx, &ret);
+ if (!handle) {
+ nss_warning("%p:virt_if handle creation failed ret %d\n", nss_ctx, ret);
+ return NULL;
+ }
+
+ /* Initializes the semaphore and also sets the msg handler for if_num */
+ ret = nss_virt_if_register_handler(handle);
+ if (ret != NSS_VIRT_IF_SUCCESS) {
+ nss_warning("%p: Registration handler failed reason: %d\n", nss_ctx, ret);
+ goto error;
+ }
+
+ nss_virt_if_msg_init(&nvim, handle->if_num, NSS_VIRT_IF_TX_CREATE_MSG,
+ sizeof(struct nss_virt_if_create_msg), nss_virt_if_callback, handle);
+
+ nvcm = &nvim.msg.if_create;
+ nvcm->flags = 0;
+ memcpy(nvcm->mac_addr, netdev->dev_addr, ETH_ALEN);
+
+ ret = nss_virt_if_tx_msg_sync(handle, &nvim);
+ if (ret != NSS_TX_SUCCESS) {
+ nss_warning("%p: nss_virt_if_tx_msg_sync failed %u\n", nss_ctx, ret);
+ goto error;
+ }
+
+ spin_lock_bh(&nss_top_main.lock);
+ if (!nss_top_main.subsys_dp_register[handle->if_num].ndev) {
+ nss_top_main.subsys_dp_register[handle->if_num].ndev = netdev;
+ }
+ spin_unlock_bh(&nss_top_main.lock);
+
+ /*
+ * Hold a reference to the net_device
+ */
+ dev_hold(netdev);
+
+ /*
+ * The context returned is the handle interface # which contains all the info related to
+ * the interface if_num.
+ */
+
+ return handle;
+
+error:
+ nss_virt_if_handle_destroy(handle);
+ return NULL;
+}
+EXPORT_SYMBOL(nss_virt_if_create);
+
+/*
+ * nss_virt_if_destroy()
+ * Destroy the virt interface associated with the interface number.
+ */
+nss_tx_status_t nss_virt_if_destroy(struct nss_virt_if_handle *handle)
+{
+ nss_tx_status_t status;
+ struct net_device *dev;
+ int32_t if_num;
+ struct nss_ctx_instance *nss_ctx;
+
+ if (!handle) {
+ nss_warning("nss_virt_if_destroy handle is NULL\n");
+ return NSS_TX_FAILURE_BAD_PARAM;
+ }
+
+ if_num = handle->if_num;
+ nss_ctx = handle->nss_ctx;
+
+ if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
+ nss_warning("%p: Interface could not be destroyed as core not ready\n", nss_ctx);
+ return NSS_TX_FAILURE_NOT_READY;
+ }
+
+ spin_lock_bh(&nss_top_main.lock);
+ if (!nss_top_main.subsys_dp_register[if_num].ndev) {
+ spin_unlock_bh(&nss_top_main.lock);
+ nss_warning("%p: Unregister virt interface %d: no context\n", nss_ctx, if_num);
+ return NSS_TX_FAILURE_BAD_PARAM;
+ }
+
+ dev = nss_top_main.subsys_dp_register[if_num].ndev;
+ nss_top_main.subsys_dp_register[if_num].ndev = NULL;
+ spin_unlock_bh(&nss_top_main.lock);
+ dev_put(dev);
+
+ status = nss_virt_if_handle_destroy(handle);
+ return status;
+}
+EXPORT_SYMBOL(nss_virt_if_destroy);
+/*
* nss_virt_if_tx_msg()
*/
nss_tx_status_t nss_virt_if_tx_msg(struct nss_ctx_instance *nss_ctx, struct nss_virt_if_msg *nvim)
@@ -199,8 +538,9 @@
return NSS_TX_FAILURE;
}
- nss_hal_send_interrupt(nss_ctx->nmap, nss_ctx->h2n_desc_rings[NSS_IF_CMD_QUEUE].desc_ring.int_bit,
- NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE);
+ nss_hal_send_interrupt(nss_ctx->nmap,
+ nss_ctx->h2n_desc_rings[NSS_IF_CMD_QUEUE].desc_ring.int_bit,
+ NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE);
/*
* The context returned is the virtual interface # which is, essentially, the index into the if_ctx
@@ -208,64 +548,45 @@
*/
return NSS_TX_SUCCESS;
}
+EXPORT_SYMBOL(nss_virt_if_tx_msg);
/*
* nss_virt_if_register()
*/
-struct nss_ctx_instance *nss_virt_if_register(uint32_t if_num,
- nss_virt_if_data_callback_t data_callback,
- nss_virt_if_msg_callback_t msg_callback,
- struct net_device *netdev)
+void nss_virt_if_register(struct nss_virt_if_handle *handle,
+ nss_virt_if_data_callback_t data_callback,
+ struct net_device *netdev)
{
- struct nss_ctx_instance *nss_ctx = NULL;
- uint32_t ret;
+ int32_t if_num;
- /*
- * Register handler for dynamically allocated virtual interface on NSS with nss core.
- */
- if (NSS_IS_IF_TYPE(DYNAMIC, if_num)) {
- ret = nss_core_register_handler(if_num, nss_virt_if_msg_handler, NULL);
- if (ret != NSS_CORE_STATUS_SUCCESS) {
- nss_warning("Failed to register message handler for virtual interface : %d", if_num);
- return NULL;
- }
+ if (!handle) {
+ nss_warning("nss_virt_if_register handle is NULL\n");
+ return;
}
- nss_ctx = &nss_top_main.nss[nss_top_main.ipv4_handler_id];
+ if_num = handle->if_num;
- /*
- * TODO: the use of if_ctx as a net_dev and forcing this
- * for the caller is not how app_data is typically handled.
- * Re-think this.
- */
nss_top_main.subsys_dp_register[if_num].ndev = netdev;
nss_top_main.subsys_dp_register[if_num].cb = data_callback;
nss_top_main.subsys_dp_register[if_num].app_data = NULL;
nss_top_main.subsys_dp_register[if_num].features = (uint32_t)netdev->features;
-
- nss_top_main.if_rx_msg_callback[if_num] = msg_callback;
-
- return nss_ctx;
}
+EXPORT_SYMBOL(nss_virt_if_register);
/*
* nss_virt_if_unregister()
*/
-void nss_virt_if_unregister(uint32_t if_num)
+void nss_virt_if_unregister(struct nss_virt_if_handle *handle)
{
- uint32_t ret;
+ int32_t if_num;
- /*
- * Un-register handler for dynamically allocated virtual interface on NSS with nss core.
- */
- if (NSS_IS_IF_TYPE(DYNAMIC, if_num)) {
- ret = nss_core_unregister_handler(if_num);
- if (ret != NSS_CORE_STATUS_SUCCESS) {
- nss_warning("Failed to register message handler for virtual interface : %d", if_num);
- return;
- }
+ if (!handle) {
+ nss_warning("nss_virt_if_unregister handle is NULL\n");
+ return;
}
+ if_num = handle->if_num;
+
nss_top_main.subsys_dp_register[if_num].ndev = NULL;
nss_top_main.subsys_dp_register[if_num].cb = NULL;
nss_top_main.subsys_dp_register[if_num].app_data = NULL;
@@ -273,38 +594,92 @@
nss_top_main.if_rx_msg_callback[if_num] = NULL;
}
+EXPORT_SYMBOL(nss_virt_if_unregister);
+
+/*
+ * nss_virt_if_copy_stats()
+ * Copy stats from the virt_if handle to buffer(line)
+ */
+int32_t nss_virt_if_copy_stats(int32_t if_num, int i, char *line)
+{
+ int32_t bytes = 0;
+ struct nss_virt_if_stats *stats;
+ int32_t ifnum;
+ uint32_t len = 80;
+ struct nss_virt_if_handle *handle = NULL;
+
+ if (if_num < 0) {
+ nss_warning("nss_virt_if_copy_stats invalid if_num\n");
+ return bytes;
+ }
+
+ ifnum = NSS_VIRT_IF_INDEX_OFFSET(if_num);
+
+ spin_lock_bh(&virt_if_lock);
+ if (!virt_handle[ifnum]) {
+ spin_unlock_bh(&virt_if_lock);
+ goto end;
+ }
+
+ handle = virt_handle[ifnum];
+ spin_unlock_bh(&virt_if_lock);
+
+ stats = &handle->stats;
+
+ switch (i) {
+ case 0:
+ bytes = scnprintf(line, len, "rx_packets=%d\n",
+ stats->node_stats.rx_packets);
+ break;
+
+ case 1:
+ bytes = scnprintf(line, len, "rx_bytes=%d\n",
+ stats->node_stats.rx_bytes);
+ break;
+
+ case 2:
+ bytes = scnprintf(line, len, "rx_dropped=%d\n",
+ stats->node_stats.rx_dropped);
+ break;
+
+ case 3:
+ bytes = scnprintf(line, len, "tx_packets=%d\n",
+ stats->node_stats.tx_packets);
+ break;
+
+ case 4:
+ bytes = scnprintf(line, len, "tx_bytes=%d\n",
+ stats->node_stats.tx_bytes);
+ break;
+
+ case 5:
+ bytes = scnprintf(line, len, "tx_enqueue_failed=%d\n",
+ stats->tx_enqueue_failed);
+ break;
+
+ case 6:
+ bytes = scnprintf(line, len, "shaper_enqueue_failed=%d\n",
+ stats->shaper_enqueue_failed);
+ break;
+ }
+
+end:
+ return bytes;
+}
/*
* nss_virt_if_get_interface_num()
- * Get interface number for a virtual interface
+ * Get interface number for a virtual interface
*/
-int32_t nss_virt_if_get_interface_num(void *if_ctx)
+int32_t nss_virt_if_get_interface_num(void *ctx)
{
- int32_t if_num = (int32_t)if_ctx;
- nss_assert(NSS_IS_IF_TYPE(DYNAMIC, if_num) || NSS_IS_IF_TYPE(VIRTUAL, if_num));
- return if_num;
-}
+ struct nss_virt_if_handle *handle = (struct nss_virt_if_handle *)ctx;
-/*
- * nss_virt_if_register_handler()
- * register handler for statically allocated virtual interface on NSS with NSS core.
- */
-void nss_virt_if_register_handler(void)
-{
- int i;
- uint32_t ret;
- int end = NSS_VIRTUAL_IF_START + NSS_MAX_VIRTUAL_INTERFACES;
-
- for (i = NSS_VIRTUAL_IF_START; i < end; i++) {
- ret = nss_core_register_handler(i, nss_virt_if_msg_handler, NULL);
- if (ret != NSS_CORE_STATUS_SUCCESS) {
- nss_warning("Failed to register message handler for virtual interface : %d", i);
- }
+ if (!handle) {
+ nss_warning("virt_if handle is NULL\n");
+ return -1;
}
-}
-EXPORT_SYMBOL(nss_virt_if_tx_msg);
-EXPORT_SYMBOL(nss_virt_if_tx_rxbuf);
+ return handle->if_num;
+}
EXPORT_SYMBOL(nss_virt_if_get_interface_num);
-EXPORT_SYMBOL(nss_virt_if_register);
-EXPORT_SYMBOL(nss_virt_if_unregister);