diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-29 10:15:11 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-29 10:15:11 -0800 |
commit | 7ba31c3f2f1ee095d8126f4d3757fc3b2bc3c838 (patch) | |
tree | 591a3aab91b3a46a668c97c3cc537b47d004d64b /drivers/staging/axis-fifo/axis-fifo.c | |
parent | Merge tag 'tty-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty (diff) | |
parent | staging: most: usb: check for NULL device (diff) | |
download | linux-dev-7ba31c3f2f1ee095d8126f4d3757fc3b2bc3c838.tar.xz linux-dev-7ba31c3f2f1ee095d8126f4d3757fc3b2bc3c838.zip |
Merge tag 'staging-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging and IIO updates from Greg KH:
"Here is the big staging/iio driver patches for 5.6-rc1
Included in here are:
- lots of new IIO drivers and updates for that subsystem
- the usual huge quantity of minor cleanups for staging drivers
- removal of the following staging drivers:
- isdn/avm
- isdn/gigaset
- isdn/hysdn
- octeon-usb
- octeon ethernet
Overall we deleted far more lines than we added, removing over 40k of
old and obsolete driver code.
All of these changes have been in linux-next for a while with no
reported issues"
* tag 'staging-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (353 commits)
staging: most: usb: check for NULL device
staging: next: configfs: fix release link
staging: most: core: fix logging messages
staging: most: core: remove container struct
staging: most: remove struct device core driver
staging: most: core: drop device reference
staging: most: remove device from interface structure
staging: comedi: drivers: fix spelling mistake "to" -> "too"
staging: exfat: remove fs_func struct.
staging: wilc1000: avoid mutex unlock without lock in wilc_wlan_handle_txq()
staging: wilc1000: return zero on success and non-zero on function failure
staging: axis-fifo: replace spinlock with mutex
staging: wilc1000: remove unused code prior to throughput enhancement in SPI
staging: wilc1000: added 'wilc_' prefix for 'struct assoc_resp' name
staging: wilc1000: move firmware API struct's to separate header file
staging: wilc1000: remove use of infinite loop conditions
staging: kpc2000: rename variables with kpc namespace
staging: vt6656: Remove memory buffer from vnt_download_firmware.
staging: vt6656: Just check NEWRSR_DECRYPTOK for RX_FLAG_DECRYPTED.
staging: vt6656: Use vnt_rx_tail struct for tail variables.
...
Diffstat (limited to 'drivers/staging/axis-fifo/axis-fifo.c')
-rw-r--r-- | drivers/staging/axis-fifo/axis-fifo.c | 160 |
1 files changed, 101 insertions, 59 deletions
diff --git a/drivers/staging/axis-fifo/axis-fifo.c b/drivers/staging/axis-fifo/axis-fifo.c index 39e6c59df1e9..5801067e7c1b 100644 --- a/drivers/staging/axis-fifo/axis-fifo.c +++ b/drivers/staging/axis-fifo/axis-fifo.c @@ -16,7 +16,7 @@ #include <linux/kernel.h> #include <linux/wait.h> -#include <linux/spinlock_types.h> +#include <linux/mutex.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/init.h> @@ -133,9 +133,9 @@ struct axis_fifo { int has_tx_fifo; /* whether the IP has the tx fifo enabled */ wait_queue_head_t read_queue; /* wait queue for asynchronos read */ - spinlock_t read_queue_lock; /* lock for reading waitqueue */ + struct mutex read_lock; /* lock for reading */ wait_queue_head_t write_queue; /* wait queue for asynchronos write */ - spinlock_t write_queue_lock; /* lock for writing waitqueue */ + struct mutex write_lock; /* lock for writing */ unsigned int write_flags; /* write file flags */ unsigned int read_flags; /* read file flags */ @@ -336,7 +336,21 @@ static void reset_ip_core(struct axis_fifo *fifo) iowrite32(XLLF_INT_ALL_MASK, fifo->base_addr + XLLF_ISR_OFFSET); } -/* reads a single packet from the fifo as dictated by the tlast signal */ +/** + * axis_fifo_write() - Read a packet from AXIS-FIFO character device. + * @f Open file. + * @buf User space buffer to read to. + * @len User space buffer length. + * @off Buffer offset. + * + * As defined by the device's documentation, we need to check the device's + * occupancy before reading the length register and then the data. All these + * operations must be executed atomically, in order and one after the other + * without missing any. + * + * Returns the number of bytes read from the device or negative error code + * on failure. + */ static ssize_t axis_fifo_read(struct file *f, char __user *buf, size_t len, loff_t *off) { @@ -350,36 +364,37 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf, u32 tmp_buf[READ_BUF_SIZE]; if (fifo->read_flags & O_NONBLOCK) { - /* opened in non-blocking mode - * return if there are no packets available + /* + * Device opened in non-blocking mode. Try to lock it and then + * check if any packet is available. */ - if (!ioread32(fifo->base_addr + XLLF_RDFO_OFFSET)) + if (!mutex_trylock(&fifo->read_lock)) return -EAGAIN; + + if (!ioread32(fifo->base_addr + XLLF_RDFO_OFFSET)) { + ret = -EAGAIN; + goto end_unlock; + } } else { /* opened in blocking mode * wait for a packet available interrupt (or timeout) * if nothing is currently available */ - spin_lock_irq(&fifo->read_queue_lock); - ret = wait_event_interruptible_lock_irq_timeout - (fifo->read_queue, - ioread32(fifo->base_addr + XLLF_RDFO_OFFSET), - fifo->read_queue_lock, - (read_timeout >= 0) ? msecs_to_jiffies(read_timeout) : + mutex_lock(&fifo->read_lock); + ret = wait_event_interruptible_timeout(fifo->read_queue, + ioread32(fifo->base_addr + XLLF_RDFO_OFFSET), + (read_timeout >= 0) ? msecs_to_jiffies(read_timeout) : MAX_SCHEDULE_TIMEOUT); - spin_unlock_irq(&fifo->read_queue_lock); - if (ret == 0) { - /* timeout occurred */ - dev_dbg(fifo->dt_device, "read timeout"); - return -EAGAIN; - } else if (ret == -ERESTARTSYS) { - /* signal received */ - return -ERESTARTSYS; - } else if (ret < 0) { - dev_err(fifo->dt_device, "wait_event_interruptible_timeout() error in read (ret=%i)\n", - ret); - return ret; + if (ret <= 0) { + if (ret == 0) { + ret = -EAGAIN; + } else if (ret != -ERESTARTSYS) { + dev_err(fifo->dt_device, "wait_event_interruptible_timeout() error in read (ret=%i)\n", + ret); + } + + goto end_unlock; } } @@ -387,14 +402,16 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf, if (!bytes_available) { dev_err(fifo->dt_device, "received a packet of length 0 - fifo core will be reset\n"); reset_ip_core(fifo); - return -EIO; + ret = -EIO; + goto end_unlock; } if (bytes_available > len) { dev_err(fifo->dt_device, "user read buffer too small (available bytes=%zu user buffer bytes=%zu) - fifo core will be reset\n", bytes_available, len); reset_ip_core(fifo); - return -EINVAL; + ret = -EINVAL; + goto end_unlock; } if (bytes_available % sizeof(u32)) { @@ -403,7 +420,8 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf, */ dev_err(fifo->dt_device, "received a packet that isn't word-aligned - fifo core will be reset\n"); reset_ip_core(fifo); - return -EIO; + ret = -EIO; + goto end_unlock; } words_available = bytes_available / sizeof(u32); @@ -423,16 +441,37 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf, if (copy_to_user(buf + copied * sizeof(u32), tmp_buf, copy * sizeof(u32))) { reset_ip_core(fifo); - return -EFAULT; + ret = -EFAULT; + goto end_unlock; } copied += copy; words_available -= copy; } - return bytes_available; + ret = bytes_available; + +end_unlock: + mutex_unlock(&fifo->read_lock); + + return ret; } +/** + * axis_fifo_write() - Write buffer to AXIS-FIFO character device. + * @f Open file. + * @buf User space buffer to write to the device. + * @len User space buffer length. + * @off Buffer offset. + * + * As defined by the device's documentation, we need to write to the device's + * data buffer then to the device's packet length register atomically. Also, + * we need to lock before checking if the device has available space to avoid + * any concurrency issue. + * + * Returns the number of bytes written to the device or negative error code + * on failure. + */ static ssize_t axis_fifo_write(struct file *f, const char __user *buf, size_t len, loff_t *off) { @@ -465,12 +504,17 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf, } if (fifo->write_flags & O_NONBLOCK) { - /* opened in non-blocking mode - * return if there is not enough room available in the fifo + /* + * Device opened in non-blocking mode. Try to lock it and then + * check if there is any room to write the given buffer. */ + if (!mutex_trylock(&fifo->write_lock)) + return -EAGAIN; + if (words_to_write > ioread32(fifo->base_addr + XLLF_TDFV_OFFSET)) { - return -EAGAIN; + ret = -EAGAIN; + goto end_unlock; } } else { /* opened in blocking mode */ @@ -478,30 +522,22 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf, /* wait for an interrupt (or timeout) if there isn't * currently enough room in the fifo */ - spin_lock_irq(&fifo->write_queue_lock); - ret = wait_event_interruptible_lock_irq_timeout - (fifo->write_queue, - ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) + mutex_lock(&fifo->write_lock); + ret = wait_event_interruptible_timeout(fifo->write_queue, + ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) >= words_to_write, - fifo->write_queue_lock, - (write_timeout >= 0) ? - msecs_to_jiffies(write_timeout) : + (write_timeout >= 0) ? msecs_to_jiffies(write_timeout) : MAX_SCHEDULE_TIMEOUT); - spin_unlock_irq(&fifo->write_queue_lock); - if (ret == 0) { - /* timeout occurred */ - dev_dbg(fifo->dt_device, "write timeout\n"); - return -EAGAIN; - } else if (ret == -ERESTARTSYS) { - /* signal received */ - return -ERESTARTSYS; - } else if (ret < 0) { - /* unknown error */ - dev_err(fifo->dt_device, - "wait_event_interruptible_timeout() error in write (ret=%i)\n", - ret); - return ret; + if (ret <= 0) { + if (ret == 0) { + ret = -EAGAIN; + } else if (ret != -ERESTARTSYS) { + dev_err(fifo->dt_device, "wait_event_interruptible_timeout() error in write (ret=%i)\n", + ret); + } + + goto end_unlock; } } @@ -515,7 +551,8 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf, if (copy_from_user(tmp_buf, buf + copied * sizeof(u32), copy * sizeof(u32))) { reset_ip_core(fifo); - return -EFAULT; + ret = -EFAULT; + goto end_unlock; } for (i = 0; i < copy; i++) @@ -526,10 +563,15 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf, words_to_write -= copy; } + ret = copied * sizeof(u32); + /* write packet size to fifo */ - iowrite32(copied * sizeof(u32), fifo->base_addr + XLLF_TLR_OFFSET); + iowrite32(ret, fifo->base_addr + XLLF_TLR_OFFSET); + +end_unlock: + mutex_unlock(&fifo->write_lock); - return (ssize_t)copied * sizeof(u32); + return ret; } static irqreturn_t axis_fifo_irq(int irq, void *dw) @@ -789,8 +831,8 @@ static int axis_fifo_probe(struct platform_device *pdev) init_waitqueue_head(&fifo->read_queue); init_waitqueue_head(&fifo->write_queue); - spin_lock_init(&fifo->read_queue_lock); - spin_lock_init(&fifo->write_queue_lock); + mutex_init(&fifo->read_lock); + mutex_init(&fifo->write_lock); /* ---------------------------- * init device memory space |