diff options
Diffstat (limited to 'drivers/rmnet/shs/rmnet_shs_wq_genl.c')
-rw-r--r-- | drivers/rmnet/shs/rmnet_shs_wq_genl.c | 358 |
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; +} |