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

Commit 4581c3ae authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: ipa: eth: Use a wake lock of 500 ms on device activity"

parents 6933743c d3928121
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ ipa-eth-y := \
	ipa_eth_offload.o \
	ipa_eth_pci.o \
	ipa_eth_pm.o \
	ipa_eth_trace.o \
	ipa_eth_uc.o \
	ipa_eth_utils.o

+66 −36
Original line number Diff line number Diff line
@@ -11,7 +11,6 @@
 */

#include <linux/module.h>
#include <linux/suspend.h>
#include <linux/timer.h>

#include "ipa_eth_i.h"
@@ -28,9 +27,14 @@ MODULE_PARM_DESC(ipa_eth_noauto,

static struct workqueue_struct *ipa_eth_wq;

bool ipa_eth_ready(void)
bool ipa_eth_is_ready(void)
{
	return test_bit(IPA_ETH_ST_READY, &ipa_eth_state) &&
	return test_bit(IPA_ETH_ST_READY, &ipa_eth_state);
}

bool ipa_eth_all_ready(void)
{
	return ipa_eth_is_ready() &&
		test_bit(IPA_ETH_ST_UC_READY, &ipa_eth_state) &&
		test_bit(IPA_ETH_ST_IPA_READY, &ipa_eth_state);
}
@@ -51,7 +55,7 @@ static inline bool reachable(struct ipa_eth_device *eth_dev)
static inline bool offloadable(struct ipa_eth_device *eth_dev)
{
	return
		ipa_eth_ready() &&
		ipa_eth_all_ready() &&
		reachable(eth_dev) &&
		!test_bit(IPA_ETH_DEV_F_UNPAIRING, &eth_dev->flags);
}
@@ -378,7 +382,7 @@ static void ipa_eth_global_refresh_work(struct work_struct *work)

	mutex_lock(&ipa_eth_devices_lock);

	if (ipa_eth_ready()) {
	if (ipa_eth_all_ready()) {
		list_for_each_entry(eth_dev, &ipa_eth_devices, device_list) {
			ipa_eth_device_refresh_sched(eth_dev);
		}
@@ -550,6 +554,52 @@ static int ipa_eth_panic_notifier(struct notifier_block *nb,
	return NOTIFY_DONE;
}

/* During a system suspend, suspend-prepare callbacks are called first by the
 * PM framework before freezing processes. This gives us an early opportunity
 * to abort the suspend and reduces the chances for device resumes at a later
 * stage.
 */
static int ipa_eth_pm_notifier_event_suspend_prepare(
	struct ipa_eth_device *eth_dev)
{
	/* We look for any ethernet rx activity since previous attempt to
	 * suspend, and if such an activity is found, we hold a wake lock
	 * for WAKE_TIME_MS time. Any Rx packets received beyond this point
	 * should cause a wake up due to the Rx interrupt. In rare cases
	 * where Rx interrupt was already processed and NAPI poll is yet to
	 * complete, we perform a second check in the suspend late handler
	 * and reverts the device suspension by aborting the system suspend.
	 */
	if (ipa_eth_net_check_active(eth_dev)) {
		pr_info("%s: %s is active, preventing suspend for some time",
				IPA_ETH_SUBSYS, eth_dev->net_dev->name);
		ipa_eth_dev_wakeup_event(eth_dev);
		return NOTIFY_BAD;
	}

	return NOTIFY_OK;
}

static int ipa_eth_pm_notifier_cb(struct notifier_block *nb,
	unsigned long pm_event, void *unused)
{
	struct ipa_eth_device_private *ipa_priv = container_of(nb,
				struct ipa_eth_device_private, pm_nb);
	struct ipa_eth_device *eth_dev = ipa_priv->eth_dev;

	ipa_eth_dbg("PM notifier called for event %s (0x%04lx)",
			ipa_eth_pm_notifier_event_name(pm_event), pm_event);

	switch (pm_event) {
	case PM_SUSPEND_PREPARE:
		return ipa_eth_pm_notifier_event_suspend_prepare(eth_dev);
	default:
		break;
	}

	return NOTIFY_DONE;
}

/*
 * ipa_eth_alloc_device() - Allocate an ipa_eth_device structure and initialize
 *                          all common fields
@@ -607,6 +657,7 @@ struct ipa_eth_device *ipa_eth_alloc_device(

	INIT_LIST_HEAD(&ipa_priv->upper_devices);

	ipa_priv->pm_nb.notifier_call = ipa_eth_pm_notifier_cb;
	ipa_priv->panic_nb.notifier_call = ipa_eth_panic_notifier;

	eth_dev->ipa_priv = ipa_priv;
@@ -658,6 +709,8 @@ int ipa_eth_register_device(struct ipa_eth_device *eth_dev)
	list_add(&eth_dev->device_list, &ipa_eth_devices);
	mutex_unlock(&ipa_eth_devices_lock);

	(void) register_pm_notifier(&ipa_priv->pm_nb);

	ipa_eth_dev_log(eth_dev, "Registered new device");

	rc = ipa_eth_offload_pair_device(eth_dev);
@@ -725,6 +778,8 @@ void ipa_eth_unregister_device(struct ipa_eth_device *eth_dev)
	 */
	ipa_eth_unpair_device(eth_dev);

	(void) unregister_pm_notifier(&ipa_priv->pm_nb);

	/* Remove from devices list so that no new global refreshes are
	 * scheduled.
	 */
@@ -833,33 +888,17 @@ void ipa_eth_unregister_offload_driver(struct ipa_eth_offload_driver *od)
}
EXPORT_SYMBOL(ipa_eth_unregister_offload_driver);

static int ipa_eth_pm_notifier_cb(struct notifier_block *nb,
	unsigned long pm_event, void *unused)
{
	ipa_eth_log("PM notifier called for event %lu", pm_event);

	/* Permissible offload states for a device can change due to certain
	 * wake up events. Ex. if start_on_wakeup property is set for a device,
	 * the eth_dev->start will be set to true during eth bus resume. Do a
	 * global refresh on all devices to update their offload state based on
	 * any such changes in permissible offload states that may have occurred
	 * during resume.
	 */
	if (pm_event == PM_POST_SUSPEND)
		ipa_eth_global_refresh_sched();

	return NOTIFY_DONE;
}

static struct notifier_block pm_notifier = {
	.notifier_call = ipa_eth_pm_notifier_cb,
};

int ipa_eth_init(void)
{
	int rc;
	unsigned int wq_flags = WQ_UNBOUND | WQ_MEM_RECLAIM;

	/* Freeze the workqueue so that a refresh will not happen while the
	 * device is suspended as the suspend operation itself can generate
	 * Netlink events.
	 */
	wq_flags |= WQ_FREEZABLE;

	rc = ipa_eth_ipc_log_init();
	if (rc) {
		ipa_eth_err("Failed to initialize IPC logging");
@@ -886,12 +925,6 @@ int ipa_eth_init(void)
		goto err_dbgfs;
	}

	rc = register_pm_notifier(&pm_notifier);
	if (rc) {
		ipa_eth_err("Failed to register for PM notification");
		goto err_pm_notifier;
	}

	rc = ipa3_uc_register_ready_cb(&uc_ready_cb);
	if (rc) {
		ipa_eth_err("Failed to register for uC ready cb");
@@ -920,8 +953,6 @@ int ipa_eth_init(void)
err_ipa:
	ipa3_uc_unregister_ready_cb(&uc_ready_cb);
err_uc:
	unregister_pm_notifier(&pm_notifier);
err_pm_notifier:
	ipa_eth_debugfs_cleanup();
err_dbgfs:
	ipa_eth_bus_modexit();
@@ -950,7 +981,6 @@ void ipa_eth_exit(void)

	ipa3_uc_unregister_ready_cb(&uc_ready_cb);

	unregister_pm_notifier(&pm_notifier);
	ipa_eth_debugfs_cleanup();

	/* Wait for all offload paths to deinit. Although the chances for any
+2 −2
Original line number Diff line number Diff line
/* Copyright (c) 2019 The Linux Foundation. All rights reserved.
/* Copyright (c) 2019-2020, 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
@@ -231,7 +231,7 @@ static ssize_t eth_dev_ready_read(struct file *file, char __user *user_buf,
			"Ready" : "Not Ready");

	n += scnprintf(&buf[n], size - n, "ALL: %s\n",
		ipa_eth_ready() ? "Ready" : "Not Ready");
		ipa_eth_all_ready() ? "Ready" : "Not Ready");

	n = simple_read_from_buffer(user_buf, count, ppos, buf, n);

+3 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/if_vlan.h>

#include "ipa_eth_i.h"
#include "ipa_eth_trace.h"

static void handle_ipa_receive(struct ipa_eth_channel *ch,
			       unsigned long data)
@@ -20,6 +21,8 @@ static void handle_ipa_receive(struct ipa_eth_channel *ch,
	bool success = false;
	struct sk_buff *skb = (struct sk_buff *) data;

	trace_lan_rx_skb(ch, skb);

	ch->exception_total++;

#ifndef IPA_ETH_EP_LOOPBACK
+22 −1
Original line number Diff line number Diff line
@@ -13,6 +13,8 @@
#ifndef _IPA_ETH_I_H_
#define _IPA_ETH_I_H_

#include <linux/suspend.h>

#define IPA_ETH_NET_DRIVER
#define IPA_ETH_OFFLOAD_DRIVER
#include <linux/ipa_eth.h>
@@ -33,10 +35,13 @@
#define IPA_ETH_IPC_LOGDBG_DEFAULT false
#endif

#define IPA_ETH_WAKE_TIME_MS 500

#define IPA_ETH_PFDEV (ipa3_ctx ? ipa3_ctx->pdev : NULL)
#define IPA_ETH_SUBSYS "ipa_eth"

#define IPA_ETH_NET_DEVICE_MAX_EVENTS (NETDEV_CHANGE_TX_QUEUE_LEN + 1)
#define IPA_ETH_PM_NOTIFIER_MAX_EVENTS (PM_POST_RESTORE + 1)

enum ipa_eth_states {
	IPA_ETH_ST_READY,
@@ -117,9 +122,16 @@ struct ipa_eth_device_private {

	struct list_head upper_devices;

	unsigned long assume_active;
	struct rtnl_link_stats64 last_rtnl_stats;

	struct notifier_block pm_nb;
	struct notifier_block panic_nb;
};

#define eth_dev_priv(eth_dev) \
		((struct ipa_eth_device_private *)((eth_dev)->ipa_priv))

struct ipa_eth_bus {
	struct list_head bus_list;

@@ -146,7 +158,13 @@ extern unsigned long ipa_eth_state;
extern bool ipa_eth_noauto;
extern bool ipa_eth_ipc_logdbg;

bool ipa_eth_ready(void);
bool ipa_eth_is_ready(void);
bool ipa_eth_all_ready(void);

static inline void ipa_eth_dev_wakeup_event(struct ipa_eth_device *eth_dev)
{
	pm_wakeup_dev_event(eth_dev->dev, IPA_ETH_WAKE_TIME_MS, false);
}

struct ipa_eth_device *ipa_eth_alloc_device(
	struct device *dev,
@@ -191,6 +209,8 @@ int ipa_eth_offload_save_regs(struct ipa_eth_device *eth_dev);
int ipa_eth_offload_prepare_reset(struct ipa_eth_device *eth_dev, void *data);
int ipa_eth_offload_complete_reset(struct ipa_eth_device *eth_dev, void *data);

bool ipa_eth_net_check_active(struct ipa_eth_device *eth_dev);

int ipa_eth_net_register_driver(struct ipa_eth_net_driver *nd);
void ipa_eth_net_unregister_driver(struct ipa_eth_net_driver *nd);
int ipa_eth_net_register_upper(struct ipa_eth_device *eth_dev);
@@ -232,6 +252,7 @@ int ipa_eth_uc_stats_stop(struct ipa_eth_device *eth_dev);

const char *ipa_eth_device_event_name(enum ipa_eth_device_event event);
const char *ipa_eth_net_device_event_name(unsigned long event);
const char *ipa_eth_pm_notifier_event_name(unsigned long event);

int ipa_eth_send_msg_connect(struct net_device *net_dev);
int ipa_eth_send_msg_disconnect(struct net_device *net_dev);
Loading