mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-01 00:58:39 +02:00 
			
		
		
		
	lib: Add memcat_p(): paste 2 pointer arrays together
This adds a helper to paste 2 pointer arrays together, useful for merging various types of attribute arrays. There are a few places in the kernel tree where this is open coded, and I just added one more in the STM class. The naming is inspired by memset_p() and memcat(), and partial credit for it goes to Andy Shevchenko. This patch adds the function wrapped in a type-enforcing macro and a test module. Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Tested-by: Mathieu Poirier <mathieu.poirier@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									6c7e4b6882
								
							
						
					
					
						commit
						ce76d938dd
					
				
					 5 changed files with 162 additions and 0 deletions
				
			
		|  | @ -131,6 +131,13 @@ static inline void *memset_p(void **p, void *v, __kernel_size_t n) | ||||||
| 		return memset64((uint64_t *)p, (uintptr_t)v, n); | 		return memset64((uint64_t *)p, (uintptr_t)v, n); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | extern void **__memcat_p(void **a, void **b); | ||||||
|  | #define memcat_p(a, b) ({					\ | ||||||
|  | 	BUILD_BUG_ON_MSG(!__same_type(*(a), *(b)),		\ | ||||||
|  | 			 "type mismatch in memcat_p()");	\ | ||||||
|  | 	(typeof(*a) *)__memcat_p((void **)(a), (void **)(b));	\ | ||||||
|  | }) | ||||||
|  | 
 | ||||||
| #ifndef __HAVE_ARCH_MEMCPY | #ifndef __HAVE_ARCH_MEMCPY | ||||||
| extern void * memcpy(void *,const void *,__kernel_size_t); | extern void * memcpy(void *,const void *,__kernel_size_t); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -1965,6 +1965,14 @@ config TEST_DEBUG_VIRTUAL | ||||||
| 
 | 
 | ||||||
| 	  If unsure, say N. | 	  If unsure, say N. | ||||||
| 
 | 
 | ||||||
|  | config TEST_MEMCAT_P | ||||||
|  | 	tristate "Test memcat_p() helper function" | ||||||
|  | 	help | ||||||
|  | 	  Test the memcat_p() helper for correctly merging two | ||||||
|  | 	  pointer arrays together. | ||||||
|  | 
 | ||||||
|  | 	  If unsure, say N. | ||||||
|  | 
 | ||||||
| endif # RUNTIME_TESTING_MENU | endif # RUNTIME_TESTING_MENU | ||||||
| 
 | 
 | ||||||
| config MEMTEST | config MEMTEST | ||||||
|  |  | ||||||
|  | @ -71,6 +71,7 @@ obj-$(CONFIG_TEST_UUID) += test_uuid.o | ||||||
| obj-$(CONFIG_TEST_PARMAN) += test_parman.o | obj-$(CONFIG_TEST_PARMAN) += test_parman.o | ||||||
| obj-$(CONFIG_TEST_KMOD) += test_kmod.o | obj-$(CONFIG_TEST_KMOD) += test_kmod.o | ||||||
| obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o | obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o | ||||||
|  | obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o | ||||||
| 
 | 
 | ||||||
| ifeq ($(CONFIG_DEBUG_KOBJECT),y) | ifeq ($(CONFIG_DEBUG_KOBJECT),y) | ||||||
| CFLAGS_kobject.o += -DDEBUG | CFLAGS_kobject.o += -DDEBUG | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								lib/string.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								lib/string.c
									
									
									
									
									
								
							|  | @ -27,6 +27,7 @@ | ||||||
| #include <linux/export.h> | #include <linux/export.h> | ||||||
| #include <linux/bug.h> | #include <linux/bug.h> | ||||||
| #include <linux/errno.h> | #include <linux/errno.h> | ||||||
|  | #include <linux/slab.h> | ||||||
| 
 | 
 | ||||||
| #include <asm/byteorder.h> | #include <asm/byteorder.h> | ||||||
| #include <asm/word-at-a-time.h> | #include <asm/word-at-a-time.h> | ||||||
|  | @ -890,6 +891,36 @@ void *memscan(void *addr, int c, size_t size) | ||||||
| EXPORT_SYMBOL(memscan); | EXPORT_SYMBOL(memscan); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Merge two NULL-terminated pointer arrays into a newly allocated | ||||||
|  |  * array, which is also NULL-terminated. Nomenclature is inspired by | ||||||
|  |  * memset_p() and memcat() found elsewhere in the kernel source tree. | ||||||
|  |  */ | ||||||
|  | void **__memcat_p(void **a, void **b) | ||||||
|  | { | ||||||
|  | 	void **p = a, **new; | ||||||
|  | 	int nr; | ||||||
|  | 
 | ||||||
|  | 	/* count the elements in both arrays */ | ||||||
|  | 	for (nr = 0, p = a; *p; nr++, p++) | ||||||
|  | 		; | ||||||
|  | 	for (p = b; *p; nr++, p++) | ||||||
|  | 		; | ||||||
|  | 	/* one for the NULL-terminator */ | ||||||
|  | 	nr++; | ||||||
|  | 
 | ||||||
|  | 	new = kmalloc_array(nr, sizeof(void *), GFP_KERNEL); | ||||||
|  | 	if (!new) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	/* nr -> last index; p points to NULL in b[] */ | ||||||
|  | 	for (nr--; nr >= 0; nr--, p = p == b ? &a[nr] : p - 1) | ||||||
|  | 		new[nr] = *p; | ||||||
|  | 
 | ||||||
|  | 	return new; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL_GPL(__memcat_p); | ||||||
|  | 
 | ||||||
| #ifndef __HAVE_ARCH_STRSTR | #ifndef __HAVE_ARCH_STRSTR | ||||||
| /**
 | /**
 | ||||||
|  * strstr - Find the first substring in a %NUL terminated string |  * strstr - Find the first substring in a %NUL terminated string | ||||||
|  |  | ||||||
							
								
								
									
										115
									
								
								lib/test_memcat_p.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								lib/test_memcat_p.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0
 | ||||||
|  | /*
 | ||||||
|  |  * Test cases for memcat_p() in lib/string.c | ||||||
|  |  */ | ||||||
|  | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||||
|  | 
 | ||||||
|  | #include <linux/string.h> | ||||||
|  | #include <linux/slab.h> | ||||||
|  | #include <linux/module.h> | ||||||
|  | 
 | ||||||
|  | struct test_struct { | ||||||
|  | 	int		num; | ||||||
|  | 	unsigned int	magic; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define MAGIC		0xf00ff00f | ||||||
|  | /* Size of each of the NULL-terminated input arrays */ | ||||||
|  | #define INPUT_MAX	128 | ||||||
|  | /* Expected number of non-NULL elements in the output array */ | ||||||
|  | #define EXPECT		(INPUT_MAX * 2 - 2) | ||||||
|  | 
 | ||||||
|  | static int __init test_memcat_p_init(void) | ||||||
|  | { | ||||||
|  | 	struct test_struct **in0, **in1, **out, **p; | ||||||
|  | 	int err = -ENOMEM, i, r, total = 0; | ||||||
|  | 
 | ||||||
|  | 	in0 = kcalloc(INPUT_MAX, sizeof(*in0), GFP_KERNEL); | ||||||
|  | 	if (!in0) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	in1 = kcalloc(INPUT_MAX, sizeof(*in1), GFP_KERNEL); | ||||||
|  | 	if (!in1) | ||||||
|  | 		goto err_free_in0; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0, r = 1; i < INPUT_MAX - 1; i++) { | ||||||
|  | 		in0[i] = kmalloc(sizeof(**in0), GFP_KERNEL); | ||||||
|  | 		if (!in0[i]) | ||||||
|  | 			goto err_free_elements; | ||||||
|  | 
 | ||||||
|  | 		in1[i] = kmalloc(sizeof(**in1), GFP_KERNEL); | ||||||
|  | 		if (!in1[i]) { | ||||||
|  | 			kfree(in0[i]); | ||||||
|  | 			goto err_free_elements; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/* lifted from test_sort.c */ | ||||||
|  | 		r = (r * 725861) % 6599; | ||||||
|  | 		in0[i]->num = r; | ||||||
|  | 		in1[i]->num = -r; | ||||||
|  | 		in0[i]->magic = MAGIC; | ||||||
|  | 		in1[i]->magic = MAGIC; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	in0[i] = in1[i] = NULL; | ||||||
|  | 
 | ||||||
|  | 	out = memcat_p(in0, in1); | ||||||
|  | 	if (!out) | ||||||
|  | 		goto err_free_all_elements; | ||||||
|  | 
 | ||||||
|  | 	err = -EINVAL; | ||||||
|  | 	for (i = 0, p = out; *p && (i < INPUT_MAX * 2 - 1); p++, i++) { | ||||||
|  | 		total += (*p)->num; | ||||||
|  | 
 | ||||||
|  | 		if ((*p)->magic != MAGIC) { | ||||||
|  | 			pr_err("test failed: wrong magic at %d: %u\n", i, | ||||||
|  | 			       (*p)->magic); | ||||||
|  | 			goto err_free_out; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (total) { | ||||||
|  | 		pr_err("test failed: expected zero total, got %d\n", total); | ||||||
|  | 		goto err_free_out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (i != EXPECT) { | ||||||
|  | 		pr_err("test failed: expected output size %d, got %d\n", | ||||||
|  | 		       EXPECT, i); | ||||||
|  | 		goto err_free_out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < INPUT_MAX - 1; i++) | ||||||
|  | 		if (out[i] != in0[i] || out[i + INPUT_MAX - 1] != in1[i]) { | ||||||
|  | 			pr_err("test failed: wrong element order at %d\n", i); | ||||||
|  | 			goto err_free_out; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	err = 0; | ||||||
|  | 	pr_info("test passed\n"); | ||||||
|  | 
 | ||||||
|  | err_free_out: | ||||||
|  | 	kfree(out); | ||||||
|  | err_free_all_elements: | ||||||
|  | 	i = INPUT_MAX; | ||||||
|  | err_free_elements: | ||||||
|  | 	for (i--; i >= 0; i--) { | ||||||
|  | 		kfree(in1[i]); | ||||||
|  | 		kfree(in0[i]); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	kfree(in1); | ||||||
|  | err_free_in0: | ||||||
|  | 	kfree(in0); | ||||||
|  | 
 | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void __exit test_memcat_p_exit(void) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module_init(test_memcat_p_init); | ||||||
|  | module_exit(test_memcat_p_exit); | ||||||
|  | 
 | ||||||
|  | MODULE_LICENSE("GPL"); | ||||||
		Loading…
	
		Reference in a new issue
	
	 Alexander Shishkin
						Alexander Shishkin