diff options
author | Dmitry Kalinkin <dmitry.kalinkin@gmail.com> | 2015-02-26 10:53:10 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-03-06 20:03:22 -0500 |
commit | c74a804f115bdedcac72ea52ca33f46cfae3b74f (patch) | |
tree | 0e2c88fa7c1c0ba49147594f70d99688e9345c10 | |
parent | 0cd189a42da07c89c809debc1f6a75f5ec0f5c43 (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.c | 85 | ||||
-rw-r--r-- | drivers/vme/vme.c | 26 | ||||
-rw-r--r-- | include/linux/vme.h | 1 |
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 | }; |
103 | static struct image_desc image[VME_DEVS]; | 105 | static 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 *); |
135 | static loff_t vme_user_llseek(struct file *, loff_t, int); | 137 | static loff_t vme_user_llseek(struct file *, loff_t, int); |
136 | static long vme_user_unlocked_ioctl(struct file *, unsigned int, unsigned long); | 138 | static long vme_user_unlocked_ioctl(struct file *, unsigned int, unsigned long); |
139 | static int vme_user_mmap(struct file *file, struct vm_area_struct *vma); | ||
140 | |||
141 | static void vme_user_vm_open(struct vm_area_struct *vma); | ||
142 | static void vme_user_vm_close(struct vm_area_struct *vma); | ||
137 | 143 | ||
138 | static int vme_user_match(struct vme_dev *); | 144 | static int vme_user_match(struct vme_dev *); |
139 | static int vme_user_probe(struct vme_dev *); | 145 | static 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 | |||
159 | struct vme_user_vma_priv { | ||
160 | unsigned int minor; | ||
161 | atomic_t refcnt; | ||
162 | }; | ||
163 | |||
164 | static 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 | ||
589 | static 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 | |||
596 | static 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 | |||
611 | static 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 | |||
642 | static 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 | } |
610 | EXPORT_SYMBOL(vme_master_rmw); | 610 | EXPORT_SYMBOL(vme_master_rmw); |
611 | 611 | ||
612 | int 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 | } | ||
636 | EXPORT_SYMBOL(vme_master_mmap); | ||
637 | |||
612 | void vme_master_free(struct vme_resource *resource) | 638 | void 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); | |||
137 | ssize_t vme_master_write(struct vme_resource *, void *, size_t, loff_t); | 137 | ssize_t vme_master_write(struct vme_resource *, void *, size_t, loff_t); |
138 | unsigned int vme_master_rmw(struct vme_resource *, unsigned int, unsigned int, | 138 | unsigned int vme_master_rmw(struct vme_resource *, unsigned int, unsigned int, |
139 | unsigned int, loff_t); | 139 | unsigned int, loff_t); |
140 | int vme_master_mmap(struct vme_resource *resource, struct vm_area_struct *vma); | ||
140 | void vme_master_free(struct vme_resource *); | 141 | void vme_master_free(struct vme_resource *); |
141 | 142 | ||
142 | struct vme_resource *vme_dma_request(struct vme_dev *, u32); | 143 | struct vme_resource *vme_dma_request(struct vme_dev *, u32); |