All of lore.kernel.org
 help / color / mirror / Atom feed
From: "David Härdeman" <david@hardeman.nu>
To: linux-media@vger.kernel.org
Cc: m.chehab@samsung.com
Subject: [PATCH 36/49] rc-core: make keytable RCU-friendly
Date: Fri, 04 Apr 2014 01:34:18 +0200	[thread overview]
Message-ID: <20140403233418.27099.61408.stgit@zeus.muc.hardeman.nu> (raw)
In-Reply-To: <20140403232420.27099.94872.stgit@zeus.muc.hardeman.nu>

Change struct rc_keytable to be RCU-friendly by kmalloc():ing an
entire new scancode,protocol <-> keycode table every time the table
is changed (i.e. via EVIOCSKEYCODE(_V2)).

The advantage is that the performance-critical keycode lookup path
can be made entirely lock-free and that GFP_ATOMIC allocations
can be avoided entirely at the cost of a couple of extra kmalloc()
calls when changing a keytable (which is normally done once during
boot).

Signed-off-by: David Härdeman <david@hardeman.nu>
---
 drivers/media/rc/rc-keytable.c |  668 +++++++++++++++++++---------------------
 include/media/rc-core.h        |   32 +-
 2 files changed, 332 insertions(+), 368 deletions(-)

diff --git a/drivers/media/rc/rc-keytable.c b/drivers/media/rc/rc-keytable.c
index f4d03d2..89295f3 100644
--- a/drivers/media/rc/rc-keytable.c
+++ b/drivers/media/rc/rc-keytable.c
@@ -25,12 +25,10 @@
 #include <linux/poll.h>
 #include "rc-core-priv.h"
 
-/* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */
-#define IR_TAB_MIN_SIZE		256
-#define IR_TAB_MAX_SIZE		8192
+#define RC_TAB_MAX_SIZE		1024 /* entries */
 
-/* FIXME: IR_KEYPRESS_TIMEOUT should be protocol specific */
-#define IR_KEYPRESS_TIMEOUT 250
+/* FIXME: RC_KEYPRESS_TIMEOUT should be protocol specific */
+#define RC_KEYPRESS_TIMEOUT 250
 
 /* Used to keep track of known keymaps */
 static LIST_HEAD(rc_map_list);
@@ -99,237 +97,189 @@ void rc_map_unregister(struct rc_map_list *map)
 EXPORT_SYMBOL_GPL(rc_map_unregister);
 
 /**
- * ir_create_table() - initializes a scancode table
- * @rc_map:	the rc_map to initialize
- * @name:	name to assign to the table
- * @size:	initial size of the table
- * @return:	zero on success or a negative error code
- *
- * This routine will initialize the rc_map and will allocate
- * memory to hold at least the specified number of elements.
+ * rc_scan_size() - determine the necessary size for a rc_scan struct
+ * @len:	the number of keytable entries the struct should hold
+ * @return:	the size of the struct in bytes
  */
-static int ir_create_table(struct rc_map *rc_map,
-			   const char *name, size_t size)
+static inline size_t rc_scan_size(unsigned len)
 {
-	rc_map->name = name;
-	rc_map->alloc = roundup_pow_of_two(size * sizeof(struct rc_map_table));
-	rc_map->size = rc_map->alloc / sizeof(struct rc_map_table);
-	rc_map->scan = kmalloc(rc_map->alloc, GFP_KERNEL);
-	if (!rc_map->scan)
-		return -ENOMEM;
-
-	IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
-		   rc_map->size, rc_map->alloc);
-	return 0;
+	return sizeof(struct rc_scan) + len * sizeof(struct rc_map_table);
 }
 
 /**
- * ir_free_table() - frees memory allocated by a scancode table
- * @rc_map:	the table whose mappings need to be freed
+ * rc_keytable_update_entry() - update an existing entry in the keytable
+ * @kt:		the keytable to update
+ * @i:		the index of the entry to update
+ * @entry:	the new values for the entry
+ * @return:	the old rc_scan struct, NULL if memory could not be allocated
  *
- * This routine will free memory alloctaed for key mappings used by given
- * scancode table.
+ * Updates a keytable by replacing an existing entry at the given index.
+ * The old rc_scan struct is returned so that it can be freed at a
+ * later stage.
  */
-void ir_free_table(struct rc_map *rc_map)
+static struct rc_scan *rc_keytable_update_entry(struct rc_keytable *kt,
+						unsigned i,
+						struct rc_map_table *entry)
 {
-	rc_map->size = 0;
-	kfree(rc_map->scan);
-	rc_map->scan = NULL;
-}
+	struct rc_scan *old_scan = kt->scan;
+	struct rc_scan *new_scan = kt->scan;
+	u32 old_keycode;
 
-/**
- * ir_resize_table() - resizes a scancode table if necessary
- * @rc_map:	the rc_map to resize
- * @gfp_flags:	gfp flags to use when allocating memory
- * @return:	zero on success or a negative error code
- *
- * This routine will shrink the rc_map if it has lots of
- * unused entries and grow it if it is full.
- */
-static int ir_resize_table(struct rc_map *rc_map, gfp_t gfp_flags)
-{
-	unsigned int oldalloc = rc_map->alloc;
-	unsigned int newalloc = oldalloc;
-	struct rc_map_table *oldscan = rc_map->scan;
-	struct rc_map_table *newscan;
-
-	if (rc_map->size == rc_map->len) {
-		/* All entries in use -> grow keytable */
-		if (rc_map->alloc >= IR_TAB_MAX_SIZE)
-			return -ENOMEM;
-
-		newalloc *= 2;
-		IR_dprintk(1, "Growing table to %u bytes\n", newalloc);
-	}
+	if (i >= old_scan->len)
+		return NULL;
 
-	if ((rc_map->len * 3 < rc_map->size) && (oldalloc > IR_TAB_MIN_SIZE)) {
-		/* Less than 1/3 of entries in use -> shrink keytable */
-		newalloc /= 2;
-		IR_dprintk(1, "Shrinking table to %u bytes\n", newalloc);
-	}
+	new_scan = kmalloc(rc_scan_size(old_scan->len), GFP_KERNEL);
+	if (!new_scan)
+		return NULL;
+	memcpy(new_scan, old_scan, rc_scan_size(old_scan->len));
 
-	if (newalloc == oldalloc)
-		return 0;
+	IR_dprintk(1, "#%d: New keycode 0x%04x\n", i, entry->keycode);
+	new_scan->table[i].keycode = entry->keycode;
 
-	newscan = kmalloc(newalloc, gfp_flags);
-	if (!newscan) {
-		IR_dprintk(1, "Failed to kmalloc %u bytes\n", newalloc);
-		return -ENOMEM;
-	}
+	/* Another scancode might use the old keycode... */
+	__set_bit(entry->keycode, kt->idev->keybit);
+	old_keycode = old_scan->table[i].keycode;
+	for (i = 0; i < new_scan->len; i++)
+		if (new_scan->table[i].keycode == old_keycode)
+			break;
 
-	memcpy(newscan, rc_map->scan, rc_map->len * sizeof(struct rc_map_table));
-	rc_map->scan = newscan;
-	rc_map->alloc = newalloc;
-	rc_map->size = rc_map->alloc / sizeof(struct rc_map_table);
-	kfree(oldscan);
-	return 0;
+	if (i >= new_scan->len)
+		/* ...nope */
+		__clear_bit(old_keycode, kt->idev->keybit);
+
+	rcu_assign_pointer(kt->scan, new_scan);
+	return old_scan;
 }
 
 /**
- * ir_update_mapping() - set a keycode in the scancode->keycode table
- * @kt:		the struct rc_keytable
- * @rc_map:	scancode table to be adjusted
- * @index:	index of the mapping that needs to be updated
- * @keycode:	the desired keycode
- * @return:	previous keycode assigned to the mapping
+ * rc_keytable_remove_entry() - remove an existing entry in the keytable
+ * @kt:		the keytable to update
+ * @i:		the index of the entry to remove
+ * @return:	the old rc_scan struct, NULL if memory could not be allocated
  *
- * This routine is used to update scancode->keycode mapping at given
- * position.
+ * Updates a keytable by removing an existing entry at the given index.
+ * The old rc_scan struct is returned so that it can be freed at a
+ * later stage.
  */
-static unsigned int ir_update_mapping(struct rc_keytable *kt,
-				      struct rc_map *rc_map,
-				      unsigned int index,
-				      unsigned int new_keycode)
+static struct rc_scan *rc_keytable_remove_entry(struct rc_keytable *kt,
+						unsigned i)
 {
-	int old_keycode = rc_map->scan[index].keycode;
-	int i;
-
-	/* Did the user wish to remove the mapping? */
-	if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) {
-		IR_dprintk(1, "#%d: Deleting proto 0x%04x, scan 0x%08llx\n",
-			   index, rc_map->scan[index].protocol,
-			   (unsigned long long)rc_map->scan[index].scancode);
-		rc_map->len--;
-		memmove(&rc_map->scan[index], &rc_map->scan[index+ 1],
-			(rc_map->len - index) * sizeof(struct rc_map_table));
-	} else {
-		IR_dprintk(1, "#%d: %s proto 0x%04x, scan 0x%08llx "
-			   "with key 0x%04x\n",
-			   index,
-			   old_keycode == KEY_RESERVED ? "New" : "Replacing",
-			   rc_map->scan[index].protocol,
-			   (unsigned long long)rc_map->scan[index].scancode,
-			   new_keycode);
-		rc_map->scan[index].keycode = new_keycode;
-		__set_bit(new_keycode, kt->idev->keybit);
-	}
+	struct rc_scan *old_scan = kt->scan;
+	struct rc_scan *new_scan = kt->scan;
+	u32 old_keycode;
 
-	if (old_keycode != KEY_RESERVED) {
-		/* A previous mapping was updated... */
-		__clear_bit(old_keycode, kt->idev->keybit);
-		/* ... but another scancode might use the same keycode */
-		for (i = 0; i < rc_map->len; i++) {
-			if (rc_map->scan[i].keycode == old_keycode) {
-				__set_bit(old_keycode, kt->idev->keybit);
-				break;
-			}
-		}
+	if (i >= old_scan->len)
+		return NULL;
 
-		/* Possibly shrink the keytable, failure is not a problem */
-		ir_resize_table(rc_map, GFP_ATOMIC);
-	}
+	new_scan = kmalloc(rc_scan_size(old_scan->len - 1), GFP_ATOMIC);
+	if (!new_scan)
+		return NULL;
+	new_scan->len = old_scan->len - 1;
+	memcpy(&new_scan->table[0], &old_scan->table[0],
+	       i * sizeof(struct rc_map_table));
+	memcpy(&new_scan->table[i], &old_scan->table[i + 1],
+	       (new_scan->len - i) * sizeof(struct rc_map_table));
+	IR_dprintk(1, "#%d: Deleted\n", i);
+
+	/* Another scancode might use the removed keycode... */
+	old_keycode = old_scan->table[i].keycode;
+	for (i = 0; i < new_scan->len; i++)
+		if (new_scan->table[i].keycode == old_keycode)
+			break;
+
+	if (i >= new_scan->len)
+		/* ...nope */
+		__clear_bit(old_keycode, kt->idev->keybit);
 
-	return old_keycode;
+	rcu_assign_pointer(kt->scan, new_scan);
+	return old_scan;
 }
 
 /**
- * ir_establish_scancode() - set a keycode in the scancode->keycode table
- * @kt:		the struct rc_keytable descriptor
- * @rc_map:	scancode table to be searched
- * @entry:	the entry to be added to the table
- * @resize:	controls whether we are allowed to resize the table to
- *		accomodate not yet present scancodes
- * @return:	index of the mapping containing scancode in question
- *		or -1U in case of failure.
+ * rc_keytable_add_entry() - add an existing entry in the keytable
+ * @kt:		the keytable to update
+ * @entry:	the new entry to insert
+ * @init:	whether the keytable is being initialized for the first time
+ * @return:	the old rc_scan struct, NULL if memory could not be allocated
  *
- * This routine is used to locate given scancode in rc_map.
- * If scancode is not yet present the routine will allocate a new slot
- * for it.
+ * Updates a keytable by inserting an entry at the proper index. Unless @init is
+ * %true, the old rc_scan struct is returned so that it can be freed at a
+ * later stage.
  */
-static unsigned int ir_establish_scancode(struct rc_keytable *kt,
-					  struct rc_map *rc_map,
-					  struct rc_map_table *entry,
-					  bool resize)
+static struct rc_scan *rc_keytable_add_entry(struct rc_keytable *kt,
+					     struct rc_map_table *entry,
+					     bool init)
 {
-	unsigned int i;
+	struct rc_scan *old_scan = kt->scan;
+	struct rc_scan *new_scan = kt->scan;
+	unsigned i;
 
-	/*
-	 * Unfortunately, some hardware-based IR decoders don't provide
-	 * all bits for the complete IR code. In general, they provide only
-	 * the command part of the IR code. Yet, as it is possible to replace
-	 * the provided IR with another one, it is needed to allow loading
-	 * IR tables from other remotes. So, we support specifying a mask to
-	 * indicate the valid bits of the scancodes.
-	 */
-	if (kt->dev->scancode_mask)
-		entry->scancode &= kt->dev->scancode_mask;
+	if (old_scan->len >= RC_TAB_MAX_SIZE)
+		return NULL;
 
-	/*
-	 * First check if we already have a mapping for this command.
-	 * Note that the keytable is sorted first on protocol and second
-	 * on scancode (lowest to highest).
-	 */
-	for (i = 0; i < rc_map->len; i++) {
-		if (rc_map->scan[i].protocol < entry->protocol)
+	/* Find the right index to insert the new entry at */
+	for (i = 0; i < old_scan->len; i++) {
+		if (old_scan->table[i].protocol < entry->protocol)
 			continue;
 
-		if (rc_map->scan[i].protocol > entry->protocol)
+		if (old_scan->table[i].protocol > entry->protocol)
 			break;
 
-		if (rc_map->scan[i].scancode < entry->scancode)
+		if (old_scan->table[i].scancode < entry->scancode)
 			continue;
 
-		if (rc_map->scan[i].scancode > entry->scancode)
+		if (old_scan->table[i].scancode > entry->scancode)
 			break;
 
-		return i;
+		/* BUG: We already have a matching entry */
+		return NULL;
 	}
 
-	/* No previous mapping found, we might need to grow the table */
-	if (rc_map->size == rc_map->len) {
-		if (!resize || ir_resize_table(rc_map, GFP_ATOMIC))
-			return -1U;
+	if (init) {
+		/* The init code already allocates a suitably sized table */
+		memmove(&new_scan->table[i + 1], &new_scan->table[i],
+			(new_scan->len - i) * sizeof(struct rc_map_table));
+		new_scan->len++;
+	} else {
+		new_scan = kmalloc(rc_scan_size(old_scan->len + 1), GFP_ATOMIC);
+		if (!new_scan)
+			return NULL;
+		new_scan->len = old_scan->len + 1;
+		memcpy(&new_scan->table[0], &old_scan->table[0],
+		       i * sizeof(struct rc_map_table));
+		memcpy(&new_scan->table[i + 1], &old_scan->table[i],
+		       (old_scan->len - i) * sizeof(struct rc_map_table));
 	}
 
-	/* i is the proper index to insert our new keycode */
-	if (i < rc_map->len)
-		memmove(&rc_map->scan[i + 1], &rc_map->scan[i],
-			(rc_map->len - i) * sizeof(struct rc_map_table));
-	rc_map->scan[i].scancode = entry->scancode;
-	rc_map->scan[i].protocol = entry->protocol;
-	rc_map->scan[i].keycode = KEY_RESERVED;
-	rc_map->len++;
+	new_scan->table[i].scancode = entry->scancode;
+	new_scan->table[i].protocol = entry->protocol;
+	new_scan->table[i].keycode = entry->keycode;
+	IR_dprintk(1, "#%d: New proto 0x%04x, scan 0x%08llx with key 0x%04x\n",
+		   i, entry->protocol, (unsigned long long)entry->scancode,
+		   entry->keycode);
+	__set_bit(entry->keycode, kt->idev->keybit);
 
-	return i;
+	rcu_assign_pointer(kt->scan, new_scan);
+	return old_scan;
 }
 
 /**
  * guess_protocol() - heuristics to guess the protocol for a scancode
  * @rdev:	the struct rc_dev device descriptor
- * @return:	the guessed RC_TYPE_* protocol
+ * @scan:	the struct rc_scan table to use
+ * @return:     the guessed RC_TYPE_* protocol
  *
  * Internal routine to guess the current IR protocol for legacy ioctls.
  */
-static inline enum rc_type guess_protocol(struct rc_dev *rdev)
+static inline enum rc_type guess_protocol(struct rc_dev *rdev,
+					  struct rc_scan *scan)
 {
-	struct rc_map *rc_map = &rdev->keytables[0]->rc_map;
-
 	if (hweight64(rdev->enabled_protocols) == 1)
 		return rc_bitmap_to_type(rdev->enabled_protocols);
 	else if (hweight64(rdev->allowed_protocols) == 1)
 		return rc_bitmap_to_type(rdev->allowed_protocols);
-	else if (rc_map->len > 0)
-		return rc_map->scan[0].protocol;
+	else if (scan->len > 0)
+		return scan->table[0].protocol;
 	else
 		return RC_TYPE_OTHER;
 }
@@ -359,33 +309,71 @@ static u32 to_nec32(u32 orig)
 }
 
 /**
- * ir_setkeycode() - set a keycode in the scancode->keycode table
+ * rc_scancode_to_index() - locate keytable index by scancode
+ * @rc_scan:	the struct rc_scan to search
+ * @protocol:	protocol to look for in the table
+ * @scancode:	scancode to look for in the table
+ * @return:	index in the table, -1U if not found
+ *
+ * This routine performs a binary search in a keytable for a
+ * given scancode.
+ */
+static unsigned rc_scancode_to_index(struct rc_scan *scan,
+				     u16 protocol, u64 scancode)
+{
+	int start = 0;
+	int end = scan->len - 1;
+	int mid;
+	struct rc_map_table *m;
+
+	while (start <= end) {
+		mid = (start + end) / 2;
+		m = &scan->table[mid];
+
+		if (m->protocol < protocol)
+			start = mid + 1;
+		else if (m->protocol > protocol)
+			end = mid - 1;
+		else if (m->scancode < scancode)
+			start = mid + 1;
+		else if (m->scancode > scancode)
+			end = mid - 1;
+		else
+			return mid;
+	}
+
+	return -1U;
+}
+
+/**
+ * rc_keytable_set() - add/update/remove an entry in the keytable
  * @idev:	the struct input_dev device descriptor
- * @scancode:	the desired scancode
- * @keycode:	result
- * @return:	-EINVAL if the keycode could not be inserted, otherwise zero.
+ * @ke:		the keymap entry to add/update/remove
+ * @old_keycode:used to return the previous keycode for this entry
+ * @return:	zero on success or a negative error code
  *
- * This routine is used to handle evdev EVIOCSKEY ioctl.
+ * This function handles the evdev EVIOCSKEYCODE(_V2) ioctls.
  */
-static int ir_setkeycode(struct input_dev *idev,
-			 const struct input_keymap_entry *ke,
-			 unsigned int *old_keycode)
+static int rc_keytable_set(struct input_dev *idev,
+			   const struct input_keymap_entry *ke,
+			   unsigned int *old_keycode)
 {
 	struct rc_keytable *kt = input_get_drvdata(idev);
 	struct rc_dev *rdev = kt->dev;
-	struct rc_map *rc_map = &kt->rc_map;
+	struct rc_scan *old_scan = NULL;
 	unsigned int index;
 	struct rc_map_table entry;
 	int retval = 0;
-	unsigned long flags;
 
 	entry.keycode = ke->keycode;
 
-	spin_lock_irqsave(&kt->lock, flags);
+	retval = mutex_lock_interruptible(&kt->scan_mutex);
+	if (retval)
+		return retval;
 
 	if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
 		index = ke->index;
-		if (index >= rc_map->len) {
+		if (index >= kt->scan->len) {
 			retval = -EINVAL;
 			goto out;
 		}
@@ -397,15 +385,16 @@ static int ir_setkeycode(struct input_dev *idev,
 			goto out;
 
 		entry.scancode = scancode;
-		entry.protocol = guess_protocol(rdev);
+		entry.protocol = guess_protocol(rdev, kt->scan);
 		if (entry.protocol == RC_TYPE_NEC)
 			entry.scancode = to_nec32(scancode);
 
-		index = ir_establish_scancode(kt, rc_map, &entry, true);
-		if (index >= rc_map->len) {
-			retval = -ENOMEM;
-			goto out;
-		}
+		if (kt->dev->scancode_mask)
+			entry.scancode &= kt->dev->scancode_mask;
+
+		index = rc_scancode_to_index(kt->scan, entry.protocol,
+					     entry.scancode);
+
 	} else if (ke->len == sizeof(struct rc_scancode)) {
 		/* New EVIOCSKEYCODE_V2 ioctl */
 		const struct rc_keymap_entry *rke = (struct rc_keymap_entry *)ke;
@@ -417,127 +406,63 @@ static int ir_setkeycode(struct input_dev *idev,
 			goto out;
 		}
 
-		index = ir_establish_scancode(kt, rc_map, &entry, true);
-		if (index >= rc_map->len) {
-			retval = -ENOMEM;
-			goto out;
-		}
+		if (kt->dev->scancode_mask)
+			entry.scancode &= kt->dev->scancode_mask;
+
+		index = rc_scancode_to_index(kt->scan, entry.protocol,
+					     entry.scancode);
+
 	} else {
 		retval = -EINVAL;
 		goto out;
 	}
 
-	*old_keycode = ir_update_mapping(kt, rc_map, index, ke->keycode);
-
-out:
-	spin_unlock_irqrestore(&kt->lock, flags);
-	return retval;
-}
-
-/**
- * ir_setkeytable() - sets several entries in the scancode->keycode table
- * @kt:		the struct rc_keytable descriptor
- * @to:		the struct rc_map to copy entries to
- * @from:	the struct rc_map to copy entries from
- * @return:	-ENOMEM if all keycodes could not be inserted, otherwise zero.
- *
- * This routine is used to handle table initialization.
- */
-int rc_setkeytable(struct rc_keytable *kt, const struct rc_map *from)
-{
-	struct rc_map *rc_map = &kt->rc_map;
-	struct rc_map_table entry;
-	unsigned int i, index;
-	int rc;
-
-	rc = ir_create_table(rc_map, from->name, from->size);
-	if (rc)
-		return rc;
-
-	IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
-		   rc_map->size, rc_map->alloc);
-
-	for (i = 0; i < from->size; i++) {
-		if (from->rc_type == RC_TYPE_NEC)
-			entry.scancode = to_nec32(from->scan[i].scancode);
+	if (index >= kt->scan->len) {
+		/* Old entry not found */
+		*old_keycode = KEY_RESERVED;
+		if (ke->keycode == KEY_RESERVED)
+			/* removing a non-existing entry eh? */
+			goto out;
+		old_scan = rc_keytable_add_entry(kt, &entry, false);
+	} else {
+		/* Previous entry found */
+		*old_keycode = kt->scan->table[index].keycode;
+		if (ke->keycode == KEY_RESERVED)
+			old_scan = rc_keytable_remove_entry(kt, index);
 		else
-			entry.scancode = from->scan[i].scancode;
-
-		entry.protocol = from->rc_type;
-		index = ir_establish_scancode(kt, rc_map, &entry, false);
-		if (index >= rc_map->len) {
-			rc = -ENOMEM;
-			break;
-		}
-
-		ir_update_mapping(kt, rc_map, index, from->scan[i].keycode);
+			old_scan = rc_keytable_update_entry(kt, index, &entry);
 	}
 
-	if (rc)
-		ir_free_table(rc_map);
-
-	return rc;
-}
-
-/**
- * ir_lookup_by_scancode() - locate mapping by scancode
- * @rc_map:	the struct rc_map to search
- * @protocol:	protocol to look for in the table
- * @scancode:	scancode to look for in the table
- * @return:	index in the table, -1U if not found
- *
- * This routine performs binary search in RC keykeymap table for
- * given scancode.
- */
-static unsigned int ir_lookup_by_scancode(const struct rc_map *rc_map,
-					  u16 protocol, u64 scancode)
-{
-	int start = 0;
-	int end = rc_map->len - 1;
-	int mid;
-	struct rc_map_table *m;
-
-	while (start <= end) {
-		mid = (start + end) / 2;
-		m = &rc_map->scan[mid];
-
-		if (m->protocol < protocol)
-			start = mid + 1;
-		else if (m->protocol > protocol)
-			end = mid - 1;
-		else if (m->scancode < scancode)
-			start = mid + 1;
-		else if (m->scancode > scancode)
-			end = mid - 1;
-		else
-			return mid;
+out:
+	mutex_unlock(&kt->scan_mutex);
+	if (old_scan) {
+		synchronize_rcu();
+		kfree(old_scan);
 	}
-
-	return -1U;
+	return retval;
 }
 
 /**
- * ir_getkeycode() - get a keycode from the scancode->keycode table
+ * rc_keytable_get() - get an entry from the keytable
  * @idev:	the struct input_dev device descriptor
- * @scancode:	the desired scancode
- * @keycode:	used to return the keycode, if found, or KEY_RESERVED
- * @return:	always returns zero.
+ * @ke:		the requested entry which is filled in by this function
+ * @return:	zero on success, or a negative error code
  *
- * This routine is used to handle evdev EVIOCGKEY ioctl.
+ * This function handles the evdev EVIOCGKEYCODE(_V2) ioctls.
  */
-int ir_getkeycode(struct input_dev *idev,
-		  struct input_keymap_entry *ke)
+static int rc_keytable_get(struct input_dev *idev,
+			   struct input_keymap_entry *ke)
 {
 	struct rc_keymap_entry *rke = (struct rc_keymap_entry *)ke;
 	struct rc_keytable *kt = input_get_drvdata(idev);
 	struct rc_dev *rdev = kt->dev;
-	struct rc_map *rc_map = &kt->rc_map;
+	struct rc_scan *scan;
 	struct rc_map_table *entry;
-	unsigned long flags;
 	unsigned int index;
 	int retval;
 
-	spin_lock_irqsave(&kt->lock, flags);
+	rcu_read_lock();
+	scan = rcu_dereference(kt->scan);
 
 	if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
 		index = ke->index;
@@ -550,11 +475,11 @@ int ir_getkeycode(struct input_dev *idev,
 		if (retval)
 			goto out;
 
-		protocol = guess_protocol(rdev);
+		protocol = guess_protocol(rdev, scan);
 		if (protocol == RC_TYPE_NEC)
 			scancode = to_nec32(scancode);
 
-		index = ir_lookup_by_scancode(rc_map, protocol, scancode);
+		index = rc_scancode_to_index(scan, protocol, scancode);
 
 	} else if (ke->len == sizeof(struct rc_scancode)) {
 		/* New EVIOCGKEYCODE_V2 ioctl */
@@ -563,16 +488,17 @@ int ir_getkeycode(struct input_dev *idev,
 			goto out;
 		}
 
-		index = ir_lookup_by_scancode(rc_map,
-					      rke->rc.protocol, rke->rc.scancode);
+		index = rc_scancode_to_index(scan,
+					     rke->rc.protocol,
+					     rke->rc.scancode);
 
 	} else {
 		retval = -EINVAL;
 		goto out;
 	}
 
-	if (index < rc_map->len) {
-		entry = &rc_map->scan[index];
+	if (index < scan->len) {
+		entry = &scan->table[index];
 		ke->index = index;
 		ke->keycode = entry->keycode;
 		if (ke->len == sizeof(int)) {
@@ -600,29 +526,10 @@ int ir_getkeycode(struct input_dev *idev,
 	retval = 0;
 
 out:
-	spin_unlock_irqrestore(&kt->lock, flags);
+	rcu_read_unlock();
 	return retval;
 }
 
-static u32 rc_get_keycode(struct rc_keytable *kt,
-			  enum rc_type protocol, u64 scancode)
-{
-	struct rc_map *rc_map;
-	unsigned int keycode = KEY_RESERVED;
-	unsigned int index;
-
-	rc_map = &kt->rc_map;
-	if (!rc_map)
-		return KEY_RESERVED;
-
-	index = ir_lookup_by_scancode(rc_map, protocol, scancode);
-	if (index < rc_map->len)
-		keycode = rc_map->scan[index].keycode;
-
-	return keycode;
-}
-
-
 /**
  * rc_g_keycode_from_table() - gets the keycode that corresponds to a scancode
  * @dev:	the struct rc_dev descriptor of the device
@@ -638,18 +545,20 @@ u32 rc_g_keycode_from_table(struct rc_dev *dev,
 			    enum rc_type protocol, u64 scancode)
 {
 	struct rc_keytable *kt;
-	unsigned int keycode = KEY_RESERVED;
-	unsigned long flags;
+	struct rc_scan *scan;
+	unsigned keycode = KEY_RESERVED;
+	unsigned index;
 
 	/* FIXME: This entire function is a hack. Remove it */
 	rcu_read_lock();
 	kt = rcu_dereference(dev->keytables[0]);
 	if (!kt)
 		goto out;
+	scan = rcu_dereference(kt->scan);
 
-	spin_lock_irqsave(&kt->lock, flags);
-	keycode = rc_get_keycode(kt, protocol, scancode);
-	spin_unlock_irqrestore(&kt->lock, flags);
+	index = rc_scancode_to_index(scan, protocol, scancode);
+	if (index < scan->len)
+		keycode = scan->table[index].keycode;
 
 out:
 	rcu_read_unlock();
@@ -663,11 +572,11 @@ EXPORT_SYMBOL_GPL(rc_g_keycode_from_table);
  * @sync:	whether or not to call input_sync
  *
  * This function is used internally to release a keypress, it must be
- * called with kt->lock held.
+ * called with kt->key_lock held.
  */
 static void rc_do_keyup(struct rc_keytable *kt, bool sync)
 {
-	if (!kt->keypressed)
+	if (!kt->key_pressed)
 		return;
 
 	IR_dprintk(1, "keyup key 0x%04x\n", kt->last_keycode);
@@ -675,7 +584,7 @@ static void rc_do_keyup(struct rc_keytable *kt, bool sync)
 	led_trigger_event(led_feedback, LED_OFF);
 	if (sync)
 		input_sync(kt->idev);
-	kt->keypressed = false;
+	kt->key_pressed = false;
 }
 
 /**
@@ -688,14 +597,14 @@ void rc_keytable_keyup(struct rc_keytable *kt)
 {
 	unsigned long flags;
 
-	spin_lock_irqsave(&kt->lock, flags);
+	spin_lock_irqsave(&kt->key_lock, flags);
 	rc_do_keyup(kt, true);
-	spin_unlock_irqrestore(&kt->lock, flags);
+	spin_unlock_irqrestore(&kt->key_lock, flags);
 }
 
 /**
- * ir_timer_keyup() - generates a keyup event after a timeout
- * @cookie:	a pointer to the struct rc_keytable
+ * rc_timer_keyup() - generates a keyup event after a timeout
+ * @cookie:	a pointer to the struct rc_keytable descriptor of the keytable
  *
  * This routine will generate a keyup event some time after a keydown event
  * is generated when no further activity has been detected.
@@ -715,37 +624,36 @@ static void rc_timer_keyup(unsigned long cookie)
 	 * to allow the input subsystem to do its auto-repeat magic or
 	 * a keyup event might follow immediately after the keydown.
 	 */
-	spin_lock_irqsave(&kt->lock, flags);
+	spin_lock_irqsave(&kt->key_lock, flags);
 	if (time_is_before_eq_jiffies(kt->keyup_jiffies))
 		rc_do_keyup(kt, true);
-	spin_unlock_irqrestore(&kt->lock, flags);
+	spin_unlock_irqrestore(&kt->key_lock, flags);
 }
 
 /**
  * rc_keytable_repeat() - signals that a key is still pressed
  * @kt:		the keytable
  *
- * This routine is used by IR decoders when a repeat message which does
- * not include the necessary bits to reproduce the scancode has been
- * received.
+ * This routine is used when a repeat message which does not include the
+ * necessary bits to reproduce the scancode has been received.
  */
 void rc_keytable_repeat(struct rc_keytable *kt)
 {
 	unsigned long flags;
 
-	spin_lock_irqsave(&kt->lock, flags);
+	spin_lock_irqsave(&kt->key_lock, flags);
 
 	input_event(kt->idev, EV_MSC, MSC_SCAN, kt->last_scancode);
 	input_sync(kt->idev);
 
-	if (!kt->keypressed)
+	if (!kt->key_pressed)
 		goto out;
 
-	kt->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT);
+	kt->keyup_jiffies = jiffies + msecs_to_jiffies(RC_KEYPRESS_TIMEOUT);
 	mod_timer(&kt->timer_keyup, kt->keyup_jiffies);
 
 out:
-	spin_unlock_irqrestore(&kt->lock, flags);
+	spin_unlock_irqrestore(&kt->key_lock, flags);
 }
 
 /**
@@ -762,26 +670,33 @@ out:
 void rc_keytable_keydown(struct rc_keytable *kt, enum rc_type protocol,
 			 u32 scancode, u8 toggle, bool autoup)
 {
+	struct rc_scan *scan;
+	unsigned index;
+	u32 keycode = KEY_RESERVED;
 	unsigned long flags;
-	u32 keycode;
 	bool new_event;
 
-	spin_lock_irqsave(&kt->lock, flags);
+	rcu_read_lock();
+	scan = rcu_dereference(kt->scan);
+	index = rc_scancode_to_index(scan, protocol, scancode);
+	if (index < scan->len)
+		keycode = scan->table[index].keycode;
+	rcu_read_unlock();
 
-	keycode = rc_get_keycode(kt, protocol, scancode);
-	new_event = (!kt->keypressed ||
+	spin_lock_irqsave(&kt->key_lock, flags);
+	new_event = (!kt->key_pressed ||
 		     kt->last_protocol != protocol ||
 		     kt->last_scancode != scancode ||
 		     kt->last_toggle != toggle);
 
-	if (new_event && kt->keypressed)
+	if (new_event)
 		rc_do_keyup(kt, false);
 
 	input_event(kt->idev, EV_MSC, MSC_SCAN, scancode);
 
 	if (new_event && keycode != KEY_RESERVED) {
 		/* Register a keypress */
-		kt->keypressed = true;
+		kt->key_pressed = true;
 		kt->last_protocol = protocol;
 		kt->last_scancode = scancode;
 		kt->last_toggle = toggle;
@@ -795,11 +710,11 @@ void rc_keytable_keydown(struct rc_keytable *kt, enum rc_type protocol,
 	}
 	input_sync(kt->idev);
 
-	if (autoup && kt->keypressed) {
-		kt->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT);
+	if (autoup && kt->key_pressed) {
+		kt->keyup_jiffies = jiffies + msecs_to_jiffies(RC_KEYPRESS_TIMEOUT);
 		mod_timer(&kt->timer_keyup, kt->keyup_jiffies);
 	}
-	spin_unlock_irqrestore(&kt->lock, flags);
+	spin_unlock_irqrestore(&kt->key_lock, flags);
 }
 
 static int rc_input_open(struct input_dev *idev)
@@ -817,6 +732,42 @@ static void rc_input_close(struct input_dev *idev)
 }
 
 /**
+ * rc_keytable_init() - performs initial setup of a keytable
+ * @dev:	the struct rc_dev device descriptor
+ * @from:	the struct rc_map to copy entries from
+ * @return:	zero on success, or a negative error code
+ *
+ * This function is used to handle table initialization.
+ */
+static int rc_keytable_init(struct rc_keytable *kt,
+			    const struct rc_map *from)
+{
+	unsigned size;
+	unsigned i;
+	struct rc_map_table entry;
+
+	size = from ? from->size : 0;
+	kt->scan = kmalloc(rc_scan_size(size), GFP_KERNEL);
+	if (!kt->scan)
+		return -ENOMEM;
+
+	kt->scan->len = 0;
+	for (i = 0; i < size; i++) {
+		entry.protocol = from->rc_type;
+		if (entry.protocol == RC_TYPE_NEC)
+			entry.scancode = to_nec32(from->scan[i].scancode);
+		else
+			entry.scancode = from->scan[i].scancode;
+		if (kt->dev->scancode_mask)
+			entry.scancode &= kt->dev->scancode_mask;
+		entry.keycode = from->scan[i].keycode;
+		rc_keytable_add_entry(kt, &entry, true);
+	}
+
+	return 0;
+}
+
+/**
  * rc_keytable_create() - create a new keytable
  * @dev:	the struct rc_dev device this keytable should belong to
  * @name:	the userfriendly name of this keymap
@@ -848,10 +799,11 @@ struct rc_keytable *rc_keytable_create(struct rc_dev *dev, const char *name,
 
 	kt->idev = idev;
 	kt->dev = dev;
-	spin_lock_init(&kt->lock);
+	spin_lock_init(&kt->key_lock);
+	mutex_init(&kt->scan_mutex);
 	snprintf(kt->name, sizeof(*kt->name), name ? name : "undefined");
-	idev->getkeycode = ir_getkeycode;
-	idev->setkeycode = ir_setkeycode;
+	idev->getkeycode = rc_keytable_get;
+	idev->setkeycode = rc_keytable_set;
 	idev->open = rc_input_open;
 	idev->close = rc_input_close;
 	set_bit(EV_KEY, idev->evbit);
@@ -861,7 +813,7 @@ struct rc_keytable *rc_keytable_create(struct rc_dev *dev, const char *name,
 	input_set_drvdata(idev, kt);
 	setup_timer(&kt->timer_keyup, rc_timer_keyup, (unsigned long)kt);
 
-	err = rc_setkeytable(kt, rc_map);
+	err = rc_keytable_init(kt, rc_map);
 	if (err)
 		goto out;
 
@@ -892,8 +844,8 @@ struct rc_keytable *rc_keytable_create(struct rc_dev *dev, const char *name,
 	return kt;
 
 out:
-	ir_free_table(&kt->rc_map);
 	input_free_device(idev);
+	kfree(kt->scan);
 	kfree(kt);
 	return ERR_PTR(err);
 }
@@ -910,8 +862,8 @@ void rc_keytable_destroy(struct rc_keytable *kt)
 		return;
 
 	del_timer_sync(&kt->timer_keyup);
-	ir_free_table(&kt->rc_map);
 	input_unregister_device(kt->idev);
+	kfree(kt->scan);
 	kfree(kt);
 }
 
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index af63188..228510e 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -386,35 +386,47 @@ struct rc_dev {
 };
 
 /**
+ * struct rc_scan - rcu-friendly scancode<->keycode table
+ * @len:	number of elements in the table array
+ * @table:	array of struct rc_map_table elements
+ */
+struct rc_scan {
+	unsigned len;
+	struct rc_map_table table[];
+};
+
+/**
  * struct rc_keytable - represents one keytable for a rc_dev device
  * @node:		used to iterate over all keytables for a rc_dev device
  * @dev:		the rc_dev device this keytable belongs to
  * @idev:		the input_dev device which belongs to this keytable
  * @name:		the user-friendly name of this keytable
- * @rc_map:		holds the scancode <-> keycode mappings
- * @keypressed:		whether a key is currently pressed or not
- * @keyup_jiffies:	when the key should be auto-released
- * @timer_keyup:	responsible for the auto-release of keys
- * @lock:		protects the key state
+ * @scan_mutex:		protects @scan against concurrent writers
+ * @scan:		the current scancode<->keycode table
+ * @key_lock:		protects the key state
+ * @key_pressed:	whether a key is currently pressed or not
  * @last_keycode:	keycode of the last keypress
  * @last_protocol:	protocol of the last keypress
  * @last_scancode:	scancode of the last keypress
  * @last_toggle:	toggle of the last keypress
+ * @timer_keyup:	responsible for the auto-release of keys
+ * @keyup_jiffies:	when the key should be auto-released
  */
 struct rc_keytable {
 	struct list_head		node;
 	struct rc_dev			*dev;
 	struct input_dev		*idev;
 	char				name[RC_KEYTABLE_NAME_SIZE];
-	struct rc_map			rc_map;
-	bool				keypressed;
-	unsigned long			keyup_jiffies;
-	struct timer_list		timer_keyup;
-	spinlock_t			lock;
+	struct mutex			scan_mutex;
+	struct rc_scan __rcu		*scan;
+	spinlock_t			key_lock;
+	bool				key_pressed;
 	u32				last_keycode;
 	enum rc_type			last_protocol;
 	u32				last_scancode;
 	u8				last_toggle;
+	struct timer_list		timer_keyup;
+	unsigned long			keyup_jiffies;
 };
 
 #define to_rc_dev(d) container_of(d, struct rc_dev, dev)


  parent reply	other threads:[~2014-04-03 23:34 UTC|newest]

Thread overview: 68+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-04-03 23:31 [PATCH 00/49] rc-core: my current patch queue David Härdeman
2014-04-03 23:31 ` [PATCH 01/49] bt8xx: fixup RC5 decoding David Härdeman
2014-04-03 23:31 ` [PATCH 02/49] rc-core: improve ir-kbd-i2c get_key functions David Härdeman
2014-04-03 23:31 ` [PATCH 03/49] rc-core: document the protocol type David Härdeman
2014-04-03 23:31 ` [PATCH 04/49] rc-core: do not change 32bit NEC scancode format for now David Härdeman
2014-04-04 13:18   ` James Hogan
2014-04-03 23:31 ` [PATCH 05/49] rc-core: split dev->s_filter David Härdeman
2014-04-04 13:08   ` James Hogan
2014-04-03 23:31 ` [PATCH 06/49] rc-core: remove generic scancode filter David Härdeman
2014-04-04 13:30   ` James Hogan
2014-04-03 23:31 ` [PATCH 07/49] dib0700: NEC scancode cleanup David Härdeman
2014-04-03 23:31 ` [PATCH 08/49] lmedm04: " David Härdeman
2014-04-03 23:32 ` [PATCH 09/49] saa7134: NEC scancode fix David Härdeman
2014-04-03 23:32 ` [PATCH 10/49] [RFC] rc-core: use the full 32 bits for NEC scancodes David Härdeman
2014-04-03 23:32 ` [PATCH 11/49] [RFC] rc-core: don't throw away protocol information David Härdeman
2014-04-03 23:32 ` [PATCH 12/49] rc-core: simplify sysfs code David Härdeman
2014-04-03 23:32 ` [PATCH 13/49] rc-core: remove protocol arrays David Härdeman
2014-04-03 23:32 ` [PATCH 14/49] rc-core: rename dev->scanmask to dev->scancode_mask David Härdeman
2014-04-03 23:32 ` [PATCH 15/49] rc-core: merge rc5 and streamzap decoders David Härdeman
2014-04-03 23:32 ` [PATCH 16/49] rc-core: use an IDA rather than a bitmap David Härdeman
2014-07-25 22:39   ` Mauro Carvalho Chehab
2014-04-03 23:32 ` [PATCH 17/49] rc-core: add chardev David Härdeman
2014-04-03 23:32 ` [PATCH 18/49] rc-core: allow chardev to be read David Härdeman
2014-04-03 23:32 ` [PATCH 19/49] rc-core: use a kfifo for TX data David Härdeman
2014-04-03 23:32 ` [PATCH 20/49] rc-core: allow chardev to be written David Härdeman
2014-04-03 23:33 ` [PATCH 21/49] rc-core: add ioctl support to the rc chardev David Härdeman
2014-04-03 23:33 ` [PATCH 22/49] rc-core: add an ioctl for getting IR RX settings David Härdeman
2014-04-03 23:33 ` [PATCH 23/49] rc-loopback: add RCIOCGIRRX ioctl support David Härdeman
2014-04-03 23:33 ` [PATCH 24/49] rc-core: add an ioctl for setting IR RX settings David Härdeman
2014-04-03 23:33 ` [PATCH 25/49] rc-loopback: add RCIOCSIRRX ioctl support David Härdeman
2014-04-03 23:33 ` [PATCH 26/49] rc-core: add an ioctl for getting IR TX settings David Härdeman
2014-04-03 23:33 ` [PATCH 27/49] rc-loopback: add RCIOCGIRTX ioctl support David Härdeman
2014-04-03 23:33 ` [PATCH 28/49] rc-core: add an ioctl for setting IR TX settings David Härdeman
2014-04-03 23:33 ` [PATCH 29/49] rc-loopback: add RCIOCSIRTX ioctl support David Härdeman
2014-04-03 23:33 ` [PATCH 30/49] rc-core: leave the internals of rc_dev alone David Härdeman
2014-07-24  1:50   ` Mauro Carvalho Chehab
2014-04-03 23:33 ` [PATCH 31/49] rc-core: split rc-main.c into rc-main.c and rc-keytable.c David Härdeman
2014-07-25 22:44   ` Mauro Carvalho Chehab
2014-04-03 23:33 ` [PATCH 32/49] rc-core: prepare for multiple keytables David Härdeman
2014-07-25 22:52   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 33/49] rc-core: make the keytable of rc_dev an array David Härdeman
2014-04-03 23:34 ` [PATCH 34/49] rc-core: add ioctls for adding/removing keytables from userspace David Härdeman
2014-04-03 23:34 ` [PATCH 35/49] rc-core: remove redundant spinlock David Härdeman
2014-04-03 23:34 ` David Härdeman [this message]
2014-04-03 23:34 ` [PATCH 37/49] rc-core: allow empty keymaps David Härdeman
2014-07-25 22:58   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 38/49] rc-core: rename ir-raw.c David Härdeman
2014-04-03 23:34 ` [PATCH 39/49] rc-core: make IR raw handling a separate module David Härdeman
2014-07-25 23:04   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 40/49] rc-ir-raw: simplify locking David Härdeman
2014-07-25 23:08   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 41/49] rc-core: rename mutex David Härdeman
2014-04-10 21:28   ` James Hogan
2014-07-25 23:12   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 42/49] rc-ir-raw: atomic reads of protocols David Härdeman
2014-07-25 23:13   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 43/49] rc-core: fix various sparse warnings David Härdeman
2014-04-03 23:34 ` [PATCH 44/49] rc-core: don't report scancodes via input devices David Härdeman
2014-07-25 23:16   ` Mauro Carvalho Chehab
2014-04-03 23:35 ` [PATCH 45/49] rc-ir-raw: add various rc_events David Härdeman
2014-07-25 23:16   ` Mauro Carvalho Chehab
2014-04-03 23:35 ` [PATCH 46/49] rc-core: use struct rc_event for all rc communication David Härdeman
2014-07-25 23:19   ` Mauro Carvalho Chehab
2014-04-03 23:35 ` [PATCH 47/49] rc-core: add keytable events David Härdeman
2014-04-03 23:35 ` [PATCH 48/49] rc-core: move remaining keytable functions David Härdeman
2014-04-03 23:35 ` [PATCH 49/49] rc-core: make rc-core.h userspace friendly David Härdeman
2014-04-04  2:05 ` [PATCH 00/49] rc-core: my current patch queue Mauro Carvalho Chehab
2014-06-26 20:07 ` David Härdeman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20140403233418.27099.61408.stgit@zeus.muc.hardeman.nu \
    --to=david@hardeman.nu \
    --cc=linux-media@vger.kernel.org \
    --cc=m.chehab@samsung.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.