summaryrefslogtreecommitdiff
path: root/drivers/rmnet/shs/rmnet_shs_wq_genl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rmnet/shs/rmnet_shs_wq_genl.c')
-rw-r--r--drivers/rmnet/shs/rmnet_shs_wq_genl.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/drivers/rmnet/shs/rmnet_shs_wq_genl.c b/drivers/rmnet/shs/rmnet_shs_wq_genl.c
new file mode 100644
index 0000000..7d07ace
--- /dev/null
+++ b/drivers/rmnet/shs/rmnet_shs_wq_genl.c
@@ -0,0 +1,358 @@
+/* Copyright (c) 2019 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * RMNET Data Smart Hash Workqueue Generic Netlink Functions
+ *
+ */
+
+#include "rmnet_shs_wq_genl.h"
+#include <net/sock.h>
+#include <linux/skbuff.h>
+
+MODULE_LICENSE("GPL v2");
+
+static struct net *last_net;
+static u32 last_snd_portid;
+
+uint32_t rmnet_shs_genl_seqnum;
+int rmnet_shs_userspace_connected;
+
+/* Static Functions and Definitions */
+static struct nla_policy rmnet_shs_genl_attr_policy[RMNET_SHS_GENL_ATTR_MAX + 1] = {
+ [RMNET_SHS_GENL_ATTR_INT] = { .type = NLA_S32 },
+ [RMNET_SHS_GENL_ATTR_SUGG] = { .len = sizeof(struct rmnet_shs_wq_sugg_info) },
+ [RMNET_SHS_GENL_ATTR_SEG] = { .len = sizeof(struct rmnet_shs_wq_seg_info) },
+ [RMNET_SHS_GENL_ATTR_STR] = { .type = NLA_NUL_STRING },
+};
+
+#define RMNET_SHS_GENL_OP(_cmd, _func) \
+ { \
+ .cmd = _cmd, \
+ .policy = rmnet_shs_genl_attr_policy, \
+ .doit = _func, \
+ .dumpit = NULL, \
+ .flags = 0, \
+ }
+
+static const struct genl_ops rmnet_shs_genl_ops[] = {
+ RMNET_SHS_GENL_OP(RMNET_SHS_GENL_CMD_INIT_DMA,
+ rmnet_shs_genl_dma_init),
+ RMNET_SHS_GENL_OP(RMNET_SHS_GENL_CMD_TRY_TO_MOVE_FLOW,
+ rmnet_shs_genl_try_to_move_flow),
+ RMNET_SHS_GENL_OP(RMNET_SHS_GENL_CMD_SET_FLOW_SEGMENTATION,
+ rmnet_shs_genl_set_flow_segmentation),
+ RMNET_SHS_GENL_OP(RMNET_SHS_GENL_CMD_MEM_SYNC,
+ rmnet_shs_genl_mem_sync),
+};
+
+struct genl_family rmnet_shs_genl_family = {
+ .hdrsize = 0,
+ .name = RMNET_SHS_GENL_FAMILY_NAME,
+ .version = RMNET_SHS_GENL_VERSION,
+ .maxattr = RMNET_SHS_GENL_ATTR_MAX,
+ .ops = rmnet_shs_genl_ops,
+ .n_ops = ARRAY_SIZE(rmnet_shs_genl_ops),
+};
+
+int rmnet_shs_genl_send_int_to_userspace(struct genl_info *info, int val)
+{
+ struct sk_buff *skb;
+ void *msg_head;
+ int rc;
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
+
+ msg_head = genlmsg_put(skb, 0, info->snd_seq+1, &rmnet_shs_genl_family,
+ 0, RMNET_SHS_GENL_CMD_INIT_DMA);
+ if (msg_head == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = nla_put_u32(skb, RMNET_SHS_GENL_ATTR_INT, val);
+ if (rc != 0)
+ goto out;
+
+ genlmsg_end(skb, msg_head);
+
+ rc = genlmsg_unicast(genl_info_net(info), skb, info->snd_portid);
+ if (rc != 0)
+ goto out;
+
+ rm_err("SHS_GNL: Successfully sent int %d\n", val);
+ return 0;
+
+out:
+ /* TODO: Need to free skb?? */
+ rm_err("SHS_GNL: FAILED to send int %d\n", val);
+ return -1;
+}
+
+int rmnet_shs_genl_send_int_to_userspace_no_info(int val)
+{
+ struct sk_buff *skb;
+ void *msg_head;
+ int rc;
+
+ if (last_net == NULL) {
+ rm_err("SHS_GNL: FAILED to send int %d - last_net is NULL\n",
+ val);
+ return -1;
+ }
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
+
+ msg_head = genlmsg_put(skb, 0, rmnet_shs_genl_seqnum++, &rmnet_shs_genl_family,
+ 0, RMNET_SHS_GENL_CMD_INIT_DMA);
+ if (msg_head == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = nla_put_u32(skb, RMNET_SHS_GENL_ATTR_INT, val);
+ if (rc != 0)
+ goto out;
+
+ genlmsg_end(skb, msg_head);
+
+ rc = genlmsg_unicast(last_net, skb, last_snd_portid);
+ if (rc != 0)
+ goto out;
+
+ rm_err("SHS_GNL: Successfully sent int %d\n", val);
+ return 0;
+
+out:
+ /* TODO: Need to free skb?? */
+ rm_err("SHS_GNL: FAILED to send int %d\n", val);
+ rmnet_shs_userspace_connected = 0;
+ return -1;
+}
+
+
+int rmnet_shs_genl_send_msg_to_userspace(void)
+{
+ struct sk_buff *skb;
+ void *msg_head;
+ int rc;
+ int val = rmnet_shs_genl_seqnum++;
+
+ rm_err("SHS_GNL: Trying to send msg %d\n", val);
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
+
+ msg_head = genlmsg_put(skb, 0, rmnet_shs_genl_seqnum++, &rmnet_shs_genl_family,
+ 0, RMNET_SHS_GENL_CMD_INIT_DMA);
+ if (msg_head == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = nla_put_u32(skb, RMNET_SHS_GENL_ATTR_INT, val);
+ if (rc != 0)
+ goto out;
+
+ genlmsg_end(skb, msg_head);
+
+ genlmsg_multicast(&rmnet_shs_genl_family, skb, 0, 0, GFP_ATOMIC);
+
+ rm_err("SHS_GNL: Successfully sent int %d\n", val);
+ return 0;
+
+out:
+ /* TODO: Need to free skb?? */
+ rm_err("SHS_GNL: FAILED to send int %d\n", val);
+ rmnet_shs_userspace_connected = 0;
+ return -1;
+}
+
+/* Currently unused - handles message from userspace to initialize the shared memory,
+ * memory is inited by kernel wq automatically
+ */
+int rmnet_shs_genl_dma_init(struct sk_buff *skb_2, struct genl_info *info)
+{
+ rm_err("%s", "SHS_GNL: rmnet_shs_genl_dma_init");
+
+ if (info == NULL) {
+ rm_err("%s", "SHS_GNL: an error occured - info is null");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int rmnet_shs_genl_set_flow_segmentation(struct sk_buff *skb_2, struct genl_info *info)
+{
+ struct nlattr *na;
+ struct rmnet_shs_wq_seg_info seg_info;
+ int rc = 0;
+
+ rm_err("%s", "SHS_GNL: rmnet_shs_genl_set_flow_segmentation");
+
+ if (info == NULL) {
+ rm_err("%s", "SHS_GNL: an error occured - info is null");
+ return -1;
+ }
+
+ na = info->attrs[RMNET_SHS_GENL_ATTR_SEG];
+ if (na) {
+ if (nla_memcpy(&seg_info, na, sizeof(seg_info)) > 0) {
+ rm_err("SHS_GNL: recv segmentation req "
+ "hash_to_set = 0x%x segment_enable = %u",
+ seg_info.hash_to_set,
+ seg_info.segment_enable);
+
+ rc = rmnet_shs_wq_set_flow_segmentation(seg_info.hash_to_set,
+ seg_info.segment_enable);
+
+ if (rc == 1) {
+ rmnet_shs_genl_send_int_to_userspace(info, 0);
+ trace_rmnet_shs_wq_high(RMNET_SHS_WQ_SHSUSR,
+ RMNET_SHS_WQ_FLOW_SEG_SET_PASS,
+ seg_info.hash_to_set, seg_info.segment_enable,
+ 0xDEF, 0xDEF, NULL, NULL);
+ } else {
+ rmnet_shs_genl_send_int_to_userspace(info, -1);
+ trace_rmnet_shs_wq_high(RMNET_SHS_WQ_SHSUSR,
+ RMNET_SHS_WQ_FLOW_SEG_SET_FAIL,
+ seg_info.hash_to_set, seg_info.segment_enable,
+ 0xDEF, 0xDEF, NULL, NULL);
+ return 0;
+ }
+ } else {
+ rm_err("SHS_GNL: nla_memcpy failed %d\n",
+ RMNET_SHS_GENL_ATTR_SEG);
+ rmnet_shs_genl_send_int_to_userspace(info, -1);
+ return 0;
+ }
+ } else {
+ rm_err("SHS_GNL: no info->attrs %d\n",
+ RMNET_SHS_GENL_ATTR_SEG);
+ rmnet_shs_genl_send_int_to_userspace(info, -1);
+ return 0;
+ }
+
+ return 0;
+}
+
+int rmnet_shs_genl_try_to_move_flow(struct sk_buff *skb_2, struct genl_info *info)
+{
+ struct nlattr *na;
+ struct rmnet_shs_wq_sugg_info sugg_info;
+ int rc = 0;
+
+ rm_err("%s", "SHS_GNL: rmnet_shs_genl_try_to_move_flow");
+
+ if (info == NULL) {
+ rm_err("%s", "SHS_GNL: an error occured - info is null");
+ return -1;
+ }
+
+ na = info->attrs[RMNET_SHS_GENL_ATTR_SUGG];
+ if (na) {
+ if (nla_memcpy(&sugg_info, na, sizeof(sugg_info)) > 0) {
+ rm_err("SHS_GNL: cur_cpu =%u dest_cpu = %u "
+ "hash_to_move = 0x%x sugg_type = %u",
+ sugg_info.cur_cpu,
+ sugg_info.dest_cpu,
+ sugg_info.hash_to_move,
+ sugg_info.sugg_type);
+ rc = rmnet_shs_wq_try_to_move_flow(sugg_info.cur_cpu,
+ sugg_info.dest_cpu,
+ sugg_info.hash_to_move,
+ sugg_info.sugg_type);
+ if (rc == 1) {
+ rmnet_shs_genl_send_int_to_userspace(info, 0);
+ trace_rmnet_shs_wq_high(RMNET_SHS_WQ_SHSUSR, RMNET_SHS_WQ_TRY_PASS,
+ sugg_info.cur_cpu, sugg_info.dest_cpu,
+ sugg_info.hash_to_move, sugg_info.sugg_type, NULL, NULL);
+
+ } else {
+ rmnet_shs_genl_send_int_to_userspace(info, -1);
+ trace_rmnet_shs_wq_high(RMNET_SHS_WQ_SHSUSR, RMNET_SHS_WQ_TRY_FAIL,
+ sugg_info.cur_cpu, sugg_info.dest_cpu,
+ sugg_info.hash_to_move, sugg_info.sugg_type, NULL, NULL);
+ return 0;
+ }
+ } else {
+ rm_err("SHS_GNL: nla_memcpy failed %d\n",
+ RMNET_SHS_GENL_ATTR_SUGG);
+ rmnet_shs_genl_send_int_to_userspace(info, -1);
+ return 0;
+ }
+ } else {
+ rm_err("SHS_GNL: no info->attrs %d\n",
+ RMNET_SHS_GENL_ATTR_SUGG);
+ rmnet_shs_genl_send_int_to_userspace(info, -1);
+ return 0;
+ }
+
+ return 0;
+}
+
+int rmnet_shs_genl_mem_sync(struct sk_buff *skb_2, struct genl_info *info)
+{
+ rm_err("%s", "SHS_GNL: rmnet_shs_genl_mem_sync");
+
+ if (!rmnet_shs_userspace_connected)
+ rmnet_shs_userspace_connected = 1;
+
+ /* Todo: detect when userspace is disconnected. If we dont get
+ * a sync message in the next 2 wq ticks, we got disconnected
+ */
+
+ trace_rmnet_shs_wq_high(RMNET_SHS_WQ_SHSUSR, RMNET_SHS_WQ_SHSUSR_SYNC_START,
+ 0xDEF, 0xDEF, 0xDEF, 0xDEF, NULL, NULL);
+
+ if (info == NULL) {
+ rm_err("%s", "SHS_GNL: an error occured - info is null");
+ return -1;
+ }
+
+ last_net = genl_info_net(info);
+ last_snd_portid = info->snd_portid;
+ return 0;
+}
+
+/* register new generic netlink family */
+int rmnet_shs_wq_genl_init(void)
+{
+ int ret;
+
+ rmnet_shs_userspace_connected = 0;
+ ret = genl_register_family(&rmnet_shs_genl_family);
+ if (ret != 0) {
+ rm_err("SHS_GNL: register family failed: %i", ret);
+ genl_unregister_family(&rmnet_shs_genl_family);
+ return -1;
+ }
+
+ rm_err("SHS_GNL: successfully registered generic netlink familiy: %s",
+ RMNET_SHS_GENL_FAMILY_NAME);
+
+ return 0;
+}
+
+/* Unregister the generic netlink family */
+int rmnet_shs_wq_genl_deinit(void)
+{
+ int ret;
+
+ ret = genl_unregister_family(&rmnet_shs_genl_family);
+ if(ret != 0){
+ rm_err("SHS_GNL: unregister family failed: %i\n",ret);
+ }
+ rmnet_shs_userspace_connected = 0;
+ return 0;
+}