summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/comedi/comedi_fops.c78
-rw-r--r--drivers/comedi/drivers.c1
-rw-r--r--include/linux/comedi/comedidev.h7
3 files changed, 78 insertions, 8 deletions
diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c
index 40d8b29797ae..8253e4e8232b 100644
--- a/drivers/comedi/comedi_fops.c
+++ b/drivers/comedi/comedi_fops.c
@@ -38,6 +38,7 @@
* COMEDI_SRF_ERROR: indicates an COMEDI_CB_ERROR event has occurred
* since the last command was started
* COMEDI_SRF_RUNNING: command is running
+ * COMEDI_SRF_BUSY: command was started and subdevice still busy
* COMEDI_SRF_FREE_SPRIV: free s->private on detach
*
* COMEDI_SRF_BUSY_MASK: runflags that indicate the subdevice is "busy"
@@ -45,9 +46,11 @@
#define COMEDI_SRF_RT BIT(1)
#define COMEDI_SRF_ERROR BIT(2)
#define COMEDI_SRF_RUNNING BIT(27)
+#define COMEDI_SRF_BUSY BIT(28)
#define COMEDI_SRF_FREE_SPRIV BIT(31)
-#define COMEDI_SRF_BUSY_MASK (COMEDI_SRF_ERROR | COMEDI_SRF_RUNNING)
+#define COMEDI_SRF_BUSY_MASK \
+ (COMEDI_SRF_ERROR | COMEDI_SRF_RUNNING | COMEDI_SRF_BUSY)
/**
* struct comedi_file - Per-file private data for COMEDI device
@@ -665,6 +668,11 @@ static bool comedi_is_runflags_in_error(unsigned int runflags)
return runflags & COMEDI_SRF_ERROR;
}
+static bool comedi_is_runflags_busy(unsigned int runflags)
+{
+ return runflags & COMEDI_SRF_BUSY;
+}
+
/**
* comedi_is_subdevice_running() - Check if async command running on subdevice
* @s: COMEDI subdevice.
@@ -687,6 +695,46 @@ static bool __comedi_is_subdevice_running(struct comedi_subdevice *s)
return comedi_is_runflags_running(runflags);
}
+/**
+ * comedi_get_is_subdevice_running() - Get if async command running on subdevice
+ * @s: COMEDI subdevice.
+ *
+ * If an asynchronous COMEDI command is running on the subdevice, increment
+ * a reference counter. If the function return value indicates that a
+ * command is running, then the details of the command will not be destroyed
+ * before a matching call to comedi_put_is_subdevice_running().
+ *
+ * Return: %true if an asynchronous COMEDI command is active on the
+ * subdevice, else %false.
+ */
+bool comedi_get_is_subdevice_running(struct comedi_subdevice *s)
+{
+ unsigned long flags;
+ bool running;
+
+ spin_lock_irqsave(&s->spin_lock, flags);
+ running = __comedi_is_subdevice_running(s);
+ if (running)
+ refcount_inc(&s->async->run_active);
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+ return running;
+}
+EXPORT_SYMBOL_GPL(comedi_get_is_subdevice_running);
+
+/**
+ * comedi_put_is_subdevice_running() - Put if async command running on subdevice
+ * @s: COMEDI subdevice.
+ *
+ * Decrements the reference counter that was incremented when
+ * comedi_get_is_subdevice_running() returned %true.
+ */
+void comedi_put_is_subdevice_running(struct comedi_subdevice *s)
+{
+ if (refcount_dec_and_test(&s->async->run_active))
+ complete_all(&s->async->run_complete);
+}
+EXPORT_SYMBOL_GPL(comedi_put_is_subdevice_running);
+
bool comedi_can_auto_free_spriv(struct comedi_subdevice *s)
{
unsigned int runflags = __comedi_get_subdevice_runflags(s);
@@ -736,20 +784,28 @@ static void do_become_nonbusy(struct comedi_device *dev,
struct comedi_subdevice *s)
{
struct comedi_async *async = s->async;
+ unsigned int runflags;
+ unsigned long flags;
lockdep_assert_held(&dev->mutex);
- comedi_update_subdevice_runflags(s, COMEDI_SRF_RUNNING, 0);
- if (async) {
+ spin_lock_irqsave(&s->spin_lock, flags);
+ runflags = __comedi_get_subdevice_runflags(s);
+ __comedi_clear_subdevice_runflags(s, COMEDI_SRF_RUNNING |
+ COMEDI_SRF_BUSY);
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+ if (comedi_is_runflags_busy(runflags)) {
+ /*
+ * "Run active" counter was set to 1 when setting up the
+ * command. Decrement it and wait for it to become 0.
+ */
+ comedi_put_is_subdevice_running(s);
+ wait_for_completion(&async->run_complete);
comedi_buf_reset(s);
async->inttrig = NULL;
kfree(async->cmd.chanlist);
async->cmd.chanlist = NULL;
s->busy = NULL;
wake_up_interruptible_all(&async->wait_head);
- } else {
- dev_err(dev->class_dev,
- "BUG: (?) %s called with async=NULL\n", __func__);
- s->busy = NULL;
}
}
@@ -1860,8 +1916,14 @@ static int do_cmd_ioctl(struct comedi_device *dev,
if (async->cmd.flags & CMDF_WAKE_EOS)
async->cb_mask |= COMEDI_CB_EOS;
+ /*
+ * Set the "run active" counter with an initial count of 1 that will
+ * complete the "safe to reset" event when it is decremented to 0.
+ */
+ refcount_set(&s->async->run_active, 1);
+ reinit_completion(&s->async->run_complete);
comedi_update_subdevice_runflags(s, COMEDI_SRF_BUSY_MASK,
- COMEDI_SRF_RUNNING);
+ COMEDI_SRF_RUNNING | COMEDI_SRF_BUSY);
/*
* Set s->busy _after_ setting COMEDI_SRF_RUNNING flag to avoid
diff --git a/drivers/comedi/drivers.c b/drivers/comedi/drivers.c
index c9ebaadc5e82..fd6e6cbe47ad 100644
--- a/drivers/comedi/drivers.c
+++ b/drivers/comedi/drivers.c
@@ -677,6 +677,7 @@ static int __comedi_device_postconfig_async(struct comedi_device *dev,
return -ENOMEM;
init_waitqueue_head(&async->wait_head);
+ init_completion(&async->run_complete);
s->async = async;
async->max_bufsize = comedi_default_buf_maxsize_kb * 1024;
diff --git a/include/linux/comedi/comedidev.h b/include/linux/comedi/comedidev.h
index 4cb0400ad616..35fdc41845ce 100644
--- a/include/linux/comedi/comedidev.h
+++ b/include/linux/comedi/comedidev.h
@@ -15,6 +15,7 @@
#include <linux/spinlock_types.h>
#include <linux/rwsem.h>
#include <linux/kref.h>
+#include <linux/completion.h>
#include <linux/comedi.h>
#define COMEDI_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
@@ -272,6 +273,8 @@ struct comedi_buf_map {
* @events: Bit-vector of events that have occurred.
* @cmd: Details of comedi command in progress.
* @wait_head: Task wait queue for file reader or writer.
+ * @run_complete: "run complete" completion event.
+ * @run_active: "run active" reference counter.
* @cb_mask: Bit-vector of events that should wake waiting tasks.
* @inttrig: Software trigger function for command, or NULL.
*
@@ -357,6 +360,8 @@ struct comedi_async {
unsigned int events;
struct comedi_cmd cmd;
wait_queue_head_t wait_head;
+ struct completion run_complete;
+ refcount_t run_active;
unsigned int cb_mask;
int (*inttrig)(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int x);
@@ -584,6 +589,8 @@ struct comedi_device *comedi_dev_get_from_minor(unsigned int minor);
int comedi_dev_put(struct comedi_device *dev);
bool comedi_is_subdevice_running(struct comedi_subdevice *s);
+bool comedi_get_is_subdevice_running(struct comedi_subdevice *s);
+void comedi_put_is_subdevice_running(struct comedi_subdevice *s);
void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size);
void comedi_set_spriv_auto_free(struct comedi_subdevice *s);