From 7a21a3cc0be92e70474cc2ab06cb074f6a7c3f09 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Wed, 12 Jan 2011 17:00:49 -0800 Subject: pps: trivial fixes Here are some very trivial fixes combined: - add macro definitions to protect header file from including several times - remove declaration for an unexistent array - fix typos Signed-off-by: Alexander Gordeev Acked-by: Rodolfo Giometti Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pps_kernel.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux/pps_kernel.h') diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h index e0a193f830ef..23f63c3263bb 100644 --- a/include/linux/pps_kernel.h +++ b/include/linux/pps_kernel.h @@ -18,6 +18,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifndef LINUX_PPS_KERNEL_H +#define LINUX_PPS_KERNEL_H + #include #include @@ -71,7 +74,6 @@ struct pps_device { extern spinlock_t pps_idr_lock; extern struct idr pps_idr; -extern struct timespec pps_irq_ts[]; extern struct device_attribute pps_attrs[]; @@ -87,3 +89,5 @@ extern void pps_unregister_source(int source); extern int pps_register_cdev(struct pps_device *pps); extern void pps_unregister_cdev(struct pps_device *pps); extern void pps_event(int source, struct pps_ktime *ts, int event, void *data); + +#endif /* LINUX_PPS_KERNEL_H */ -- cgit v1.2.3 From 3003d55b59aa98aeaff2773df69732b27c0cbf6a Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Wed, 12 Jan 2011 17:00:50 -0800 Subject: pps: fix race in PPS_FETCH handler There was a race in PPS_FETCH ioctl handler when several processes want to obtain PPS data simultaneously using sleeping PPS_FETCH. They all sleep most of the time in the system call. With the old approach when the first process waiting on the pps queue is waken up it makes new system call right away and zeroes pps->go. So other processes continue to sleep. This is a clear race condition because of the global 'go' variable. With the new approach pps->last_ev holds some value increasing at each PPS event. PPS_FETCH ioctl handler saves current value to the local variable at the very beginning so it can safely check that there is a new event by just comparing both variables. Signed-off-by: Alexander Gordeev Acked-by: Rodolfo Giometti Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pps/kapi.c | 4 ++-- drivers/pps/pps.c | 10 +++++++--- include/linux/pps_kernel.h | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) (limited to 'include/linux/pps_kernel.h') diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index 55f39618261b..3f89f5eba81c 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -326,8 +326,8 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data) /* Wake up if captured something */ if (captured) { - pps->go = ~0; - wake_up_interruptible(&pps->queue); + pps->last_ev++; + wake_up_interruptible_all(&pps->queue); kill_fasync(&pps->async_queue, SIGIO, POLL_IN); } diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index c76afb980a98..dc7e66cb2762 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -136,6 +136,7 @@ static long pps_cdev_ioctl(struct file *file, case PPS_FETCH: { struct pps_fdata fdata; + unsigned int ev; pr_debug("PPS_FETCH: source %d\n", pps->id); @@ -143,11 +144,12 @@ static long pps_cdev_ioctl(struct file *file, if (err) return -EFAULT; - pps->go = 0; + ev = pps->last_ev; /* Manage the timeout */ if (fdata.timeout.flags & PPS_TIME_INVALID) - err = wait_event_interruptible(pps->queue, pps->go); + err = wait_event_interruptible(pps->queue, + ev != pps->last_ev); else { unsigned long ticks; @@ -159,7 +161,9 @@ static long pps_cdev_ioctl(struct file *file, if (ticks != 0) { err = wait_event_interruptible_timeout( - pps->queue, pps->go, ticks); + pps->queue, + ev != pps->last_ev, + ticks); if (err == 0) return -ETIMEDOUT; } diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h index 23f63c3263bb..65194fe498bb 100644 --- a/include/linux/pps_kernel.h +++ b/include/linux/pps_kernel.h @@ -55,7 +55,7 @@ struct pps_device { struct pps_ktime clear_tu; int current_mode; /* PPS mode at event time */ - int go; /* PPS event is arrived? */ + unsigned int last_ev; /* last PPS event id */ wait_queue_head_t queue; /* PPS event queue */ unsigned int id; /* PPS source unique ID */ -- cgit v1.2.3 From 6f4229b51106cbc859e9d8209b22c8a2ec749e64 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Wed, 12 Jan 2011 17:00:50 -0800 Subject: pps: unify timestamp gathering Add a helper function to gather timestamps. This way clients don't have to duplicate it. Signed-off-by: Alexander Gordeev Acked-by: Rodolfo Giometti Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pps/clients/pps-ktimer.c | 9 ++------- drivers/pps/clients/pps-ldisc.c | 18 ++++++------------ drivers/pps/kapi.c | 19 ++++++++++++------- include/linux/pps_kernel.h | 20 +++++++++++++++++++- include/linux/serial_core.h | 5 +++-- include/linux/tty_ldisc.h | 5 +++-- 6 files changed, 45 insertions(+), 31 deletions(-) (limited to 'include/linux/pps_kernel.h') diff --git a/drivers/pps/clients/pps-ktimer.c b/drivers/pps/clients/pps-ktimer.c index e7ef5b8186d0..e1bdd8bc8c9c 100644 --- a/drivers/pps/clients/pps-ktimer.c +++ b/drivers/pps/clients/pps-ktimer.c @@ -40,18 +40,13 @@ static struct timer_list ktimer; static void pps_ktimer_event(unsigned long ptr) { - struct timespec __ts; - struct pps_ktime ts; + struct pps_event_time ts; /* First of all we get the time stamp... */ - getnstimeofday(&__ts); + pps_get_ts(&ts); pr_info("PPS event at %lu\n", jiffies); - /* ... and translate it to PPS time data struct */ - ts.sec = __ts.tv_sec; - ts.nsec = __ts.tv_nsec; - pps_event(source, &ts, PPS_CAPTUREASSERT, NULL); mod_timer(&ktimer, jiffies + HZ); diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c index 8e1932d29fd4..20fc9f7645c8 100644 --- a/drivers/pps/clients/pps-ldisc.c +++ b/drivers/pps/clients/pps-ldisc.c @@ -27,26 +27,20 @@ #define PPS_TTY_MAGIC 0x0001 static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status, - struct timespec *ts) + struct pps_event_time *ts) { int id = (long)tty->disc_data; - struct timespec __ts; - struct pps_ktime pps_ts; + struct pps_event_time __ts; /* First of all we get the time stamp... */ - getnstimeofday(&__ts); + pps_get_ts(&__ts); /* Does caller give us a timestamp? */ - if (ts) { /* Yes. Let's use it! */ - pps_ts.sec = ts->tv_sec; - pps_ts.nsec = ts->tv_nsec; - } else { /* No. Do it ourself! */ - pps_ts.sec = __ts.tv_sec; - pps_ts.nsec = __ts.tv_nsec; - } + if (!ts) /* No. Do it ourself! */ + ts = &__ts; /* Now do the PPS event report */ - pps_event(id, &pps_ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR, + pps_event(id, ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR, NULL); pr_debug("PPS %s at %lu on source #%d\n", diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index 3f89f5eba81c..b431d83b824a 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -268,11 +268,12 @@ EXPORT_SYMBOL(pps_unregister_source); * pps->info.echo(source, event, data); */ -void pps_event(int source, struct pps_ktime *ts, int event, void *data) +void pps_event(int source, struct pps_event_time *ts, int event, void *data) { struct pps_device *pps; unsigned long flags; int captured = 0; + struct pps_ktime ts_real; if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0) { printk(KERN_ERR "pps: unknown event (%x) for source %d\n", @@ -284,8 +285,10 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data) if (!pps) return; - pr_debug("PPS event on source %d at %llu.%06u\n", - pps->id, (unsigned long long) ts->sec, ts->nsec); + pr_debug("PPS event on source %d at %ld.%09ld\n", + pps->id, ts->ts_real.tv_sec, ts->ts_real.tv_nsec); + + timespec_to_pps_ktime(&ts_real, ts->ts_real); spin_lock_irqsave(&pps->lock, flags); @@ -299,10 +302,11 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data) (pps->params.mode & PPS_CAPTUREASSERT)) { /* We have to add an offset? */ if (pps->params.mode & PPS_OFFSETASSERT) - pps_add_offset(ts, &pps->params.assert_off_tu); + pps_add_offset(&ts_real, + &pps->params.assert_off_tu); /* Save the time stamp */ - pps->assert_tu = *ts; + pps->assert_tu = ts_real; pps->assert_sequence++; pr_debug("capture assert seq #%u for source %d\n", pps->assert_sequence, source); @@ -313,10 +317,11 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data) (pps->params.mode & PPS_CAPTURECLEAR)) { /* We have to add an offset? */ if (pps->params.mode & PPS_OFFSETCLEAR) - pps_add_offset(ts, &pps->params.clear_off_tu); + pps_add_offset(&ts_real, + &pps->params.clear_off_tu); /* Save the time stamp */ - pps->clear_tu = *ts; + pps->clear_tu = ts_real; pps->clear_sequence++; pr_debug("capture clear seq #%u for source %d\n", pps->clear_sequence, source); diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h index 65194fe498bb..32aa6763ca1b 100644 --- a/include/linux/pps_kernel.h +++ b/include/linux/pps_kernel.h @@ -43,6 +43,10 @@ struct pps_source_info { struct device *dev; }; +struct pps_event_time { + struct timespec ts_real; +}; + /* The main struct */ struct pps_device { struct pps_source_info info; /* PSS source info */ @@ -88,6 +92,20 @@ extern int pps_register_source(struct pps_source_info *info, extern void pps_unregister_source(int source); extern int pps_register_cdev(struct pps_device *pps); extern void pps_unregister_cdev(struct pps_device *pps); -extern void pps_event(int source, struct pps_ktime *ts, int event, void *data); +extern void pps_event(int source, struct pps_event_time *ts, int event, + void *data); + +static inline void timespec_to_pps_ktime(struct pps_ktime *kt, + struct timespec ts) +{ + kt->sec = ts.tv_sec; + kt->nsec = ts.tv_nsec; +} + +static inline void pps_get_ts(struct pps_event_time *ts) +{ + getnstimeofday(&ts->ts_real); +} #endif /* LINUX_PPS_KERNEL_H */ + diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index a23fa29d4eb0..758c5b0c6fd3 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -212,6 +212,7 @@ #include #include #include +#include struct uart_port; struct serial_struct; @@ -528,10 +529,10 @@ uart_handle_dcd_change(struct uart_port *uport, unsigned int status) struct uart_state *state = uport->state; struct tty_port *port = &state->port; struct tty_ldisc *ld = tty_ldisc_ref(port->tty); - struct timespec ts; + struct pps_event_time ts; if (ld && ld->ops->dcd_change) - getnstimeofday(&ts); + pps_get_ts(&ts); uport->icount.dcd++; #ifdef CONFIG_HARD_PPS diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 526d66f066a3..763e061e19f1 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -101,7 +101,7 @@ * any pending driver I/O is completed. * * void (*dcd_change)(struct tty_struct *tty, unsigned int status, - * struct timespec *ts) + * struct pps_event_time *ts) * * Tells the discipline that the DCD pin has changed its status and * the relative timestamp. Pointer ts can be NULL. @@ -109,6 +109,7 @@ #include #include +#include struct tty_ldisc_ops { int magic; @@ -143,7 +144,7 @@ struct tty_ldisc_ops { char *fp, int count); void (*write_wakeup)(struct tty_struct *); void (*dcd_change)(struct tty_struct *, unsigned int, - struct timespec *); + struct pps_event_time *); struct module *owner; -- cgit v1.2.3 From 5e196d34a776420278e4117b4742cd9d3f2350ed Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Wed, 12 Jan 2011 17:00:51 -0800 Subject: pps: access pps device by direct pointer Using device index as a pointer needs some unnecessary work to be done every time the pointer is needed (in irq handler for example). Using a direct pointer is much more easy (and safe as well). Signed-off-by: Alexander Gordeev Acked-by: Rodolfo Giometti Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pps/clients/pps-ktimer.c | 30 ++++------ drivers/pps/clients/pps-ldisc.c | 41 ++++++++----- drivers/pps/kapi.c | 124 +++++++++------------------------------ drivers/pps/pps.c | 22 ++----- include/linux/pps_kernel.h | 23 +++----- 5 files changed, 82 insertions(+), 158 deletions(-) (limited to 'include/linux/pps_kernel.h') diff --git a/drivers/pps/clients/pps-ktimer.c b/drivers/pps/clients/pps-ktimer.c index e1bdd8bc8c9c..966ead188ee2 100644 --- a/drivers/pps/clients/pps-ktimer.c +++ b/drivers/pps/clients/pps-ktimer.c @@ -31,7 +31,7 @@ * Global variables */ -static int source; +static struct pps_device *pps; static struct timer_list ktimer; /* @@ -47,7 +47,7 @@ static void pps_ktimer_event(unsigned long ptr) pr_info("PPS event at %lu\n", jiffies); - pps_event(source, &ts, PPS_CAPTUREASSERT, NULL); + pps_event(pps, &ts, PPS_CAPTUREASSERT, NULL); mod_timer(&ktimer, jiffies + HZ); } @@ -56,12 +56,11 @@ static void pps_ktimer_event(unsigned long ptr) * The echo function */ -static void pps_ktimer_echo(int source, int event, void *data) +static void pps_ktimer_echo(struct pps_device *pps, int event, void *data) { - pr_info("echo %s %s for source %d\n", + dev_info(pps->dev, "echo %s %s\n", event & PPS_CAPTUREASSERT ? "assert" : "", - event & PPS_CAPTURECLEAR ? "clear" : "", - source); + event & PPS_CAPTURECLEAR ? "clear" : ""); } /* @@ -84,30 +83,27 @@ static struct pps_source_info pps_ktimer_info = { static void __exit pps_ktimer_exit(void) { - del_timer_sync(&ktimer); - pps_unregister_source(source); + dev_info(pps->dev, "ktimer PPS source unregistered\n"); - pr_info("ktimer PPS source unregistered\n"); + del_timer_sync(&ktimer); + pps_unregister_source(pps); } static int __init pps_ktimer_init(void) { - int ret; - - ret = pps_register_source(&pps_ktimer_info, + pps = pps_register_source(&pps_ktimer_info, PPS_CAPTUREASSERT | PPS_OFFSETASSERT); - if (ret < 0) { + if (pps == NULL) { printk(KERN_ERR "cannot register ktimer source\n"); - return ret; + return -ENOMEM; } - source = ret; setup_timer(&ktimer, pps_ktimer_event, 0); mod_timer(&ktimer, jiffies + HZ); - pr_info("ktimer PPS source registered at %d\n", source); + dev_info(pps->dev, "ktimer PPS source registered\n"); - return 0; + return 0; } module_init(pps_ktimer_init); diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c index 20fc9f7645c8..1517f5498ec0 100644 --- a/drivers/pps/clients/pps-ldisc.c +++ b/drivers/pps/clients/pps-ldisc.c @@ -29,7 +29,7 @@ static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status, struct pps_event_time *ts) { - int id = (long)tty->disc_data; + struct pps_device *pps = (struct pps_device *)tty->disc_data; struct pps_event_time __ts; /* First of all we get the time stamp... */ @@ -39,12 +39,14 @@ static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status, if (!ts) /* No. Do it ourself! */ ts = &__ts; + BUG_ON(pps == NULL); + /* Now do the PPS event report */ - pps_event(id, ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR, - NULL); + pps_event(pps, ts, status ? PPS_CAPTUREASSERT : + PPS_CAPTURECLEAR, NULL); - pr_debug("PPS %s at %lu on source #%d\n", - status ? "assert" : "clear", jiffies, id); + dev_dbg(pps->dev, "PPS %s at %lu\n", + status ? "assert" : "clear", jiffies); } static int (*alias_n_tty_open)(struct tty_struct *tty); @@ -54,6 +56,7 @@ static int pps_tty_open(struct tty_struct *tty) struct pps_source_info info; struct tty_driver *drv = tty->driver; int index = tty->index + drv->name_base; + struct pps_device *pps; int ret; info.owner = THIS_MODULE; @@ -64,34 +67,42 @@ static int pps_tty_open(struct tty_struct *tty) PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \ PPS_CANWAIT | PPS_TSFMT_TSPEC; - ret = pps_register_source(&info, PPS_CAPTUREBOTH | \ + pps = pps_register_source(&info, PPS_CAPTUREBOTH | \ PPS_OFFSETASSERT | PPS_OFFSETCLEAR); - if (ret < 0) { + if (pps == NULL) { pr_err("cannot register PPS source \"%s\"\n", info.path); - return ret; + return -ENOMEM; } - tty->disc_data = (void *)(long)ret; + tty->disc_data = pps; /* Should open N_TTY ldisc too */ ret = alias_n_tty_open(tty); - if (ret < 0) - pps_unregister_source((long)tty->disc_data); + if (ret < 0) { + pr_err("cannot open tty ldisc \"%s\"\n", info.path); + goto err_unregister; + } - pr_info("PPS source #%d \"%s\" added\n", ret, info.path); + dev_info(pps->dev, "source \"%s\" added\n", info.path); return 0; + +err_unregister: + tty->disc_data = NULL; + pps_unregister_source(pps); + return ret; } static void (*alias_n_tty_close)(struct tty_struct *tty); static void pps_tty_close(struct tty_struct *tty) { - int id = (long)tty->disc_data; + struct pps_device *pps = (struct pps_device *)tty->disc_data; - pps_unregister_source(id); alias_n_tty_close(tty); - pr_info("PPS source #%d removed\n", id); + tty->disc_data = NULL; + dev_info(pps->dev, "removed\n"); + pps_unregister_source(pps); } static struct tty_ldisc_ops pps_ldisc_ops; diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index b431d83b824a..98d4012ca595 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -32,11 +32,11 @@ #include /* - * Global variables + * Local variables */ -DEFINE_SPINLOCK(pps_idr_lock); -DEFINE_IDR(pps_idr); +static DEFINE_SPINLOCK(pps_idr_lock); +static DEFINE_IDR(pps_idr); /* * Local functions @@ -60,60 +60,6 @@ static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset) * Exported functions */ -/* pps_get_source - find a PPS source - * @source: the PPS source ID. - * - * This function is used to find an already registered PPS source into the - * system. - * - * The function returns NULL if found nothing, otherwise it returns a pointer - * to the PPS source data struct (the refcounter is incremented by 1). - */ - -struct pps_device *pps_get_source(int source) -{ - struct pps_device *pps; - unsigned long flags; - - spin_lock_irqsave(&pps_idr_lock, flags); - - pps = idr_find(&pps_idr, source); - if (pps != NULL) - atomic_inc(&pps->usage); - - spin_unlock_irqrestore(&pps_idr_lock, flags); - - return pps; -} - -/* pps_put_source - free the PPS source data - * @pps: a pointer to the PPS source. - * - * This function is used to free a PPS data struct if its refcount is 0. - */ - -void pps_put_source(struct pps_device *pps) -{ - unsigned long flags; - - spin_lock_irqsave(&pps_idr_lock, flags); - BUG_ON(atomic_read(&pps->usage) == 0); - - if (!atomic_dec_and_test(&pps->usage)) { - pps = NULL; - goto exit; - } - - /* No more reference to the PPS source. We can safely remove the - * PPS data struct. - */ - idr_remove(&pps_idr, pps->id); - -exit: - spin_unlock_irqrestore(&pps_idr_lock, flags); - kfree(pps); -} - /* pps_register_source - add a PPS source in the system * @info: the PPS info struct * @default_params: the default PPS parameters of the new source @@ -122,10 +68,11 @@ exit: * source is described by info's fields and it will have, as default PPS * parameters, the ones specified into default_params. * - * The function returns, in case of success, the PPS source ID. + * The function returns, in case of success, the PPS device. Otherwise NULL. */ -int pps_register_source(struct pps_source_info *info, int default_params) +struct pps_device *pps_register_source(struct pps_source_info *info, + int default_params) { struct pps_device *pps; int id; @@ -168,7 +115,6 @@ int pps_register_source(struct pps_source_info *info, int default_params) init_waitqueue_head(&pps->queue); spin_lock_init(&pps->lock); - atomic_set(&pps->usage, 1); /* Get new ID for the new PPS source */ if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0) { @@ -211,7 +157,7 @@ int pps_register_source(struct pps_source_info *info, int default_params) pr_info("new PPS source %s at ID %d\n", info->name, id); - return id; + return pps; free_idr: spin_lock_irq(&pps_idr_lock); @@ -224,38 +170,33 @@ kfree_pps: pps_register_source_exit: printk(KERN_ERR "pps: %s: unable to register source\n", info->name); - return err; + return NULL; } EXPORT_SYMBOL(pps_register_source); /* pps_unregister_source - remove a PPS source from the system - * @source: the PPS source ID + * @pps: the PPS source * * This function is used to remove a previously registered PPS source from * the system. */ -void pps_unregister_source(int source) +void pps_unregister_source(struct pps_device *pps) { - struct pps_device *pps; + unsigned int id = pps->id; - spin_lock_irq(&pps_idr_lock); - pps = idr_find(&pps_idr, source); + pps_unregister_cdev(pps); - if (!pps) { - BUG(); - spin_unlock_irq(&pps_idr_lock); - return; - } + spin_lock_irq(&pps_idr_lock); + idr_remove(&pps_idr, pps->id); spin_unlock_irq(&pps_idr_lock); - pps_unregister_cdev(pps); - pps_put_source(pps); + kfree(pps); } EXPORT_SYMBOL(pps_unregister_source); /* pps_event - register a PPS event into the system - * @source: the PPS source ID + * @pps: the PPS device * @ts: the event timestamp * @event: the event type * @data: userdef pointer @@ -263,30 +204,24 @@ EXPORT_SYMBOL(pps_unregister_source); * This function is used by each PPS client in order to register a new * PPS event into the system (it's usually called inside an IRQ handler). * - * If an echo function is associated with the PPS source it will be called + * If an echo function is associated with the PPS device it will be called * as: - * pps->info.echo(source, event, data); + * pps->info.echo(pps, event, data); */ - -void pps_event(int source, struct pps_event_time *ts, int event, void *data) +void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, + void *data) { - struct pps_device *pps; unsigned long flags; int captured = 0; struct pps_ktime ts_real; if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0) { - printk(KERN_ERR "pps: unknown event (%x) for source %d\n", - event, source); + dev_err(pps->dev, "unknown event (%x)\n", event); return; } - pps = pps_get_source(source); - if (!pps) - return; - - pr_debug("PPS event on source %d at %ld.%09ld\n", - pps->id, ts->ts_real.tv_sec, ts->ts_real.tv_nsec); + dev_dbg(pps->dev, "PPS event at %ld.%09ld\n", + ts->ts_real.tv_sec, ts->ts_real.tv_nsec); timespec_to_pps_ktime(&ts_real, ts->ts_real); @@ -294,7 +229,7 @@ void pps_event(int source, struct pps_event_time *ts, int event, void *data) /* Must call the echo function? */ if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR))) - pps->info.echo(source, event, data); + pps->info.echo(pps, event, data); /* Check the event */ pps->current_mode = pps->params.mode; @@ -308,8 +243,8 @@ void pps_event(int source, struct pps_event_time *ts, int event, void *data) /* Save the time stamp */ pps->assert_tu = ts_real; pps->assert_sequence++; - pr_debug("capture assert seq #%u for source %d\n", - pps->assert_sequence, source); + dev_dbg(pps->dev, "capture assert seq #%u\n", + pps->assert_sequence); captured = ~0; } @@ -323,8 +258,8 @@ void pps_event(int source, struct pps_event_time *ts, int event, void *data) /* Save the time stamp */ pps->clear_tu = ts_real; pps->clear_sequence++; - pr_debug("capture clear seq #%u for source %d\n", - pps->clear_sequence, source); + dev_dbg(pps->dev, "capture clear seq #%u\n", + pps->clear_sequence); captured = ~0; } @@ -338,8 +273,5 @@ void pps_event(int source, struct pps_event_time *ts, int event, void *data) } spin_unlock_irqrestore(&pps->lock, flags); - - /* Now we can release the PPS source for (possible) deregistration */ - pps_put_source(pps); } EXPORT_SYMBOL(pps_event); diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index dc7e66cb2762..1922f27a52ac 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -204,12 +204,6 @@ static int pps_cdev_open(struct inode *inode, struct file *file) { struct pps_device *pps = container_of(inode->i_cdev, struct pps_device, cdev); - int found; - - found = pps_get_source(pps->id) != 0; - if (!found) - return -ENODEV; - file->private_data = pps; return 0; @@ -217,11 +211,6 @@ static int pps_cdev_open(struct inode *inode, struct file *file) static int pps_cdev_release(struct inode *inode, struct file *file) { - struct pps_device *pps = file->private_data; - - /* Free the PPS source and wake up (possible) deregistration */ - pps_put_source(pps); - return 0; } @@ -242,22 +231,23 @@ static const struct file_operations pps_cdev_fops = { int pps_register_cdev(struct pps_device *pps) { int err; + dev_t devt; + + devt = MKDEV(MAJOR(pps_devt), pps->id); - pps->devno = MKDEV(MAJOR(pps_devt), pps->id); cdev_init(&pps->cdev, &pps_cdev_fops); pps->cdev.owner = pps->info.owner; - err = cdev_add(&pps->cdev, pps->devno, 1); + err = cdev_add(&pps->cdev, devt, 1); if (err) { printk(KERN_ERR "pps: %s: failed to add char device %d:%d\n", pps->info.name, MAJOR(pps_devt), pps->id); return err; } - pps->dev = device_create(pps_class, pps->info.dev, pps->devno, NULL, + pps->dev = device_create(pps_class, pps->info.dev, devt, pps, "pps%d", pps->id); if (IS_ERR(pps->dev)) goto del_cdev; - dev_set_drvdata(pps->dev, pps); pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, MAJOR(pps_devt), pps->id); @@ -272,7 +262,7 @@ del_cdev: void pps_unregister_cdev(struct pps_device *pps) { - device_destroy(pps_class, pps->devno); + device_destroy(pps_class, pps->dev->devt); cdev_del(&pps->cdev); } diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h index 32aa6763ca1b..1aedf50088cf 100644 --- a/include/linux/pps_kernel.h +++ b/include/linux/pps_kernel.h @@ -31,13 +31,16 @@ * Global defines */ +struct pps_device; + /* The specific PPS source info */ struct pps_source_info { char name[PPS_MAX_NAME_LEN]; /* simbolic name */ char path[PPS_MAX_NAME_LEN]; /* path of connected device */ int mode; /* PPS's allowed mode */ - void (*echo)(int source, int event, void *data); /* PPS echo function */ + void (*echo)(struct pps_device *pps, + int event, void *data); /* PPS echo function */ struct module *owner; struct device *dev; @@ -65,35 +68,27 @@ struct pps_device { unsigned int id; /* PPS source unique ID */ struct cdev cdev; struct device *dev; - int devno; struct fasync_struct *async_queue; /* fasync method */ spinlock_t lock; - - atomic_t usage; /* usage count */ }; /* * Global variables */ -extern spinlock_t pps_idr_lock; -extern struct idr pps_idr; - extern struct device_attribute pps_attrs[]; /* * Exported functions */ -struct pps_device *pps_get_source(int source); -extern void pps_put_source(struct pps_device *pps); -extern int pps_register_source(struct pps_source_info *info, - int default_params); -extern void pps_unregister_source(int source); +extern struct pps_device *pps_register_source( + struct pps_source_info *info, int default_params); +extern void pps_unregister_source(struct pps_device *pps); extern int pps_register_cdev(struct pps_device *pps); extern void pps_unregister_cdev(struct pps_device *pps); -extern void pps_event(int source, struct pps_event_time *ts, int event, - void *data); +extern void pps_event(struct pps_device *pps, + struct pps_event_time *ts, int event, void *data); static inline void timespec_to_pps_ktime(struct pps_ktime *kt, struct timespec ts) -- cgit v1.2.3 From e2c18e49a0d4f822ffc29fb4958943beb1ff08b7 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Wed, 12 Jan 2011 17:00:57 -0800 Subject: pps: capture MONOTONIC_RAW timestamps as well MONOTONIC_RAW clock timestamps are ideally suited for frequency calculation and also fit well into the original NTP hardpps design. Now phase and frequency can be adjusted separately: the former based on REALTIME clock and the latter based on MONOTONIC_RAW clock. A new function getnstime_raw_and_real is added to timekeeping subsystem to capture both timestamps at the same time and atomically. Signed-off-by: Alexander Gordeev Acked-by: John Stultz Cc: Rodolfo Giometti Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pps_kernel.h | 14 ++++++++++++++ include/linux/time.h | 2 ++ kernel/time/timekeeping.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) (limited to 'include/linux/pps_kernel.h') diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h index 1aedf50088cf..94048547f29a 100644 --- a/include/linux/pps_kernel.h +++ b/include/linux/pps_kernel.h @@ -47,6 +47,9 @@ struct pps_source_info { }; struct pps_event_time { +#ifdef CONFIG_NTP_PPS + struct timespec ts_raw; +#endif /* CONFIG_NTP_PPS */ struct timespec ts_real; }; @@ -97,10 +100,21 @@ static inline void timespec_to_pps_ktime(struct pps_ktime *kt, kt->nsec = ts.tv_nsec; } +#ifdef CONFIG_NTP_PPS + +static inline void pps_get_ts(struct pps_event_time *ts) +{ + getnstime_raw_and_real(&ts->ts_raw, &ts->ts_real); +} + +#else /* CONFIG_NTP_PPS */ + static inline void pps_get_ts(struct pps_event_time *ts) { getnstimeofday(&ts->ts_real); } +#endif /* CONFIG_NTP_PPS */ + #endif /* LINUX_PPS_KERNEL_H */ diff --git a/include/linux/time.h b/include/linux/time.h index 9f15ac7ab92a..1e6d3b59238d 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -158,6 +158,8 @@ extern unsigned int alarm_setitimer(unsigned int seconds); extern int do_getitimer(int which, struct itimerval *value); extern void getnstimeofday(struct timespec *tv); extern void getrawmonotonic(struct timespec *ts); +extern void getnstime_raw_and_real(struct timespec *ts_raw, + struct timespec *ts_real); extern void getboottime(struct timespec *ts); extern void monotonic_to_bootbased(struct timespec *ts); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 5bb86da82003..5536aaf3ba36 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -288,6 +288,49 @@ void ktime_get_ts(struct timespec *ts) } EXPORT_SYMBOL_GPL(ktime_get_ts); +#ifdef CONFIG_NTP_PPS + +/** + * getnstime_raw_and_real - get day and raw monotonic time in timespec format + * @ts_raw: pointer to the timespec to be set to raw monotonic time + * @ts_real: pointer to the timespec to be set to the time of day + * + * This function reads both the time of day and raw monotonic time at the + * same time atomically and stores the resulting timestamps in timespec + * format. + */ +void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real) +{ + unsigned long seq; + s64 nsecs_raw, nsecs_real; + + WARN_ON_ONCE(timekeeping_suspended); + + do { + u32 arch_offset; + + seq = read_seqbegin(&xtime_lock); + + *ts_raw = raw_time; + *ts_real = xtime; + + nsecs_raw = timekeeping_get_ns_raw(); + nsecs_real = timekeeping_get_ns(); + + /* If arch requires, add in gettimeoffset() */ + arch_offset = arch_gettimeoffset(); + nsecs_raw += arch_offset; + nsecs_real += arch_offset; + + } while (read_seqretry(&xtime_lock, seq)); + + timespec_add_ns(ts_raw, nsecs_raw); + timespec_add_ns(ts_real, nsecs_real); +} +EXPORT_SYMBOL(getnstime_raw_and_real); + +#endif /* CONFIG_NTP_PPS */ + /** * do_gettimeofday - Returns the time of day in a timeval * @tv: pointer to the timeval to be set -- cgit v1.2.3