|
@@ -1,5 +1,6 @@
|
|
/*
|
|
/*
|
|
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
|
|
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
|
|
|
|
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* 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
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
@@ -24,7 +25,6 @@
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/ipv6.h>
|
|
#include <linux/ipv6.h>
|
|
-#include <linux/alarmtimer.h>
|
|
|
|
|
|
|
|
#define NLMSG_FLOW_ACTIVATE 1
|
|
#define NLMSG_FLOW_ACTIVATE 1
|
|
#define NLMSG_FLOW_DEACTIVATE 2
|
|
#define NLMSG_FLOW_DEACTIVATE 2
|
|
@@ -226,21 +226,6 @@ int qmi_rmnet_flow_control(struct net_device *dev, u32 mq_idx, int enable)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static void qmi_rmnet_reset_txq(struct net_device *dev, unsigned int txq)
|
|
|
|
-{
|
|
|
|
- struct Qdisc *qdisc;
|
|
|
|
-
|
|
|
|
- if (unlikely(txq >= dev->num_tx_queues))
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- qdisc = rtnl_dereference(netdev_get_tx_queue(dev, txq)->qdisc);
|
|
|
|
- if (qdisc) {
|
|
|
|
- spin_lock_bh(qdisc_lock(qdisc));
|
|
|
|
- qdisc_reset(qdisc);
|
|
|
|
- spin_unlock_bh(qdisc_lock(qdisc));
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* qmi_rmnet_watchdog_fn - watchdog timer func
|
|
* qmi_rmnet_watchdog_fn - watchdog timer func
|
|
*/
|
|
*/
|
|
@@ -371,15 +356,13 @@ static void __qmi_rmnet_bearer_put(struct net_device *dev,
|
|
|
|
|
|
mq->bearer = NULL;
|
|
mq->bearer = NULL;
|
|
mq->is_ll_ch = false;
|
|
mq->is_ll_ch = false;
|
|
- if (reset) {
|
|
|
|
- qmi_rmnet_reset_txq(dev, i);
|
|
|
|
- qmi_rmnet_flow_control(dev, i, 1);
|
|
|
|
-
|
|
|
|
- if (dfc_mode == DFC_MODE_SA) {
|
|
|
|
- j = i + ACK_MQ_OFFSET;
|
|
|
|
- qmi_rmnet_reset_txq(dev, j);
|
|
|
|
- qmi_rmnet_flow_control(dev, j, 1);
|
|
|
|
- }
|
|
|
|
|
|
+ mq->drop_on_remove = reset;
|
|
|
|
+ smp_mb();
|
|
|
|
+
|
|
|
|
+ qmi_rmnet_flow_control(dev, i, 1);
|
|
|
|
+ if (dfc_mode == DFC_MODE_SA) {
|
|
|
|
+ j = i + ACK_MQ_OFFSET;
|
|
|
|
+ qmi_rmnet_flow_control(dev, j, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -404,6 +387,8 @@ static void __qmi_rmnet_update_mq(struct net_device *dev,
|
|
if (!mq->bearer) {
|
|
if (!mq->bearer) {
|
|
mq->bearer = bearer;
|
|
mq->bearer = bearer;
|
|
mq->is_ll_ch = bearer->ch_switch.current_ch;
|
|
mq->is_ll_ch = bearer->ch_switch.current_ch;
|
|
|
|
+ mq->drop_on_remove = false;
|
|
|
|
+ smp_mb();
|
|
|
|
|
|
if (dfc_mode == DFC_MODE_SA) {
|
|
if (dfc_mode == DFC_MODE_SA) {
|
|
bearer->mq_idx = itm->mq_idx;
|
|
bearer->mq_idx = itm->mq_idx;
|
|
@@ -412,12 +397,15 @@ static void __qmi_rmnet_update_mq(struct net_device *dev,
|
|
bearer->mq_idx = itm->mq_idx;
|
|
bearer->mq_idx = itm->mq_idx;
|
|
}
|
|
}
|
|
|
|
|
|
- qmi_rmnet_flow_control(dev, itm->mq_idx,
|
|
|
|
- bearer->grant_size > 0 ? 1 : 0);
|
|
|
|
-
|
|
|
|
|
|
+ /* Always enable flow for the newly associated bearer */
|
|
|
|
+ if (!bearer->grant_size) {
|
|
|
|
+ bearer->grant_size = DEFAULT_GRANT;
|
|
|
|
+ bearer->grant_thresh =
|
|
|
|
+ qmi_rmnet_grant_per(DEFAULT_GRANT);
|
|
|
|
+ }
|
|
|
|
+ qmi_rmnet_flow_control(dev, itm->mq_idx, 1);
|
|
if (dfc_mode == DFC_MODE_SA)
|
|
if (dfc_mode == DFC_MODE_SA)
|
|
- qmi_rmnet_flow_control(dev, bearer->ack_mq_idx,
|
|
|
|
- bearer->grant_size > 0 ? 1 : 0);
|
|
|
|
|
|
+ qmi_rmnet_flow_control(dev, bearer->ack_mq_idx, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -634,7 +622,6 @@ qmi_rmnet_setup_client(void *port, struct qmi_info *qmi, struct tcmsg *tcm)
|
|
if (!qmi)
|
|
if (!qmi)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
- qmi->ws = wakeup_source_register(NULL, "RMNET_DFC");
|
|
|
|
rmnet_init_qmi_pt(port, qmi);
|
|
rmnet_init_qmi_pt(port, qmi);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -684,7 +671,6 @@ __qmi_rmnet_delete_client(void *port, struct qmi_info *qmi, int idx)
|
|
|
|
|
|
if (!qmi_rmnet_has_client(qmi) && !qmi_rmnet_has_pending(qmi)) {
|
|
if (!qmi_rmnet_has_client(qmi) && !qmi_rmnet_has_pending(qmi)) {
|
|
rmnet_reset_qmi_pt(port);
|
|
rmnet_reset_qmi_pt(port);
|
|
- wakeup_source_unregister(qmi->ws);
|
|
|
|
kfree(qmi);
|
|
kfree(qmi);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -757,7 +743,6 @@ int qmi_rmnet_change_link(struct net_device *dev, void *port, void *tcm_pt,
|
|
!qmi_rmnet_has_client(qmi) &&
|
|
!qmi_rmnet_has_client(qmi) &&
|
|
!qmi_rmnet_has_pending(qmi)) {
|
|
!qmi_rmnet_has_pending(qmi)) {
|
|
rmnet_reset_qmi_pt(port);
|
|
rmnet_reset_qmi_pt(port);
|
|
- wakeup_source_unregister(qmi->ws);
|
|
|
|
kfree(qmi);
|
|
kfree(qmi);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -955,8 +940,8 @@ void qmi_rmnet_prepare_ps_bearers(struct net_device *dev, u8 *num_bearers,
|
|
EXPORT_SYMBOL(qmi_rmnet_prepare_ps_bearers);
|
|
EXPORT_SYMBOL(qmi_rmnet_prepare_ps_bearers);
|
|
|
|
|
|
#ifdef CONFIG_QTI_QMI_DFC
|
|
#ifdef CONFIG_QTI_QMI_DFC
|
|
-bool qmi_rmnet_flow_is_low_latency(struct net_device *dev,
|
|
|
|
- struct sk_buff *skb)
|
|
|
|
|
|
+bool qmi_rmnet_get_flow_state(struct net_device *dev, struct sk_buff *skb,
|
|
|
|
+ bool *drop, bool *is_low_latency)
|
|
{
|
|
{
|
|
struct qos_info *qos = rmnet_get_qos_pt(dev);
|
|
struct qos_info *qos = rmnet_get_qos_pt(dev);
|
|
int txq = skb->queue_mapping;
|
|
int txq = skb->queue_mapping;
|
|
@@ -967,9 +952,15 @@ bool qmi_rmnet_flow_is_low_latency(struct net_device *dev,
|
|
if (unlikely(!qos || txq >= MAX_MQ_NUM))
|
|
if (unlikely(!qos || txq >= MAX_MQ_NUM))
|
|
return false;
|
|
return false;
|
|
|
|
|
|
- return qos->mq[txq].is_ll_ch;
|
|
|
|
|
|
+ /* If the bearer is gone, packets may need to be dropped */
|
|
|
|
+ *drop = (txq != DEFAULT_MQ_NUM && !READ_ONCE(qos->mq[txq].bearer) &&
|
|
|
|
+ READ_ONCE(qos->mq[txq].drop_on_remove));
|
|
|
|
+
|
|
|
|
+ *is_low_latency = READ_ONCE(qos->mq[txq].is_ll_ch);
|
|
|
|
+
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(qmi_rmnet_flow_is_low_latency);
|
|
|
|
|
|
+EXPORT_SYMBOL(qmi_rmnet_get_flow_state);
|
|
|
|
|
|
void qmi_rmnet_burst_fc_check(struct net_device *dev,
|
|
void qmi_rmnet_burst_fc_check(struct net_device *dev,
|
|
int ip_type, u32 mark, unsigned int len)
|
|
int ip_type, u32 mark, unsigned int len)
|
|
@@ -1152,7 +1143,6 @@ static u8 ps_bearer_id[32];
|
|
|
|
|
|
struct rmnet_powersave_work {
|
|
struct rmnet_powersave_work {
|
|
struct delayed_work work;
|
|
struct delayed_work work;
|
|
- struct alarm atimer;
|
|
|
|
void *port;
|
|
void *port;
|
|
u64 old_rx_pkts;
|
|
u64 old_rx_pkts;
|
|
u64 old_tx_pkts;
|
|
u64 old_tx_pkts;
|
|
@@ -1209,7 +1199,8 @@ done:
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(qmi_rmnet_ps_ind_deregister);
|
|
EXPORT_SYMBOL(qmi_rmnet_ps_ind_deregister);
|
|
|
|
|
|
-int qmi_rmnet_set_powersave_mode(void *port, uint8_t enable)
|
|
|
|
|
|
+int qmi_rmnet_set_powersave_mode(void *port, uint8_t enable, u8 num_bearers,
|
|
|
|
+ u8 *bearer_id)
|
|
{
|
|
{
|
|
int rc = -EINVAL;
|
|
int rc = -EINVAL;
|
|
struct qmi_info *qmi = (struct qmi_info *)rmnet_get_qmi_pt(port);
|
|
struct qmi_info *qmi = (struct qmi_info *)rmnet_get_qmi_pt(port);
|
|
@@ -1217,7 +1208,8 @@ int qmi_rmnet_set_powersave_mode(void *port, uint8_t enable)
|
|
if (!qmi || !qmi->wda_client)
|
|
if (!qmi || !qmi->wda_client)
|
|
return rc;
|
|
return rc;
|
|
|
|
|
|
- rc = wda_set_powersave_mode(qmi->wda_client, enable);
|
|
|
|
|
|
+ rc = wda_set_powersave_mode(qmi->wda_client, enable, num_bearers,
|
|
|
|
+ bearer_id);
|
|
if (rc < 0) {
|
|
if (rc < 0) {
|
|
pr_err("%s() failed set powersave mode[%u], err=%d\n",
|
|
pr_err("%s() failed set powersave mode[%u], err=%d\n",
|
|
__func__, enable, rc);
|
|
__func__, enable, rc);
|
|
@@ -1236,32 +1228,6 @@ static void qmi_rmnet_work_restart(void *port)
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
}
|
|
}
|
|
|
|
|
|
-static enum alarmtimer_restart qmi_rmnet_work_alarm(struct alarm *atimer,
|
|
|
|
- ktime_t now)
|
|
|
|
-{
|
|
|
|
- struct rmnet_powersave_work *real_work;
|
|
|
|
-
|
|
|
|
- real_work = container_of(atimer, struct rmnet_powersave_work, atimer);
|
|
|
|
- qmi_rmnet_work_restart(real_work->port);
|
|
|
|
- return ALARMTIMER_NORESTART;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void dfc_wakelock_acquire(struct qmi_info *qmi)
|
|
|
|
-{
|
|
|
|
- if (qmi && !qmi->wakelock_active) {
|
|
|
|
- __pm_stay_awake(qmi->ws);
|
|
|
|
- qmi->wakelock_active = true;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void dfc_wakelock_release(struct qmi_info *qmi)
|
|
|
|
-{
|
|
|
|
- if (qmi && qmi->wakelock_active) {
|
|
|
|
- __pm_relax(qmi->ws);
|
|
|
|
- qmi->wakelock_active = false;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static void qmi_rmnet_check_stats(struct work_struct *work)
|
|
static void qmi_rmnet_check_stats(struct work_struct *work)
|
|
{
|
|
{
|
|
struct rmnet_powersave_work *real_work;
|
|
struct rmnet_powersave_work *real_work;
|
|
@@ -1269,7 +1235,6 @@ static void qmi_rmnet_check_stats(struct work_struct *work)
|
|
u64 rxd, txd;
|
|
u64 rxd, txd;
|
|
u64 rx, tx;
|
|
u64 rx, tx;
|
|
bool dl_msg_active;
|
|
bool dl_msg_active;
|
|
- bool use_alarm_timer = true;
|
|
|
|
|
|
|
|
real_work = container_of(to_delayed_work(work),
|
|
real_work = container_of(to_delayed_work(work),
|
|
struct rmnet_powersave_work, work);
|
|
struct rmnet_powersave_work, work);
|
|
@@ -1281,8 +1246,6 @@ static void qmi_rmnet_check_stats(struct work_struct *work)
|
|
if (unlikely(!qmi))
|
|
if (unlikely(!qmi))
|
|
return;
|
|
return;
|
|
|
|
|
|
- dfc_wakelock_release(qmi);
|
|
|
|
-
|
|
|
|
rmnet_get_packets(real_work->port, &rx, &tx);
|
|
rmnet_get_packets(real_work->port, &rx, &tx);
|
|
rxd = rx - real_work->old_rx_pkts;
|
|
rxd = rx - real_work->old_rx_pkts;
|
|
txd = tx - real_work->old_tx_pkts;
|
|
txd = tx - real_work->old_tx_pkts;
|
|
@@ -1298,7 +1261,8 @@ static void qmi_rmnet_check_stats(struct work_struct *work)
|
|
qmi->ps_ignore_grant = false;
|
|
qmi->ps_ignore_grant = false;
|
|
|
|
|
|
/* Register to get QMI DFC and DL marker */
|
|
/* Register to get QMI DFC and DL marker */
|
|
- if (qmi_rmnet_set_powersave_mode(real_work->port, 0) < 0)
|
|
|
|
|
|
+ if (qmi_rmnet_set_powersave_mode(real_work->port, 0,
|
|
|
|
+ 0, NULL) < 0)
|
|
goto end;
|
|
goto end;
|
|
|
|
|
|
qmi->ps_enabled = false;
|
|
qmi->ps_enabled = false;
|
|
@@ -1317,13 +1281,12 @@ static void qmi_rmnet_check_stats(struct work_struct *work)
|
|
* (likely in RLF), no need to enter powersave
|
|
* (likely in RLF), no need to enter powersave
|
|
*/
|
|
*/
|
|
if (!dl_msg_active &&
|
|
if (!dl_msg_active &&
|
|
- !rmnet_all_flows_enabled(real_work->port)) {
|
|
|
|
- use_alarm_timer = false;
|
|
|
|
|
|
+ !rmnet_all_flows_enabled(real_work->port))
|
|
goto end;
|
|
goto end;
|
|
- }
|
|
|
|
|
|
|
|
/* Deregister to suppress QMI DFC and DL marker */
|
|
/* Deregister to suppress QMI DFC and DL marker */
|
|
- if (qmi_rmnet_set_powersave_mode(real_work->port, 1) < 0)
|
|
|
|
|
|
+ if (qmi_rmnet_set_powersave_mode(real_work->port, 1,
|
|
|
|
+ 0, NULL) < 0)
|
|
goto end;
|
|
goto end;
|
|
|
|
|
|
qmi->ps_enabled = true;
|
|
qmi->ps_enabled = true;
|
|
@@ -1344,21 +1307,9 @@ static void qmi_rmnet_check_stats(struct work_struct *work)
|
|
}
|
|
}
|
|
end:
|
|
end:
|
|
rcu_read_lock();
|
|
rcu_read_lock();
|
|
- if (!rmnet_work_quit) {
|
|
|
|
- if (use_alarm_timer) {
|
|
|
|
- /* Suspend will fail and get delayed for 2s if
|
|
|
|
- * alarmtimer expires within 2s. Hold a wakelock
|
|
|
|
- * for the actual timer duration to prevent suspend
|
|
|
|
- */
|
|
|
|
- if (PS_INTERVAL_MS < 2000)
|
|
|
|
- dfc_wakelock_acquire(qmi);
|
|
|
|
- alarm_start_relative(&real_work->atimer,
|
|
|
|
- PS_INTERVAL_KT);
|
|
|
|
- } else {
|
|
|
|
- queue_delayed_work(rmnet_ps_wq, &real_work->work,
|
|
|
|
- PS_INTERVAL_JF);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ if (!rmnet_work_quit)
|
|
|
|
+ queue_delayed_work(rmnet_ps_wq, &real_work->work,
|
|
|
|
+ PS_INTERVAL_JF);
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1369,6 +1320,7 @@ static void qmi_rmnet_check_stats_2(struct work_struct *work)
|
|
u64 rxd, txd;
|
|
u64 rxd, txd;
|
|
u64 rx, tx;
|
|
u64 rx, tx;
|
|
u8 num_bearers;
|
|
u8 num_bearers;
|
|
|
|
+ int rc;
|
|
|
|
|
|
real_work = container_of(to_delayed_work(work),
|
|
real_work = container_of(to_delayed_work(work),
|
|
struct rmnet_powersave_work, work);
|
|
struct rmnet_powersave_work, work);
|
|
@@ -1380,9 +1332,6 @@ static void qmi_rmnet_check_stats_2(struct work_struct *work)
|
|
if (unlikely(!qmi))
|
|
if (unlikely(!qmi))
|
|
return;
|
|
return;
|
|
|
|
|
|
- if (PS_INTERVAL_MS < 2000)
|
|
|
|
- dfc_wakelock_acquire(qmi);
|
|
|
|
-
|
|
|
|
rmnet_get_packets(real_work->port, &rx, &tx);
|
|
rmnet_get_packets(real_work->port, &rx, &tx);
|
|
rxd = rx - real_work->old_rx_pkts;
|
|
rxd = rx - real_work->old_rx_pkts;
|
|
txd = tx - real_work->old_tx_pkts;
|
|
txd = tx - real_work->old_tx_pkts;
|
|
@@ -1395,7 +1344,12 @@ static void qmi_rmnet_check_stats_2(struct work_struct *work)
|
|
qmi->ps_ignore_grant = false;
|
|
qmi->ps_ignore_grant = false;
|
|
|
|
|
|
/* Out of powersave */
|
|
/* Out of powersave */
|
|
- if (dfc_qmap_set_powersave(0, 0, NULL))
|
|
|
|
|
|
+ if (dfc_qmap)
|
|
|
|
+ rc = dfc_qmap_set_powersave(0, 0, NULL);
|
|
|
|
+ else
|
|
|
|
+ rc = qmi_rmnet_set_powersave_mode(real_work->port, 0,
|
|
|
|
+ 0, NULL);
|
|
|
|
+ if (rc)
|
|
goto end;
|
|
goto end;
|
|
|
|
|
|
qmi->ps_enabled = false;
|
|
qmi->ps_enabled = false;
|
|
@@ -1419,20 +1373,22 @@ static void qmi_rmnet_check_stats_2(struct work_struct *work)
|
|
ps_bearer_id);
|
|
ps_bearer_id);
|
|
|
|
|
|
/* Enter powersave */
|
|
/* Enter powersave */
|
|
- dfc_qmap_set_powersave(1, num_bearers, ps_bearer_id);
|
|
|
|
|
|
+ if (dfc_qmap)
|
|
|
|
+ dfc_qmap_set_powersave(1, num_bearers, ps_bearer_id);
|
|
|
|
+ else
|
|
|
|
+ qmi_rmnet_set_powersave_mode(real_work->port, 1,
|
|
|
|
+ num_bearers, ps_bearer_id);
|
|
|
|
|
|
if (rmnet_get_powersave_notif(real_work->port))
|
|
if (rmnet_get_powersave_notif(real_work->port))
|
|
qmi_rmnet_ps_on_notify(real_work->port);
|
|
qmi_rmnet_ps_on_notify(real_work->port);
|
|
|
|
|
|
- dfc_wakelock_release(qmi);
|
|
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
end:
|
|
end:
|
|
rcu_read_lock();
|
|
rcu_read_lock();
|
|
if (!rmnet_work_quit)
|
|
if (!rmnet_work_quit)
|
|
- alarm_start_relative(&real_work->atimer, PS_INTERVAL_KT);
|
|
|
|
- else
|
|
|
|
- dfc_wakelock_release(qmi);
|
|
|
|
|
|
+ queue_delayed_work(rmnet_ps_wq, &real_work->work,
|
|
|
|
+ PS_INTERVAL_JF);
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1468,13 +1424,12 @@ void qmi_rmnet_work_init(void *port)
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- if (dfc_qmap && dfc_ps_ext)
|
|
|
|
- INIT_DEFERRABLE_WORK(&rmnet_work->work,
|
|
|
|
|
|
+ if (dfc_ps_ext)
|
|
|
|
+ INIT_DELAYED_WORK(&rmnet_work->work,
|
|
qmi_rmnet_check_stats_2);
|
|
qmi_rmnet_check_stats_2);
|
|
else
|
|
else
|
|
- INIT_DEFERRABLE_WORK(&rmnet_work->work, qmi_rmnet_check_stats);
|
|
|
|
|
|
+ INIT_DELAYED_WORK(&rmnet_work->work, qmi_rmnet_check_stats);
|
|
|
|
|
|
- alarm_init(&rmnet_work->atimer, ALARM_BOOTTIME, qmi_rmnet_work_alarm);
|
|
|
|
rmnet_work->port = port;
|
|
rmnet_work->port = port;
|
|
rmnet_get_packets(rmnet_work->port, &rmnet_work->old_rx_pkts,
|
|
rmnet_get_packets(rmnet_work->port, &rmnet_work->old_rx_pkts,
|
|
&rmnet_work->old_tx_pkts);
|
|
&rmnet_work->old_tx_pkts);
|
|
@@ -1509,14 +1464,12 @@ void qmi_rmnet_work_exit(void *port)
|
|
synchronize_rcu();
|
|
synchronize_rcu();
|
|
|
|
|
|
rmnet_work_inited = false;
|
|
rmnet_work_inited = false;
|
|
- alarm_cancel(&rmnet_work->atimer);
|
|
|
|
cancel_delayed_work_sync(&rmnet_work->work);
|
|
cancel_delayed_work_sync(&rmnet_work->work);
|
|
destroy_workqueue(rmnet_ps_wq);
|
|
destroy_workqueue(rmnet_ps_wq);
|
|
qmi_rmnet_work_set_active(port, 0);
|
|
qmi_rmnet_work_set_active(port, 0);
|
|
rmnet_ps_wq = NULL;
|
|
rmnet_ps_wq = NULL;
|
|
kfree(rmnet_work);
|
|
kfree(rmnet_work);
|
|
rmnet_work = NULL;
|
|
rmnet_work = NULL;
|
|
- dfc_wakelock_release((struct qmi_info *)rmnet_get_qmi_pt(port));
|
|
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(qmi_rmnet_work_exit);
|
|
EXPORT_SYMBOL(qmi_rmnet_work_exit);
|
|
|
|
|