|
|
|
Re: [PATCH 8/25] USB: EHCI: introduce high-res timer | |
| [Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] |
|
On Wed, Jul 11, 2012 at 11:21 PM, Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> wrote:
> This patch (as1572) begins the conversion of ehci-hcd over to using
> high-resolution timers rather than old-fashioned low-resolution kernel
> timers. This reduces overhead caused by timer roundoff on systems
> where HZ is smaller than 1000. Also, the new timer framework
> introduced here is much more logical and easily extended than the
> ad-hoc approach ehci-hcd currently uses for timers.
>
> An hrtimer structure is added to ehci_hcd, along with a bitflag array
> and an array of ktime_t values, to keep track of which timing events
> are pending and what their expiration times are.
>
> Only the infrastructure for the timing operations is added in this
> patch. Later patches will add routines for handling each of the
> various timing events the driver needs. In some cases the new hrtimer
> handlers will replace the existing handlers for ehci-hcd's kernel
> timers; as this happens the old timers will be removed. In other
> cases the new timing events will replace busy-wait loops.
>
> Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>
>
> ---
>
> drivers/usb/host/ehci-hcd.c | 15 +++++
> drivers/usb/host/ehci-hub.c | 3 +
> drivers/usb/host/ehci-timer.c | 106 ++++++++++++++++++++++++++++++++++++++++++
> drivers/usb/host/ehci.h | 16 ++++++
> 4 files changed, 138 insertions(+), 2 deletions(-)
>
> Index: usb-3.4/drivers/usb/host/ehci.h
> ===================================================================
> --- usb-3.4.orig/drivers/usb/host/ehci.h
> +++ usb-3.4/drivers/usb/host/ehci.h
> @@ -73,7 +73,23 @@ enum ehci_rh_state {
> EHCI_RH_STOPPING
> };
>
> +/*
> + * Timer events, ordered by increasing delay length.
> + * Always update event_delays_ns[] and event_handlers[] (defined in
> + * ehci-timer.c) in parallel with this list.
> + */
> +enum ehci_hrtimer_event {
> + EHCI_HRTIMER_NUM_EVENTS /* Must come last */
> +};
> +#define EHCI_HRTIMER_NO_EVENT 99
> +
> struct ehci_hcd { /* one per controller */
> + /* timing support */
> + enum ehci_hrtimer_event next_hrtimer_event;
> + unsigned enabled_hrtimer_events;
> + ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS];
> + struct hrtimer hrtimer;
> +
> /* glue to PCI and HCD framework */
> struct ehci_caps __iomem *caps;
> struct ehci_regs __iomem *regs;
> Index: usb-3.4/drivers/usb/host/ehci-hcd.c
> ===================================================================
> --- usb-3.4.orig/drivers/usb/host/ehci-hcd.c
> +++ usb-3.4/drivers/usb/host/ehci-hcd.c
> @@ -30,8 +30,7 @@
> #include <linux/vmalloc.h>
> #include <linux/errno.h>
> #include <linux/init.h>
> -#include <linux/timer.h>
> -#include <linux/ktime.h>
> +#include <linux/hrtimer.h>
> #include <linux/list.h>
> #include <linux/interrupt.h>
> #include <linux/usb.h>
> @@ -380,6 +379,7 @@ static void ehci_quiesce (struct ehci_hc
> static void end_unlink_async(struct ehci_hcd *ehci);
> static void ehci_work(struct ehci_hcd *ehci);
>
> +#include "ehci-timer.c"
> #include "ehci-hub.c"
> #include "ehci-lpm.c"
> #include "ehci-mem.c"
> @@ -494,7 +494,10 @@ static void ehci_shutdown(struct usb_hcd
> spin_lock_irq(&ehci->lock);
> ehci->rh_state = EHCI_RH_STOPPING;
> ehci_silence_controller(ehci);
> + ehci->enabled_hrtimer_events = 0;
> spin_unlock_irq(&ehci->lock);
> +
> + hrtimer_cancel(&ehci->hrtimer);
> }
>
> static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
> @@ -561,12 +564,14 @@ static void ehci_stop (struct usb_hcd *h
> del_timer_sync(&ehci->iaa_watchdog);
>
> spin_lock_irq(&ehci->lock);
> + ehci->enabled_hrtimer_events = 0;
> ehci_quiesce(ehci);
>
> ehci_silence_controller(ehci);
> ehci_reset (ehci);
> spin_unlock_irq(&ehci->lock);
>
> + hrtimer_cancel(&ehci->hrtimer);
> remove_sysfs_files(ehci);
> remove_debug_files (ehci);
>
> @@ -615,6 +620,10 @@ static int ehci_init(struct usb_hcd *hcd
> ehci->iaa_watchdog.function = ehci_iaa_watchdog;
> ehci->iaa_watchdog.data = (unsigned long) ehci;
>
> + hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
> + ehci->hrtimer.function = ehci_hrtimer_func;
> + ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
> +
> hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
>
> /*
> @@ -954,6 +963,8 @@ static irqreturn_t ehci_irq (struct usb_
> dbg_status(ehci, "fatal", status);
> ehci_halt(ehci);
> dead:
> + ehci->enabled_hrtimer_events = 0;
> + hrtimer_try_to_cancel(&ehci->hrtimer);
> ehci_reset(ehci);
> ehci_writel(ehci, 0, &ehci->regs->configured_flag);
> usb_hc_died(hcd);
> Index: usb-3.4/drivers/usb/host/ehci-hub.c
> ===================================================================
> --- usb-3.4.orig/drivers/usb/host/ehci-hub.c
> +++ usb-3.4/drivers/usb/host/ehci-hub.c
> @@ -311,12 +311,15 @@ static int ehci_bus_suspend (struct usb_
> ehci_readl(ehci, &ehci->regs->intr_enable);
>
> ehci->next_statechange = jiffies + msecs_to_jiffies(10);
> + ehci->enabled_hrtimer_events = 0;
> + ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
> spin_unlock_irq (&ehci->lock);
>
> /* ehci_work() may have re-enabled the watchdog timer, which we do not
> * want, and so we must delete any pending watchdog timer events.
> */
> del_timer_sync(&ehci->watchdog);
> + hrtimer_cancel(&ehci->hrtimer);
> return 0;
> }
>
> Index: usb-3.4/drivers/usb/host/ehci-timer.c
> ===================================================================
> --- /dev/null
> +++ usb-3.4/drivers/usb/host/ehci-timer.c
> @@ -0,0 +1,106 @@
> +/*
> + * Copyright (C) 2012 by Alan Stern
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + * for more details.
> + */
> +
> +/* This file is part of ehci-hcd.c */
> +
> +/*-------------------------------------------------------------------------*/
> +
> +/*
> + * EHCI timer support... Now using hrtimers.
> + *
> + * Lots of different events are triggered from ehci->hrtimer. Whenever
> + * the timer routine runs, it checks each possible event; events that are
> + * currently enabled and whose expiration time has passed get handled.
> + * The set of enabled events is stored as a collection of bitflags in
> + * ehci->enabled_hrtimer_events, and they are numbered in order of
> + * increasing delay values (ranging between 1 ms and 100 ms).
> + *
> + * Rather than implementing a sorted list or tree of all pending events,
Another candidate may be to use one standalone hrtimer for each event.
> + * we keep track only of the lowest-numbered pending event, in
> + * ehci->next_hrtimer_event. Whenever ehci->hrtimer gets restarted, its
> + * expiration time is set to the timeout value for this event.
> + *
> + * As a result, events might not get handled right away; the actual delay
> + * could be anywhere up to twice the requested delay. This doesn't
> + * matter, because none of the events are especially time-critical. The
> + * ones that matter most all have a delay of 1 ms, so they will be
> + * handled after 2 ms at most, which is okay. In addition to this, we
> + * allow for an expiration range of 1 ms.
> + */
> +
> +/*
> + * Delay lengths for the hrtimer event types.
> + * Keep this list sorted by delay length, in the same order as
> + * the event types indexed by enum ehci_hrtimer_event in ehci.h.
> + */
> +static unsigned event_delays_ns[] = {
According to your design, the time in the above array can't be changed,
so it should be marked as 'const' to reflect the design principle.
> +};
> +
> +/* Enable a pending hrtimer event */
> +static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event,
> + bool resched)
> +{
> + ktime_t *timeout = &ehci->hr_timeouts[event];
> +
> + if (resched)
> + *timeout = ktime_add(ktime_get(),
> + ktime_set(0, event_delays_ns[event]));
> + ehci->enabled_hrtimer_events |= (1 << event);
> +
> + /* Track only the lowest-numbered pending event */
> + if (event < ehci->next_hrtimer_event) {
> + ehci->next_hrtimer_event = event;
> + hrtimer_start_range_ns(&ehci->hrtimer, *timeout,
> + NSEC_PER_MSEC, HRTIMER_MODE_ABS);
The *timeout may have been expired for some time, so can the timer handler
still be called after return from hrtimer_start_range_ns? If not, it
may be a problem.
> + }
> +}
> +
> +
> +/*
> + * Handler functions for the hrtimer event types.
> + * Keep this array in the same order as the event types indexed by
> + * enum ehci_hrtimer_event in ehci.h.
> + */
> +static void (*event_handlers[])(struct ehci_hcd *) = {
> +};
> +
> +static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t)
> +{
> + struct ehci_hcd *ehci = container_of(t, struct ehci_hcd, hrtimer);
> + ktime_t now;
> + unsigned long events;
> + unsigned long flags;
> + unsigned e;
> +
> + spin_lock_irqsave(&ehci->lock, flags);
> +
> + events = ehci->enabled_hrtimer_events;
> + ehci->enabled_hrtimer_events = 0;
> + ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
> +
> + /*
> + * Check each pending event. If its time has expired, handle
> + * the event; otherwise re-enable it.
> + */
> + now = ktime_get();
It is better to move the above line after the next line since the event_hander
may consume some timer.
> + for_each_set_bit(e, &events, EHCI_HRTIMER_NUM_EVENTS) {
> + if (now.tv64 >= ehci->hr_timeouts[e].tv64)
> + event_handlers[e](ehci);
> + else
> + ehci_enable_event(ehci, e, false);
> + }
> +
> + spin_unlock_irqrestore(&ehci->lock, flags);
> + return HRTIMER_NORESTART;
> +}
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Thanks,
--
Ming Lei
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html

[Linux Media] [Video for Linux] [Linux Input] [Linux Audio Users] [Photo] [Yosemite News] [Yosemite Photos] [Free Online Dating] [Linux Kernel] [Linux SCSI] [Old Linux USB Devel Archive] [More Archives]
![]() |
![]() |