All of lore.kernel.org
 help / color / mirror / Atom feed
From: minyard@acm.org (Corey Minyard)
To: Greg KH <greg@kroah.com>, lkml <linux-kernel@vger.kernel.org>,
	Sensors <sensors@stimpy.netroedge.com>
Subject: [PATCH] Add a non-blocking interface to the I2C code, part 4
Date: Thu, 19 May 2005 06:25:40 +0000	[thread overview]
Message-ID: <42261BCD.6030301@acm.org> (raw)
In-Reply-To: <42261AFB.40001@acm.org>

See part 1 for details on what this does...

-------------- next part --------------
Add a timer to the I2C layer.  This doesn't do much until the
non-blocking code shows up.

Signed-off-by: Corey Minyard <minyard@acm.org>

Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
=================================--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -30,9 +30,16 @@
 #include <linux/init.h>
 #include <linux/idr.h>
 #include <linux/seq_file.h>
+#include <linux/rcupdate.h>
 #include <asm/uaccess.h>
 
 
+static int i2c_stop_timer(struct i2c_adapter * adap);
+static void i2c_start_timer(struct i2c_adapter * adap,
+			    struct i2c_op_q_entry * entry);
+
+#define USEC_PER_JIFFIE (1000000 / HZ)
+
 static LIST_HEAD(adapters);
 static LIST_HEAD(drivers);
 static DECLARE_MUTEX(core_lists);
@@ -174,6 +181,19 @@
 	list_add_tail(&adap->list,&adapters);
 	INIT_LIST_HEAD(&adap->clients);
 
+	adap->timer = kmalloc(sizeof(*adap->timer), GFP_KERNEL);
+	if (!adap->timer) {
+		res = -ENOMEM;
+		goto out_unlock;
+	}
+		
+	init_timer(&adap->timer->timer);
+	spin_lock_init(&adap->timer->lock);
+	adap->timer->deleted = 0;
+	adap->timer->running = 0;
+	adap->timer->next_call_time = 0;
+	adap->timer->adapter = adap;
+
 	/* Add the adapter to the driver core.
 	 * If the parent pointer is not set up,
 	 * we add this adapter to the host bus.
@@ -216,6 +236,7 @@
 	struct i2c_driver *driver;
 	struct i2c_client *client;
 	int res = 0;
+	unsigned long flags;
 
 	down(&core_lists);
 
@@ -260,6 +281,19 @@
 		}
 	}
 
+	/* Stop the timer and free its memory */
+	spin_lock_irqsave(&adap->timer->lock, flags);
+	if (i2c_stop_timer(adap)) {
+		spin_unlock_irqrestore(&adap->timer->lock, flags);
+		kfree(adap->timer);
+	} else {
+		adap->timer->deleted = 1;
+		spin_unlock_irqrestore(&adap->timer->lock, flags);
+	}
+	/* Wait to make sure the timer is done. */
+	synchronize_kernel();
+	adap->timer = NULL;
+
 	/* clean up the sysfs representation */
 	init_completion(&adap->dev_released);
 	init_completion(&adap->class_dev_released);
@@ -582,6 +616,83 @@
 module_exit(i2c_exit);
 
 /* ----------------------------------------------------
+ * Timer operations
+ * ----------------------------------------------------
+ */
+static void i2c_handle_timer(unsigned long data);
+
+static void i2c_start_timer(struct i2c_adapter * adap,
+			    struct i2c_op_q_entry * entry)
+{
+	unsigned int wait_jiffies;
+	struct i2c_timer *t = adap->timer;
+	unsigned long flags;
+
+	wait_jiffies = ((entry->call_again_us + USEC_PER_JIFFIE - 1)
+			/ USEC_PER_JIFFIE);
+	if (wait_jiffies = 0)
+		wait_jiffies = 1;
+	/* This won't be polled from the user code, so
+	   start a timer to poll it. */
+	spin_lock_irqsave(&t->lock, flags);
+	if (! t->running) {
+		t->timer.expires = jiffies + wait_jiffies;
+		t->timer.data = (unsigned long) t;
+		t->timer.function = i2c_handle_timer;
+		t->running = 1;
+		t->next_call_time = wait_jiffies * USEC_PER_JIFFIE;
+		add_timer(&t->timer);
+		t->sequence = adap->timer_sequence;
+	}
+	spin_unlock_irqrestore(&t->lock, flags);
+}
+
+/* Returns true if the timer is stopped (or was not running), false if
+   not.  Must be called with the timer lock held. */
+static int i2c_stop_timer(struct i2c_adapter * adap)
+{
+	return (!adap->timer->running || del_timer(&adap->timer->timer));
+}
+
+static void i2c_handle_timer(unsigned long data)
+{
+	struct i2c_timer      * t = (void *) data;
+	struct i2c_adapter    * adap;
+	unsigned long         flags;
+	struct i2c_op_q_entry * entry;
+	unsigned int          sequence_match;
+
+	spin_lock_irqsave(&t->lock, flags);
+	if (t->deleted) {
+		spin_unlock_irqrestore(&t->lock, flags);
+		kfree(t);
+		return;
+	}
+
+	adap = t->adapter;
+	t->running = 0;
+	sequence_match = adap->timer_sequence = t->sequence;
+	spin_unlock_irqrestore(&t->lock, flags);
+
+	entry = i2c_entry_get(adap);
+	pr_debug("i2c_handle_timer: %p %p\n", adap, entry);
+	if (!entry)
+		return;
+
+	if (sequence_match) {
+		/* Poll will go here. */
+
+		if (!entry_completed(entry))
+			i2c_start_timer(adap, entry);
+	} else if (entry->use_timer)
+		/* We raced in timer deletion, just restart the
+		   timer if necessary. */
+		i2c_start_timer(adap, entry);
+
+	i2c_entry_put(adap, entry);
+}
+
+/* ----------------------------------------------------
  * the functional interface to the i2c busses.
  * ----------------------------------------------------
  */
@@ -1419,6 +1530,21 @@
 	if (atomic_dec_and_test(&e->completed)) {
 		/* We are the lucky winner!  We get to clean up the
 		   entry. */
+		if (e->use_timer) {
+			unsigned long    flags;
+			struct i2c_timer *t = adap->timer;
+			spin_lock_irqsave(&t->lock, flags);
+			if (!i2c_stop_timer(adap))
+				/* If we are unable to stop the timer, that
+				   means the timer has gone off but has not
+				   yet run the first part of the handler call.
+				   Increment the sequence so the timer handler
+				   can detect this. */
+				adap->timer_sequence++;
+			else
+				t->running = 0;
+			spin_unlock_irqrestore(&t->lock, flags);
+		}
 		if (e->complete)
 			e->complete(adap, e);
 	}
Index: linux-2.6.11-rc5-mm1/include/linux/i2c.h
=================================--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -36,6 +36,7 @@
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/timer.h>
 #include <asm/semaphore.h>
 #include <asm/atomic.h>
 
@@ -242,6 +243,21 @@
 };
 
 /*
+ * The timer has it's own separately allocated data structure because
+ * it needs to be able to exist even if the adapter is deleted (due to
+ * timer cancellation races).
+ */
+struct i2c_timer {
+	spinlock_t lock;
+	int deleted;
+	struct timer_list timer;
+	int running;
+	unsigned int next_call_time;
+	struct i2c_adapter *adapter;
+	unsigned int sequence;
+};
+
+/*
  * i2c_adapter is the structure used to identify a physical i2c bus along
  * with the access algorithms necessary to access it.
  */
@@ -263,6 +279,11 @@
 
 	struct semaphore bus_lock;
 
+	/* Used to time non-blocking operations.  The sequence is used
+	   to handle race conditions in the timer handler. */
+	struct i2c_timer *timer;
+	unsigned int timer_sequence;
+
 	int timeout;
 	int retries;
 	struct device dev;		/* the adapter device */

WARNING: multiple messages have this Message-ID (diff)
From: Corey Minyard <minyard@acm.org>
To: Greg KH <greg@kroah.com>, lkml <linux-kernel@vger.kernel.org>,
	Sensors <sensors@stimpy.netroedge.com>
Subject: [PATCH] Add a non-blocking interface to the I2C code, part 4
Date: Wed, 02 Mar 2005 14:02:21 -0600	[thread overview]
Message-ID: <42261BCD.6030301@acm.org> (raw)

[-- Attachment #1: Type: text/plain, Size: 45 bytes --]

See part 1 for details on what this does...


[-- Attachment #2: i2c_add_timer.diff --]
[-- Type: text/plain, Size: 6329 bytes --]

Add a timer to the I2C layer.  This doesn't do much until the
non-blocking code shows up.

Signed-off-by: Corey Minyard <minyard@acm.org>

Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
===================================================================
--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -30,9 +30,16 @@
 #include <linux/init.h>
 #include <linux/idr.h>
 #include <linux/seq_file.h>
+#include <linux/rcupdate.h>
 #include <asm/uaccess.h>
 
 
+static int i2c_stop_timer(struct i2c_adapter * adap);
+static void i2c_start_timer(struct i2c_adapter * adap,
+			    struct i2c_op_q_entry * entry);
+
+#define USEC_PER_JIFFIE (1000000 / HZ)
+
 static LIST_HEAD(adapters);
 static LIST_HEAD(drivers);
 static DECLARE_MUTEX(core_lists);
@@ -174,6 +181,19 @@
 	list_add_tail(&adap->list,&adapters);
 	INIT_LIST_HEAD(&adap->clients);
 
+	adap->timer = kmalloc(sizeof(*adap->timer), GFP_KERNEL);
+	if (!adap->timer) {
+		res = -ENOMEM;
+		goto out_unlock;
+	}
+		
+	init_timer(&adap->timer->timer);
+	spin_lock_init(&adap->timer->lock);
+	adap->timer->deleted = 0;
+	adap->timer->running = 0;
+	adap->timer->next_call_time = 0;
+	adap->timer->adapter = adap;
+
 	/* Add the adapter to the driver core.
 	 * If the parent pointer is not set up,
 	 * we add this adapter to the host bus.
@@ -216,6 +236,7 @@
 	struct i2c_driver *driver;
 	struct i2c_client *client;
 	int res = 0;
+	unsigned long flags;
 
 	down(&core_lists);
 
@@ -260,6 +281,19 @@
 		}
 	}
 
+	/* Stop the timer and free its memory */
+	spin_lock_irqsave(&adap->timer->lock, flags);
+	if (i2c_stop_timer(adap)) {
+		spin_unlock_irqrestore(&adap->timer->lock, flags);
+		kfree(adap->timer);
+	} else {
+		adap->timer->deleted = 1;
+		spin_unlock_irqrestore(&adap->timer->lock, flags);
+	}
+	/* Wait to make sure the timer is done. */
+	synchronize_kernel();
+	adap->timer = NULL;
+
 	/* clean up the sysfs representation */
 	init_completion(&adap->dev_released);
 	init_completion(&adap->class_dev_released);
@@ -582,6 +616,83 @@
 module_exit(i2c_exit);
 
 /* ----------------------------------------------------
+ * Timer operations
+ * ----------------------------------------------------
+ */
+static void i2c_handle_timer(unsigned long data);
+
+static void i2c_start_timer(struct i2c_adapter * adap,
+			    struct i2c_op_q_entry * entry)
+{
+	unsigned int wait_jiffies;
+	struct i2c_timer *t = adap->timer;
+	unsigned long flags;
+
+	wait_jiffies = ((entry->call_again_us + USEC_PER_JIFFIE - 1)
+			/ USEC_PER_JIFFIE);
+	if (wait_jiffies == 0)
+		wait_jiffies = 1;
+	/* This won't be polled from the user code, so
+	   start a timer to poll it. */
+	spin_lock_irqsave(&t->lock, flags);
+	if (! t->running) {
+		t->timer.expires = jiffies + wait_jiffies;
+		t->timer.data = (unsigned long) t;
+		t->timer.function = i2c_handle_timer;
+		t->running = 1;
+		t->next_call_time = wait_jiffies * USEC_PER_JIFFIE;
+		add_timer(&t->timer);
+		t->sequence = adap->timer_sequence;
+	}
+	spin_unlock_irqrestore(&t->lock, flags);
+}
+
+/* Returns true if the timer is stopped (or was not running), false if
+   not.  Must be called with the timer lock held. */
+static int i2c_stop_timer(struct i2c_adapter * adap)
+{
+	return (!adap->timer->running || del_timer(&adap->timer->timer));
+}
+
+static void i2c_handle_timer(unsigned long data)
+{
+	struct i2c_timer      * t = (void *) data;
+	struct i2c_adapter    * adap;
+	unsigned long         flags;
+	struct i2c_op_q_entry * entry;
+	unsigned int          sequence_match;
+
+	spin_lock_irqsave(&t->lock, flags);
+	if (t->deleted) {
+		spin_unlock_irqrestore(&t->lock, flags);
+		kfree(t);
+		return;
+	}
+
+	adap = t->adapter;
+	t->running = 0;
+	sequence_match = adap->timer_sequence == t->sequence;
+	spin_unlock_irqrestore(&t->lock, flags);
+
+	entry = i2c_entry_get(adap);
+	pr_debug("i2c_handle_timer: %p %p\n", adap, entry);
+	if (!entry)
+		return;
+
+	if (sequence_match) {
+		/* Poll will go here. */
+
+		if (!entry_completed(entry))
+			i2c_start_timer(adap, entry);
+	} else if (entry->use_timer)
+		/* We raced in timer deletion, just restart the
+		   timer if necessary. */
+		i2c_start_timer(adap, entry);
+
+	i2c_entry_put(adap, entry);
+}
+
+/* ----------------------------------------------------
  * the functional interface to the i2c busses.
  * ----------------------------------------------------
  */
@@ -1419,6 +1530,21 @@
 	if (atomic_dec_and_test(&e->completed)) {
 		/* We are the lucky winner!  We get to clean up the
 		   entry. */
+		if (e->use_timer) {
+			unsigned long    flags;
+			struct i2c_timer *t = adap->timer;
+			spin_lock_irqsave(&t->lock, flags);
+			if (!i2c_stop_timer(adap))
+				/* If we are unable to stop the timer, that
+				   means the timer has gone off but has not
+				   yet run the first part of the handler call.
+				   Increment the sequence so the timer handler
+				   can detect this. */
+				adap->timer_sequence++;
+			else
+				t->running = 0;
+			spin_unlock_irqrestore(&t->lock, flags);
+		}
 		if (e->complete)
 			e->complete(adap, e);
 	}
Index: linux-2.6.11-rc5-mm1/include/linux/i2c.h
===================================================================
--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -36,6 +36,7 @@
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/timer.h>
 #include <asm/semaphore.h>
 #include <asm/atomic.h>
 
@@ -242,6 +243,21 @@
 };
 
 /*
+ * The timer has it's own separately allocated data structure because
+ * it needs to be able to exist even if the adapter is deleted (due to
+ * timer cancellation races).
+ */
+struct i2c_timer {
+	spinlock_t lock;
+	int deleted;
+	struct timer_list timer;
+	int running;
+	unsigned int next_call_time;
+	struct i2c_adapter *adapter;
+	unsigned int sequence;
+};
+
+/*
  * i2c_adapter is the structure used to identify a physical i2c bus along
  * with the access algorithms necessary to access it.
  */
@@ -263,6 +279,11 @@
 
 	struct semaphore bus_lock;
 
+	/* Used to time non-blocking operations.  The sequence is used
+	   to handle race conditions in the timer handler. */
+	struct i2c_timer *timer;
+	unsigned int timer_sequence;
+
 	int timeout;
 	int retries;
 	struct device dev;		/* the adapter device */

       reply	other threads:[~2005-05-19  6:25 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-03-02 20:02 Corey Minyard [this message]
2005-05-19  6:25 ` [PATCH] Add a non-blocking interface to the I2C code, part 4 Corey Minyard
  -- strict thread matches above, loose matches on Subject: below --
2005-03-02 20:04 [PATCH] Add a non-blocking interface to the I2C code, part 7 Corey Minyard
2005-05-19  6:25 ` Corey Minyard
2005-03-02 20:04 [PATCH] Add a non-blocking interface to the I2C code, part 6 Corey Minyard
2005-05-19  6:25 ` Corey Minyard
2005-03-02 20:02 [PATCH] Add a non-blocking interface to the I2C code, part 5 Corey Minyard
2005-05-19  6:25 ` Corey Minyard
2005-03-02 20:01 [PATCH] Add a non-blocking interface to the I2C code, part3 Corey Minyard
2005-05-19  6:25 ` Corey Minyard
2005-03-02 20:00 [PATCH] Add a non-blocking interface to the I2C code, part 2 Corey Minyard
2005-05-19  6:25 ` Corey Minyard
2005-03-02 19:58 [PATCH] Add a non-blocking interface to the I2C code, part 1 Corey Minyard
2005-05-19  6:25 ` Corey Minyard
2005-03-17  7:09 ` Greg KH
2005-05-19  6:25   ` Greg KH
2005-03-18 16:13   ` Corey Minyard
2005-05-19  6:25     ` Corey Minyard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=42261BCD.6030301@acm.org \
    --to=minyard@acm.org \
    --cc=greg@kroah.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sensors@stimpy.netroedge.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.