aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Kalinkin <dmitry.kalinkin@gmail.com>2015-02-26 10:53:10 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-03-06 20:03:22 -0500
commitc74a804f115bdedcac72ea52ca33f46cfae3b74f (patch)
tree0e2c88fa7c1c0ba49147594f70d99688e9345c10
parent0cd189a42da07c89c809debc1f6a75f5ec0f5c43 (diff)
staging: vme: mmap() support for vme_user
We also make sure that user won't be able to reconfigure the window while it is mmap'ed. Signed-off-by: Dmitry Kalinkin <dmitry.kalinkin@gmail.com> Cc: Martyn Welch <martyn.welch@ge.com> Cc: Igor Alekseev <igor.alekseev@itep.ru> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/staging/vme/devices/vme_user.c85
-rw-r--r--drivers/vme/vme.c26
-rw-r--r--include/linux/vme.h1
3 files changed, 112 insertions, 0 deletions
diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c
index 87318386034b..19ba749bb122 100644
--- a/drivers/staging/vme/devices/vme_user.c
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -17,6 +17,7 @@
17 17
18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19 19
20#include <linux/atomic.h>
20#include <linux/cdev.h> 21#include <linux/cdev.h>
21#include <linux/delay.h> 22#include <linux/delay.h>
22#include <linux/device.h> 23#include <linux/device.h>
@@ -99,6 +100,7 @@ struct image_desc {
99 struct device *device; /* Sysfs device */ 100 struct device *device; /* Sysfs device */
100 struct vme_resource *resource; /* VME resource */ 101 struct vme_resource *resource; /* VME resource */
101 int users; /* Number of current users */ 102 int users; /* Number of current users */
103 int mmap_count; /* Number of current mmap's */
102}; 104};
103static struct image_desc image[VME_DEVS]; 105static struct image_desc image[VME_DEVS];
104 106
@@ -134,6 +136,10 @@ static ssize_t vme_user_write(struct file *, const char __user *, size_t,
134 loff_t *); 136 loff_t *);
135static loff_t vme_user_llseek(struct file *, loff_t, int); 137static loff_t vme_user_llseek(struct file *, loff_t, int);
136static long vme_user_unlocked_ioctl(struct file *, unsigned int, unsigned long); 138static long vme_user_unlocked_ioctl(struct file *, unsigned int, unsigned long);
139static int vme_user_mmap(struct file *file, struct vm_area_struct *vma);
140
141static void vme_user_vm_open(struct vm_area_struct *vma);
142static void vme_user_vm_close(struct vm_area_struct *vma);
137 143
138static int vme_user_match(struct vme_dev *); 144static int vme_user_match(struct vme_dev *);
139static int vme_user_probe(struct vme_dev *); 145static int vme_user_probe(struct vme_dev *);
@@ -147,6 +153,17 @@ static const struct file_operations vme_user_fops = {
147 .llseek = vme_user_llseek, 153 .llseek = vme_user_llseek,
148 .unlocked_ioctl = vme_user_unlocked_ioctl, 154 .unlocked_ioctl = vme_user_unlocked_ioctl,
149 .compat_ioctl = vme_user_unlocked_ioctl, 155 .compat_ioctl = vme_user_unlocked_ioctl,
156 .mmap = vme_user_mmap,
157};
158
159struct vme_user_vma_priv {
160 unsigned int minor;
161 atomic_t refcnt;
162};
163
164static const struct vm_operations_struct vme_user_vm_ops = {
165 .open = vme_user_vm_open,
166 .close = vme_user_vm_close,
150}; 167};
151 168
152 169
@@ -488,6 +505,11 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
488 505
489 case VME_SET_MASTER: 506 case VME_SET_MASTER:
490 507
508 if (image[minor].mmap_count != 0) {
509 pr_warn("Can't adjust mapped window\n");
510 return -EPERM;
511 }
512
491 copied = copy_from_user(&master, argp, sizeof(master)); 513 copied = copy_from_user(&master, argp, sizeof(master));
492 if (copied != 0) { 514 if (copied != 0) {
493 pr_warn("Partial copy from userspace\n"); 515 pr_warn("Partial copy from userspace\n");
@@ -564,6 +586,69 @@ vme_user_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
564 return ret; 586 return ret;
565} 587}
566 588
589static void vme_user_vm_open(struct vm_area_struct *vma)
590{
591 struct vme_user_vma_priv *vma_priv = vma->vm_private_data;
592
593 atomic_inc(&vma_priv->refcnt);
594}
595
596static void vme_user_vm_close(struct vm_area_struct *vma)
597{
598 struct vme_user_vma_priv *vma_priv = vma->vm_private_data;
599 unsigned int minor = vma_priv->minor;
600
601 if (!atomic_dec_and_test(&vma_priv->refcnt))
602 return;
603
604 mutex_lock(&image[minor].mutex);
605 image[minor].mmap_count--;
606 mutex_unlock(&image[minor].mutex);
607
608 kfree(vma_priv);
609}
610
611static int vme_user_master_mmap(unsigned int minor, struct vm_area_struct *vma)
612{
613 int err;
614 struct vme_user_vma_priv *vma_priv;
615
616 mutex_lock(&image[minor].mutex);
617
618 err = vme_master_mmap(image[minor].resource, vma);
619 if (err) {
620 mutex_unlock(&image[minor].mutex);
621 return err;
622 }
623
624 vma_priv = kmalloc(sizeof(struct vme_user_vma_priv), GFP_KERNEL);
625 if (vma_priv == NULL) {
626 mutex_unlock(&image[minor].mutex);
627 return -ENOMEM;
628 }
629
630 vma_priv->minor = minor;
631 atomic_set(&vma_priv->refcnt, 1);
632 vma->vm_ops = &vme_user_vm_ops;
633 vma->vm_private_data = vma_priv;
634
635 image[minor].mmap_count++;
636
637 mutex_unlock(&image[minor].mutex);
638
639 return 0;
640}
641
642static int vme_user_mmap(struct file *file, struct vm_area_struct *vma)
643{
644 unsigned int minor = MINOR(file_inode(file)->i_rdev);
645
646 if (type[minor] == MASTER_MINOR)
647 return vme_user_master_mmap(minor, vma);
648
649 return -ENODEV;
650}
651
567 652
568/* 653/*
569 * Unallocate a previously allocated buffer 654 * Unallocate a previously allocated buffer
diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c
index d95fb848dd03..6bab2c4ed77c 100644
--- a/drivers/vme/vme.c
+++ b/drivers/vme/vme.c
@@ -609,6 +609,32 @@ unsigned int vme_master_rmw(struct vme_resource *resource, unsigned int mask,
609} 609}
610EXPORT_SYMBOL(vme_master_rmw); 610EXPORT_SYMBOL(vme_master_rmw);
611 611
612int vme_master_mmap(struct vme_resource *resource, struct vm_area_struct *vma)
613{
614 struct vme_master_resource *image;
615 phys_addr_t phys_addr;
616 unsigned long vma_size;
617
618 if (resource->type != VME_MASTER) {
619 pr_err("Not a master resource\n");
620 return -EINVAL;
621 }
622
623 image = list_entry(resource->entry, struct vme_master_resource, list);
624 phys_addr = image->bus_resource.start + (vma->vm_pgoff << PAGE_SHIFT);
625 vma_size = vma->vm_end - vma->vm_start;
626
627 if (phys_addr + vma_size > image->bus_resource.end + 1) {
628 pr_err("Map size cannot exceed the window size\n");
629 return -EFAULT;
630 }
631
632 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
633
634 return vm_iomap_memory(vma, phys_addr, vma->vm_end - vma->vm_start);
635}
636EXPORT_SYMBOL(vme_master_mmap);
637
612void vme_master_free(struct vme_resource *resource) 638void vme_master_free(struct vme_resource *resource)
613{ 639{
614 struct vme_master_resource *master_image; 640 struct vme_master_resource *master_image;
diff --git a/include/linux/vme.h b/include/linux/vme.h
index 8cd6f19ca518..79242e9c06b8 100644
--- a/include/linux/vme.h
+++ b/include/linux/vme.h
@@ -137,6 +137,7 @@ ssize_t vme_master_read(struct vme_resource *, void *, size_t, loff_t);
137ssize_t vme_master_write(struct vme_resource *, void *, size_t, loff_t); 137ssize_t vme_master_write(struct vme_resource *, void *, size_t, loff_t);
138unsigned int vme_master_rmw(struct vme_resource *, unsigned int, unsigned int, 138unsigned int vme_master_rmw(struct vme_resource *, unsigned int, unsigned int,
139 unsigned int, loff_t); 139 unsigned int, loff_t);
140int vme_master_mmap(struct vme_resource *resource, struct vm_area_struct *vma);
140void vme_master_free(struct vme_resource *); 141void vme_master_free(struct vme_resource *);
141 142
142struct vme_resource *vme_dma_request(struct vme_dev *, u32); 143struct vme_resource *vme_dma_request(struct vme_dev *, u32);