On Sun, May 31, 2015 at 6:06 AM, Greg Kroah-Hartman
<[email protected]> wrote:
> On Thu, May 28, 2015 at 03:06:57PM +0300, Dmitry Kalinkin wrote:
>> The first item in this submission documents previously introduced
>> vme_master_mmap() call. Following, there are three fixes for the tsi148
>> driver's DMA. There was one bug that rendered it imposible to use DMA
>> lists with more than one item. The other was related to the place where
>> dma_map_single was called on the first DMA descriptor in the DMA list. The
>> last bug was about DMA transfer not stopping at interruption by signal,
>> which is a possible DoS attack vector. I also made an attempt to fix the
>> same issue in the ca91cx42 driver. I don't have access to this hardware to
>> test, so this is based only on my understanding of the datasheet (checked
>> ca91c042's errata as well).
>>
>> A new /sys/bus/vme/dma0 device with a new ioctl for making DMA transfers
>> was introduced in vme_user driver. The logic of the function is similar to
>> the one found in existing drivers.
>>
>> One question that I had while implementing this feature was whether we
>> should keep vme_dma_attr objects until vme_dma_list_exec() call. API
>> doesn't specify this, the existing vme bridge drivers copy all information
>> from attributes during vme_dma_list_add(). So for simplicity this
>> implementation frees vme_dma_attr's before vme_dma_list_exec() is done.
>>
>> Changes in v2 [patches 1-6, now 1-5 and 8]:
>> * vme_addr check for vme_user DMA
>> * limit on DMA operation length in vme_user
>> * reorder dma_op ioctl struct to omit __packed attribute
>> * change dma_op->write into dma_op->dir
>> * use vme_check_window assure DMA operation correctness
>>
>> New changes include vme_user code cleanup, a couple of ca91cx42 fixes
>> (again, tested for compilation only).
>>
>> I also propose a change to export some of VME subsytem related constants
>> to the user space. These can be useful if vme_user is to go into the kernel.
>> Also, email
>> http://driverdev.linuxdriverproject.org/pipermail/driverdev-devel/2012-July/029084.html
>> mentions that we probably can now get rid of this comment:
>> > /* XXX We do not want to push aspace, cycle and width
>> > * to userspace as they are
>> > */
>>
>> v3 adresses code style problems
>
> I need an ack from the VME maintainer before I can take these...
>
> thanks,
>
> greg k-h
Greg,
I don't know how to make this happen. I could resend patches CC'ing
previous contributors to linux VME subsystem, and maybe get some
feedback that way.
Also, there are some patches that IMO don't need any special VME
subsystem expertise, namely:
Documentation: mention vme_master_mmap() in VME API
vme: ca91cx42: return error code on DMA error
staging: vme_user: remove unused counters
staging: vme_user: remove forward declarations
staging: vme_user: remove open/release
staging: vme_user: remove buf_unalloc helper
vme: tsi148: depend on HAS_DMA for Kconfig
Thanks,
Dmitry
On Wed, Jun 10, 2015 at 04:09:19PM +0300, Dmitry Kalinkin wrote:
> On Sun, May 31, 2015 at 6:06 AM, Greg Kroah-Hartman
> <[email protected]> wrote:
> > On Thu, May 28, 2015 at 03:06:57PM +0300, Dmitry Kalinkin wrote:
> >> The first item in this submission documents previously introduced
> >> vme_master_mmap() call. Following, there are three fixes for the tsi148
> >> driver's DMA. There was one bug that rendered it imposible to use DMA
> >> lists with more than one item. The other was related to the place where
> >> dma_map_single was called on the first DMA descriptor in the DMA list. The
> >> last bug was about DMA transfer not stopping at interruption by signal,
> >> which is a possible DoS attack vector. I also made an attempt to fix the
> >> same issue in the ca91cx42 driver. I don't have access to this hardware to
> >> test, so this is based only on my understanding of the datasheet (checked
> >> ca91c042's errata as well).
> >>
> >> A new /sys/bus/vme/dma0 device with a new ioctl for making DMA transfers
> >> was introduced in vme_user driver. The logic of the function is similar to
> >> the one found in existing drivers.
> >>
> >> One question that I had while implementing this feature was whether we
> >> should keep vme_dma_attr objects until vme_dma_list_exec() call. API
> >> doesn't specify this, the existing vme bridge drivers copy all information
> >> from attributes during vme_dma_list_add(). So for simplicity this
> >> implementation frees vme_dma_attr's before vme_dma_list_exec() is done.
> >>
> >> Changes in v2 [patches 1-6, now 1-5 and 8]:
> >> * vme_addr check for vme_user DMA
> >> * limit on DMA operation length in vme_user
> >> * reorder dma_op ioctl struct to omit __packed attribute
> >> * change dma_op->write into dma_op->dir
> >> * use vme_check_window assure DMA operation correctness
> >>
> >> New changes include vme_user code cleanup, a couple of ca91cx42 fixes
> >> (again, tested for compilation only).
> >>
> >> I also propose a change to export some of VME subsytem related constants
> >> to the user space. These can be useful if vme_user is to go into the kernel.
> >> Also, email
> >> http://driverdev.linuxdriverproject.org/pipermail/driverdev-devel/2012-July/029084.html
> >> mentions that we probably can now get rid of this comment:
> >> > /* XXX We do not want to push aspace, cycle and width
> >> > * to userspace as they are
> >> > */
> >>
> >> v3 adresses code style problems
> >
> > I need an ack from the VME maintainer before I can take these...
> >
> > thanks,
> >
> > greg k-h
>
> Greg,
>
> I don't know how to make this happen. I could resend patches CC'ing
> previous contributors to linux VME subsystem, and maybe get some
> feedback that way.
>
> Also, there are some patches that IMO don't need any special VME
> subsystem expertise, namely:
> Documentation: mention vme_master_mmap() in VME API
> vme: ca91cx42: return error code on DMA error
> staging: vme_user: remove unused counters
> staging: vme_user: remove forward declarations
> staging: vme_user: remove open/release
> staging: vme_user: remove buf_unalloc helper
> vme: tsi148: depend on HAS_DMA for Kconfig
I've taken all of these except patches 12, 13, 14 and 16.
thanks,
greg k-h
On Sat, Jun 13, 2015 at 3:31 AM, Greg Kroah-Hartman
<[email protected]> wrote:
> On Wed, Jun 10, 2015 at 04:09:19PM +0300, Dmitry Kalinkin wrote:
>> Also, there are some patches that IMO don't need any special VME
>> subsystem expertise, namely:
>> Documentation: mention vme_master_mmap() in VME API
>> vme: ca91cx42: return error code on DMA error
>> staging: vme_user: remove unused counters
>> staging: vme_user: remove forward declarations
>> staging: vme_user: remove open/release
>> staging: vme_user: remove buf_unalloc helper
>> vme: tsi148: depend on HAS_DMA for Kconfig
>
> I've taken all of these except patches 12, 13, 14 and 16.
>
I thought 12 was the most harmless out of the whole set. Am I wrong?
On Sat, Jun 13, 2015 at 05:04:28AM +0300, Dmitry Kalinkin wrote:
> On Sat, Jun 13, 2015 at 3:31 AM, Greg Kroah-Hartman
> <[email protected]> wrote:
> > On Wed, Jun 10, 2015 at 04:09:19PM +0300, Dmitry Kalinkin wrote:
> >> Also, there are some patches that IMO don't need any special VME
> >> subsystem expertise, namely:
> >> Documentation: mention vme_master_mmap() in VME API
> >> vme: ca91cx42: return error code on DMA error
> >> staging: vme_user: remove unused counters
> >> staging: vme_user: remove forward declarations
> >> staging: vme_user: remove open/release
> >> staging: vme_user: remove buf_unalloc helper
> >> vme: tsi148: depend on HAS_DMA for Kconfig
> >
> > I've taken all of these except patches 12, 13, 14 and 16.
> >
> I thought 12 was the most harmless out of the whole set. Am I wrong?
You added a new userspace api, that someone else is going to have to
maintain, that's not "harmless" at all.
> On 13 Jun 2015, at 05:24, Greg Kroah-Hartman <[email protected]> wrote:
>
> On Sat, Jun 13, 2015 at 05:04:28AM +0300, Dmitry Kalinkin wrote:
>> On Sat, Jun 13, 2015 at 3:31 AM, Greg Kroah-Hartman
>> <[email protected]> wrote:
>> I thought 12 was the most harmless out of the whole set. Am I wrong?
>
> You added a new userspace api, that someone else is going to have to
> maintain, that's not "harmless" at all.
That is 16.
12, 13, 14 are unrelated to uapi:
staging: vme_user: remove forward declarations
staging: vme_user: remove open/release
staging: vme_user: remove buf_unalloc helper-
On Sat, Jun 13, 2015 at 05:30:09AM +0300, Dmitry Kalinkin wrote:
>
> > On 13 Jun 2015, at 05:24, Greg Kroah-Hartman <[email protected]> wrote:
> >
> > On Sat, Jun 13, 2015 at 05:04:28AM +0300, Dmitry Kalinkin wrote:
> >> On Sat, Jun 13, 2015 at 3:31 AM, Greg Kroah-Hartman
> >> <[email protected]> wrote:
> >> I thought 12 was the most harmless out of the whole set. Am I wrong?
> >
> > You added a new userspace api, that someone else is going to have to
> > maintain, that's not "harmless" at all.
> That is 16.
Oops, no, that was 8, I didn't include that either, maybe that's why 12
and others didn't apply.
Anyway, rebase on my tree and resend and we can go from there.
thanks,
greg k-h
This reorders patches so that Greg can take first three, while fourth awaits
maintainer's approval.
Dmitry Kalinkin (4):
staging: vme_user: remove forward declarations
staging: vme_user: remove open/release
staging: vme_user: remove buf_unalloc helper
staging: vme_user: provide DMA functionality
drivers/staging/vme/devices/vme_user.c | 409 +++++++++++++++++++++------------
drivers/staging/vme/devices/vme_user.h | 11 +
2 files changed, 270 insertions(+), 150 deletions(-)
--
1.8.3.1
Reorder code so that forward declarations are not needed.
Signed-off-by: Dmitry Kalinkin <[email protected]>
Cc: Igor Alekseev <[email protected]>
---
drivers/staging/vme/devices/vme_user.c | 139 ++++++++++++++-------------------
1 file changed, 60 insertions(+), 79 deletions(-)
diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c
index 449b8cd..8e46d60 100644
--- a/drivers/staging/vme/devices/vme_user.c
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -116,44 +116,11 @@ static const int type[VME_DEVS] = { MASTER_MINOR, MASTER_MINOR,
CONTROL_MINOR
};
-
-static int vme_user_open(struct inode *, struct file *);
-static int vme_user_release(struct inode *, struct file *);
-static ssize_t vme_user_read(struct file *, char __user *, size_t, loff_t *);
-static ssize_t vme_user_write(struct file *, const char __user *, size_t,
- loff_t *);
-static loff_t vme_user_llseek(struct file *, loff_t, int);
-static long vme_user_unlocked_ioctl(struct file *, unsigned int, unsigned long);
-static int vme_user_mmap(struct file *file, struct vm_area_struct *vma);
-
-static void vme_user_vm_open(struct vm_area_struct *vma);
-static void vme_user_vm_close(struct vm_area_struct *vma);
-
-static int vme_user_match(struct vme_dev *);
-static int vme_user_probe(struct vme_dev *);
-static int vme_user_remove(struct vme_dev *);
-
-static const struct file_operations vme_user_fops = {
- .open = vme_user_open,
- .release = vme_user_release,
- .read = vme_user_read,
- .write = vme_user_write,
- .llseek = vme_user_llseek,
- .unlocked_ioctl = vme_user_unlocked_ioctl,
- .compat_ioctl = vme_user_unlocked_ioctl,
- .mmap = vme_user_mmap,
-};
-
struct vme_user_vma_priv {
unsigned int minor;
atomic_t refcnt;
};
-static const struct vm_operations_struct vme_user_vm_ops = {
- .open = vme_user_vm_open,
- .close = vme_user_vm_close,
-};
-
static int vme_user_open(struct inode *inode, struct file *file)
{
@@ -582,6 +549,11 @@ static void vme_user_vm_close(struct vm_area_struct *vma)
kfree(vma_priv);
}
+static const struct vm_operations_struct vme_user_vm_ops = {
+ .open = vme_user_vm_open,
+ .close = vme_user_vm_close,
+};
+
static int vme_user_master_mmap(unsigned int minor, struct vm_area_struct *vma)
{
int err;
@@ -623,6 +595,16 @@ static int vme_user_mmap(struct file *file, struct vm_area_struct *vma)
return -ENODEV;
}
+static const struct file_operations vme_user_fops = {
+ .open = vme_user_open,
+ .release = vme_user_release,
+ .read = vme_user_read,
+ .write = vme_user_write,
+ .llseek = vme_user_llseek,
+ .unlocked_ioctl = vme_user_unlocked_ioctl,
+ .compat_ioctl = vme_user_unlocked_ioctl,
+ .mmap = vme_user_mmap,
+};
/*
* Unallocate a previously allocated buffer
@@ -649,52 +631,6 @@ static void buf_unalloc(int num)
}
}
-static struct vme_driver vme_user_driver = {
- .name = driver_name,
- .match = vme_user_match,
- .probe = vme_user_probe,
- .remove = vme_user_remove,
-};
-
-
-static int __init vme_user_init(void)
-{
- int retval = 0;
-
- pr_info("VME User Space Access Driver\n");
-
- if (bus_num == 0) {
- pr_err("No cards, skipping registration\n");
- retval = -ENODEV;
- goto err_nocard;
- }
-
- /* Let's start by supporting one bus, we can support more than one
- * in future revisions if that ever becomes necessary.
- */
- if (bus_num > VME_USER_BUS_MAX) {
- pr_err("Driver only able to handle %d buses\n",
- VME_USER_BUS_MAX);
- bus_num = VME_USER_BUS_MAX;
- }
-
- /*
- * Here we just register the maximum number of devices we can and
- * leave vme_user_match() to allow only 1 to go through to probe().
- * This way, if we later want to allow multiple user access devices,
- * we just change the code in vme_user_match().
- */
- retval = vme_register_driver(&vme_user_driver, VME_MAX_SLOTS);
- if (retval != 0)
- goto err_reg;
-
- return retval;
-
-err_reg:
-err_nocard:
- return retval;
-}
-
static int vme_user_match(struct vme_dev *vdev)
{
int i;
@@ -916,6 +852,51 @@ static int vme_user_remove(struct vme_dev *dev)
return 0;
}
+static struct vme_driver vme_user_driver = {
+ .name = driver_name,
+ .match = vme_user_match,
+ .probe = vme_user_probe,
+ .remove = vme_user_remove,
+};
+
+static int __init vme_user_init(void)
+{
+ int retval = 0;
+
+ pr_info("VME User Space Access Driver\n");
+
+ if (bus_num == 0) {
+ pr_err("No cards, skipping registration\n");
+ retval = -ENODEV;
+ goto err_nocard;
+ }
+
+ /* Let's start by supporting one bus, we can support more than one
+ * in future revisions if that ever becomes necessary.
+ */
+ if (bus_num > VME_USER_BUS_MAX) {
+ pr_err("Driver only able to handle %d buses\n",
+ VME_USER_BUS_MAX);
+ bus_num = VME_USER_BUS_MAX;
+ }
+
+ /*
+ * Here we just register the maximum number of devices we can and
+ * leave vme_user_match() to allow only 1 to go through to probe().
+ * This way, if we later want to allow multiple user access devices,
+ * we just change the code in vme_user_match().
+ */
+ retval = vme_register_driver(&vme_user_driver, VME_MAX_SLOTS);
+ if (retval != 0)
+ goto err_reg;
+
+ return retval;
+
+err_reg:
+err_nocard:
+ return retval;
+}
+
static void __exit vme_user_exit(void)
{
vme_unregister_driver(&vme_user_driver);
--
1.8.3.1
Checking for image[minor].resource != NULL is not needed since all
resources are allocated before device is created.
image[minor].users accounting is deleted because it's not being used.
Signed-off-by: Dmitry Kalinkin <[email protected]>
Cc: Igor Alekseev <[email protected]>
---
drivers/staging/vme/devices/vme_user.c | 44 ----------------------------------
1 file changed, 44 deletions(-)
diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c
index 8e46d60..a72f7a9 100644
--- a/drivers/staging/vme/devices/vme_user.c
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -99,7 +99,6 @@ struct image_desc {
struct mutex mutex; /* Mutex for locking image */
struct device *device; /* Sysfs device */
struct vme_resource *resource; /* VME resource */
- int users; /* Number of current users */
int mmap_count; /* Number of current mmap's */
};
static struct image_desc image[VME_DEVS];
@@ -122,46 +121,6 @@ struct vme_user_vma_priv {
};
-static int vme_user_open(struct inode *inode, struct file *file)
-{
- int err;
- unsigned int minor = MINOR(inode->i_rdev);
-
- mutex_lock(&image[minor].mutex);
- /* Allow device to be opened if a resource is needed and allocated. */
- if (minor < CONTROL_MINOR && image[minor].resource == NULL) {
- pr_err("No resources allocated for device\n");
- err = -EINVAL;
- goto err_res;
- }
-
- /* Increment user count */
- image[minor].users++;
-
- mutex_unlock(&image[minor].mutex);
-
- return 0;
-
-err_res:
- mutex_unlock(&image[minor].mutex);
-
- return err;
-}
-
-static int vme_user_release(struct inode *inode, struct file *file)
-{
- unsigned int minor = MINOR(inode->i_rdev);
-
- mutex_lock(&image[minor].mutex);
-
- /* Decrement user count */
- image[minor].users--;
-
- mutex_unlock(&image[minor].mutex);
-
- return 0;
-}
-
/*
* We are going ot alloc a page during init per window for small transfers.
* Small transfers will go VME -> buffer -> user space. Larger (more than a
@@ -596,8 +555,6 @@ static int vme_user_mmap(struct file *file, struct vm_area_struct *vma)
}
static const struct file_operations vme_user_fops = {
- .open = vme_user_open,
- .release = vme_user_release,
.read = vme_user_read,
.write = vme_user_write,
.llseek = vme_user_llseek,
@@ -670,7 +627,6 @@ static int vme_user_probe(struct vme_dev *vdev)
mutex_init(&image[i].mutex);
image[i].device = NULL;
image[i].resource = NULL;
- image[i].users = 0;
}
/* Assign major and minor numbers for the driver */
--
1.8.3.1
buf_unalloc is essentially a vme_free_consistent:
1) image[i].kern_buf is never NULL in buf_alloc call
2) kern_buf, pci_buf and size_buf get zeroed in vme_user_probe anyway
Signed-off-by: Dmitry Kalinkin <[email protected]>
Cc: Igor Alekseev <[email protected]>
---
drivers/staging/vme/devices/vme_user.c | 31 ++++---------------------------
1 file changed, 4 insertions(+), 27 deletions(-)
diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c
index a72f7a9..9cca97a 100644
--- a/drivers/staging/vme/devices/vme_user.c
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -563,31 +563,6 @@ static const struct file_operations vme_user_fops = {
.mmap = vme_user_mmap,
};
-/*
- * Unallocate a previously allocated buffer
- */
-static void buf_unalloc(int num)
-{
- if (image[num].kern_buf) {
-#ifdef VME_DEBUG
- pr_debug("UniverseII:Releasing buffer at %p\n",
- image[num].pci_buf);
-#endif
-
- vme_free_consistent(image[num].resource, image[num].size_buf,
- image[num].kern_buf, image[num].pci_buf);
-
- image[num].kern_buf = NULL;
- image[num].pci_buf = 0;
- image[num].size_buf = 0;
-
-#ifdef VME_DEBUG
- } else {
- pr_debug("UniverseII: Buffer not allocated\n");
-#endif
- }
-}
-
static int vme_user_match(struct vme_dev *vdev)
{
int i;
@@ -765,7 +740,8 @@ err_master:
err_slave:
while (i > SLAVE_MINOR) {
i--;
- buf_unalloc(i);
+ vme_free_consistent(image[i].resource, image[i].size_buf,
+ image[i].kern_buf, image[i].pci_buf);
vme_slave_free(image[i].resource);
}
err_class:
@@ -795,7 +771,8 @@ static int vme_user_remove(struct vme_dev *dev)
for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0);
- buf_unalloc(i);
+ vme_free_consistent(image[i].resource, image[i].size_buf,
+ image[i].kern_buf, image[i].pci_buf);
vme_slave_free(image[i].resource);
}
--
1.8.3.1
This introduces a new dma device that provides a single ioctl call that
provides DMA read and write functionality to the user space.
Signed-off-by: Dmitry Kalinkin <[email protected]>
Cc: Igor Alekseev <[email protected]>
---
drivers/staging/vme/devices/vme_user.c | 201 ++++++++++++++++++++++++++++++++-
drivers/staging/vme/devices/vme_user.h | 11 ++
2 files changed, 209 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c
index 9cca97a..5cc782e 100644
--- a/drivers/staging/vme/devices/vme_user.c
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -79,15 +79,18 @@ static unsigned int bus_num;
* We shall support 4 masters and 4 slaves with this driver.
*/
#define VME_MAJOR 221 /* VME Major Device Number */
-#define VME_DEVS 9 /* Number of dev entries */
+#define VME_DEVS 10 /* Number of dev entries */
#define MASTER_MINOR 0
#define MASTER_MAX 3
#define SLAVE_MINOR 4
#define SLAVE_MAX 7
#define CONTROL_MINOR 8
+#define DMA_MINOR 9
-#define PCI_BUF_SIZE 0x20000 /* Size of one slave image buffer */
+#define PCI_BUF_SIZE 0x20000 /* Size of one slave image buffer */
+
+#define VME_MAX_DMA_LEN 0x4000000 /* Maximal DMA transfer length */
/*
* Structure to handle image related parameters.
@@ -112,7 +115,7 @@ static const int type[VME_DEVS] = { MASTER_MINOR, MASTER_MINOR,
MASTER_MINOR, MASTER_MINOR,
SLAVE_MINOR, SLAVE_MINOR,
SLAVE_MINOR, SLAVE_MINOR,
- CONTROL_MINOR
+ CONTROL_MINOR, DMA_MINOR
};
struct vme_user_vma_priv {
@@ -343,6 +346,168 @@ static loff_t vme_user_llseek(struct file *file, loff_t off, int whence)
return -EINVAL;
}
+static int vme_user_sg_to_dma_list(const struct vme_dma_op *dma_op,
+ struct sg_table *sgt,
+ int sg_count, struct vme_dma_list *dma_list)
+{
+ ssize_t pos = 0;
+ struct scatterlist *sg;
+ int i, ret;
+
+ for_each_sg(sgt->sgl, sg, sg_count, i) {
+ struct vme_dma_attr *pci_attr, *vme_attr, *src, *dest;
+ dma_addr_t hw_address = sg_dma_address(sg);
+ unsigned int hw_len = sg_dma_len(sg);
+
+ vme_attr = vme_dma_vme_attribute(dma_op->vme_addr + pos,
+ dma_op->aspace,
+ dma_op->cycle,
+ dma_op->dwidth);
+ if (!vme_attr)
+ return -ENOMEM;
+
+ pci_attr = vme_dma_pci_attribute(hw_address);
+ if (!pci_attr) {
+ vme_dma_free_attribute(vme_attr);
+ return -ENOMEM;
+ }
+
+ switch (dma_op->dir) {
+ case VME_DMA_MEM_TO_VME:
+ src = pci_attr;
+ dest = vme_attr;
+ break;
+ case VME_DMA_VME_TO_MEM:
+ src = vme_attr;
+ dest = pci_attr;
+ break;
+ }
+
+ ret = vme_dma_list_add(dma_list, src, dest, hw_len);
+
+ /*
+ * XXX VME API doesn't mention whether we should keep
+ * attributes around
+ */
+ vme_dma_free_attribute(vme_attr);
+ vme_dma_free_attribute(pci_attr);
+
+ if (ret)
+ return ret;
+
+ pos += hw_len;
+ }
+
+ return 0;
+}
+
+static enum dma_data_direction vme_dir_to_dma_dir(unsigned vme_dir)
+{
+ switch (vme_dir) {
+ case VME_DMA_VME_TO_MEM:
+ return DMA_FROM_DEVICE;
+ case VME_DMA_MEM_TO_VME:
+ return DMA_TO_DEVICE;
+ }
+
+ return DMA_NONE;
+}
+
+static ssize_t vme_user_dma_ioctl(unsigned int minor,
+ const struct vme_dma_op *dma_op)
+{
+ unsigned int offset = offset_in_page(dma_op->buf_vaddr);
+ unsigned long nr_pages;
+ enum dma_data_direction dir;
+ struct vme_dma_list *dma_list;
+ struct sg_table *sgt = NULL;
+ struct page **pages = NULL;
+ long got_pages;
+ ssize_t count;
+ int retval, sg_count;
+
+ /* Prevent WARN from dma_map_sg */
+ if (dma_op->count == 0)
+ return 0;
+
+ /*
+ * This is a voluntary limit to prevent huge allocation for pages
+ * array. VME_MAX_DMA_LEN is not a fundamental VME constraint.
+ */
+ count = min_t(size_t, dma_op->count, VME_MAX_DMA_LEN);
+ nr_pages = (offset + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ dir = vme_dir_to_dma_dir(dma_op->dir);
+ if (dir == DMA_NONE)
+ return -EINVAL;
+
+ pages = kmalloc_array(nr_pages, sizeof(pages[0]), GFP_KERNEL);
+ if (!pages) {
+ retval = -ENOMEM;
+ goto free;
+ }
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt) {
+ retval = -ENOMEM;
+ goto free;
+ }
+
+ dma_list = vme_new_dma_list(image[minor].resource);
+ if (!dma_list) {
+ retval = -ENOMEM;
+ goto free;
+ }
+
+ got_pages = get_user_pages_fast(dma_op->buf_vaddr, nr_pages,
+ dir == DMA_FROM_DEVICE, pages);
+ if (got_pages != nr_pages) {
+ pr_debug("Not all pages were pinned\n");
+ retval = (got_pages < 0) ? got_pages : -EFAULT;
+ goto release_pages;
+ }
+
+ retval = sg_alloc_table_from_pages(sgt, pages, nr_pages,
+ offset, count, GFP_KERNEL);
+ if (retval)
+ goto release_pages;
+
+ sg_count = dma_map_sg(vme_user_bridge->dev.parent,
+ sgt->sgl, sgt->nents, dir);
+ if (!sg_count) {
+ pr_debug("DMA mapping error\n");
+ retval = -EFAULT;
+ goto free_sgt;
+ }
+
+ retval = vme_user_sg_to_dma_list(dma_op, sgt, sg_count, dma_list);
+ if (retval)
+ goto dma_unmap;
+
+ retval = vme_dma_list_exec(dma_list);
+
+dma_unmap:
+ dma_unmap_sg(vme_user_bridge->dev.parent, sgt->sgl, sgt->nents, dir);
+
+free_sgt:
+ sg_free_table(sgt);
+
+release_pages:
+ if (got_pages > 0)
+ release_pages(pages, got_pages, 0);
+
+ vme_dma_list_free(dma_list);
+
+free:
+ kfree(sgt);
+ kfree(pages);
+
+ if (retval)
+ return retval;
+
+ return count;
+}
+
/*
* The ioctls provided by the old VME access method (the one at vmelinux.org)
* are most certainly wrong as the effectively push the registers layout
@@ -359,6 +524,7 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
struct vme_master master;
struct vme_slave slave;
struct vme_irq_id irq_req;
+ struct vme_dma_op dma_op;
unsigned long copied;
unsigned int minor = MINOR(inode->i_rdev);
int retval;
@@ -467,6 +633,19 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
break;
}
break;
+ case DMA_MINOR:
+ switch (cmd) {
+ case VME_DO_DMA:
+ copied = copy_from_user(&dma_op, argp,
+ sizeof(dma_op));
+ if (copied != 0) {
+ pr_warn("Partial copy from userspace\n");
+ return -EFAULT;
+ }
+
+ return vme_user_dma_ioctl(minor, &dma_op);
+ }
+ break;
}
return -EINVAL;
@@ -678,6 +857,15 @@ static int vme_user_probe(struct vme_dev *vdev)
}
}
+ image[DMA_MINOR].resource = vme_dma_request(vme_user_bridge,
+ VME_DMA_VME_TO_MEM | VME_DMA_MEM_TO_VME);
+ if (!image[DMA_MINOR].resource) {
+ dev_warn(&vdev->dev,
+ "Unable to allocate dma resource\n");
+ err = -ENOMEM;
+ goto err_master;
+ }
+
/* Create sysfs entries - on udev systems this creates the dev files */
vme_user_sysfs_class = class_create(THIS_MODULE, driver_name);
if (IS_ERR(vme_user_sysfs_class)) {
@@ -700,6 +888,9 @@ static int vme_user_probe(struct vme_dev *vdev)
case SLAVE_MINOR:
name = "bus/vme/s%d";
break;
+ case DMA_MINOR:
+ name = "bus/vme/dma0";
+ break;
default:
err = -EINVAL;
goto err_sysfs;
@@ -724,6 +915,8 @@ err_sysfs:
}
class_destroy(vme_user_sysfs_class);
+ vme_dma_free(image[DMA_MINOR].resource);
+
/* Ensure counter set correcty to unalloc all master windows */
i = MASTER_MAX + 1;
err_master:
@@ -764,6 +957,8 @@ static int vme_user_remove(struct vme_dev *dev)
}
class_destroy(vme_user_sysfs_class);
+ vme_dma_free(image[DMA_MINOR].resource);
+
for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
kfree(image[i].kern_buf);
vme_master_free(image[i].resource);
diff --git a/drivers/staging/vme/devices/vme_user.h b/drivers/staging/vme/devices/vme_user.h
index b8cc7bc..252b1c9 100644
--- a/drivers/staging/vme/devices/vme_user.h
+++ b/drivers/staging/vme/devices/vme_user.h
@@ -48,11 +48,22 @@ struct vme_irq_id {
__u8 statid;
};
+struct vme_dma_op {
+ __u64 vme_addr; /* Starting Address on the VMEbus */
+ __u64 buf_vaddr; /* Pointer to userspace memory */
+ __u32 count; /* Count of bytes to copy */
+ __u32 aspace; /* Address Space */
+ __u32 cycle; /* Cycle properties */
+ __u32 dwidth; /* Data transfer width */
+ __u32 dir; /* Transfer Direction */
+};
+
#define VME_GET_SLAVE _IOR(VME_IOC_MAGIC, 1, struct vme_slave)
#define VME_SET_SLAVE _IOW(VME_IOC_MAGIC, 2, struct vme_slave)
#define VME_GET_MASTER _IOR(VME_IOC_MAGIC, 3, struct vme_master)
#define VME_SET_MASTER _IOW(VME_IOC_MAGIC, 4, struct vme_master)
#define VME_IRQ_GEN _IOW(VME_IOC_MAGIC, 5, struct vme_irq_id)
+#define VME_DO_DMA _IOW(VME_IOC_MAGIC, 7, struct vme_dma_op)
#endif /* _VME_USER_H_ */
--
1.8.3.1
On Sat, Jun 13, 2015 at 04:34:00PM +0300, Dmitry Kalinkin wrote:
> This reorders patches so that Greg can take first three, while fourth awaits
> maintainer's approval.
>
> Dmitry Kalinkin (4):
> staging: vme_user: remove forward declarations
> staging: vme_user: remove open/release
> staging: vme_user: remove buf_unalloc helper
> staging: vme_user: provide DMA functionality
Applied the first 3, thanks.
greg k-h