]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
hpet: factor timer allocate from open
authorMagnus Lynch <maglyx@gmail.com>
Thu, 8 Dec 2011 04:41:51 +0000 (15:41 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Wed, 14 Dec 2011 05:48:08 +0000 (16:48 +1100)
The current implementation of the /dev/hpet driver couples opening the
device with allocating one of the (scarce) timers (aka comparators).  This
is a limitation in that the main counter may be valuable to applications
seeking a high-resolution timer who have no use for the interrupt
generating functionality of the comparators.

This patch alters the open semantics so that when the device is opened, no
timer is allocated.  Operations that depend on a timer being in context
implicitly attempt allocating a timer, to maintain backward compatibility.
 There is also an IOCTL (HPET_ALLOC_TIMER _IO) added so that the
allocation may be done explicitly.  (I prefer the explicit open then
allocate pattern but don't know how practical it would be to require all
existing code to be changed.)

/dev/hpet is accessed via mmap().  This is the only interface of /dev/hpet
that is actually used in practice.

[akpm@linux-foundation.org: coding-style tweaks]
[arnd@arndb.de: fix build]
Signed-off-by: Magnus Lynch <maglyx@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: john stultz <johnstul@us.ibm.com>
Acked-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
drivers/char/hpet.c
include/linux/hpet.h

index 0833896cf6f2aeb98721441619a9fdeae0ae54bb..c9aeb7fce8782c96987a743e19f73c45289827b3 100644 (file)
@@ -263,16 +263,40 @@ static void hpet_timer_set_irq(struct hpet_dev *devp)
 
 static int hpet_open(struct inode *inode, struct file *file)
 {
-       struct hpet_dev *devp;
        struct hpets *hpetp;
-       int i;
 
        if (file->f_mode & FMODE_WRITE)
                return -EINVAL;
 
+       hpetp = hpets;
+       /* starting with timer-neutral instance */
+       file->private_data = &hpetp->hp_dev[hpetp->hp_ntimer];
+
+       return 0;
+}
+
+static int hpet_alloc_timer(struct file *file)
+{
+       struct hpet_dev *devp;
+       struct hpets *hpetp;
+       int i;
+
+       /* once acquired, will remain */
+       devp = file->private_data;
+       if (devp->hd_timer)
+               return 0;
+
        mutex_lock(&hpet_mutex);
        spin_lock_irq(&hpet_lock);
 
+       /* check for race acquiring */
+       devp = file->private_data;
+       if (devp->hd_timer) {
+               spin_unlock_irq(&hpet_lock);
+               mutex_unlock(&hpet_mutex);
+               return 0;
+       }
+
        for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next)
                for (i = 0; i < hpetp->hp_ntimer; i++)
                        if (hpetp->hp_dev[i].hd_flags & HPET_OPEN)
@@ -402,6 +426,11 @@ static int hpet_mmap(struct file *file, struct vm_area_struct *vma)
 static int hpet_fasync(int fd, struct file *file, int on)
 {
        struct hpet_dev *devp;
+       int r;
+
+       r = hpet_alloc_timer(file);
+       if (r < 0)
+               return r;
 
        devp = file->private_data;
 
@@ -420,6 +449,9 @@ static int hpet_release(struct inode *inode, struct file *file)
        devp = file->private_data;
        timer = devp->hd_timer;
 
+       if (!timer)
+               goto out;
+
        spin_lock_irq(&hpet_lock);
 
        writeq((readq(&timer->hpet_config) & ~Tn_INT_ENB_CNF_MASK),
@@ -444,7 +476,7 @@ static int hpet_release(struct inode *inode, struct file *file)
 
        if (irq)
                free_irq(irq, devp);
-
+out:
        file->private_data = NULL;
        return 0;
 }
@@ -593,6 +625,9 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg,
                break;
        case HPET_IE_ON:
                return hpet_ioctl_ieon(devp);
+       case HPET_ALLOC_TIMER:
+               /* nothing to do */
+               return 0;
        default:
                return -EINVAL;
        }
@@ -859,7 +894,11 @@ int hpet_alloc(struct hpet_data *hdp)
                return 0;
        }
 
-       siz = sizeof(struct hpets) + ((hdp->hd_nirqs - 1) *
+       /*
+        * last hpet_dev will have null timer pointer, gives timer-neutral
+        * representation of block
+        */
+       siz = sizeof(struct hpets) + ((hdp->hd_nirqs) *
                                      sizeof(struct hpet_dev));
 
        hpetp = kzalloc(siz, GFP_KERNEL);
@@ -925,13 +964,16 @@ int hpet_alloc(struct hpet_data *hdp)
                writeq(mcfg, &hpet->hpet_config);
        }
 
-       for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer; i++, devp++) {
+       for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer + 1;
+            i++, devp++) {
                struct hpet_timer __iomem *timer;
 
-               timer = &hpet->hpet_timers[devp - hpetp->hp_dev];
-
                devp->hd_hpets = hpetp;
                devp->hd_hpet = hpet;
+               if (i == hpetp->hp_ntimer)
+                       continue;
+
+               timer = &hpet->hpet_timers[devp - hpetp->hp_dev];
                devp->hd_timer = timer;
 
                /*
index 219ca4f6bea66a0a755101ea2d9c615a67854283..d690c0fe4b8b24b425790789b66e317285af1e5e 100644 (file)
@@ -125,6 +125,7 @@ struct hpet_info {
 #define        HPET_EPI        _IO('h', 0x04)  /* enable periodic */
 #define        HPET_DPI        _IO('h', 0x05)  /* disable periodic */
 #define        HPET_IRQFREQ    _IOW('h', 0x6, unsigned long)   /* IRQFREQ usec */
+#define        HPET_ALLOC_TIMER _IO('h', 0x7)
 
 #define MAX_HPET_TBS   8               /* maximum hpet timer blocks */