99f4e6a
From 92eb77d0ffbaa71b501a0a8dabf09a351bf4267f Mon Sep 17 00:00:00 2001
99f4e6a
From: Daniel Stone <daniel@fooishbar.org>
99f4e6a
Date: Thu, 31 Oct 2013 07:25:34 +0000
99f4e6a
Subject: Input: evdev - fall back to vmalloc for client event buffer
99f4e6a
99f4e6a
evdev always tries to allocate the event buffer for clients using
99f4e6a
kzalloc rather than vmalloc, presumably to avoid mapping overhead where
99f4e6a
possible.  However, drivers like bcm5974, which claims support for
99f4e6a
reporting 16 fingers simultaneously, can have an extraordinarily large
99f4e6a
buffer.  The resultant contiguous order-4 allocation attempt fails due
99f4e6a
to fragmentation, and the device is thus unusable until reboot.
99f4e6a
99f4e6a
Try kzalloc if we can to avoid the mapping overhead, but if that fails,
99f4e6a
fall back to vzalloc.
99f4e6a
99f4e6a
Signed-off-by: Daniel Stone <daniels@collabora.com>
99f4e6a
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
99f4e6a
---
99f4e6a
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
99f4e6a
index b6ded17..a06e125 100644
99f4e6a
--- a/drivers/input/evdev.c
99f4e6a
+++ b/drivers/input/evdev.c
99f4e6a
@@ -18,6 +18,8 @@
99f4e6a
 #include <linux/poll.h>
99f4e6a
 #include <linux/sched.h>
99f4e6a
 #include <linux/slab.h>
99f4e6a
+#include <linux/vmalloc.h>
99f4e6a
+#include <linux/mm.h>
99f4e6a
 #include <linux/module.h>
99f4e6a
 #include <linux/init.h>
99f4e6a
 #include <linux/input/mt.h>
99f4e6a
@@ -369,7 +371,11 @@ static int evdev_release(struct inode *inode, struct file *file)
99f4e6a
 	mutex_unlock(&evdev->mutex);
99f4e6a
 
99f4e6a
 	evdev_detach_client(evdev, client);
99f4e6a
-	kfree(client);
99f4e6a
+
99f4e6a
+	if (is_vmalloc_addr(client))
99f4e6a
+		vfree(client);
99f4e6a
+	else
99f4e6a
+		kfree(client);
99f4e6a
 
99f4e6a
 	evdev_close_device(evdev);
99f4e6a
 
99f4e6a
@@ -389,12 +395,14 @@ static int evdev_open(struct inode *inode, struct file *file)
99f4e6a
 {
99f4e6a
 	struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
99f4e6a
 	unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
99f4e6a
+	unsigned int size = sizeof(struct evdev_client) +
99f4e6a
+					bufsize * sizeof(struct input_event);
99f4e6a
 	struct evdev_client *client;
99f4e6a
 	int error;
99f4e6a
 
99f4e6a
-	client = kzalloc(sizeof(struct evdev_client) +
99f4e6a
-				bufsize * sizeof(struct input_event),
99f4e6a
-			 GFP_KERNEL);
99f4e6a
+	client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
99f4e6a
+	if (!client)
99f4e6a
+		client = vzalloc(size);
99f4e6a
 	if (!client)
99f4e6a
 		return -ENOMEM;
99f4e6a
 
99f4e6a
--
99f4e6a
cgit v0.9.2