Re: [PATCH v9 4/4] bus: mhi: Add userspace client interface driver

2020-10-26 Thread Hemant Kumar

Hi Loic,

On 10/26/20 10:34 AM, Loic Poulain wrote:

Hi Hemant,

That looks better IMHO, just small comments on locking.


[..]

+static ssize_t mhi_uci_write(struct file *file,
+                            const char __user *buf,
+                            size_t count,
+                            loff_t *offp)
+{
+       struct uci_dev *udev = file->private_data;
+       struct mhi_device *mhi_dev = udev->mhi_dev;
+       struct device *dev = _dev->dev;
+       struct uci_chan *uchan = udev->uchan;
+       size_t bytes_xfered = 0;
+       int ret, nr_avail = 0;
+
+       /* if ul channel is not supported return error */
+       if (!buf || !count || !mhi_dev->ul_chan)
+               return -EINVAL;
+
+       dev_dbg(dev, "%s: to xfer: %zu bytes\n", __func__, count);
+
+       mutex_lock(>write_lock);


Maybe mutex_lock_interruptible is more appropriate here (same in read fops).

i agree, will return -EINTR if mutex_lock_interruptible returns < 0.



[..]

+static ssize_t mhi_uci_read(struct file *file,
+                           char __user *buf,
+                           size_t count,
+                           loff_t *ppos)
+{
+       struct uci_dev *udev = file->private_data;
+       struct mhi_device *mhi_dev = udev->mhi_dev;
+       struct uci_chan *uchan = udev->uchan;
+       struct device *dev = _dev->dev;
+       struct uci_buf *ubuf;
+       size_t rx_buf_size;
+       char *ptr;
+       size_t to_copy;
+       int ret = 0;
+
+       /* if dl channel is not supported return error */
+       if (!buf || !mhi_dev->dl_chan)
+               return -EINVAL;
+
+       mutex_lock(>read_lock);
+       spin_lock_bh(>dl_pending_lock);
+       /* No data available to read, wait */
+       if (!uchan->cur_buf && list_empty(>dl_pending)) {
+               dev_dbg(dev, "No data available to read, waiting\n");
+
+               spin_unlock_bh(>dl_pending_lock);
+               ret = wait_event_interruptible(uchan->dl_wq,
+                                              (!udev->enabled ||
+   
  !list_empty(>dl_pending)));



If you need to protect dl_pending list against concurent access, you 
need to do it in wait_event as well. I would suggest to lookg at 
`wait_event_interruptible_lock_irq` function, that allows to pass a 
locked spinlock as parameter. That would be safer and prevent this 
lock/unlock dance.
When using this API difference is, first we take spin_lock_bh() and then 
wait API is using spin_unlock_irq()/spin_lock_irq(). I am getting
"BUG: scheduling while atomic" when i use this way. When i changed 
spin_lock_bh to spin_lock_irq then we got rid of the kernel BUG.


Thanks,
Hemant

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


[PATCH v9 4/4] bus: mhi: Add userspace client interface driver

2020-10-23 Thread Hemant Kumar
This MHI client driver allows userspace clients to transfer
raw data between MHI device and host using standard file operations.
Driver instantiates UCI device object which is associated to device
file node. UCI device object instantiates UCI channel object when device
file node is opened. UCI channel object is used to manage MHI channels
by calling MHI core APIs for read and write operations. MHI channels
are started as part of device open(). MHI channels remain in start
state until last release() is called on UCI device file node. Device
file node is created with format

/dev/mhi__

Currently it supports LOOPBACK channel.

Signed-off-by: Hemant Kumar 
---
 drivers/bus/mhi/Kconfig  |  13 +
 drivers/bus/mhi/Makefile |   4 +
 drivers/bus/mhi/uci.c| 658 +++
 3 files changed, 675 insertions(+)
 create mode 100644 drivers/bus/mhi/uci.c

diff --git a/drivers/bus/mhi/Kconfig b/drivers/bus/mhi/Kconfig
index e841c10..476cc55 100644
--- a/drivers/bus/mhi/Kconfig
+++ b/drivers/bus/mhi/Kconfig
@@ -20,3 +20,16 @@ config MHI_BUS_DEBUG
  Enable debugfs support for use with the MHI transport. Allows
  reading and/or modifying some values within the MHI controller
  for debug and test purposes.
+
+config MHI_UCI
+   tristate "MHI UCI"
+   depends on MHI_BUS
+   help
+ MHI based Userspace Client Interface (UCI) driver is used for
+ transferring raw data between host and device using standard file
+ operations from userspace. Open, read, write, and close operations
+ are supported by this driver. Please check mhi_uci_match_table for
+ all supported channels that are exposed to userspace.
+
+ To compile this driver as a module, choose M here: the module will be
+ called mhi_uci.
diff --git a/drivers/bus/mhi/Makefile b/drivers/bus/mhi/Makefile
index 19e6443..80feefb 100644
--- a/drivers/bus/mhi/Makefile
+++ b/drivers/bus/mhi/Makefile
@@ -1,2 +1,6 @@
 # core layer
 obj-y += core/
+
+# MHI client
+mhi_uci-y := uci.o
+obj-$(CONFIG_MHI_UCI) += mhi_uci.o
diff --git a/drivers/bus/mhi/uci.c b/drivers/bus/mhi/uci.c
new file mode 100644
index 000..6c64356
--- /dev/null
+++ b/drivers/bus/mhi/uci.c
@@ -0,0 +1,658 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.*/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define DEVICE_NAME "mhi"
+#define MHI_UCI_DRIVER_NAME "mhi_uci"
+#define MAX_UCI_MINORS 128
+
+static DEFINE_IDR(uci_idr);
+static DEFINE_MUTEX(uci_drv_mutex);
+static struct class *uci_dev_class;
+static int uci_dev_major;
+
+/**
+ * struct uci_chan - MHI channel for a UCI device
+ * @udev: associated UCI device object
+ * @ul_wq: wait queue for writer
+ * @write_lock: mutex write lock for ul channel
+ * @dl_wq: wait queue for reader
+ * @read_lock: mutex read lock for dl channel
+ * @dl_pending_lock: spin lock for dl_pending list
+ * @dl_pending: list of dl buffers userspace is waiting to read
+ * @cur_buf: current buffer userspace is reading
+ * @dl_size: size of the current dl buffer userspace is reading
+ * @ref_count: uci_chan reference count
+ */
+struct uci_chan {
+   struct uci_dev *udev;
+   wait_queue_head_t ul_wq;
+
+   /* ul channel lock to synchronize multiple writes */
+   struct mutex write_lock;
+
+   wait_queue_head_t dl_wq;
+
+   /* dl channel lock to synchronize multiple reads */
+   struct mutex read_lock;
+
+   /*
+* protects pending list in bh context, channel release, read and
+* poll
+*/
+   spinlock_t dl_pending_lock;
+
+   struct list_head dl_pending;
+   struct uci_buf *cur_buf;
+   size_t dl_size;
+   struct kref ref_count;
+};
+
+/**
+ * struct uci_buf - UCI buffer
+ * @data: data buffer
+ * @len: length of data buffer
+ * @node: list node of the UCI buffer
+ */
+struct uci_buf {
+   void *data;
+   size_t len;
+   struct list_head node;
+};
+
+/**
+ * struct uci_dev - MHI UCI device
+ * @minor: UCI device node minor number
+ * @mhi_dev: associated mhi device object
+ * @uchan: UCI uplink and downlink channel object
+ * @mtu: max TRE buffer length
+ * @enabled: Flag to track the state of the UCI device
+ * @lock: mutex lock to manage uchan object
+ * @ref_count: uci_dev reference count
+ */
+struct uci_dev {
+   unsigned int minor;
+   struct mhi_device *mhi_dev;
+   struct uci_chan *uchan;
+   size_t mtu;
+   bool enabled;
+
+   /* synchronize open, release and driver remove */
+   struct mutex lock;
+   struct kref ref_count;
+};
+
+static void mhi_uci_dev_chan_release(struct kref *ref)
+{
+   struct uci_buf *buf_itr, *tmp;
+   struct uci_chan *uchan =
+   container_of(ref, struct uci_chan, ref_count);
+
+   if (uchan->udev->enabled)
+   mhi_unprepare_from_transfer(uchan->udev->mhi_dev);
+
+