--- /dev/null
+From fe25deb7737ce6c0879ccf79c99fa1221d428bf2 Mon Sep 17 00:00:00 2001
+From: Thomas Hellstrom <thellstrom@vmware.com>
+Date: Mon, 27 Mar 2017 11:21:25 +0200
+Subject: drm/ttm, drm/vmwgfx: Relax permission checking when opening surfaces
+
+From: Thomas Hellstrom <thellstrom@vmware.com>
+
+commit fe25deb7737ce6c0879ccf79c99fa1221d428bf2 upstream.
+
+Previously, when a surface was opened using a legacy (non prime) handle,
+it was verified to have been created by a client in the same master realm.
+Relax this so that opening is also allowed recursively if the client
+already has the surface open.
+
+This works around a regression in svga mesa where opening of a shared
+surface is used recursively to obtain surface information.
+
+Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
+Reviewed-by: Sinclair Yeh <syeh@vmware.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/gpu/drm/ttm/ttm_object.c | 10 +++++++---
+ drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 6 ++----
+ drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 4 ++--
+ drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 22 +++++++++-------------
+ include/drm/ttm/ttm_object.h | 5 ++++-
+ 5 files changed, 24 insertions(+), 23 deletions(-)
+
+--- a/drivers/gpu/drm/ttm/ttm_object.c
++++ b/drivers/gpu/drm/ttm/ttm_object.c
+@@ -179,7 +179,7 @@ int ttm_base_object_init(struct ttm_obje
+ if (unlikely(ret != 0))
+ goto out_err0;
+
+- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
++ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, false);
+ if (unlikely(ret != 0))
+ goto out_err1;
+
+@@ -318,7 +318,8 @@ EXPORT_SYMBOL(ttm_ref_object_exists);
+
+ int ttm_ref_object_add(struct ttm_object_file *tfile,
+ struct ttm_base_object *base,
+- enum ttm_ref_type ref_type, bool *existed)
++ enum ttm_ref_type ref_type, bool *existed,
++ bool require_existed)
+ {
+ struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
+ struct ttm_ref_object *ref;
+@@ -345,6 +346,9 @@ int ttm_ref_object_add(struct ttm_object
+ }
+
+ rcu_read_unlock();
++ if (require_existed)
++ return -EPERM;
++
+ ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref),
+ false, false);
+ if (unlikely(ret != 0))
+@@ -635,7 +639,7 @@ int ttm_prime_fd_to_handle(struct ttm_ob
+ prime = (struct ttm_prime_object *) dma_buf->priv;
+ base = &prime->base;
+ *handle = base->hash.key;
+- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
++ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, false);
+
+ dma_buf_put(dma_buf);
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+@@ -1075,10 +1075,8 @@ int vmw_fence_event_ioctl(struct drm_dev
+ (void) vmw_fence_obj_reference(fence);
+
+ if (user_fence_rep != NULL) {
+- bool existed;
+-
+- ret = ttm_ref_object_add(tfile, base,
+- TTM_REF_USAGE, &existed);
++ ret = ttm_ref_object_add(vmw_fp->tfile, base,
++ TTM_REF_USAGE, NULL, false);
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Failed to reference a fence "
+ "object.\n");
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+@@ -589,7 +589,7 @@ static int vmw_user_dmabuf_synccpu_grab(
+ return ret;
+
+ ret = ttm_ref_object_add(tfile, &user_bo->prime.base,
+- TTM_REF_SYNCCPU_WRITE, &existed);
++ TTM_REF_SYNCCPU_WRITE, &existed, false);
+ if (ret != 0 || existed)
+ ttm_bo_synccpu_write_release(&user_bo->dma.base);
+
+@@ -773,7 +773,7 @@ int vmw_user_dmabuf_reference(struct ttm
+
+ *handle = user_bo->prime.base.hash.key;
+ return ttm_ref_object_add(tfile, &user_bo->prime.base,
+- TTM_REF_USAGE, NULL);
++ TTM_REF_USAGE, NULL, false);
+ }
+
+ /*
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+@@ -891,17 +891,16 @@ vmw_surface_handle_reference(struct vmw_
+ uint32_t handle;
+ struct ttm_base_object *base;
+ int ret;
++ bool require_exist = false;
+
+ if (handle_type == DRM_VMW_HANDLE_PRIME) {
+ ret = ttm_prime_fd_to_handle(tfile, u_handle, &handle);
+ if (unlikely(ret != 0))
+ return ret;
+ } else {
+- if (unlikely(drm_is_render_client(file_priv))) {
+- DRM_ERROR("Render client refused legacy "
+- "surface reference.\n");
+- return -EACCES;
+- }
++ if (unlikely(drm_is_render_client(file_priv)))
++ require_exist = true;
++
+ if (ACCESS_ONCE(vmw_fpriv(file_priv)->locked_master)) {
+ DRM_ERROR("Locked master refused legacy "
+ "surface reference.\n");
+@@ -929,17 +928,14 @@ vmw_surface_handle_reference(struct vmw_
+
+ /*
+ * Make sure the surface creator has the same
+- * authenticating master.
++ * authenticating master, or is already registered with us.
+ */
+ if (drm_is_primary_client(file_priv) &&
+- user_srf->master != file_priv->master) {
+- DRM_ERROR("Trying to reference surface outside of"
+- " master domain.\n");
+- ret = -EACCES;
+- goto out_bad_resource;
+- }
++ user_srf->master != file_priv->master)
++ require_exist = true;
+
+- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
++ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL,
++ require_exist);
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Could not add a reference to a surface.\n");
+ goto out_bad_resource;
+--- a/include/drm/ttm/ttm_object.h
++++ b/include/drm/ttm/ttm_object.h
+@@ -229,6 +229,8 @@ extern void ttm_base_object_unref(struct
+ * @ref_type: The type of reference.
+ * @existed: Upon completion, indicates that an identical reference object
+ * already existed, and the refcount was upped on that object instead.
++ * @require_existed: Fail with -EPERM if an identical ref object didn't
++ * already exist.
+ *
+ * Checks that the base object is shareable and adds a ref object to it.
+ *
+@@ -243,7 +245,8 @@ extern void ttm_base_object_unref(struct
+ */
+ extern int ttm_ref_object_add(struct ttm_object_file *tfile,
+ struct ttm_base_object *base,
+- enum ttm_ref_type ref_type, bool *existed);
++ enum ttm_ref_type ref_type, bool *existed,
++ bool require_existed);
+
+ extern bool ttm_ref_object_exists(struct ttm_object_file *tfile,
+ struct ttm_base_object *base);
--- /dev/null
+From 63774069d9527a1aeaa4aa20e929ef5e8e9ecc38 Mon Sep 17 00:00:00 2001
+From: Murray McAllister <murray.mcallister@insomniasec.com>
+Date: Mon, 27 Mar 2017 11:15:12 +0200
+Subject: drm/vmwgfx: avoid calling vzalloc with a 0 size in vmw_get_cap_3d_ioctl()
+
+From: Murray McAllister <murray.mcallister@insomniasec.com>
+
+commit 63774069d9527a1aeaa4aa20e929ef5e8e9ecc38 upstream.
+
+In vmw_get_cap_3d_ioctl(), a user can supply 0 for a size that is
+used in vzalloc(). This eventually calls dump_stack() (in warn_alloc()),
+which can leak useful addresses to dmesg.
+
+Add check to avoid a size of 0.
+
+Signed-off-by: Murray McAllister <murray.mcallister@insomniasec.com>
+Reviewed-by: Sinclair Yeh <syeh@vmware.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
+@@ -186,7 +186,7 @@ int vmw_get_cap_3d_ioctl(struct drm_devi
+ bool gb_objects = !!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS);
+ struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
+
+- if (unlikely(arg->pad64 != 0)) {
++ if (unlikely(arg->pad64 != 0 || arg->max_size == 0)) {
+ DRM_ERROR("Illegal GET_3D_CAP argument.\n");
+ return -EINVAL;
+ }
--- /dev/null
+From e7e11f99564222d82f0ce84bd521e57d78a6b678 Mon Sep 17 00:00:00 2001
+From: Li Qiang <liq3ea@gmail.com>
+Date: Mon, 27 Mar 2017 20:10:53 -0700
+Subject: drm/vmwgfx: fix integer overflow in vmw_surface_define_ioctl()
+
+From: Li Qiang <liq3ea@gmail.com>
+
+commit e7e11f99564222d82f0ce84bd521e57d78a6b678 upstream.
+
+In vmw_surface_define_ioctl(), the 'num_sizes' is the sum of the
+'req->mip_levels' array. This array can be assigned any value from
+the user space. As both the 'num_sizes' and the array is uint32_t,
+it is easy to make 'num_sizes' overflow. The later 'mip_levels' is
+used as the loop count. This can lead an oob write. Add the check of
+'req->mip_levels' to avoid this.
+
+Signed-off-by: Li Qiang <liqiang6-s@360.cn>
+Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+@@ -713,8 +713,11 @@ int vmw_surface_define_ioctl(struct drm_
+ 128;
+
+ num_sizes = 0;
+- for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
++ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
++ if (req->mip_levels[i] > DRM_VMW_MAX_MIP_LEVELS)
++ return -EINVAL;
+ num_sizes += req->mip_levels[i];
++ }
+
+ if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * DRM_VMW_MAX_MIP_LEVELS ||
+ num_sizes == 0)
--- /dev/null
+From 36274ab8c596f1240c606bb514da329add2a1bcd Mon Sep 17 00:00:00 2001
+From: Murray McAllister <murray.mcallister@insomniasec.com>
+Date: Mon, 27 Mar 2017 11:12:53 +0200
+Subject: drm/vmwgfx: NULL pointer dereference in vmw_surface_define_ioctl()
+
+From: Murray McAllister <murray.mcallister@insomniasec.com>
+
+commit 36274ab8c596f1240c606bb514da329add2a1bcd upstream.
+
+Before memory allocations vmw_surface_define_ioctl() checks the
+upper-bounds of a user-supplied size, but does not check if the
+supplied size is 0.
+
+Add check to avoid NULL pointer dereferences.
+
+Signed-off-by: Murray McAllister <murray.mcallister@insomniasec.com>
+Reviewed-by: Sinclair Yeh <syeh@vmware.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+@@ -716,8 +716,8 @@ int vmw_surface_define_ioctl(struct drm_
+ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
+ num_sizes += req->mip_levels[i];
+
+- if (num_sizes > DRM_VMW_MAX_SURFACE_FACES *
+- DRM_VMW_MAX_MIP_LEVELS)
++ if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * DRM_VMW_MAX_MIP_LEVELS ||
++ num_sizes == 0)
+ return -EINVAL;
+
+ size = vmw_user_surface_size + 128 +
--- /dev/null
+From 53e16798b0864464c5444a204e1bb93ae246c429 Mon Sep 17 00:00:00 2001
+From: Thomas Hellstrom <thellstrom@vmware.com>
+Date: Mon, 27 Mar 2017 13:06:05 +0200
+Subject: drm/vmwgfx: Remove getparam error message
+
+From: Thomas Hellstrom <thellstrom@vmware.com>
+
+commit 53e16798b0864464c5444a204e1bb93ae246c429 upstream.
+
+The mesa winsys sometimes uses unimplemented parameter requests to
+check for features. Remove the error message to avoid bloating the
+kernel log.
+
+Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
+Reviewed-by: Brian Paul <brianp@vmware.com>
+Reviewed-by: Sinclair Yeh <syeh@vmware.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
+@@ -114,8 +114,6 @@ int vmw_getparam_ioctl(struct drm_device
+ param->value = dev_priv->has_dx;
+ break;
+ default:
+- DRM_ERROR("Illegal vmwgfx get param request: %d\n",
+- param->param);
+ return -EINVAL;
+ }
+
--- /dev/null
+From f7652afa8eadb416b23eb57dec6f158529942041 Mon Sep 17 00:00:00 2001
+From: Thomas Hellstrom <thellstrom@vmware.com>
+Date: Mon, 27 Mar 2017 11:09:08 +0200
+Subject: drm/vmwgfx: Type-check lookups of fence objects
+
+From: Thomas Hellstrom <thellstrom@vmware.com>
+
+commit f7652afa8eadb416b23eb57dec6f158529942041 upstream.
+
+A malicious caller could otherwise hand over handles to other objects
+causing all sorts of interesting problems.
+
+Testing done: Ran a Fedora 25 desktop using both Xorg and
+gnome-shell/Wayland.
+
+Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
+Reviewed-by: Sinclair Yeh <syeh@vmware.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 75 ++++++++++++++++++++++------------
+ 1 file changed, 49 insertions(+), 26 deletions(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+@@ -538,7 +538,7 @@ int vmw_fence_create(struct vmw_fence_ma
+ struct vmw_fence_obj **p_fence)
+ {
+ struct vmw_fence_obj *fence;
+- int ret;
++ int ret;
+
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+ if (unlikely(fence == NULL))
+@@ -701,6 +701,41 @@ void vmw_fence_fifo_up(struct vmw_fence_
+ }
+
+
++/**
++ * vmw_fence_obj_lookup - Look up a user-space fence object
++ *
++ * @tfile: A struct ttm_object_file identifying the caller.
++ * @handle: A handle identifying the fence object.
++ * @return: A struct vmw_user_fence base ttm object on success or
++ * an error pointer on failure.
++ *
++ * The fence object is looked up and type-checked. The caller needs
++ * to have opened the fence object first, but since that happens on
++ * creation and fence objects aren't shareable, that's not an
++ * issue currently.
++ */
++static struct ttm_base_object *
++vmw_fence_obj_lookup(struct ttm_object_file *tfile, u32 handle)
++{
++ struct ttm_base_object *base = ttm_base_object_lookup(tfile, handle);
++
++ if (!base) {
++ pr_err("Invalid fence object handle 0x%08lx.\n",
++ (unsigned long)handle);
++ return ERR_PTR(-EINVAL);
++ }
++
++ if (base->refcount_release != vmw_user_fence_base_release) {
++ pr_err("Invalid fence object handle 0x%08lx.\n",
++ (unsigned long)handle);
++ ttm_base_object_unref(&base);
++ return ERR_PTR(-EINVAL);
++ }
++
++ return base;
++}
++
++
+ int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+ {
+@@ -726,13 +761,9 @@ int vmw_fence_obj_wait_ioctl(struct drm_
+ arg->kernel_cookie = jiffies + wait_timeout;
+ }
+
+- base = ttm_base_object_lookup(tfile, arg->handle);
+- if (unlikely(base == NULL)) {
+- printk(KERN_ERR "Wait invalid fence object handle "
+- "0x%08lx.\n",
+- (unsigned long)arg->handle);
+- return -EINVAL;
+- }
++ base = vmw_fence_obj_lookup(tfile, arg->handle);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
+
+ fence = &(container_of(base, struct vmw_user_fence, base)->fence);
+
+@@ -771,13 +802,9 @@ int vmw_fence_obj_signaled_ioctl(struct
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_private *dev_priv = vmw_priv(dev);
+
+- base = ttm_base_object_lookup(tfile, arg->handle);
+- if (unlikely(base == NULL)) {
+- printk(KERN_ERR "Fence signaled invalid fence object handle "
+- "0x%08lx.\n",
+- (unsigned long)arg->handle);
+- return -EINVAL;
+- }
++ base = vmw_fence_obj_lookup(tfile, arg->handle);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
+
+ fence = &(container_of(base, struct vmw_user_fence, base)->fence);
+ fman = fman_from_fence(fence);
+@@ -1024,6 +1051,7 @@ int vmw_fence_event_ioctl(struct drm_dev
+ (struct drm_vmw_fence_event_arg *) data;
+ struct vmw_fence_obj *fence = NULL;
+ struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
++ struct ttm_object_file *tfile = vmw_fp->tfile;
+ struct drm_vmw_fence_rep __user *user_fence_rep =
+ (struct drm_vmw_fence_rep __user *)(unsigned long)
+ arg->fence_rep;
+@@ -1037,15 +1065,11 @@ int vmw_fence_event_ioctl(struct drm_dev
+ */
+ if (arg->handle) {
+ struct ttm_base_object *base =
+- ttm_base_object_lookup_for_ref(dev_priv->tdev,
+- arg->handle);
++ vmw_fence_obj_lookup(tfile, arg->handle);
++
++ if (IS_ERR(base))
++ return PTR_ERR(base);
+
+- if (unlikely(base == NULL)) {
+- DRM_ERROR("Fence event invalid fence object handle "
+- "0x%08lx.\n",
+- (unsigned long)arg->handle);
+- return -EINVAL;
+- }
+ fence = &(container_of(base, struct vmw_user_fence,
+ base)->fence);
+ (void) vmw_fence_obj_reference(fence);
+@@ -1053,7 +1077,7 @@ int vmw_fence_event_ioctl(struct drm_dev
+ if (user_fence_rep != NULL) {
+ bool existed;
+
+- ret = ttm_ref_object_add(vmw_fp->tfile, base,
++ ret = ttm_ref_object_add(tfile, base,
+ TTM_REF_USAGE, &existed);
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Failed to reference a fence "
+@@ -1097,8 +1121,7 @@ int vmw_fence_event_ioctl(struct drm_dev
+ return 0;
+ out_no_create:
+ if (user_fence_rep != NULL)
+- ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
+- handle, TTM_REF_USAGE);
++ ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE);
+ out_no_ref_obj:
+ vmw_fence_obj_unreference(&fence);
+ return ret;
--- /dev/null
+From dd5c472a60e43549d789a17a8444513eec64bd7e Mon Sep 17 00:00:00 2001
+From: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+Date: Sat, 12 Nov 2016 21:22:12 +0000
+Subject: ppdev: check before attaching port
+
+From: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+
+commit dd5c472a60e43549d789a17a8444513eec64bd7e upstream.
+
+After parport starts using the device model, all pardevice drivers
+should decide in their match_port callback function if they want to
+attach with that particulatr port. ppdev has been converted to use the
+new parport device-model code but pp_attach() tried to attach with all
+the ports.
+Create a new array of pointer and use that to remember the ports we
+have attached. And use that information to skip attaching ports which
+we have already attached.
+
+Tested-by: Joe Lawrence <joe.lawrence@redhat.com>
+Signed-off-by: Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/char/ppdev.c | 23 +++++++++++++++++++++--
+ 1 file changed, 21 insertions(+), 2 deletions(-)
+
+--- a/drivers/char/ppdev.c
++++ b/drivers/char/ppdev.c
+@@ -86,6 +86,9 @@ struct pp_struct {
+ long default_inactivity;
+ };
+
++/* should we use PARDEVICE_MAX here? */
++static struct device *devices[PARPORT_MAX];
++
+ /* pp_struct.flags bitfields */
+ #define PP_CLAIMED (1<<0)
+ #define PP_EXCL (1<<1)
+@@ -789,13 +792,29 @@ static const struct file_operations pp_f
+
+ static void pp_attach(struct parport *port)
+ {
+- device_create(ppdev_class, port->dev, MKDEV(PP_MAJOR, port->number),
+- NULL, "parport%d", port->number);
++ struct device *ret;
++
++ if (devices[port->number])
++ return;
++
++ ret = device_create(ppdev_class, port->dev,
++ MKDEV(PP_MAJOR, port->number), NULL,
++ "parport%d", port->number);
++ if (IS_ERR(ret)) {
++ pr_err("Failed to create device parport%d\n",
++ port->number);
++ return;
++ }
++ devices[port->number] = ret;
+ }
+
+ static void pp_detach(struct parport *port)
+ {
++ if (!devices[port->number])
++ return;
++
+ device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number));
++ devices[port->number] = NULL;
+ }
+
+ static int pp_probe(struct pardevice *par_dev)
--- /dev/null
+From 9a69645dde1188723d80745c1bc6ee9af2cbe2a7 Mon Sep 17 00:00:00 2001
+From: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+Date: Mon, 6 Mar 2017 23:23:43 +0000
+Subject: ppdev: fix registering same device name
+
+From: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+
+commit 9a69645dde1188723d80745c1bc6ee9af2cbe2a7 upstream.
+
+Usually every parallel port will have a single pardev registered with
+it. But ppdev driver is an exception. This userspace parallel port
+driver allows to create multiple parrallel port devices for a single
+parallel port. And as a result we were having a big warning like:
+"sysfs: cannot create duplicate filename '/devices/parport0/ppdev0.0'".
+And with that many parallel port printers stopped working.
+
+We have been using the minor number as the id field while registering
+a parralel port device with a parralel port. But when there are
+multiple parrallel port device for one single parallel port, they all
+tried to register with the same name like 'pardev0.0' and everything
+started failing.
+Use an incremented index as the id instead of the minor number.
+
+Fixes: 8b7d3a9d903e ("ppdev: use new parport device model")
+Cc: stable <stable@vger.kernel.org> # v4.9+
+Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1414656
+Bugzilla: https://bugs.archlinux.org/task/52322
+Tested-by: James Feeney <james@nurealm.net>
+Signed-off-by: Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ drivers/char/ppdev.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+--- a/drivers/char/ppdev.c
++++ b/drivers/char/ppdev.c
+@@ -84,11 +84,14 @@ struct pp_struct {
+ struct ieee1284_info state;
+ struct ieee1284_info saved_state;
+ long default_inactivity;
++ int index;
+ };
+
+ /* should we use PARDEVICE_MAX here? */
+ static struct device *devices[PARPORT_MAX];
+
++static DEFINE_IDA(ida_index);
++
+ /* pp_struct.flags bitfields */
+ #define PP_CLAIMED (1<<0)
+ #define PP_EXCL (1<<1)
+@@ -290,6 +293,7 @@ static int register_device(int minor, st
+ struct pardevice *pdev = NULL;
+ char *name;
+ struct pardev_cb ppdev_cb;
++ int index;
+
+ name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor);
+ if (name == NULL)
+@@ -302,20 +306,23 @@ static int register_device(int minor, st
+ return -ENXIO;
+ }
+
++ index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL);
+ memset(&ppdev_cb, 0, sizeof(ppdev_cb));
+ ppdev_cb.irq_func = pp_irq;
+ ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
+ ppdev_cb.private = pp;
+- pdev = parport_register_dev_model(port, name, &ppdev_cb, minor);
++ pdev = parport_register_dev_model(port, name, &ppdev_cb, index);
+ parport_put_port(port);
+
+ if (!pdev) {
+ printk(KERN_WARNING "%s: failed to register device!\n", name);
++ ida_simple_remove(&ida_index, index);
+ kfree(name);
+ return -ENXIO;
+ }
+
+ pp->pdev = pdev;
++ pp->index = index;
+ dev_dbg(&pdev->dev, "registered pardevice\n");
+ return 0;
+ }
+@@ -752,6 +759,7 @@ static int pp_release(struct inode *inod
+
+ if (pp->pdev) {
+ parport_unregister_device(pp->pdev);
++ ida_simple_remove(&ida_index, pp->index);
+ pp->pdev = NULL;
+ pr_debug(CHRDEV "%x: unregistered pardevice\n", minor);
+ }