Loading drivers/net/ethernet/qualcomm/rmnet/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -10,4 +10,5 @@ rmnet-y += rmnet_handlers.o rmnet-y += rmnet_map_data.o rmnet-y += rmnet_map_command.o rmnet-y += rmnet_descriptor.o rmnet-y += rmnet_genl.o obj-$(CONFIG_RMNET) += rmnet.o drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +4 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include "rmnet_private.h" #include "rmnet_map.h" #include "rmnet_descriptor.h" #include "rmnet_genl.h" #include <soc/qcom/rmnet_qmi.h> #include <soc/qcom/qmi_rmnet.h> Loading Loading @@ -726,6 +727,8 @@ static int __init rmnet_init(void) unregister_netdevice_notifier(&rmnet_dev_notifier); return rc; } rmnet_core_genl_init(); return rc; } Loading @@ -733,6 +736,7 @@ static void __exit rmnet_exit(void) { unregister_netdevice_notifier(&rmnet_dev_notifier); rtnl_link_unregister(&rmnet_link_ops); rmnet_core_genl_deinit(); } module_init(rmnet_init) Loading drivers/net/ethernet/qualcomm/rmnet/rmnet_genl.c 0 → 100644 +401 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2019, The Linux Foundation. All rights reserved. * * RMNET Data Generic Netlink * */ #include "rmnet_genl.h" #include <net/sock.h> #include <linux/skbuff.h> /* Static Functions and Definitions */ static struct nla_policy rmnet_genl_attr_policy[RMNET_CORE_GENL_ATTR_MAX + 1] = { [RMNET_CORE_GENL_ATTR_INT] = { .type = NLA_S32 }, [RMNET_CORE_GENL_ATTR_PID_BPS] = { .len = sizeof(struct rmnet_core_pid_bps_resp) }, [RMNET_CORE_GENL_ATTR_PID_BOOST] = { .len = sizeof(struct rmnet_core_pid_boost_req) }, [RMNET_CORE_GENL_ATTR_STR] = { .type = NLA_NUL_STRING }, }; #define RMNET_CORE_GENL_OP(_cmd, _func) \ { \ .cmd = _cmd, \ .policy = rmnet_genl_attr_policy, \ .doit = _func, \ .dumpit = NULL, \ .flags = 0, \ } static const struct genl_ops rmnet_core_genl_ops[] = { RMNET_CORE_GENL_OP(RMNET_CORE_GENL_CMD_PID_BPS_REQ, rmnet_core_genl_pid_bps_req_hdlr), RMNET_CORE_GENL_OP(RMNET_CORE_GENL_CMD_PID_BOOST_REQ, rmnet_core_genl_pid_boost_req_hdlr), }; struct genl_family rmnet_core_genl_family = { .hdrsize = 0, .name = RMNET_CORE_GENL_FAMILY_NAME, .version = RMNET_CORE_GENL_VERSION, .maxattr = RMNET_CORE_GENL_ATTR_MAX, .ops = rmnet_core_genl_ops, .n_ops = ARRAY_SIZE(rmnet_core_genl_ops), }; #define RMNET_PID_STATS_HT_SIZE (8) #define RMNET_PID_STATS_HT rmnet_pid_ht DEFINE_HASHTABLE(rmnet_pid_ht, RMNET_PID_STATS_HT_SIZE); spinlock_t rmnet_pid_ht_splock; /* Spinlock definition for pid hash table */ #define RMNET_GENL_SEC_TO_MSEC(x) ((x) * 1000) #define RMNET_GENL_SEC_TO_NSEC(x) ((x) * 1000000000) #define RMNET_GENL_NSEC_TO_SEC(x) ((x) / 1000000000) #define RMNET_GENL_BYTES_TO_BITS(x) ((x) * 8) int rmnet_core_userspace_connected; #define RMNET_QUERY_PERIOD_SEC (1) /* Period of pid/bps queries */ struct rmnet_pid_node_s { struct hlist_node list; time_t timstamp_last_query; u64 tx_bytes; u64 tx_bytes_last_query; u64 tx_bps; u64 sched_boost_period_ms; int sched_boost_remaining_ms; int sched_boost_enable; pid_t pid; }; void rmnet_update_pid_and_check_boost(pid_t pid, unsigned int len, int *boost_enable, u64 *boost_period) { struct hlist_node *tmp; struct rmnet_pid_node_s *node_p; unsigned long ht_flags; u8 is_match_found = 0; u64 tx_bytes = 0; *boost_enable = 0; *boost_period = 0; /* Using do while to spin lock and unlock only once */ spin_lock_irqsave(&rmnet_pid_ht_splock, ht_flags); do { hash_for_each_possible_safe(RMNET_PID_STATS_HT, node_p, tmp, list, pid) { if (pid != node_p->pid) continue; /* PID Match found */ is_match_found = 1; node_p->tx_bytes += len; tx_bytes = node_p->tx_bytes; if (node_p->sched_boost_enable) { rm_err("boost triggered for pid %d", pid); /* Just triggered boost, dont re-trigger */ node_p->sched_boost_enable = 0; *boost_enable = 1; *boost_period = node_p->sched_boost_period_ms; node_p->sched_boost_remaining_ms = (int)*boost_period; } break; } if (is_match_found) break; /* No PID match */ node_p = kzalloc(sizeof(*node_p), GFP_ATOMIC); if (!node_p) break; node_p->pid = pid; node_p->tx_bytes = len; node_p->sched_boost_enable = 0; node_p->sched_boost_period_ms = 0; node_p->sched_boost_remaining_ms = 0; hash_add_rcu(RMNET_PID_STATS_HT, &node_p->list, pid); break; } while (0); spin_unlock_irqrestore(&rmnet_pid_ht_splock, ht_flags); } void rmnet_boost_for_pid(pid_t pid, int boost_enable, u64 boost_period) { struct hlist_node *tmp; struct rmnet_pid_node_s *node_p; unsigned long ht_flags; /* Using do while to spin lock and unlock only once */ spin_lock_irqsave(&rmnet_pid_ht_splock, ht_flags); do { hash_for_each_possible_safe(RMNET_PID_STATS_HT, node_p, tmp, list, pid) { if (pid != node_p->pid) continue; /* PID Match found */ rm_err("CORE_BOOST: enable boost for pid %d for %d ms", pid, boost_period); node_p->sched_boost_enable = boost_enable; node_p->sched_boost_period_ms = boost_period; break; } break; } while (0); spin_unlock_irqrestore(&rmnet_pid_ht_splock, ht_flags); } static void rmnet_create_pid_bps_resp(struct rmnet_core_pid_bps_resp *pid_bps_resp_ptr) { struct timespec time; struct hlist_node *tmp; struct rmnet_pid_node_s *node_p; unsigned long ht_flags; u64 tx_bytes_cur, byte_diff, time_diff_ns; int i; u16 bkt; (void)getnstimeofday(&time); pid_bps_resp_ptr->timestamp = RMNET_GENL_SEC_TO_NSEC(time.tv_sec) + time.tv_nsec; /* Using do while to spin lock and unlock only once */ spin_lock_irqsave(&rmnet_pid_ht_splock, ht_flags); do { i = 0; hash_for_each_safe(RMNET_PID_STATS_HT, bkt, tmp, node_p, list) { tx_bytes_cur = node_p->tx_bytes; if (tx_bytes_cur <= node_p->tx_bytes_last_query) { /* Dont send inactive pids to userspace */ /* TODO: can remove from hash table probably */ node_p->tx_bps = 0; node_p->timstamp_last_query = pid_bps_resp_ptr->timestamp; node_p->sched_boost_remaining_ms = 0; continue; } /* Compute bits per second */ byte_diff = (node_p->tx_bytes - node_p->tx_bytes_last_query); time_diff_ns = (pid_bps_resp_ptr->timestamp - node_p->timstamp_last_query); node_p->tx_bps = (RMNET_GENL_BYTES_TO_BITS(byte_diff) / RMNET_GENL_NSEC_TO_SEC(time_diff_ns)); if (node_p->sched_boost_remaining_ms >= RMNET_GENL_SEC_TO_MSEC(RMNET_QUERY_PERIOD_SEC)) { node_p->sched_boost_remaining_ms -= RMNET_GENL_SEC_TO_MSEC(RMNET_QUERY_PERIOD_SEC); rm_err("CORE_BOOST: enabling boost for pid %d\n" "sched boost remaining = %d ms", node_p->pid, node_p->sched_boost_remaining_ms); } else { node_p->sched_boost_remaining_ms = 0; } pid_bps_resp_ptr->list[i].pid = node_p->pid; pid_bps_resp_ptr->list[i].tx_bps = node_p->tx_bps; pid_bps_resp_ptr->list[i].boost_remaining_ms = node_p->sched_boost_remaining_ms; node_p->timstamp_last_query = pid_bps_resp_ptr->timestamp; node_p->tx_bytes_last_query = tx_bytes_cur; i++; /* Support copying up to 32 active pids */ if (i >= RMNET_CORE_GENL_MAX_PIDS) break; } break; } while (0); spin_unlock_irqrestore(&rmnet_pid_ht_splock, ht_flags); pid_bps_resp_ptr->list_len = i; } int rmnet_core_genl_send_resp(struct genl_info *info, struct rmnet_core_pid_bps_resp *pid_bps_resp) { struct sk_buff *skb; void *msg_head; int rc; if (!info || !pid_bps_resp) { rm_err("%s", "SHS_GNL: Invalid params\n"); goto out; } skb = genlmsg_new(sizeof(struct rmnet_core_pid_bps_resp), GFP_KERNEL); if (!skb) goto out; msg_head = genlmsg_put(skb, 0, info->snd_seq + 1, &rmnet_core_genl_family, 0, RMNET_CORE_GENL_CMD_PID_BPS_REQ); if (!msg_head) { rc = -ENOMEM; goto out; } rc = nla_put(skb, RMNET_CORE_GENL_ATTR_PID_BPS, sizeof(struct rmnet_core_pid_bps_resp), pid_bps_resp); 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("%s", "SHS_GNL: Successfully sent pid/bytes info\n"); return RMNET_GENL_SUCCESS; out: /* TODO: Need to free skb?? */ rm_err("%s", "SHS_GNL: FAILED to send pid/bytes info\n"); rmnet_core_userspace_connected = 0; return RMNET_GENL_FAILURE; } int rmnet_core_genl_pid_bps_req_hdlr(struct sk_buff *skb_2, struct genl_info *info) { struct nlattr *na; struct rmnet_core_pid_bps_req pid_bps_req; struct rmnet_core_pid_bps_resp pid_bps_resp; int is_req_valid = 0; rm_err("CORE_GNL: %s connected = %d", __func__, rmnet_core_userspace_connected); if (!info) { rm_err("%s", "CORE_GNL: error - info is null"); pid_bps_resp.valid = 0; } else { na = info->attrs[RMNET_CORE_GENL_ATTR_PID_BPS]; if (na) { if (nla_memcpy(&pid_bps_req, na, sizeof(pid_bps_req)) > 0) { is_req_valid = 1; } else { rm_err("CORE_GNL: nla_memcpy failed %d\n", RMNET_CORE_GENL_ATTR_PID_BPS); } } else { rm_err("CORE_GNL: no info->attrs %d\n", RMNET_CORE_GENL_ATTR_PID_BPS); } } if (!rmnet_core_userspace_connected) rmnet_core_userspace_connected = 1; /* Copy to pid/byte list to the payload */ if (is_req_valid) { memset(&pid_bps_resp, 0x0, sizeof(pid_bps_resp)); rmnet_create_pid_bps_resp(&pid_bps_resp); } pid_bps_resp.valid = 1; rmnet_core_genl_send_resp(info, &pid_bps_resp); return RMNET_GENL_SUCCESS; } int rmnet_core_genl_pid_boost_req_hdlr(struct sk_buff *skb_2, struct genl_info *info) { struct nlattr *na; struct rmnet_core_pid_boost_req pid_boost_req; int is_req_valid = 0; u16 boost_pid_cnt = RMNET_CORE_GENL_MAX_PIDS; u16 i = 0; rm_err("%s", "CORE_GNL: %s", __func__); if (!info) { rm_err("%s", "CORE_GNL: error - info is null"); return RMNET_GENL_FAILURE; } na = info->attrs[RMNET_CORE_GENL_ATTR_PID_BOOST]; if (na) { if (nla_memcpy(&pid_boost_req, na, sizeof(pid_boost_req)) > 0) { is_req_valid = 1; } else { rm_err("CORE_GNL: nla_memcpy failed %d\n", RMNET_CORE_GENL_ATTR_PID_BOOST); return RMNET_GENL_FAILURE; } } else { rm_err("CORE_GNL: no info->attrs %d\n", RMNET_CORE_GENL_ATTR_PID_BOOST); return RMNET_GENL_FAILURE; } if (pid_boost_req.list_len < RMNET_CORE_GENL_MAX_PIDS) boost_pid_cnt = pid_boost_req.list_len; if (!pid_boost_req.valid) boost_pid_cnt = 0; for (i = 0; i < boost_pid_cnt; i++) { if (pid_boost_req.list[i].boost_enabled) { rmnet_boost_for_pid(pid_boost_req.list[i].pid, 1, pid_boost_req.list[i].boost_period); } } return RMNET_GENL_SUCCESS; } /* register new rmnet core driver generic netlink family */ int rmnet_core_genl_init(void) { int ret; ret = genl_register_family(&rmnet_core_genl_family); if (ret != 0) { rm_err("CORE_GNL: register family failed: %i", ret); genl_unregister_family(&rmnet_core_genl_family); return RMNET_GENL_FAILURE; } rm_err("CORE_GNL: successfully registered generic netlink family: %s", RMNET_CORE_GENL_FAMILY_NAME); return RMNET_GENL_SUCCESS; } /* Unregister the generic netlink family */ int rmnet_core_genl_deinit(void) { int ret; ret = genl_unregister_family(&rmnet_core_genl_family); if (ret != 0) rm_err("CORE_GNL: unregister family failed: %i\n", ret); return RMNET_GENL_SUCCESS; } drivers/net/ethernet/qualcomm/rmnet/rmnet_genl.h 0 → 100644 +96 −0 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) 2019, The Linux Foundation. All rights reserved. * * RMNET Data Generic Netlink * */ #ifndef _RMNET_GENL_H_ #define _RMNET_GENL_H_ #include <net/genetlink.h> #define RMNET_CORE_DEBUG 0 #define rm_err(fmt, ...) \ do { if (RMNET_CORE_DEBUG) pr_err(fmt, __VA_ARGS__); } while (0) /* Generic Netlink Definitions */ #define RMNET_CORE_GENL_VERSION 1 #define RMNET_CORE_GENL_FAMILY_NAME "RMNET_CORE" #define RMNET_CORE_GENL_MAX_PIDS 32 #define RMNET_GENL_SUCCESS (0) #define RMNET_GENL_FAILURE (-1) extern int rmnet_core_userspace_connected; enum { RMNET_CORE_GENL_CMD_UNSPEC, RMNET_CORE_GENL_CMD_PID_BPS_REQ, RMNET_CORE_GENL_CMD_PID_BOOST_REQ, __RMNET_CORE_GENL_CMD_MAX, }; enum { RMNET_CORE_GENL_ATTR_UNSPEC, RMNET_CORE_GENL_ATTR_STR, RMNET_CORE_GENL_ATTR_INT, RMNET_CORE_GENL_ATTR_PID_BPS, RMNET_CORE_GENL_ATTR_PID_BOOST, __RMNET_CORE_GENL_ATTR_MAX, }; #define RMNET_CORE_GENL_ATTR_MAX (__RMNET_CORE_GENL_ATTR_MAX - 1) struct rmnet_core_pid_bps_info { u64 tx_bps; u32 pid; u32 boost_remaining_ms; }; struct rmnet_core_pid_boost_info { u32 boost_enabled; /* Boost period in ms */ u32 boost_period; u32 pid; }; struct rmnet_core_pid_bps_req { struct rmnet_core_pid_bps_info list[RMNET_CORE_GENL_MAX_PIDS]; u64 timestamp; u16 list_len; u8 valid; }; struct rmnet_core_pid_bps_resp { struct rmnet_core_pid_bps_info list[RMNET_CORE_GENL_MAX_PIDS]; u64 timestamp; u16 list_len; u8 valid; }; struct rmnet_core_pid_boost_req { struct rmnet_core_pid_boost_info list[RMNET_CORE_GENL_MAX_PIDS]; u64 timestamp; u16 list_len; u8 valid; }; /* Function Prototypes */ int rmnet_core_genl_pid_bps_req_hdlr(struct sk_buff *skb_2, struct genl_info *info); int rmnet_core_genl_pid_boost_req_hdlr(struct sk_buff *skb_2, struct genl_info *info); /* Called by vnd select queue */ void rmnet_update_pid_and_check_boost(pid_t pid, unsigned int len, int *boost_enable, u64 *boost_period); int rmnet_core_genl_init(void); int rmnet_core_genl_deinit(void); #endif /*_RMNET_CORE_GENL_H_*/ drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +13 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ #include "rmnet_private.h" #include "rmnet_map.h" #include "rmnet_vnd.h" #include "rmnet_genl.h" #include "rmnet_trace.h" #include <soc/qcom/qmi_rmnet.h> Loading Loading @@ -158,12 +159,24 @@ static u16 rmnet_vnd_select_queue(struct net_device *dev, struct net_device *sb_dev, select_queue_fallback_t fallback) { u64 boost_period = 0; int boost_trigger = 0; struct rmnet_priv *priv = netdev_priv(dev); int txq = 0; if (priv->real_dev) txq = qmi_rmnet_get_queue(dev, skb); if (rmnet_core_userspace_connected) { rmnet_update_pid_and_check_boost(task_pid_nr(current), skb->len, &boost_trigger, &boost_period); if (boost_trigger) set_task_boost(1, boost_period); } return (txq < dev->real_num_tx_queues) ? txq : 0; } Loading Loading
drivers/net/ethernet/qualcomm/rmnet/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -10,4 +10,5 @@ rmnet-y += rmnet_handlers.o rmnet-y += rmnet_map_data.o rmnet-y += rmnet_map_command.o rmnet-y += rmnet_descriptor.o rmnet-y += rmnet_genl.o obj-$(CONFIG_RMNET) += rmnet.o
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +4 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include "rmnet_private.h" #include "rmnet_map.h" #include "rmnet_descriptor.h" #include "rmnet_genl.h" #include <soc/qcom/rmnet_qmi.h> #include <soc/qcom/qmi_rmnet.h> Loading Loading @@ -726,6 +727,8 @@ static int __init rmnet_init(void) unregister_netdevice_notifier(&rmnet_dev_notifier); return rc; } rmnet_core_genl_init(); return rc; } Loading @@ -733,6 +736,7 @@ static void __exit rmnet_exit(void) { unregister_netdevice_notifier(&rmnet_dev_notifier); rtnl_link_unregister(&rmnet_link_ops); rmnet_core_genl_deinit(); } module_init(rmnet_init) Loading
drivers/net/ethernet/qualcomm/rmnet/rmnet_genl.c 0 → 100644 +401 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2019, The Linux Foundation. All rights reserved. * * RMNET Data Generic Netlink * */ #include "rmnet_genl.h" #include <net/sock.h> #include <linux/skbuff.h> /* Static Functions and Definitions */ static struct nla_policy rmnet_genl_attr_policy[RMNET_CORE_GENL_ATTR_MAX + 1] = { [RMNET_CORE_GENL_ATTR_INT] = { .type = NLA_S32 }, [RMNET_CORE_GENL_ATTR_PID_BPS] = { .len = sizeof(struct rmnet_core_pid_bps_resp) }, [RMNET_CORE_GENL_ATTR_PID_BOOST] = { .len = sizeof(struct rmnet_core_pid_boost_req) }, [RMNET_CORE_GENL_ATTR_STR] = { .type = NLA_NUL_STRING }, }; #define RMNET_CORE_GENL_OP(_cmd, _func) \ { \ .cmd = _cmd, \ .policy = rmnet_genl_attr_policy, \ .doit = _func, \ .dumpit = NULL, \ .flags = 0, \ } static const struct genl_ops rmnet_core_genl_ops[] = { RMNET_CORE_GENL_OP(RMNET_CORE_GENL_CMD_PID_BPS_REQ, rmnet_core_genl_pid_bps_req_hdlr), RMNET_CORE_GENL_OP(RMNET_CORE_GENL_CMD_PID_BOOST_REQ, rmnet_core_genl_pid_boost_req_hdlr), }; struct genl_family rmnet_core_genl_family = { .hdrsize = 0, .name = RMNET_CORE_GENL_FAMILY_NAME, .version = RMNET_CORE_GENL_VERSION, .maxattr = RMNET_CORE_GENL_ATTR_MAX, .ops = rmnet_core_genl_ops, .n_ops = ARRAY_SIZE(rmnet_core_genl_ops), }; #define RMNET_PID_STATS_HT_SIZE (8) #define RMNET_PID_STATS_HT rmnet_pid_ht DEFINE_HASHTABLE(rmnet_pid_ht, RMNET_PID_STATS_HT_SIZE); spinlock_t rmnet_pid_ht_splock; /* Spinlock definition for pid hash table */ #define RMNET_GENL_SEC_TO_MSEC(x) ((x) * 1000) #define RMNET_GENL_SEC_TO_NSEC(x) ((x) * 1000000000) #define RMNET_GENL_NSEC_TO_SEC(x) ((x) / 1000000000) #define RMNET_GENL_BYTES_TO_BITS(x) ((x) * 8) int rmnet_core_userspace_connected; #define RMNET_QUERY_PERIOD_SEC (1) /* Period of pid/bps queries */ struct rmnet_pid_node_s { struct hlist_node list; time_t timstamp_last_query; u64 tx_bytes; u64 tx_bytes_last_query; u64 tx_bps; u64 sched_boost_period_ms; int sched_boost_remaining_ms; int sched_boost_enable; pid_t pid; }; void rmnet_update_pid_and_check_boost(pid_t pid, unsigned int len, int *boost_enable, u64 *boost_period) { struct hlist_node *tmp; struct rmnet_pid_node_s *node_p; unsigned long ht_flags; u8 is_match_found = 0; u64 tx_bytes = 0; *boost_enable = 0; *boost_period = 0; /* Using do while to spin lock and unlock only once */ spin_lock_irqsave(&rmnet_pid_ht_splock, ht_flags); do { hash_for_each_possible_safe(RMNET_PID_STATS_HT, node_p, tmp, list, pid) { if (pid != node_p->pid) continue; /* PID Match found */ is_match_found = 1; node_p->tx_bytes += len; tx_bytes = node_p->tx_bytes; if (node_p->sched_boost_enable) { rm_err("boost triggered for pid %d", pid); /* Just triggered boost, dont re-trigger */ node_p->sched_boost_enable = 0; *boost_enable = 1; *boost_period = node_p->sched_boost_period_ms; node_p->sched_boost_remaining_ms = (int)*boost_period; } break; } if (is_match_found) break; /* No PID match */ node_p = kzalloc(sizeof(*node_p), GFP_ATOMIC); if (!node_p) break; node_p->pid = pid; node_p->tx_bytes = len; node_p->sched_boost_enable = 0; node_p->sched_boost_period_ms = 0; node_p->sched_boost_remaining_ms = 0; hash_add_rcu(RMNET_PID_STATS_HT, &node_p->list, pid); break; } while (0); spin_unlock_irqrestore(&rmnet_pid_ht_splock, ht_flags); } void rmnet_boost_for_pid(pid_t pid, int boost_enable, u64 boost_period) { struct hlist_node *tmp; struct rmnet_pid_node_s *node_p; unsigned long ht_flags; /* Using do while to spin lock and unlock only once */ spin_lock_irqsave(&rmnet_pid_ht_splock, ht_flags); do { hash_for_each_possible_safe(RMNET_PID_STATS_HT, node_p, tmp, list, pid) { if (pid != node_p->pid) continue; /* PID Match found */ rm_err("CORE_BOOST: enable boost for pid %d for %d ms", pid, boost_period); node_p->sched_boost_enable = boost_enable; node_p->sched_boost_period_ms = boost_period; break; } break; } while (0); spin_unlock_irqrestore(&rmnet_pid_ht_splock, ht_flags); } static void rmnet_create_pid_bps_resp(struct rmnet_core_pid_bps_resp *pid_bps_resp_ptr) { struct timespec time; struct hlist_node *tmp; struct rmnet_pid_node_s *node_p; unsigned long ht_flags; u64 tx_bytes_cur, byte_diff, time_diff_ns; int i; u16 bkt; (void)getnstimeofday(&time); pid_bps_resp_ptr->timestamp = RMNET_GENL_SEC_TO_NSEC(time.tv_sec) + time.tv_nsec; /* Using do while to spin lock and unlock only once */ spin_lock_irqsave(&rmnet_pid_ht_splock, ht_flags); do { i = 0; hash_for_each_safe(RMNET_PID_STATS_HT, bkt, tmp, node_p, list) { tx_bytes_cur = node_p->tx_bytes; if (tx_bytes_cur <= node_p->tx_bytes_last_query) { /* Dont send inactive pids to userspace */ /* TODO: can remove from hash table probably */ node_p->tx_bps = 0; node_p->timstamp_last_query = pid_bps_resp_ptr->timestamp; node_p->sched_boost_remaining_ms = 0; continue; } /* Compute bits per second */ byte_diff = (node_p->tx_bytes - node_p->tx_bytes_last_query); time_diff_ns = (pid_bps_resp_ptr->timestamp - node_p->timstamp_last_query); node_p->tx_bps = (RMNET_GENL_BYTES_TO_BITS(byte_diff) / RMNET_GENL_NSEC_TO_SEC(time_diff_ns)); if (node_p->sched_boost_remaining_ms >= RMNET_GENL_SEC_TO_MSEC(RMNET_QUERY_PERIOD_SEC)) { node_p->sched_boost_remaining_ms -= RMNET_GENL_SEC_TO_MSEC(RMNET_QUERY_PERIOD_SEC); rm_err("CORE_BOOST: enabling boost for pid %d\n" "sched boost remaining = %d ms", node_p->pid, node_p->sched_boost_remaining_ms); } else { node_p->sched_boost_remaining_ms = 0; } pid_bps_resp_ptr->list[i].pid = node_p->pid; pid_bps_resp_ptr->list[i].tx_bps = node_p->tx_bps; pid_bps_resp_ptr->list[i].boost_remaining_ms = node_p->sched_boost_remaining_ms; node_p->timstamp_last_query = pid_bps_resp_ptr->timestamp; node_p->tx_bytes_last_query = tx_bytes_cur; i++; /* Support copying up to 32 active pids */ if (i >= RMNET_CORE_GENL_MAX_PIDS) break; } break; } while (0); spin_unlock_irqrestore(&rmnet_pid_ht_splock, ht_flags); pid_bps_resp_ptr->list_len = i; } int rmnet_core_genl_send_resp(struct genl_info *info, struct rmnet_core_pid_bps_resp *pid_bps_resp) { struct sk_buff *skb; void *msg_head; int rc; if (!info || !pid_bps_resp) { rm_err("%s", "SHS_GNL: Invalid params\n"); goto out; } skb = genlmsg_new(sizeof(struct rmnet_core_pid_bps_resp), GFP_KERNEL); if (!skb) goto out; msg_head = genlmsg_put(skb, 0, info->snd_seq + 1, &rmnet_core_genl_family, 0, RMNET_CORE_GENL_CMD_PID_BPS_REQ); if (!msg_head) { rc = -ENOMEM; goto out; } rc = nla_put(skb, RMNET_CORE_GENL_ATTR_PID_BPS, sizeof(struct rmnet_core_pid_bps_resp), pid_bps_resp); 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("%s", "SHS_GNL: Successfully sent pid/bytes info\n"); return RMNET_GENL_SUCCESS; out: /* TODO: Need to free skb?? */ rm_err("%s", "SHS_GNL: FAILED to send pid/bytes info\n"); rmnet_core_userspace_connected = 0; return RMNET_GENL_FAILURE; } int rmnet_core_genl_pid_bps_req_hdlr(struct sk_buff *skb_2, struct genl_info *info) { struct nlattr *na; struct rmnet_core_pid_bps_req pid_bps_req; struct rmnet_core_pid_bps_resp pid_bps_resp; int is_req_valid = 0; rm_err("CORE_GNL: %s connected = %d", __func__, rmnet_core_userspace_connected); if (!info) { rm_err("%s", "CORE_GNL: error - info is null"); pid_bps_resp.valid = 0; } else { na = info->attrs[RMNET_CORE_GENL_ATTR_PID_BPS]; if (na) { if (nla_memcpy(&pid_bps_req, na, sizeof(pid_bps_req)) > 0) { is_req_valid = 1; } else { rm_err("CORE_GNL: nla_memcpy failed %d\n", RMNET_CORE_GENL_ATTR_PID_BPS); } } else { rm_err("CORE_GNL: no info->attrs %d\n", RMNET_CORE_GENL_ATTR_PID_BPS); } } if (!rmnet_core_userspace_connected) rmnet_core_userspace_connected = 1; /* Copy to pid/byte list to the payload */ if (is_req_valid) { memset(&pid_bps_resp, 0x0, sizeof(pid_bps_resp)); rmnet_create_pid_bps_resp(&pid_bps_resp); } pid_bps_resp.valid = 1; rmnet_core_genl_send_resp(info, &pid_bps_resp); return RMNET_GENL_SUCCESS; } int rmnet_core_genl_pid_boost_req_hdlr(struct sk_buff *skb_2, struct genl_info *info) { struct nlattr *na; struct rmnet_core_pid_boost_req pid_boost_req; int is_req_valid = 0; u16 boost_pid_cnt = RMNET_CORE_GENL_MAX_PIDS; u16 i = 0; rm_err("%s", "CORE_GNL: %s", __func__); if (!info) { rm_err("%s", "CORE_GNL: error - info is null"); return RMNET_GENL_FAILURE; } na = info->attrs[RMNET_CORE_GENL_ATTR_PID_BOOST]; if (na) { if (nla_memcpy(&pid_boost_req, na, sizeof(pid_boost_req)) > 0) { is_req_valid = 1; } else { rm_err("CORE_GNL: nla_memcpy failed %d\n", RMNET_CORE_GENL_ATTR_PID_BOOST); return RMNET_GENL_FAILURE; } } else { rm_err("CORE_GNL: no info->attrs %d\n", RMNET_CORE_GENL_ATTR_PID_BOOST); return RMNET_GENL_FAILURE; } if (pid_boost_req.list_len < RMNET_CORE_GENL_MAX_PIDS) boost_pid_cnt = pid_boost_req.list_len; if (!pid_boost_req.valid) boost_pid_cnt = 0; for (i = 0; i < boost_pid_cnt; i++) { if (pid_boost_req.list[i].boost_enabled) { rmnet_boost_for_pid(pid_boost_req.list[i].pid, 1, pid_boost_req.list[i].boost_period); } } return RMNET_GENL_SUCCESS; } /* register new rmnet core driver generic netlink family */ int rmnet_core_genl_init(void) { int ret; ret = genl_register_family(&rmnet_core_genl_family); if (ret != 0) { rm_err("CORE_GNL: register family failed: %i", ret); genl_unregister_family(&rmnet_core_genl_family); return RMNET_GENL_FAILURE; } rm_err("CORE_GNL: successfully registered generic netlink family: %s", RMNET_CORE_GENL_FAMILY_NAME); return RMNET_GENL_SUCCESS; } /* Unregister the generic netlink family */ int rmnet_core_genl_deinit(void) { int ret; ret = genl_unregister_family(&rmnet_core_genl_family); if (ret != 0) rm_err("CORE_GNL: unregister family failed: %i\n", ret); return RMNET_GENL_SUCCESS; }
drivers/net/ethernet/qualcomm/rmnet/rmnet_genl.h 0 → 100644 +96 −0 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) 2019, The Linux Foundation. All rights reserved. * * RMNET Data Generic Netlink * */ #ifndef _RMNET_GENL_H_ #define _RMNET_GENL_H_ #include <net/genetlink.h> #define RMNET_CORE_DEBUG 0 #define rm_err(fmt, ...) \ do { if (RMNET_CORE_DEBUG) pr_err(fmt, __VA_ARGS__); } while (0) /* Generic Netlink Definitions */ #define RMNET_CORE_GENL_VERSION 1 #define RMNET_CORE_GENL_FAMILY_NAME "RMNET_CORE" #define RMNET_CORE_GENL_MAX_PIDS 32 #define RMNET_GENL_SUCCESS (0) #define RMNET_GENL_FAILURE (-1) extern int rmnet_core_userspace_connected; enum { RMNET_CORE_GENL_CMD_UNSPEC, RMNET_CORE_GENL_CMD_PID_BPS_REQ, RMNET_CORE_GENL_CMD_PID_BOOST_REQ, __RMNET_CORE_GENL_CMD_MAX, }; enum { RMNET_CORE_GENL_ATTR_UNSPEC, RMNET_CORE_GENL_ATTR_STR, RMNET_CORE_GENL_ATTR_INT, RMNET_CORE_GENL_ATTR_PID_BPS, RMNET_CORE_GENL_ATTR_PID_BOOST, __RMNET_CORE_GENL_ATTR_MAX, }; #define RMNET_CORE_GENL_ATTR_MAX (__RMNET_CORE_GENL_ATTR_MAX - 1) struct rmnet_core_pid_bps_info { u64 tx_bps; u32 pid; u32 boost_remaining_ms; }; struct rmnet_core_pid_boost_info { u32 boost_enabled; /* Boost period in ms */ u32 boost_period; u32 pid; }; struct rmnet_core_pid_bps_req { struct rmnet_core_pid_bps_info list[RMNET_CORE_GENL_MAX_PIDS]; u64 timestamp; u16 list_len; u8 valid; }; struct rmnet_core_pid_bps_resp { struct rmnet_core_pid_bps_info list[RMNET_CORE_GENL_MAX_PIDS]; u64 timestamp; u16 list_len; u8 valid; }; struct rmnet_core_pid_boost_req { struct rmnet_core_pid_boost_info list[RMNET_CORE_GENL_MAX_PIDS]; u64 timestamp; u16 list_len; u8 valid; }; /* Function Prototypes */ int rmnet_core_genl_pid_bps_req_hdlr(struct sk_buff *skb_2, struct genl_info *info); int rmnet_core_genl_pid_boost_req_hdlr(struct sk_buff *skb_2, struct genl_info *info); /* Called by vnd select queue */ void rmnet_update_pid_and_check_boost(pid_t pid, unsigned int len, int *boost_enable, u64 *boost_period); int rmnet_core_genl_init(void); int rmnet_core_genl_deinit(void); #endif /*_RMNET_CORE_GENL_H_*/
drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +13 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ #include "rmnet_private.h" #include "rmnet_map.h" #include "rmnet_vnd.h" #include "rmnet_genl.h" #include "rmnet_trace.h" #include <soc/qcom/qmi_rmnet.h> Loading Loading @@ -158,12 +159,24 @@ static u16 rmnet_vnd_select_queue(struct net_device *dev, struct net_device *sb_dev, select_queue_fallback_t fallback) { u64 boost_period = 0; int boost_trigger = 0; struct rmnet_priv *priv = netdev_priv(dev); int txq = 0; if (priv->real_dev) txq = qmi_rmnet_get_queue(dev, skb); if (rmnet_core_userspace_connected) { rmnet_update_pid_and_check_boost(task_pid_nr(current), skb->len, &boost_trigger, &boost_period); if (boost_trigger) set_task_boost(1, boost_period); } return (txq < dev->real_num_tx_queues) ? txq : 0; } Loading