diff options
| -rw-r--r-- | lib/Kconfig.debug | 7 | ||||
| -rw-r--r-- | lib/Makefile | 1 | ||||
| -rw-r--r-- | lib/list_sort.c | 149 | ||||
| -rw-r--r-- | lib/test_list_sort.c | 150 |
4 files changed, 155 insertions, 152 deletions
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index de3f7c151320..e4587ebe52c7 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
| @@ -1719,11 +1719,12 @@ config LKDTM | |||
| 1719 | Documentation/fault-injection/provoke-crashes.txt | 1719 | Documentation/fault-injection/provoke-crashes.txt |
| 1720 | 1720 | ||
| 1721 | config TEST_LIST_SORT | 1721 | config TEST_LIST_SORT |
| 1722 | bool "Linked list sorting test" | 1722 | tristate "Linked list sorting test" |
| 1723 | depends on DEBUG_KERNEL | 1723 | depends on DEBUG_KERNEL || m |
| 1724 | help | 1724 | help |
| 1725 | Enable this to turn on 'list_sort()' function test. This test is | 1725 | Enable this to turn on 'list_sort()' function test. This test is |
| 1726 | executed only once during system boot, so affects only boot time. | 1726 | executed only once during system boot (so affects only boot time), |
| 1727 | or at module load time. | ||
| 1727 | 1728 | ||
| 1728 | If unsure, say N. | 1729 | If unsure, say N. |
| 1729 | 1730 | ||
diff --git a/lib/Makefile b/lib/Makefile index a155c73e3437..0166fbc0fa81 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
| @@ -52,6 +52,7 @@ obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o | |||
| 52 | obj-$(CONFIG_TEST_HASH) += test_hash.o test_siphash.o | 52 | obj-$(CONFIG_TEST_HASH) += test_hash.o test_siphash.o |
| 53 | obj-$(CONFIG_TEST_KASAN) += test_kasan.o | 53 | obj-$(CONFIG_TEST_KASAN) += test_kasan.o |
| 54 | obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o | 54 | obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o |
| 55 | obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o | ||
| 55 | obj-$(CONFIG_TEST_LKM) += test_module.o | 56 | obj-$(CONFIG_TEST_LKM) += test_module.o |
| 56 | obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o | 57 | obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o |
| 57 | obj-$(CONFIG_TEST_SORT) += test_sort.o | 58 | obj-$(CONFIG_TEST_SORT) += test_sort.o |
diff --git a/lib/list_sort.c b/lib/list_sort.c index 3fe401067e20..9e9acc37652f 100644 --- a/lib/list_sort.c +++ b/lib/list_sort.c | |||
| @@ -1,6 +1,3 @@ | |||
| 1 | |||
| 2 | #define pr_fmt(fmt) "list_sort_test: " fmt | ||
| 3 | |||
| 4 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
| 5 | #include <linux/bug.h> | 2 | #include <linux/bug.h> |
| 6 | #include <linux/compiler.h> | 3 | #include <linux/compiler.h> |
| @@ -145,149 +142,3 @@ void list_sort(void *priv, struct list_head *head, | |||
| 145 | merge_and_restore_back_links(priv, cmp, head, part[max_lev], list); | 142 | merge_and_restore_back_links(priv, cmp, head, part[max_lev], list); |
| 146 | } | 143 | } |
| 147 | EXPORT_SYMBOL(list_sort); | 144 | EXPORT_SYMBOL(list_sort); |
| 148 | |||
| 149 | #ifdef CONFIG_TEST_LIST_SORT | ||
| 150 | |||
| 151 | #include <linux/slab.h> | ||
| 152 | #include <linux/random.h> | ||
| 153 | |||
| 154 | /* | ||
| 155 | * The pattern of set bits in the list length determines which cases | ||
| 156 | * are hit in list_sort(). | ||
| 157 | */ | ||
| 158 | #define TEST_LIST_LEN (512+128+2) /* not including head */ | ||
| 159 | |||
| 160 | #define TEST_POISON1 0xDEADBEEF | ||
| 161 | #define TEST_POISON2 0xA324354C | ||
| 162 | |||
| 163 | struct debug_el { | ||
| 164 | unsigned int poison1; | ||
| 165 | struct list_head list; | ||
| 166 | unsigned int poison2; | ||
| 167 | int value; | ||
| 168 | unsigned serial; | ||
| 169 | }; | ||
| 170 | |||
| 171 | /* Array, containing pointers to all elements in the test list */ | ||
| 172 | static struct debug_el **elts __initdata; | ||
| 173 | |||
| 174 | static int __init check(struct debug_el *ela, struct debug_el *elb) | ||
| 175 | { | ||
| 176 | if (ela->serial >= TEST_LIST_LEN) { | ||
| 177 | pr_err("error: incorrect serial %d\n", ela->serial); | ||
| 178 | return -EINVAL; | ||
| 179 | } | ||
| 180 | if (elb->serial >= TEST_LIST_LEN) { | ||
| 181 | pr_err("error: incorrect serial %d\n", elb->serial); | ||
| 182 | return -EINVAL; | ||
| 183 | } | ||
| 184 | if (elts[ela->serial] != ela || elts[elb->serial] != elb) { | ||
| 185 | pr_err("error: phantom element\n"); | ||
| 186 | return -EINVAL; | ||
| 187 | } | ||
| 188 | if (ela->poison1 != TEST_POISON1 || ela->poison2 != TEST_POISON2) { | ||
| 189 | pr_err("error: bad poison: %#x/%#x\n", | ||
| 190 | ela->poison1, ela->poison2); | ||
| 191 | return -EINVAL; | ||
| 192 | } | ||
| 193 | if (elb->poison1 != TEST_POISON1 || elb->poison2 != TEST_POISON2) { | ||
| 194 | pr_err("error: bad poison: %#x/%#x\n", | ||
| 195 | elb->poison1, elb->poison2); | ||
| 196 | return -EINVAL; | ||
| 197 | } | ||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | static int __init cmp(void *priv, struct list_head *a, struct list_head *b) | ||
| 202 | { | ||
| 203 | struct debug_el *ela, *elb; | ||
| 204 | |||
| 205 | ela = container_of(a, struct debug_el, list); | ||
| 206 | elb = container_of(b, struct debug_el, list); | ||
| 207 | |||
| 208 | check(ela, elb); | ||
| 209 | return ela->value - elb->value; | ||
| 210 | } | ||
| 211 | |||
| 212 | static int __init list_sort_test(void) | ||
| 213 | { | ||
| 214 | int i, count = 1, err = -ENOMEM; | ||
| 215 | struct debug_el *el; | ||
| 216 | struct list_head *cur; | ||
| 217 | LIST_HEAD(head); | ||
| 218 | |||
| 219 | pr_debug("start testing list_sort()\n"); | ||
| 220 | |||
| 221 | elts = kcalloc(TEST_LIST_LEN, sizeof(*elts), GFP_KERNEL); | ||
| 222 | if (!elts) { | ||
| 223 | pr_err("error: cannot allocate memory\n"); | ||
| 224 | return err; | ||
| 225 | } | ||
| 226 | |||
| 227 | for (i = 0; i < TEST_LIST_LEN; i++) { | ||
| 228 | el = kmalloc(sizeof(*el), GFP_KERNEL); | ||
| 229 | if (!el) { | ||
| 230 | pr_err("error: cannot allocate memory\n"); | ||
| 231 | goto exit; | ||
| 232 | } | ||
| 233 | /* force some equivalencies */ | ||
| 234 | el->value = prandom_u32() % (TEST_LIST_LEN / 3); | ||
| 235 | el->serial = i; | ||
| 236 | el->poison1 = TEST_POISON1; | ||
| 237 | el->poison2 = TEST_POISON2; | ||
| 238 | elts[i] = el; | ||
| 239 | list_add_tail(&el->list, &head); | ||
| 240 | } | ||
| 241 | |||
| 242 | list_sort(NULL, &head, cmp); | ||
| 243 | |||
| 244 | err = -EINVAL; | ||
| 245 | for (cur = head.next; cur->next != &head; cur = cur->next) { | ||
| 246 | struct debug_el *el1; | ||
| 247 | int cmp_result; | ||
| 248 | |||
| 249 | if (cur->next->prev != cur) { | ||
| 250 | pr_err("error: list is corrupted\n"); | ||
| 251 | goto exit; | ||
| 252 | } | ||
| 253 | |||
| 254 | cmp_result = cmp(NULL, cur, cur->next); | ||
| 255 | if (cmp_result > 0) { | ||
| 256 | pr_err("error: list is not sorted\n"); | ||
| 257 | goto exit; | ||
| 258 | } | ||
| 259 | |||
| 260 | el = container_of(cur, struct debug_el, list); | ||
| 261 | el1 = container_of(cur->next, struct debug_el, list); | ||
| 262 | if (cmp_result == 0 && el->serial >= el1->serial) { | ||
| 263 | pr_err("error: order of equivalent elements not " | ||
| 264 | "preserved\n"); | ||
| 265 | goto exit; | ||
| 266 | } | ||
| 267 | |||
| 268 | if (check(el, el1)) { | ||
| 269 | pr_err("error: element check failed\n"); | ||
| 270 | goto exit; | ||
| 271 | } | ||
| 272 | count++; | ||
| 273 | } | ||
| 274 | if (head.prev != cur) { | ||
| 275 | pr_err("error: list is corrupted\n"); | ||
| 276 | goto exit; | ||
| 277 | } | ||
| 278 | |||
| 279 | |||
| 280 | if (count != TEST_LIST_LEN) { | ||
| 281 | pr_err("error: bad list length %d", count); | ||
| 282 | goto exit; | ||
| 283 | } | ||
| 284 | |||
| 285 | err = 0; | ||
| 286 | exit: | ||
| 287 | for (i = 0; i < TEST_LIST_LEN; i++) | ||
| 288 | kfree(elts[i]); | ||
| 289 | kfree(elts); | ||
| 290 | return err; | ||
| 291 | } | ||
| 292 | late_initcall(list_sort_test); | ||
| 293 | #endif /* CONFIG_TEST_LIST_SORT */ | ||
diff --git a/lib/test_list_sort.c b/lib/test_list_sort.c new file mode 100644 index 000000000000..28e817387b04 --- /dev/null +++ b/lib/test_list_sort.c | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | #define pr_fmt(fmt) "list_sort_test: " fmt | ||
| 2 | |||
| 3 | #include <linux/kernel.h> | ||
| 4 | #include <linux/list_sort.h> | ||
| 5 | #include <linux/list.h> | ||
| 6 | #include <linux/module.h> | ||
| 7 | #include <linux/printk.h> | ||
| 8 | #include <linux/slab.h> | ||
| 9 | #include <linux/random.h> | ||
| 10 | |||
| 11 | /* | ||
| 12 | * The pattern of set bits in the list length determines which cases | ||
| 13 | * are hit in list_sort(). | ||
| 14 | */ | ||
| 15 | #define TEST_LIST_LEN (512+128+2) /* not including head */ | ||
| 16 | |||
| 17 | #define TEST_POISON1 0xDEADBEEF | ||
| 18 | #define TEST_POISON2 0xA324354C | ||
| 19 | |||
| 20 | struct debug_el { | ||
| 21 | unsigned int poison1; | ||
| 22 | struct list_head list; | ||
| 23 | unsigned int poison2; | ||
| 24 | int value; | ||
| 25 | unsigned serial; | ||
| 26 | }; | ||
| 27 | |||
| 28 | /* Array, containing pointers to all elements in the test list */ | ||
| 29 | static struct debug_el **elts __initdata; | ||
| 30 | |||
| 31 | static int __init check(struct debug_el *ela, struct debug_el *elb) | ||
| 32 | { | ||
| 33 | if (ela->serial >= TEST_LIST_LEN) { | ||
| 34 | pr_err("error: incorrect serial %d\n", ela->serial); | ||
| 35 | return -EINVAL; | ||
| 36 | } | ||
| 37 | if (elb->serial >= TEST_LIST_LEN) { | ||
| 38 | pr_err("error: incorrect serial %d\n", elb->serial); | ||
| 39 | return -EINVAL; | ||
| 40 | } | ||
| 41 | if (elts[ela->serial] != ela || elts[elb->serial] != elb) { | ||
| 42 | pr_err("error: phantom element\n"); | ||
| 43 | return -EINVAL; | ||
| 44 | } | ||
| 45 | if (ela->poison1 != TEST_POISON1 || ela->poison2 != TEST_POISON2) { | ||
| 46 | pr_err("error: bad poison: %#x/%#x\n", | ||
| 47 | ela->poison1, ela->poison2); | ||
| 48 | return -EINVAL; | ||
| 49 | } | ||
| 50 | if (elb->poison1 != TEST_POISON1 || elb->poison2 != TEST_POISON2) { | ||
| 51 | pr_err("error: bad poison: %#x/%#x\n", | ||
| 52 | elb->poison1, elb->poison2); | ||
| 53 | return -EINVAL; | ||
| 54 | } | ||
| 55 | return 0; | ||
| 56 | } | ||
| 57 | |||
| 58 | static int __init cmp(void *priv, struct list_head *a, struct list_head *b) | ||
| 59 | { | ||
| 60 | struct debug_el *ela, *elb; | ||
| 61 | |||
| 62 | ela = container_of(a, struct debug_el, list); | ||
| 63 | elb = container_of(b, struct debug_el, list); | ||
| 64 | |||
| 65 | check(ela, elb); | ||
| 66 | return ela->value - elb->value; | ||
| 67 | } | ||
| 68 | |||
| 69 | static int __init list_sort_test(void) | ||
| 70 | { | ||
| 71 | int i, count = 1, err = -ENOMEM; | ||
| 72 | struct debug_el *el; | ||
| 73 | struct list_head *cur; | ||
| 74 | LIST_HEAD(head); | ||
| 75 | |||
| 76 | pr_debug("start testing list_sort()\n"); | ||
| 77 | |||
| 78 | elts = kcalloc(TEST_LIST_LEN, sizeof(*elts), GFP_KERNEL); | ||
| 79 | if (!elts) { | ||
| 80 | pr_err("error: cannot allocate memory\n"); | ||
| 81 | return err; | ||
| 82 | } | ||
| 83 | |||
| 84 | for (i = 0; i < TEST_LIST_LEN; i++) { | ||
| 85 | el = kmalloc(sizeof(*el), GFP_KERNEL); | ||
| 86 | if (!el) { | ||
| 87 | pr_err("error: cannot allocate memory\n"); | ||
| 88 | goto exit; | ||
| 89 | } | ||
| 90 | /* force some equivalencies */ | ||
| 91 | el->value = prandom_u32() % (TEST_LIST_LEN / 3); | ||
| 92 | el->serial = i; | ||
| 93 | el->poison1 = TEST_POISON1; | ||
| 94 | el->poison2 = TEST_POISON2; | ||
| 95 | elts[i] = el; | ||
| 96 | list_add_tail(&el->list, &head); | ||
| 97 | } | ||
| 98 | |||
| 99 | list_sort(NULL, &head, cmp); | ||
| 100 | |||
| 101 | err = -EINVAL; | ||
| 102 | for (cur = head.next; cur->next != &head; cur = cur->next) { | ||
| 103 | struct debug_el *el1; | ||
| 104 | int cmp_result; | ||
| 105 | |||
| 106 | if (cur->next->prev != cur) { | ||
| 107 | pr_err("error: list is corrupted\n"); | ||
| 108 | goto exit; | ||
| 109 | } | ||
| 110 | |||
| 111 | cmp_result = cmp(NULL, cur, cur->next); | ||
| 112 | if (cmp_result > 0) { | ||
| 113 | pr_err("error: list is not sorted\n"); | ||
| 114 | goto exit; | ||
| 115 | } | ||
| 116 | |||
| 117 | el = container_of(cur, struct debug_el, list); | ||
| 118 | el1 = container_of(cur->next, struct debug_el, list); | ||
| 119 | if (cmp_result == 0 && el->serial >= el1->serial) { | ||
| 120 | pr_err("error: order of equivalent elements not " | ||
| 121 | "preserved\n"); | ||
| 122 | goto exit; | ||
| 123 | } | ||
| 124 | |||
| 125 | if (check(el, el1)) { | ||
| 126 | pr_err("error: element check failed\n"); | ||
| 127 | goto exit; | ||
| 128 | } | ||
| 129 | count++; | ||
| 130 | } | ||
| 131 | if (head.prev != cur) { | ||
| 132 | pr_err("error: list is corrupted\n"); | ||
| 133 | goto exit; | ||
| 134 | } | ||
| 135 | |||
| 136 | |||
| 137 | if (count != TEST_LIST_LEN) { | ||
| 138 | pr_err("error: bad list length %d", count); | ||
| 139 | goto exit; | ||
| 140 | } | ||
| 141 | |||
| 142 | err = 0; | ||
| 143 | exit: | ||
| 144 | for (i = 0; i < TEST_LIST_LEN; i++) | ||
| 145 | kfree(elts[i]); | ||
| 146 | kfree(elts); | ||
| 147 | return err; | ||
| 148 | } | ||
| 149 | module_init(list_sort_test); | ||
| 150 | MODULE_LICENSE("GPL"); | ||
