Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ptp: introduce ptp auxiliary worker

Many PTP drivers required to perform some asynchronous or periodic work,
like periodically handling PHC counter overflow or handle delayed timestamp
for RX/TX network packets. In most of the cases, such work is implemented
using workqueues. Unfortunately, Kernel workqueues might introduce
significant delay in work scheduling under high system load and on -RT,
which could cause misbehavior of PTP drivers due to internal counter
overflow, for example, and there is no way to tune its execution policy and
priority manuallly.

Hence, The kthread_worker can be used insted of workqueues, as it create
separte named kthread for each worker and its its execution policy and
priority can be configured using chrt tool.

This prblem was reported for two drivers TI CPSW CPTS and dp83640, so
instead of modifying each of these driver it was proposed to add PTP
auxiliary worker to the PHC subsystem.

The patch adds PTP auxiliary worker in PHC subsystem using kthread_worker
and kthread_delayed_work and introduces two new PHC subsystem APIs:

- long (*do_aux_work)(struct ptp_clock_info *ptp) callback in
ptp_clock_info structure, which driver should assign if it require to
perform asynchronous or periodic work. Driver should return the delay of
the PTP next auxiliary work scheduling time (>=0) or negative value in case
further scheduling is not required.

- int ptp_schedule_worker(struct ptp_clock *ptp, unsigned long delay) which
allows schedule PTP auxiliary work.

The name of kthread_worker thread corresponds PTP PHC device name "ptp%d".

Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Grygorii Strashko and committed by
David S. Miller
d9535cb7 bc78d646

+65
+42
drivers/ptp/ptp_clock.c
··· 28 28 #include <linux/slab.h> 29 29 #include <linux/syscalls.h> 30 30 #include <linux/uaccess.h> 31 + #include <uapi/linux/sched/types.h> 31 32 32 33 #include "ptp_private.h" 33 34 ··· 185 184 kfree(ptp); 186 185 } 187 186 187 + static void ptp_aux_kworker(struct kthread_work *work) 188 + { 189 + struct ptp_clock *ptp = container_of(work, struct ptp_clock, 190 + aux_work.work); 191 + struct ptp_clock_info *info = ptp->info; 192 + long delay; 193 + 194 + delay = info->do_aux_work(info); 195 + 196 + if (delay >= 0) 197 + kthread_queue_delayed_work(ptp->kworker, &ptp->aux_work, delay); 198 + } 199 + 188 200 /* public interface */ 189 201 190 202 struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, ··· 230 216 mutex_init(&ptp->tsevq_mux); 231 217 mutex_init(&ptp->pincfg_mux); 232 218 init_waitqueue_head(&ptp->tsev_wq); 219 + 220 + if (ptp->info->do_aux_work) { 221 + char *worker_name = kasprintf(GFP_KERNEL, "ptp%d", ptp->index); 222 + 223 + kthread_init_delayed_work(&ptp->aux_work, ptp_aux_kworker); 224 + ptp->kworker = kthread_create_worker(0, worker_name ? 225 + worker_name : info->name); 226 + kfree(worker_name); 227 + if (IS_ERR(ptp->kworker)) { 228 + err = PTR_ERR(ptp->kworker); 229 + pr_err("failed to create ptp aux_worker %d\n", err); 230 + goto kworker_err; 231 + } 232 + } 233 233 234 234 err = ptp_populate_pin_groups(ptp); 235 235 if (err) ··· 287 259 no_device: 288 260 ptp_cleanup_pin_groups(ptp); 289 261 no_pin_groups: 262 + if (ptp->kworker) 263 + kthread_destroy_worker(ptp->kworker); 264 + kworker_err: 290 265 mutex_destroy(&ptp->tsevq_mux); 291 266 mutex_destroy(&ptp->pincfg_mux); 292 267 ida_simple_remove(&ptp_clocks_map, index); ··· 304 273 { 305 274 ptp->defunct = 1; 306 275 wake_up_interruptible(&ptp->tsev_wq); 276 + 277 + if (ptp->kworker) { 278 + kthread_cancel_delayed_work_sync(&ptp->aux_work); 279 + kthread_destroy_worker(ptp->kworker); 280 + } 307 281 308 282 /* Release the clock's resources. */ 309 283 if (ptp->pps_source) ··· 374 338 return pin ? i : -1; 375 339 } 376 340 EXPORT_SYMBOL(ptp_find_pin); 341 + 342 + int ptp_schedule_worker(struct ptp_clock *ptp, unsigned long delay) 343 + { 344 + return kthread_mod_delayed_work(ptp->kworker, &ptp->aux_work, delay); 345 + } 346 + EXPORT_SYMBOL(ptp_schedule_worker); 377 347 378 348 /* module operations */ 379 349
+3
drivers/ptp/ptp_private.h
··· 22 22 23 23 #include <linux/cdev.h> 24 24 #include <linux/device.h> 25 + #include <linux/kthread.h> 25 26 #include <linux/mutex.h> 26 27 #include <linux/posix-clock.h> 27 28 #include <linux/ptp_clock.h> ··· 57 56 struct attribute_group pin_attr_group; 58 57 /* 1st entry is a pointer to the real group, 2nd is NULL terminator */ 59 58 const struct attribute_group *pin_attr_groups[2]; 59 + struct kthread_worker *kworker; 60 + struct kthread_delayed_work aux_work; 60 61 }; 61 62 62 63 /*
+20
include/linux/ptp_clock_kernel.h
··· 99 99 * parameter func: the desired function to use. 100 100 * parameter chan: the function channel index to use. 101 101 * 102 + * @do_work: Request driver to perform auxiliary (periodic) operations 103 + * Driver should return delay of the next auxiliary work scheduling 104 + * time (>=0) or negative value in case further scheduling 105 + * is not required. 106 + * 102 107 * Drivers should embed their ptp_clock_info within a private 103 108 * structure, obtaining a reference to it using container_of(). 104 109 * ··· 131 126 struct ptp_clock_request *request, int on); 132 127 int (*verify)(struct ptp_clock_info *ptp, unsigned int pin, 133 128 enum ptp_pin_function func, unsigned int chan); 129 + long (*do_aux_work)(struct ptp_clock_info *ptp); 134 130 }; 135 131 136 132 struct ptp_clock; ··· 217 211 int ptp_find_pin(struct ptp_clock *ptp, 218 212 enum ptp_pin_function func, unsigned int chan); 219 213 214 + /** 215 + * ptp_schedule_worker() - schedule ptp auxiliary work 216 + * 217 + * @ptp: The clock obtained from ptp_clock_register(). 218 + * @delay: number of jiffies to wait before queuing 219 + * See kthread_queue_delayed_work() for more info. 220 + */ 221 + 222 + int ptp_schedule_worker(struct ptp_clock *ptp, unsigned long delay); 223 + 220 224 #else 221 225 static inline struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, 222 226 struct device *parent) ··· 241 225 static inline int ptp_find_pin(struct ptp_clock *ptp, 242 226 enum ptp_pin_function func, unsigned int chan) 243 227 { return -1; } 228 + static inline int ptp_schedule_worker(struct ptp_clock *ptp, 229 + unsigned long delay) 230 + { return -EOPNOTSUPP; } 231 + 244 232 #endif 245 233 246 234 #endif