Skip to content

Commit 3ed8e1c

Browse files
Badhri Jagan Sridharangregkh
Badhri Jagan Sridharan
authored andcommitted
usb: typec: tcpm: Migrate workqueue to RT priority for processing events
"tReceiverResponse 15 ms Section 6.6.2 The receiver of a Message requiring a response Shall respond within tReceiverResponse in order to ensure that the sender’s SenderResponseTimer does not expire." When the cpu complex is busy running other lower priority work items, TCPM's work queue sometimes does not get scheduled on time to meet the above requirement from the spec. Moving to kthread_work apis to run with real time priority. Further, as observed in 1ff6882, moving to hrtimers to overcome scheduling latency while scheduling the delayed work. TCPM has three work streams: 1. tcpm_state_machine 2. vdm_state_machine 3. event_work tcpm_state_machine and vdm_state_machine both schedule work in future i.e. delayed. Hence each of them have a corresponding hrtimer, tcpm_state_machine_timer & vdm_state_machine_timer. When work is queued right away kthread_queue_work is used. Else, the relevant timer is programmed and made to queue the kthread_work upon timer expiry. kthread_create_worker only creates one kthread worker thread, hence single threadedness of workqueue is retained. Signed-off-by: Badhri Jagan Sridharan <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Reviewed-by: Heikki Krogerus <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent aefc66a commit 3ed8e1c

File tree

1 file changed

+87
-44
lines changed

1 file changed

+87
-44
lines changed

drivers/usb/typec/tcpm/tcpm.c

Lines changed: 87 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
#include <linux/completion.h>
99
#include <linux/debugfs.h>
1010
#include <linux/device.h>
11+
#include <linux/hrtimer.h>
1112
#include <linux/jiffies.h>
1213
#include <linux/kernel.h>
14+
#include <linux/kthread.h>
1315
#include <linux/module.h>
1416
#include <linux/mutex.h>
1517
#include <linux/power_supply.h>
@@ -28,7 +30,8 @@
2830
#include <linux/usb/role.h>
2931
#include <linux/usb/tcpm.h>
3032
#include <linux/usb/typec_altmode.h>
31-
#include <linux/workqueue.h>
33+
34+
#include <uapi/linux/sched/types.h>
3235

3336
#define FOREACH_STATE(S) \
3437
S(INVALID_STATE), \
@@ -203,7 +206,7 @@ struct tcpm_port {
203206
struct device *dev;
204207

205208
struct mutex lock; /* tcpm state machine lock */
206-
struct workqueue_struct *wq;
209+
struct kthread_worker *wq;
207210

208211
struct typec_capability typec_caps;
209212
struct typec_port *typec_port;
@@ -247,15 +250,17 @@ struct tcpm_port {
247250
enum tcpm_state prev_state;
248251
enum tcpm_state state;
249252
enum tcpm_state delayed_state;
250-
unsigned long delayed_runtime;
253+
ktime_t delayed_runtime;
251254
unsigned long delay_ms;
252255

253256
spinlock_t pd_event_lock;
254257
u32 pd_events;
255258

256-
struct work_struct event_work;
257-
struct delayed_work state_machine;
258-
struct delayed_work vdm_state_machine;
259+
struct kthread_work event_work;
260+
struct hrtimer state_machine_timer;
261+
struct kthread_work state_machine;
262+
struct hrtimer vdm_state_machine_timer;
263+
struct kthread_work vdm_state_machine;
259264
bool state_machine_running;
260265

261266
struct completion tx_complete;
@@ -340,7 +345,7 @@ struct tcpm_port {
340345
};
341346

342347
struct pd_rx_event {
343-
struct work_struct work;
348+
struct kthread_work work;
344349
struct tcpm_port *port;
345350
struct pd_message msg;
346351
};
@@ -914,6 +919,27 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port)
914919
return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
915920
}
916921

922+
static void mod_tcpm_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
923+
{
924+
if (delay_ms) {
925+
hrtimer_start(&port->state_machine_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
926+
} else {
927+
hrtimer_cancel(&port->state_machine_timer);
928+
kthread_queue_work(port->wq, &port->state_machine);
929+
}
930+
}
931+
932+
static void mod_vdm_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
933+
{
934+
if (delay_ms) {
935+
hrtimer_start(&port->vdm_state_machine_timer, ms_to_ktime(delay_ms),
936+
HRTIMER_MODE_REL);
937+
} else {
938+
hrtimer_cancel(&port->vdm_state_machine_timer);
939+
kthread_queue_work(port->wq, &port->vdm_state_machine);
940+
}
941+
}
942+
917943
static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state,
918944
unsigned int delay_ms)
919945
{
@@ -922,9 +948,8 @@ static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state,
922948
tcpm_states[port->state], tcpm_states[state],
923949
delay_ms);
924950
port->delayed_state = state;
925-
mod_delayed_work(port->wq, &port->state_machine,
926-
msecs_to_jiffies(delay_ms));
927-
port->delayed_runtime = jiffies + msecs_to_jiffies(delay_ms);
951+
mod_tcpm_delayed_work(port, delay_ms);
952+
port->delayed_runtime = ktime_add(ktime_get(), ms_to_ktime(delay_ms));
928953
port->delay_ms = delay_ms;
929954
} else {
930955
tcpm_log(port, "state change %s -> %s",
@@ -939,7 +964,7 @@ static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state,
939964
* machine.
940965
*/
941966
if (!port->state_machine_running)
942-
mod_delayed_work(port->wq, &port->state_machine, 0);
967+
mod_tcpm_delayed_work(port, 0);
943968
}
944969
}
945970

@@ -960,7 +985,7 @@ static void tcpm_queue_message(struct tcpm_port *port,
960985
enum pd_msg_request message)
961986
{
962987
port->queued_message = message;
963-
mod_delayed_work(port->wq, &port->state_machine, 0);
988+
mod_tcpm_delayed_work(port, 0);
964989
}
965990

966991
/*
@@ -981,7 +1006,7 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header,
9811006
port->vdm_retries = 0;
9821007
port->vdm_state = VDM_STATE_READY;
9831008

984-
mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
1009+
mod_vdm_delayed_work(port, 0);
9851010
}
9861011

9871012
static void tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header,
@@ -1244,8 +1269,7 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
12441269
port->vdm_state = VDM_STATE_WAIT_RSP_BUSY;
12451270
port->vdo_retry = (p[0] & ~VDO_CMDT_MASK) |
12461271
CMDT_INIT;
1247-
mod_delayed_work(port->wq, &port->vdm_state_machine,
1248-
msecs_to_jiffies(PD_T_VDM_BUSY));
1272+
mod_vdm_delayed_work(port, PD_T_VDM_BUSY);
12491273
return;
12501274
}
12511275
port->vdm_state = VDM_STATE_DONE;
@@ -1390,8 +1414,7 @@ static void vdm_run_state_machine(struct tcpm_port *port)
13901414
port->vdm_retries = 0;
13911415
port->vdm_state = VDM_STATE_BUSY;
13921416
timeout = vdm_ready_timeout(port->vdo_data[0]);
1393-
mod_delayed_work(port->wq, &port->vdm_state_machine,
1394-
timeout);
1417+
mod_vdm_delayed_work(port, timeout);
13951418
}
13961419
break;
13971420
case VDM_STATE_WAIT_RSP_BUSY:
@@ -1420,10 +1443,9 @@ static void vdm_run_state_machine(struct tcpm_port *port)
14201443
}
14211444
}
14221445

1423-
static void vdm_state_machine_work(struct work_struct *work)
1446+
static void vdm_state_machine_work(struct kthread_work *work)
14241447
{
1425-
struct tcpm_port *port = container_of(work, struct tcpm_port,
1426-
vdm_state_machine.work);
1448+
struct tcpm_port *port = container_of(work, struct tcpm_port, vdm_state_machine);
14271449
enum vdm_states prev_state;
14281450

14291451
mutex_lock(&port->lock);
@@ -1591,6 +1613,7 @@ static int tcpm_altmode_vdm(struct typec_altmode *altmode,
15911613
struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
15921614

15931615
tcpm_queue_vdm_unlocked(port, header, data, count - 1);
1616+
15941617
return 0;
15951618
}
15961619

@@ -2005,7 +2028,7 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port,
20052028
}
20062029
}
20072030

2008-
static void tcpm_pd_rx_handler(struct work_struct *work)
2031+
static void tcpm_pd_rx_handler(struct kthread_work *work)
20092032
{
20102033
struct pd_rx_event *event = container_of(work,
20112034
struct pd_rx_event, work);
@@ -2067,10 +2090,10 @@ void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg)
20672090
if (!event)
20682091
return;
20692092

2070-
INIT_WORK(&event->work, tcpm_pd_rx_handler);
2093+
kthread_init_work(&event->work, tcpm_pd_rx_handler);
20712094
event->port = port;
20722095
memcpy(&event->msg, msg, sizeof(*msg));
2073-
queue_work(port->wq, &event->work);
2096+
kthread_queue_work(port->wq, &event->work);
20742097
}
20752098
EXPORT_SYMBOL_GPL(tcpm_pd_receive);
20762099

@@ -2123,9 +2146,9 @@ static bool tcpm_send_queued_message(struct tcpm_port *port)
21232146
} while (port->queued_message != PD_MSG_NONE);
21242147

21252148
if (port->delayed_state != INVALID_STATE) {
2126-
if (time_is_after_jiffies(port->delayed_runtime)) {
2127-
mod_delayed_work(port->wq, &port->state_machine,
2128-
port->delayed_runtime - jiffies);
2149+
if (ktime_after(port->delayed_runtime, ktime_get())) {
2150+
mod_tcpm_delayed_work(port, ktime_to_ms(ktime_sub(port->delayed_runtime,
2151+
ktime_get())));
21292152
return true;
21302153
}
21312154
port->delayed_state = INVALID_STATE;
@@ -3258,10 +3281,9 @@ static void run_state_machine(struct tcpm_port *port)
32583281
case SNK_DISCOVERY_DEBOUNCE_DONE:
32593282
if (!tcpm_port_is_disconnected(port) &&
32603283
tcpm_port_is_sink(port) &&
3261-
time_is_after_jiffies(port->delayed_runtime)) {
3284+
ktime_after(port->delayed_runtime, ktime_get())) {
32623285
tcpm_set_state(port, SNK_DISCOVERY,
3263-
jiffies_to_msecs(port->delayed_runtime -
3264-
jiffies));
3286+
ktime_to_ms(ktime_sub(port->delayed_runtime, ktime_get())));
32653287
break;
32663288
}
32673289
tcpm_set_state(port, unattached_state(port), 0);
@@ -3656,10 +3678,9 @@ static void run_state_machine(struct tcpm_port *port)
36563678
}
36573679
}
36583680

3659-
static void tcpm_state_machine_work(struct work_struct *work)
3681+
static void tcpm_state_machine_work(struct kthread_work *work)
36603682
{
3661-
struct tcpm_port *port = container_of(work, struct tcpm_port,
3662-
state_machine.work);
3683+
struct tcpm_port *port = container_of(work, struct tcpm_port, state_machine);
36633684
enum tcpm_state prev_state;
36643685

36653686
mutex_lock(&port->lock);
@@ -4019,7 +4040,7 @@ static void _tcpm_pd_hard_reset(struct tcpm_port *port)
40194040
0);
40204041
}
40214042

4022-
static void tcpm_pd_event_handler(struct work_struct *work)
4043+
static void tcpm_pd_event_handler(struct kthread_work *work)
40234044
{
40244045
struct tcpm_port *port = container_of(work, struct tcpm_port,
40254046
event_work);
@@ -4060,7 +4081,7 @@ void tcpm_cc_change(struct tcpm_port *port)
40604081
spin_lock(&port->pd_event_lock);
40614082
port->pd_events |= TCPM_CC_EVENT;
40624083
spin_unlock(&port->pd_event_lock);
4063-
queue_work(port->wq, &port->event_work);
4084+
kthread_queue_work(port->wq, &port->event_work);
40644085
}
40654086
EXPORT_SYMBOL_GPL(tcpm_cc_change);
40664087

@@ -4069,7 +4090,7 @@ void tcpm_vbus_change(struct tcpm_port *port)
40694090
spin_lock(&port->pd_event_lock);
40704091
port->pd_events |= TCPM_VBUS_EVENT;
40714092
spin_unlock(&port->pd_event_lock);
4072-
queue_work(port->wq, &port->event_work);
4093+
kthread_queue_work(port->wq, &port->event_work);
40734094
}
40744095
EXPORT_SYMBOL_GPL(tcpm_vbus_change);
40754096

@@ -4078,7 +4099,7 @@ void tcpm_pd_hard_reset(struct tcpm_port *port)
40784099
spin_lock(&port->pd_event_lock);
40794100
port->pd_events = TCPM_RESET_EVENT;
40804101
spin_unlock(&port->pd_event_lock);
4081-
queue_work(port->wq, &port->event_work);
4102+
kthread_queue_work(port->wq, &port->event_work);
40824103
}
40834104
EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset);
40844105

@@ -4786,6 +4807,22 @@ static int devm_tcpm_psy_register(struct tcpm_port *port)
47864807
return PTR_ERR_OR_ZERO(port->psy);
47874808
}
47884809

4810+
static enum hrtimer_restart state_machine_timer_handler(struct hrtimer *timer)
4811+
{
4812+
struct tcpm_port *port = container_of(timer, struct tcpm_port, state_machine_timer);
4813+
4814+
kthread_queue_work(port->wq, &port->state_machine);
4815+
return HRTIMER_NORESTART;
4816+
}
4817+
4818+
static enum hrtimer_restart vdm_state_machine_timer_handler(struct hrtimer *timer)
4819+
{
4820+
struct tcpm_port *port = container_of(timer, struct tcpm_port, vdm_state_machine_timer);
4821+
4822+
kthread_queue_work(port->wq, &port->vdm_state_machine);
4823+
return HRTIMER_NORESTART;
4824+
}
4825+
47894826
struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
47904827
{
47914828
struct tcpm_port *port;
@@ -4807,12 +4844,18 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
48074844
mutex_init(&port->lock);
48084845
mutex_init(&port->swap_lock);
48094846

4810-
port->wq = create_singlethread_workqueue(dev_name(dev));
4811-
if (!port->wq)
4812-
return ERR_PTR(-ENOMEM);
4813-
INIT_DELAYED_WORK(&port->state_machine, tcpm_state_machine_work);
4814-
INIT_DELAYED_WORK(&port->vdm_state_machine, vdm_state_machine_work);
4815-
INIT_WORK(&port->event_work, tcpm_pd_event_handler);
4847+
port->wq = kthread_create_worker(0, dev_name(dev));
4848+
if (IS_ERR(port->wq))
4849+
return ERR_CAST(port->wq);
4850+
sched_set_fifo(port->wq->task);
4851+
4852+
kthread_init_work(&port->state_machine, tcpm_state_machine_work);
4853+
kthread_init_work(&port->vdm_state_machine, vdm_state_machine_work);
4854+
kthread_init_work(&port->event_work, tcpm_pd_event_handler);
4855+
hrtimer_init(&port->state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
4856+
port->state_machine_timer.function = state_machine_timer_handler;
4857+
hrtimer_init(&port->vdm_state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
4858+
port->vdm_state_machine_timer.function = vdm_state_machine_timer_handler;
48164859

48174860
spin_lock_init(&port->pd_event_lock);
48184861

@@ -4864,7 +4907,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
48644907
usb_role_switch_put(port->role_sw);
48654908
out_destroy_wq:
48664909
tcpm_debugfs_exit(port);
4867-
destroy_workqueue(port->wq);
4910+
kthread_destroy_worker(port->wq);
48684911
return ERR_PTR(err);
48694912
}
48704913
EXPORT_SYMBOL_GPL(tcpm_register_port);
@@ -4879,7 +4922,7 @@ void tcpm_unregister_port(struct tcpm_port *port)
48794922
typec_unregister_port(port->typec_port);
48804923
usb_role_switch_put(port->role_sw);
48814924
tcpm_debugfs_exit(port);
4882-
destroy_workqueue(port->wq);
4925+
kthread_destroy_worker(port->wq);
48834926
}
48844927
EXPORT_SYMBOL_GPL(tcpm_unregister_port);
48854928

0 commit comments

Comments
 (0)