linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: corbet@lwn.net (Jonathan Corbet)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 3/6 v14] gpio: Add userland device interface to block GPIO
Date: Tue, 22 Jan 2013 18:03:49 -0700	[thread overview]
Message-ID: <20130122180349.068f2c6a@lwn.net> (raw)
In-Reply-To: <1358856404-8975-4-git-send-email-stigge@antcom.de>

On Tue, 22 Jan 2013 13:06:41 +0100
Roland Stigge <stigge@antcom.de> wrote:

> This patch adds a character device interface to the block GPIO system.

So I was looking at this, and a couple of things caught my eye...

> +static int gpio_block_fop_open(struct inode *in, struct file *f)
> +{
> +	int i;
> +	struct gpio_block *block = gpio_block_find_by_minor(MINOR(in->i_rdev));
> +	int status;
> +	int irq;
> +
> +	if (!block)
> +		return -ENOENT;
> +
> +	block->irq_controlled = false;
> +	block->got_int = false;
> +	spin_lock_init(&block->lock);

So...  there is no protection I can find against multiple opens here.
Meaning that the second process to open the device will reinitialize the
lock (and other variables), regardless of their current state.

More to the point, though, I'm not at all clear on what this lock protects?
It seems to be restricted to the got_int flag, which could be manipulated -
without locks - with bitops?  Or am I missing something?

> +	init_waitqueue_head(&block->wait_queue);
> +	f->private_data = block;
> +
> +	for (i = 0; i < block->ngpio; i++) {
> +		status = gpio_request(block->gpio[i], block->name);

Hmm...  the documentation for the API says that gpio_request() has to be
called separately.  But now you're doing it here?  That's probably OK, but
calling gpio_free() at close time could lead to interesting results if the
code that set up the block expects them to still be allocated.  It seems
like the API should be consistent with regard to this - either call
gpio_request() when the block is created, or always require the caller to
do it.

A quick look shows that the sysfs interface does the same thing?  So now
you're already double-requesting and freeing the gpios?

> +		if (status)
> +			goto err1;
> +
> +		irq = gpio_to_irq(block->gpio[i]);
> +		if (irq >= 0 &&
> +		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
> +		    !gpio_block_is_irq_duplicate(block, i)) {
> +			status = request_irq(irq, gpio_block_irq_handler,
> +					     IRQF_SHARED,
> +					     block->name, block);
> +			if (status)
> +				goto err2;
> +
> +			block->irq_controlled = true;
> +		}
> +	}

This forces the block to work in the IRQ-driven mode if a line is capable
of it, regardless of whether the creator (or the process calling open())
wants that.  It seems like that should be controllable separately?

> +
> +	return 0;
> +
> +err1:
> +	while (i > 0) {
> +		i--;
> +
> +		irq = gpio_to_irq(block->gpio[i]);
> +		if (irq >= 0 &&
> +		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
> +		    !gpio_block_is_irq_duplicate(block, i))
> +			free_irq(irq, block);
> +err2:
> +		gpio_free(block->gpio[i]);

Um...wait...you're jumping into the middle of the while loop?  I guess that
will work, but ... hmm...

> +	}
> +	return status;
> +}
> +
> +static int gpio_block_fop_release(struct inode *in, struct file *f)
> +{
> +	int i;
> +	struct gpio_block *block = (struct gpio_block *)f->private_data;

Is there anything that will have prevented a call to gpio_block_free()
while the device is open?  This seems like a concern for all of the fops
here. 

> +	for (i = 0; i < block->ngpio; i++) {
> +		int irq = gpio_to_irq(block->gpio[i]);
> +
> +		if (irq >= 0 &&
> +		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
> +		    !gpio_block_is_irq_duplicate(block, i))
> +			free_irq(irq, block);
> +
> +		gpio_free(block->gpio[i]);
> +	}
> +
> +	return 0;
> +}
> +
> +static int got_int(struct gpio_block *block)
> +{
> +	unsigned long flags;
> +	int result;
> +
> +	spin_lock_irqsave(&block->lock, flags);
> +	result = block->got_int;
> +	spin_unlock_irqrestore(&block->lock, flags);

The lock doesn't really buy you much here.  Might you have wanted to reset
block->got_int here too?

> +
> +	return result;
> +}
> +
> +static ssize_t gpio_block_fop_read(struct file *f, char __user *buf, size_t n,
> +				   loff_t *offset)
> +{
> +	struct gpio_block *block = (struct gpio_block *)f->private_data;
> +	int err;
> +	unsigned long flags;
> +
> +	if (block->irq_controlled) {
> +		if (!(f->f_flags & O_NONBLOCK))
> +			wait_event_interruptible(block->wait_queue,
> +						 got_int(block));
> +		spin_lock_irqsave(&block->lock, flags);
> +		block->got_int = 0;
> +		spin_unlock_irqrestore(&block->lock, flags);
> +	}

If two processes are waiting on the device, they might both wake up on the
same interrupt.  The second might even reset block->got_int after *another*
interrupt has arrived, causing it to be lost.  Or am I missing something?

> +	if (n >= sizeof(unsigned long)) {
> +		unsigned long values = gpio_block_get(block, block->cur_mask);
> +
> +		err = put_user(values, (unsigned long __user *)buf);
> +		if (err)
> +			return err;
> +
> +		return sizeof(unsigned long);
> +	}
> +	return 0;

And here you've consumed the interrupt even in the case where you'll not
actually return the gpios or return any data.  This one could maybe be
considered to be user-space programmer error, but still...

> +}
> +
> +static ssize_t gpio_block_fop_write(struct file *f, const char __user *buf,
> +				    size_t n, loff_t *offset)
> +{
> +	struct gpio_block *block = (struct gpio_block *)f->private_data;
> +	int err;
> +
> +	if (n >= sizeof(unsigned long)) {
> +		unsigned long values;
> +
> +		err = get_user(values, (unsigned long __user *)buf);
> +		if (err)
> +			return err;
> +		if (gpio_block_is_output(block))
> +			gpio_block_set(block, block->cur_mask, values);
> +		else
> +			return -EPERM;

Is EPERM right?  Or maybe EINVAL?

> +		return sizeof(unsigned long);
> +	}
> +	return 0;
> +}
> +
> +static long gpio_block_fop_ioctl(struct file *f, unsigned int cmd,
> +				 unsigned long arg)
> +{
> +	struct gpio_block *block = (struct gpio_block *)f->private_data;
> +	unsigned long __user *x = (unsigned long __user *)arg;
> +
> +	if (cmd == 0)
> +		return get_user(block->cur_mask, x);

...and this is a little weird.  It seems you should define a proper ioctl()
command code like everybody else does.

> +	return -EINVAL;
> +}
> +
> +static unsigned int gpio_block_fop_poll(struct file *f,
> +					struct poll_table_struct *pt)
> +{
> +	struct gpio_block *block = (struct gpio_block *)f->private_data;
> +
> +	if (!block->irq_controlled)
> +		return -ENOSYS;

Is that what you want, or should you just return POLLIN|POLLOUT in this
case? 

> +	if (!got_int(block))
> +		poll_wait(f, &block->wait_queue, pt);
> +
> +	if (got_int(block))
> +		return POLLIN;

How about 

	if (got_int(block))
		return POLLIN;
        poll_wait(f, &block->wait_queue, pt);

?

jon

  reply	other threads:[~2013-01-23  1:03 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-01-22 12:06 [PATCH 0/6 v14] gpio: Add block GPIO Roland Stigge
2013-01-22 12:06 ` [PATCH 1/6 v14] gpio: Add a block GPIO API to gpiolib Roland Stigge
2013-01-27 13:19   ` Stijn Devriendt
2013-01-27 13:58     ` Roland Stigge
2013-01-28 11:30       ` Stijn Devriendt
2013-01-31 20:56         ` Linus Walleij
2013-01-22 12:06 ` [PATCH 2/6 v14] gpio: Add sysfs support to block GPIO API Roland Stigge
2013-01-22 12:06 ` [PATCH 3/6 v14] gpio: Add userland device interface to block GPIO Roland Stigge
2013-01-23  1:03   ` Jonathan Corbet [this message]
2013-01-23 18:47     ` Roland Stigge
2013-01-24  9:13   ` Alexander Stein
2013-01-22 12:06 ` [PATCH 4/6 v14] gpiolib: Fix default attributes for class Roland Stigge
2013-01-22 12:06 ` [PATCH 5/6 v14] gpio: Add device tree support to block GPIO API Roland Stigge
2013-01-27 13:07   ` Stijn Devriendt
2013-01-27 14:29     ` Roland Stigge
2013-01-28 11:39       ` Stijn Devriendt
2013-01-28 12:29         ` Roland Stigge
2013-01-22 12:06 ` [PATCH 6/6 v14] gpio: Add block gpio to several gpio drivers Roland Stigge
2013-01-24 12:02   ` Stijn Devriendt
2013-01-24 12:17     ` Mark Brown
2013-01-24 12:19     ` Roland Stigge
2013-01-27 12:18       ` Stijn Devriendt
2013-01-27 13:48         ` Roland Stigge
2013-01-28 11:27           ` Stijn Devriendt

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=20130122180349.068f2c6a@lwn.net \
    --to=corbet@lwn.net \
    --cc=linux-arm-kernel@lists.infradead.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).