Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8fb05b8d authored by Subash Abhinov Kasiviswanathan's avatar Subash Abhinov Kasiviswanathan
Browse files

drivers: net: rmnet: Power collapse feature



Enable power collapse in rmnet driver. Rmnet
driver is not able to go into power collapse
unless dl marker/fc indications stop.
Deregistration of these indications should
take place when there is no longer any more
rmnet traffic. This change allows for dereg
of these indications with use of periodic wq.

CRs-Fixed: 2277101
Change-Id: I6a95307f5988279507e0b0b35559d7872d693c71
Acked-by: default avatarRaul Martinez <mraul@qti.qualcomm.com>
Signed-off-by: default avatarSubash Abhinov Kasiviswanathan <subashab@codeaurora.org>
parent 5f6c7ada
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -104,7 +104,6 @@ static int rmnet_register_real_device(struct net_device *real_dev)
		kfree(port);
		return -EBUSY;
	}

	/* hold on to real dev for MAP data */
	dev_hold(real_dev);

@@ -545,6 +544,50 @@ void rmnet_init_qmi_pt(void *port, void *qmi)
		((struct rmnet_port *)port)->qmi_info = qmi;
}
EXPORT_SYMBOL(rmnet_init_qmi_pt);

void rmnet_get_packets(void *port, u64 *rx, u64 *tx)
{
	struct rmnet_priv *priv;
	struct rmnet_pcpu_stats *ps;
	unsigned int cpu, start;

	struct rmnet_endpoint *ep;
	unsigned long bkt;

	if (!port || !tx || !rx)
		return;

	*tx = 0;
	*rx = 0;
	hash_for_each(((struct rmnet_port *)port)->muxed_ep, bkt, ep, hlnode) {
		priv = netdev_priv(ep->egress_dev);
		for_each_possible_cpu(cpu) {
			ps = per_cpu_ptr(priv->pcpu_stats, cpu);
			do {
				start = u64_stats_fetch_begin_irq(&ps->syncp);
				*tx += ps->stats.tx_pkts;
				*rx += ps->stats.rx_pkts;
			} while (u64_stats_fetch_retry_irq(&ps->syncp, start));
		}
	}
}
EXPORT_SYMBOL(rmnet_get_packets);

void  rmnet_set_powersave_format(void *port)
{
	if (!port)
		return;
	((struct rmnet_port *)port)->data_format |= RMNET_INGRESS_FORMAT_PS;
}
EXPORT_SYMBOL(rmnet_set_powersave_format);

void  rmnet_clear_powersave_format(void *port)
{
	if (!port)
		return;
	((struct rmnet_port *)port)->data_format &= ~RMNET_INGRESS_FORMAT_PS;
}
EXPORT_SYMBOL(rmnet_clear_powersave_format);
#endif

/* Startup/Shutdown */
+10 −0
Original line number Diff line number Diff line
@@ -24,6 +24,10 @@
#include "rmnet_vnd.h"
#include "rmnet_map.h"
#include "rmnet_handlers.h"
#ifdef CONFIG_QCOM_QMI_HELPERS
#include <soc/qcom/rmnet_qmi.h>
#include <soc/qcom/qmi_rmnet.h>
#endif

#define RMNET_IP_VERSION_4 0x40
#define RMNET_IP_VERSION_6 0x60
@@ -125,6 +129,12 @@ __rmnet_map_ingress_handler(struct sk_buff *skb,
			skb->ip_summed = CHECKSUM_UNNECESSARY;
	}

	if ((port->data_format & RMNET_INGRESS_FORMAT_PS) &&
	    !qmi_rmnet_work_get_active(port)) {
		/* register for powersave indications*/
		qmi_rmnet_work_restart(port);
	}

	skb_trim(skb, len);
	rmnet_deliver_skb(skb);
	return;
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@
#define RMNET_EGRESS_FORMAT_AGGREGATION         BIT(31)
#define RMNET_INGRESS_FORMAT_DL_MARKER          BIT(30)

/* Power save feature*/
#define RMNET_INGRESS_FORMAT_PS                 BIT(27)

/* Replace skb->dev to a virtual rmnet device and pass up the stack */
#define RMNET_EPMODE_VND (1)
/* Pass the frame directly to another device with dev_queue_xmit() */
+124 −1
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@
#define FLAG_DFC_MASK 0x0001
#define FLAG_POWERSAVE_MASK 0x0010

#define PS_INTERVAL (0x0004 * HZ)
#define NO_DELAY (0x0000 * HZ)

struct qmi_elem_info data_ep_id_type_v01_ei[] = {
	{
		.data_type	= QMI_SIGNED_4_BYTE_ENUM,
@@ -470,11 +473,18 @@ void qmi_rmnet_change_link(struct net_device *dev, void *port, void *tcm_pt)
				rmnet_reset_qmi_pt(port);
			}
		}
		if (tcm->tcm_ifindex & FLAG_POWERSAVE_MASK) {
			qmi_rmnet_work_init(port);
			rmnet_set_powersave_format(port);
		}
		break;
	case NLMSG_CLIENT_DELETE:
		if (!qmi)
			return;

		if (tcm->tcm_ifindex & FLAG_POWERSAVE_MASK) {
			rmnet_clear_powersave_format(port);
			qmi_rmnet_work_exit(port);
		}
		qmi_rmnet_delete_client(port, qmi, tcm);
		break;
	default:
@@ -499,6 +509,8 @@ void qmi_rmnet_qmi_exit(void *qmi_pt, void *port)
		qmi->wda_client = NULL;
	}

	qmi_rmnet_work_exit(port);

	for (i = 0; i < MAX_CLIENT_NUM; i++) {
		if (!__qmi_rmnet_delete_client(port, qmi, i))
			return;
@@ -555,6 +567,16 @@ EXPORT_SYMBOL(qmi_rmnet_qos_exit);
#endif

#ifdef CONFIG_QCOM_QMI_POWER_COLLAPSE
static struct workqueue_struct  *rmnet_ps_wq;
static struct rmnet_powersave_work *rmnet_work;

struct rmnet_powersave_work {
	struct delayed_work work;
	void *port;
	u64 old_rx_pkts;
	u64 old_tx_pkts;
};

int qmi_rmnet_set_powersave_mode(void *port, uint8_t enable)
{
	int rc = -EINVAL;
@@ -575,4 +597,105 @@ int qmi_rmnet_set_powersave_mode(void *port, uint8_t enable)
	return 0;
}
EXPORT_SYMBOL(qmi_rmnet_set_powersave_mode);

void qmi_rmnet_work_restart(void *port)
{
	if (!rmnet_ps_wq || !rmnet_work)
		return;
	queue_delayed_work(rmnet_ps_wq, &rmnet_work->work, NO_DELAY);
}
EXPORT_SYMBOL(qmi_rmnet_work_restart);

static void qmi_rmnet_check_stats(struct work_struct *work)
{
	struct rmnet_powersave_work *real_work;
	u64 rxd, txd;
	u64 rx, tx;

	real_work = container_of(to_delayed_work(work),
				 struct rmnet_powersave_work, work);

	if (unlikely(!real_work || !real_work->port))
		return;

	if (!rtnl_trylock()) {
		queue_delayed_work(rmnet_ps_wq, &real_work->work, PS_INTERVAL);
		return;
	}
	if (!qmi_rmnet_work_get_active(real_work->port)) {
		qmi_rmnet_work_set_active(real_work->port, 1);
		qmi_rmnet_set_powersave_mode(real_work->port, 0);
		goto end;
	}

	rmnet_get_packets(real_work->port, &rx, &tx);
	rxd = rx - real_work->old_rx_pkts;
	txd = tx - real_work->old_tx_pkts;
	real_work->old_rx_pkts = rx;
	real_work->old_tx_pkts = tx;

	if (!rxd && !txd) {
		qmi_rmnet_work_set_active(real_work->port, 0);
		qmi_rmnet_set_powersave_mode(real_work->port, 1);
		rtnl_unlock();
		return;
	}
end:
	rtnl_unlock();
	queue_delayed_work(rmnet_ps_wq, &real_work->work, PS_INTERVAL);
}

void qmi_rmnet_work_init(void *port)
{
	if (rmnet_ps_wq)
		return;

	rmnet_ps_wq = alloc_workqueue("rmnet_powersave_work",
					WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);

	rmnet_work = kmalloc(sizeof(*rmnet_work), GFP_ATOMIC);
	if (!rmnet_work) {
		destroy_workqueue(rmnet_ps_wq);
		rmnet_ps_wq = NULL;
		return;
	}

	INIT_DEFERRABLE_WORK(&rmnet_work->work, qmi_rmnet_check_stats);
	rmnet_work->port = port;
	rmnet_get_packets(rmnet_work->port, &rmnet_work->old_rx_pkts,
			  &rmnet_work->old_tx_pkts);

	qmi_rmnet_work_set_active(rmnet_work->port, 1);
	queue_delayed_work(rmnet_ps_wq, &rmnet_work->work, PS_INTERVAL);
}
EXPORT_SYMBOL(qmi_rmnet_work_init);

void qmi_rmnet_work_set_active(void *port, int status)
{
	if (!port)
		return;
	((struct qmi_info *)rmnet_get_qmi_pt(port))->active = status;
}
EXPORT_SYMBOL(qmi_rmnet_work_set_active);

int qmi_rmnet_work_get_active(void *port)
{
	if (!port)
		return 0;
	return ((struct qmi_info *)rmnet_get_qmi_pt(port))->active;
}
EXPORT_SYMBOL(qmi_rmnet_work_get_active);

void qmi_rmnet_work_exit(void *port)
{
	qmi_rmnet_work_set_active(port, 0);
	if (!rmnet_ps_wq || !rmnet_work)
		return;
	cancel_delayed_work_sync(&rmnet_work->work);
	destroy_workqueue(rmnet_ps_wq);
	rmnet_ps_wq = NULL;
	kfree(rmnet_work);
	rmnet_work = NULL;
}
EXPORT_SYMBOL(qmi_rmnet_work_exit);
#endif
+1 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ struct qmi_info {
	struct flow_info flow[MAX_FLOW_NUM];
	void *wda_client;
	struct fc_info fc_info[MAX_CLIENT_NUM];
	int active;
};

enum data_ep_type_enum_v01 {
Loading