[LinuxPPS] safe pps_register_source()
Rodolfo Giometti
giometti at enneenne.com
Thu Aug 9 16:13:59 CEST 2007
On Thu, Aug 09, 2007 at 09:40:41AM -0400, Fabio Checconi wrote:
> I think we need something to prevent the deregistration of the
> source while someone is waiting on it; wrt this patch I think that
> pps_event() is not the best place to signal that the source is no
> more used, since after pps_event() ends, in interrupt context, the
> unregistering task will free the chardev, while the task resuming
> from fetch will try to access the (already freed) source for its
> data. Also using only the completion structure without an associated
> completion condition (i.e., the number of blocked tasks) I think
> can be racy.
What do you think about this solution?
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index ccfa123..c35e7d8 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -120,6 +120,8 @@ 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, 0);
+ init_waitqueue_head(&pps->usage_queue);
/* Create the char device */
err = pps_register_cdev(pps);
@@ -178,6 +180,8 @@ void pps_unregister_source(int source)
idr_remove(&pps_idr, pps->id);
spin_unlock_irq(&idr_lock);
+ wait_event(pps->usage_queue, atomic_read(&pps->usage) == 0);
+
pps_sysfs_remove_source_entry(pps);
pps_unregister_cdev(pps);
kfree(pps);
@@ -206,6 +210,12 @@ void pps_event(int source, int event, void *data)
spin_lock_irqsave(&idr_lock, flags);
pps = idr_find(&pps_idr, source);
+
+ /* If we find a valid PPS source we lock it before leaving
+ * the lock!
+ */
+ if (!pps)
+ atomic_inc(&pps->usage);
spin_unlock_irqrestore(&idr_lock, flags);
if (!pps)
@@ -251,5 +261,9 @@ void pps_event(int source, int event, void *data)
kill_fasync(&pps->async_queue, SIGIO, POLL_IN);
spin_unlock_irqrestore(&pps->lock, flags);
+
+ /* Now we can release the PPS source for (possible) deregistration */
+ atomic_dec(&pps->usage);
+ wake_up_all(&pps->usage_queue);
}
EXPORT_SYMBOL(pps_event);
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index 41ae2fc..52de2f1 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -212,6 +212,9 @@ static int pps_cdev_open(struct inode *inode, struct file *file)
struct pps_device *pps = container_of(inode->i_cdev,
struct pps_device, cdev);
+ /* Lock the PPS source against (possible) deregistration */
+ atomic_inc(&pps->usage);
+
file->private_data = pps;
return 0;
@@ -219,7 +222,11 @@ static int pps_cdev_open(struct inode *inode, struct file *file)
static int pps_cdev_release(struct inode *inode, struct file *file)
{
- /* Nop */
+ struct pps_device *pps = file->private_data;
+
+ /* Free the PPS source and wake up (possible) deregistration */
+ atomic_dec(&pps->usage);
+ wake_up_all(&pps->usage_queue);
return 0;
}
diff --git a/include/linux/pps.h b/include/linux/pps.h
index a513da9..387b37f 100644
--- a/include/linux/pps.h
+++ b/include/linux/pps.h
@@ -171,6 +171,9 @@ struct pps_device {
struct fasync_struct *async_queue; /* fasync method */
spinlock_t lock;
+ atomic_t usage; /* usage count */
+ wait_queue_head_t usage_queue;
+
struct class_device class_dev;
};
Rodolfo
--
GNU/Linux Solutions e-mail: giometti at enneenne.com
Linux Device Driver giometti at gnudd.com
Embedded Systems giometti at linux.it
UNIX programming phone: +39 349 2432127
More information about the LinuxPPS
mailing list