diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/dynamic_queue_limits.c | 3 | ||||
| -rw-r--r-- | lib/kobject_uevent.c | 94 | ||||
| -rw-r--r-- | lib/nlattr.c | 19 | ||||
| -rw-r--r-- | lib/once.c | 8 | ||||
| -rw-r--r-- | lib/test_rhashtable.c | 293 |
5 files changed, 342 insertions, 75 deletions
diff --git a/lib/dynamic_queue_limits.c b/lib/dynamic_queue_limits.c index da4672a50a54..e659a027036e 100644 --- a/lib/dynamic_queue_limits.c +++ b/lib/dynamic_queue_limits.c | |||
| @@ -128,12 +128,11 @@ void dql_reset(struct dql *dql) | |||
| 128 | } | 128 | } |
| 129 | EXPORT_SYMBOL(dql_reset); | 129 | EXPORT_SYMBOL(dql_reset); |
| 130 | 130 | ||
| 131 | int dql_init(struct dql *dql, unsigned hold_time) | 131 | void dql_init(struct dql *dql, unsigned int hold_time) |
| 132 | { | 132 | { |
| 133 | dql->max_limit = DQL_MAX_LIMIT; | 133 | dql->max_limit = DQL_MAX_LIMIT; |
| 134 | dql->min_limit = 0; | 134 | dql->min_limit = 0; |
| 135 | dql->slack_hold_time = hold_time; | 135 | dql->slack_hold_time = hold_time; |
| 136 | dql_reset(dql); | 136 | dql_reset(dql); |
| 137 | return 0; | ||
| 138 | } | 137 | } |
| 139 | EXPORT_SYMBOL(dql_init); | 138 | EXPORT_SYMBOL(dql_init); |
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index f237a09a5862..c3e84edc47c9 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c | |||
| @@ -294,6 +294,55 @@ static void cleanup_uevent_env(struct subprocess_info *info) | |||
| 294 | } | 294 | } |
| 295 | #endif | 295 | #endif |
| 296 | 296 | ||
| 297 | static int kobject_uevent_net_broadcast(struct kobject *kobj, | ||
| 298 | struct kobj_uevent_env *env, | ||
| 299 | const char *action_string, | ||
| 300 | const char *devpath) | ||
| 301 | { | ||
| 302 | int retval = 0; | ||
| 303 | #if defined(CONFIG_NET) | ||
| 304 | struct sk_buff *skb = NULL; | ||
| 305 | struct uevent_sock *ue_sk; | ||
| 306 | |||
| 307 | /* send netlink message */ | ||
| 308 | list_for_each_entry(ue_sk, &uevent_sock_list, list) { | ||
| 309 | struct sock *uevent_sock = ue_sk->sk; | ||
| 310 | |||
| 311 | if (!netlink_has_listeners(uevent_sock, 1)) | ||
| 312 | continue; | ||
| 313 | |||
| 314 | if (!skb) { | ||
| 315 | /* allocate message with the maximum possible size */ | ||
| 316 | size_t len = strlen(action_string) + strlen(devpath) + 2; | ||
| 317 | char *scratch; | ||
| 318 | |||
| 319 | retval = -ENOMEM; | ||
| 320 | skb = alloc_skb(len + env->buflen, GFP_KERNEL); | ||
| 321 | if (!skb) | ||
| 322 | continue; | ||
| 323 | |||
| 324 | /* add header */ | ||
| 325 | scratch = skb_put(skb, len); | ||
| 326 | sprintf(scratch, "%s@%s", action_string, devpath); | ||
| 327 | |||
| 328 | skb_put_data(skb, env->buf, env->buflen); | ||
| 329 | |||
| 330 | NETLINK_CB(skb).dst_group = 1; | ||
| 331 | } | ||
| 332 | |||
| 333 | retval = netlink_broadcast_filtered(uevent_sock, skb_get(skb), | ||
| 334 | 0, 1, GFP_KERNEL, | ||
| 335 | kobj_bcast_filter, | ||
| 336 | kobj); | ||
| 337 | /* ENOBUFS should be handled in userspace */ | ||
| 338 | if (retval == -ENOBUFS || retval == -ESRCH) | ||
| 339 | retval = 0; | ||
| 340 | } | ||
| 341 | consume_skb(skb); | ||
| 342 | #endif | ||
| 343 | return retval; | ||
| 344 | } | ||
| 345 | |||
| 297 | static void zap_modalias_env(struct kobj_uevent_env *env) | 346 | static void zap_modalias_env(struct kobj_uevent_env *env) |
| 298 | { | 347 | { |
| 299 | static const char modalias_prefix[] = "MODALIAS="; | 348 | static const char modalias_prefix[] = "MODALIAS="; |
| @@ -336,9 +385,6 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | |||
| 336 | const struct kset_uevent_ops *uevent_ops; | 385 | const struct kset_uevent_ops *uevent_ops; |
| 337 | int i = 0; | 386 | int i = 0; |
| 338 | int retval = 0; | 387 | int retval = 0; |
| 339 | #ifdef CONFIG_NET | ||
| 340 | struct uevent_sock *ue_sk; | ||
| 341 | #endif | ||
| 342 | 388 | ||
| 343 | pr_debug("kobject: '%s' (%p): %s\n", | 389 | pr_debug("kobject: '%s' (%p): %s\n", |
| 344 | kobject_name(kobj), kobj, __func__); | 390 | kobject_name(kobj), kobj, __func__); |
| @@ -460,46 +506,8 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | |||
| 460 | mutex_unlock(&uevent_sock_mutex); | 506 | mutex_unlock(&uevent_sock_mutex); |
| 461 | goto exit; | 507 | goto exit; |
| 462 | } | 508 | } |
| 463 | 509 | retval = kobject_uevent_net_broadcast(kobj, env, action_string, | |
| 464 | #if defined(CONFIG_NET) | 510 | devpath); |
| 465 | /* send netlink message */ | ||
| 466 | list_for_each_entry(ue_sk, &uevent_sock_list, list) { | ||
| 467 | struct sock *uevent_sock = ue_sk->sk; | ||
| 468 | struct sk_buff *skb; | ||
| 469 | size_t len; | ||
| 470 | |||
| 471 | if (!netlink_has_listeners(uevent_sock, 1)) | ||
| 472 | continue; | ||
| 473 | |||
| 474 | /* allocate message with the maximum possible size */ | ||
| 475 | len = strlen(action_string) + strlen(devpath) + 2; | ||
| 476 | skb = alloc_skb(len + env->buflen, GFP_KERNEL); | ||
| 477 | if (skb) { | ||
| 478 | char *scratch; | ||
| 479 | |||
| 480 | /* add header */ | ||
| 481 | scratch = skb_put(skb, len); | ||
| 482 | sprintf(scratch, "%s@%s", action_string, devpath); | ||
| 483 | |||
| 484 | /* copy keys to our continuous event payload buffer */ | ||
| 485 | for (i = 0; i < env->envp_idx; i++) { | ||
| 486 | len = strlen(env->envp[i]) + 1; | ||
| 487 | scratch = skb_put(skb, len); | ||
| 488 | strcpy(scratch, env->envp[i]); | ||
| 489 | } | ||
| 490 | |||
| 491 | NETLINK_CB(skb).dst_group = 1; | ||
| 492 | retval = netlink_broadcast_filtered(uevent_sock, skb, | ||
| 493 | 0, 1, GFP_KERNEL, | ||
| 494 | kobj_bcast_filter, | ||
| 495 | kobj); | ||
| 496 | /* ENOBUFS should be handled in userspace */ | ||
| 497 | if (retval == -ENOBUFS || retval == -ESRCH) | ||
| 498 | retval = 0; | ||
| 499 | } else | ||
| 500 | retval = -ENOMEM; | ||
| 501 | } | ||
| 502 | #endif | ||
| 503 | mutex_unlock(&uevent_sock_mutex); | 511 | mutex_unlock(&uevent_sock_mutex); |
| 504 | 512 | ||
| 505 | #ifdef CONFIG_UEVENT_HELPER | 513 | #ifdef CONFIG_UEVENT_HELPER |
diff --git a/lib/nlattr.c b/lib/nlattr.c index 3d8295c85505..8bf78b4b78f0 100644 --- a/lib/nlattr.c +++ b/lib/nlattr.c | |||
| @@ -15,19 +15,23 @@ | |||
| 15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
| 16 | #include <net/netlink.h> | 16 | #include <net/netlink.h> |
| 17 | 17 | ||
| 18 | static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = { | 18 | /* for these data types attribute length must be exactly given size */ |
| 19 | static const u8 nla_attr_len[NLA_TYPE_MAX+1] = { | ||
| 19 | [NLA_U8] = sizeof(u8), | 20 | [NLA_U8] = sizeof(u8), |
| 20 | [NLA_U16] = sizeof(u16), | 21 | [NLA_U16] = sizeof(u16), |
| 21 | [NLA_U32] = sizeof(u32), | 22 | [NLA_U32] = sizeof(u32), |
| 22 | [NLA_U64] = sizeof(u64), | 23 | [NLA_U64] = sizeof(u64), |
| 23 | [NLA_MSECS] = sizeof(u64), | ||
| 24 | [NLA_NESTED] = NLA_HDRLEN, | ||
| 25 | [NLA_S8] = sizeof(s8), | 24 | [NLA_S8] = sizeof(s8), |
| 26 | [NLA_S16] = sizeof(s16), | 25 | [NLA_S16] = sizeof(s16), |
| 27 | [NLA_S32] = sizeof(s32), | 26 | [NLA_S32] = sizeof(s32), |
| 28 | [NLA_S64] = sizeof(s64), | 27 | [NLA_S64] = sizeof(s64), |
| 29 | }; | 28 | }; |
| 30 | 29 | ||
| 30 | static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = { | ||
| 31 | [NLA_MSECS] = sizeof(u64), | ||
| 32 | [NLA_NESTED] = NLA_HDRLEN, | ||
| 33 | }; | ||
| 34 | |||
| 31 | static int validate_nla_bitfield32(const struct nlattr *nla, | 35 | static int validate_nla_bitfield32(const struct nlattr *nla, |
| 32 | u32 *valid_flags_allowed) | 36 | u32 *valid_flags_allowed) |
| 33 | { | 37 | { |
| @@ -65,6 +69,13 @@ static int validate_nla(const struct nlattr *nla, int maxtype, | |||
| 65 | 69 | ||
| 66 | BUG_ON(pt->type > NLA_TYPE_MAX); | 70 | BUG_ON(pt->type > NLA_TYPE_MAX); |
| 67 | 71 | ||
| 72 | /* for data types NLA_U* and NLA_S* require exact length */ | ||
| 73 | if (nla_attr_len[pt->type]) { | ||
| 74 | if (attrlen != nla_attr_len[pt->type]) | ||
| 75 | return -ERANGE; | ||
| 76 | return 0; | ||
| 77 | } | ||
| 78 | |||
| 68 | switch (pt->type) { | 79 | switch (pt->type) { |
| 69 | case NLA_FLAG: | 80 | case NLA_FLAG: |
| 70 | if (attrlen > 0) | 81 | if (attrlen > 0) |
| @@ -191,6 +202,8 @@ nla_policy_len(const struct nla_policy *p, int n) | |||
| 191 | for (i = 0; i < n; i++, p++) { | 202 | for (i = 0; i < n; i++, p++) { |
| 192 | if (p->len) | 203 | if (p->len) |
| 193 | len += nla_total_size(p->len); | 204 | len += nla_total_size(p->len); |
| 205 | else if (nla_attr_len[p->type]) | ||
| 206 | len += nla_total_size(nla_attr_len[p->type]); | ||
| 194 | else if (nla_attr_minlen[p->type]) | 207 | else if (nla_attr_minlen[p->type]) |
| 195 | len += nla_total_size(nla_attr_minlen[p->type]); | 208 | len += nla_total_size(nla_attr_minlen[p->type]); |
| 196 | } | 209 | } |
diff --git a/lib/once.c b/lib/once.c index bfb7420d0de3..8b7d6235217e 100644 --- a/lib/once.c +++ b/lib/once.c | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | struct once_work { | 7 | struct once_work { |
| 8 | struct work_struct work; | 8 | struct work_struct work; |
| 9 | struct static_key *key; | 9 | struct static_key_true *key; |
| 10 | }; | 10 | }; |
| 11 | 11 | ||
| 12 | static void once_deferred(struct work_struct *w) | 12 | static void once_deferred(struct work_struct *w) |
| @@ -15,11 +15,11 @@ static void once_deferred(struct work_struct *w) | |||
| 15 | 15 | ||
| 16 | work = container_of(w, struct once_work, work); | 16 | work = container_of(w, struct once_work, work); |
| 17 | BUG_ON(!static_key_enabled(work->key)); | 17 | BUG_ON(!static_key_enabled(work->key)); |
| 18 | static_key_slow_dec(work->key); | 18 | static_branch_disable(work->key); |
| 19 | kfree(work); | 19 | kfree(work); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | static void once_disable_jump(struct static_key *key) | 22 | static void once_disable_jump(struct static_key_true *key) |
| 23 | { | 23 | { |
| 24 | struct once_work *w; | 24 | struct once_work *w; |
| 25 | 25 | ||
| @@ -52,7 +52,7 @@ bool __do_once_start(bool *done, unsigned long *flags) | |||
| 52 | } | 52 | } |
| 53 | EXPORT_SYMBOL(__do_once_start); | 53 | EXPORT_SYMBOL(__do_once_start); |
| 54 | 54 | ||
| 55 | void __do_once_done(bool *done, struct static_key *once_key, | 55 | void __do_once_done(bool *done, struct static_key_true *once_key, |
| 56 | unsigned long *flags) | 56 | unsigned long *flags) |
| 57 | __releases(once_lock) | 57 | __releases(once_lock) |
| 58 | { | 58 | { |
diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c index 0ffca990a833..8e83cbdc049c 100644 --- a/lib/test_rhashtable.c +++ b/lib/test_rhashtable.c | |||
| @@ -23,14 +23,15 @@ | |||
| 23 | #include <linux/semaphore.h> | 23 | #include <linux/semaphore.h> |
| 24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
| 25 | #include <linux/sched.h> | 25 | #include <linux/sched.h> |
| 26 | #include <linux/random.h> | ||
| 26 | #include <linux/vmalloc.h> | 27 | #include <linux/vmalloc.h> |
| 27 | 28 | ||
| 28 | #define MAX_ENTRIES 1000000 | 29 | #define MAX_ENTRIES 1000000 |
| 29 | #define TEST_INSERT_FAIL INT_MAX | 30 | #define TEST_INSERT_FAIL INT_MAX |
| 30 | 31 | ||
| 31 | static int entries = 50000; | 32 | static int parm_entries = 50000; |
| 32 | module_param(entries, int, 0); | 33 | module_param(parm_entries, int, 0); |
| 33 | MODULE_PARM_DESC(entries, "Number of entries to add (default: 50000)"); | 34 | MODULE_PARM_DESC(parm_entries, "Number of entries to add (default: 50000)"); |
| 34 | 35 | ||
| 35 | static int runs = 4; | 36 | static int runs = 4; |
| 36 | module_param(runs, int, 0); | 37 | module_param(runs, int, 0); |
| @@ -66,14 +67,18 @@ struct test_obj { | |||
| 66 | struct rhash_head node; | 67 | struct rhash_head node; |
| 67 | }; | 68 | }; |
| 68 | 69 | ||
| 70 | struct test_obj_rhl { | ||
| 71 | struct test_obj_val value; | ||
| 72 | struct rhlist_head list_node; | ||
| 73 | }; | ||
| 74 | |||
| 69 | struct thread_data { | 75 | struct thread_data { |
| 76 | unsigned int entries; | ||
| 70 | int id; | 77 | int id; |
| 71 | struct task_struct *task; | 78 | struct task_struct *task; |
| 72 | struct test_obj *objs; | 79 | struct test_obj *objs; |
| 73 | }; | 80 | }; |
| 74 | 81 | ||
| 75 | static struct test_obj array[MAX_ENTRIES]; | ||
| 76 | |||
| 77 | static struct rhashtable_params test_rht_params = { | 82 | static struct rhashtable_params test_rht_params = { |
| 78 | .head_offset = offsetof(struct test_obj, node), | 83 | .head_offset = offsetof(struct test_obj, node), |
| 79 | .key_offset = offsetof(struct test_obj, value), | 84 | .key_offset = offsetof(struct test_obj, value), |
| @@ -85,7 +90,7 @@ static struct rhashtable_params test_rht_params = { | |||
| 85 | static struct semaphore prestart_sem; | 90 | static struct semaphore prestart_sem; |
| 86 | static struct semaphore startup_sem = __SEMAPHORE_INITIALIZER(startup_sem, 0); | 91 | static struct semaphore startup_sem = __SEMAPHORE_INITIALIZER(startup_sem, 0); |
| 87 | 92 | ||
| 88 | static int insert_retry(struct rhashtable *ht, struct rhash_head *obj, | 93 | static int insert_retry(struct rhashtable *ht, struct test_obj *obj, |
| 89 | const struct rhashtable_params params) | 94 | const struct rhashtable_params params) |
| 90 | { | 95 | { |
| 91 | int err, retries = -1, enomem_retries = 0; | 96 | int err, retries = -1, enomem_retries = 0; |
| @@ -93,7 +98,7 @@ static int insert_retry(struct rhashtable *ht, struct rhash_head *obj, | |||
| 93 | do { | 98 | do { |
| 94 | retries++; | 99 | retries++; |
| 95 | cond_resched(); | 100 | cond_resched(); |
| 96 | err = rhashtable_insert_fast(ht, obj, params); | 101 | err = rhashtable_insert_fast(ht, &obj->node, params); |
| 97 | if (err == -ENOMEM && enomem_retry) { | 102 | if (err == -ENOMEM && enomem_retry) { |
| 98 | enomem_retries++; | 103 | enomem_retries++; |
| 99 | err = -EBUSY; | 104 | err = -EBUSY; |
| @@ -107,11 +112,12 @@ static int insert_retry(struct rhashtable *ht, struct rhash_head *obj, | |||
| 107 | return err ? : retries; | 112 | return err ? : retries; |
| 108 | } | 113 | } |
| 109 | 114 | ||
| 110 | static int __init test_rht_lookup(struct rhashtable *ht) | 115 | static int __init test_rht_lookup(struct rhashtable *ht, struct test_obj *array, |
| 116 | unsigned int entries) | ||
| 111 | { | 117 | { |
| 112 | unsigned int i; | 118 | unsigned int i; |
| 113 | 119 | ||
| 114 | for (i = 0; i < entries * 2; i++) { | 120 | for (i = 0; i < entries; i++) { |
| 115 | struct test_obj *obj; | 121 | struct test_obj *obj; |
| 116 | bool expected = !(i % 2); | 122 | bool expected = !(i % 2); |
| 117 | struct test_obj_val key = { | 123 | struct test_obj_val key = { |
| @@ -144,7 +150,7 @@ static int __init test_rht_lookup(struct rhashtable *ht) | |||
| 144 | return 0; | 150 | return 0; |
| 145 | } | 151 | } |
| 146 | 152 | ||
| 147 | static void test_bucket_stats(struct rhashtable *ht) | 153 | static void test_bucket_stats(struct rhashtable *ht, unsigned int entries) |
| 148 | { | 154 | { |
| 149 | unsigned int err, total = 0, chain_len = 0; | 155 | unsigned int err, total = 0, chain_len = 0; |
| 150 | struct rhashtable_iter hti; | 156 | struct rhashtable_iter hti; |
| @@ -186,7 +192,8 @@ static void test_bucket_stats(struct rhashtable *ht) | |||
| 186 | pr_warn("Test failed: Total count mismatch ^^^"); | 192 | pr_warn("Test failed: Total count mismatch ^^^"); |
| 187 | } | 193 | } |
| 188 | 194 | ||
| 189 | static s64 __init test_rhashtable(struct rhashtable *ht) | 195 | static s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array, |
| 196 | unsigned int entries) | ||
| 190 | { | 197 | { |
| 191 | struct test_obj *obj; | 198 | struct test_obj *obj; |
| 192 | int err; | 199 | int err; |
| @@ -203,7 +210,7 @@ static s64 __init test_rhashtable(struct rhashtable *ht) | |||
| 203 | struct test_obj *obj = &array[i]; | 210 | struct test_obj *obj = &array[i]; |
| 204 | 211 | ||
| 205 | obj->value.id = i * 2; | 212 | obj->value.id = i * 2; |
| 206 | err = insert_retry(ht, &obj->node, test_rht_params); | 213 | err = insert_retry(ht, obj, test_rht_params); |
| 207 | if (err > 0) | 214 | if (err > 0) |
| 208 | insert_retries += err; | 215 | insert_retries += err; |
| 209 | else if (err) | 216 | else if (err) |
| @@ -214,12 +221,12 @@ static s64 __init test_rhashtable(struct rhashtable *ht) | |||
| 214 | pr_info(" %u insertions retried due to memory pressure\n", | 221 | pr_info(" %u insertions retried due to memory pressure\n", |
| 215 | insert_retries); | 222 | insert_retries); |
| 216 | 223 | ||
| 217 | test_bucket_stats(ht); | 224 | test_bucket_stats(ht, entries); |
| 218 | rcu_read_lock(); | 225 | rcu_read_lock(); |
| 219 | test_rht_lookup(ht); | 226 | test_rht_lookup(ht, array, entries); |
| 220 | rcu_read_unlock(); | 227 | rcu_read_unlock(); |
| 221 | 228 | ||
| 222 | test_bucket_stats(ht); | 229 | test_bucket_stats(ht, entries); |
| 223 | 230 | ||
| 224 | pr_info(" Deleting %d keys\n", entries); | 231 | pr_info(" Deleting %d keys\n", entries); |
| 225 | for (i = 0; i < entries; i++) { | 232 | for (i = 0; i < entries; i++) { |
| @@ -244,9 +251,227 @@ static s64 __init test_rhashtable(struct rhashtable *ht) | |||
| 244 | } | 251 | } |
| 245 | 252 | ||
| 246 | static struct rhashtable ht; | 253 | static struct rhashtable ht; |
| 254 | static struct rhltable rhlt; | ||
| 255 | |||
| 256 | static int __init test_rhltable(unsigned int entries) | ||
| 257 | { | ||
| 258 | struct test_obj_rhl *rhl_test_objects; | ||
| 259 | unsigned long *obj_in_table; | ||
| 260 | unsigned int i, j, k; | ||
| 261 | int ret, err; | ||
| 262 | |||
| 263 | if (entries == 0) | ||
| 264 | entries = 1; | ||
| 265 | |||
| 266 | rhl_test_objects = vzalloc(sizeof(*rhl_test_objects) * entries); | ||
| 267 | if (!rhl_test_objects) | ||
| 268 | return -ENOMEM; | ||
| 269 | |||
| 270 | ret = -ENOMEM; | ||
| 271 | obj_in_table = vzalloc(BITS_TO_LONGS(entries) * sizeof(unsigned long)); | ||
| 272 | if (!obj_in_table) | ||
| 273 | goto out_free; | ||
| 274 | |||
| 275 | /* nulls_base not supported in rhlist interface */ | ||
| 276 | test_rht_params.nulls_base = 0; | ||
| 277 | err = rhltable_init(&rhlt, &test_rht_params); | ||
| 278 | if (WARN_ON(err)) | ||
| 279 | goto out_free; | ||
| 280 | |||
| 281 | k = prandom_u32(); | ||
| 282 | ret = 0; | ||
| 283 | for (i = 0; i < entries; i++) { | ||
| 284 | rhl_test_objects[i].value.id = k; | ||
| 285 | err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, | ||
| 286 | test_rht_params); | ||
| 287 | if (WARN(err, "error %d on element %d\n", err, i)) | ||
| 288 | break; | ||
| 289 | if (err == 0) | ||
| 290 | set_bit(i, obj_in_table); | ||
| 291 | } | ||
| 292 | |||
| 293 | if (err) | ||
| 294 | ret = err; | ||
| 295 | |||
| 296 | pr_info("test %d add/delete pairs into rhlist\n", entries); | ||
| 297 | for (i = 0; i < entries; i++) { | ||
| 298 | struct rhlist_head *h, *pos; | ||
| 299 | struct test_obj_rhl *obj; | ||
| 300 | struct test_obj_val key = { | ||
| 301 | .id = k, | ||
| 302 | }; | ||
| 303 | bool found; | ||
| 304 | |||
| 305 | rcu_read_lock(); | ||
| 306 | h = rhltable_lookup(&rhlt, &key, test_rht_params); | ||
| 307 | if (WARN(!h, "key not found during iteration %d of %d", i, entries)) { | ||
| 308 | rcu_read_unlock(); | ||
| 309 | break; | ||
| 310 | } | ||
| 311 | |||
| 312 | if (i) { | ||
| 313 | j = i - 1; | ||
| 314 | rhl_for_each_entry_rcu(obj, pos, h, list_node) { | ||
| 315 | if (WARN(pos == &rhl_test_objects[j].list_node, "old element found, should be gone")) | ||
| 316 | break; | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | cond_resched_rcu(); | ||
| 321 | |||
| 322 | found = false; | ||
| 323 | |||
| 324 | rhl_for_each_entry_rcu(obj, pos, h, list_node) { | ||
| 325 | if (pos == &rhl_test_objects[i].list_node) { | ||
| 326 | found = true; | ||
| 327 | break; | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | rcu_read_unlock(); | ||
| 332 | |||
| 333 | if (WARN(!found, "element %d not found", i)) | ||
| 334 | break; | ||
| 335 | |||
| 336 | err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); | ||
| 337 | WARN(err, "rhltable_remove: err %d for iteration %d\n", err, i); | ||
| 338 | if (err == 0) | ||
| 339 | clear_bit(i, obj_in_table); | ||
| 340 | } | ||
| 341 | |||
| 342 | if (ret == 0 && err) | ||
| 343 | ret = err; | ||
| 344 | |||
| 345 | for (i = 0; i < entries; i++) { | ||
| 346 | WARN(test_bit(i, obj_in_table), "elem %d allegedly still present", i); | ||
| 347 | |||
| 348 | err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, | ||
| 349 | test_rht_params); | ||
| 350 | if (WARN(err, "error %d on element %d\n", err, i)) | ||
| 351 | break; | ||
| 352 | if (err == 0) | ||
| 353 | set_bit(i, obj_in_table); | ||
| 354 | } | ||
| 355 | |||
| 356 | pr_info("test %d random rhlist add/delete operations\n", entries); | ||
| 357 | for (j = 0; j < entries; j++) { | ||
| 358 | u32 i = prandom_u32_max(entries); | ||
| 359 | u32 prand = prandom_u32(); | ||
| 360 | |||
| 361 | cond_resched(); | ||
| 362 | |||
| 363 | if (prand == 0) | ||
| 364 | prand = prandom_u32(); | ||
| 365 | |||
| 366 | if (prand & 1) { | ||
| 367 | prand >>= 1; | ||
| 368 | continue; | ||
| 369 | } | ||
| 370 | |||
| 371 | err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); | ||
| 372 | if (test_bit(i, obj_in_table)) { | ||
| 373 | clear_bit(i, obj_in_table); | ||
| 374 | if (WARN(err, "cannot remove element at slot %d", i)) | ||
| 375 | continue; | ||
| 376 | } else { | ||
| 377 | if (WARN(err != -ENOENT, "removed non-existant element %d, error %d not %d", | ||
| 378 | i, err, -ENOENT)) | ||
| 379 | continue; | ||
| 380 | } | ||
| 381 | |||
| 382 | if (prand & 1) { | ||
| 383 | prand >>= 1; | ||
| 384 | continue; | ||
| 385 | } | ||
| 386 | |||
| 387 | err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); | ||
| 388 | if (err == 0) { | ||
| 389 | if (WARN(test_and_set_bit(i, obj_in_table), "succeeded to insert same object %d", i)) | ||
| 390 | continue; | ||
| 391 | } else { | ||
| 392 | if (WARN(!test_bit(i, obj_in_table), "failed to insert object %d", i)) | ||
| 393 | continue; | ||
| 394 | } | ||
| 395 | |||
| 396 | if (prand & 1) { | ||
| 397 | prand >>= 1; | ||
| 398 | continue; | ||
| 399 | } | ||
| 400 | |||
| 401 | i = prandom_u32_max(entries); | ||
| 402 | if (test_bit(i, obj_in_table)) { | ||
| 403 | err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); | ||
| 404 | WARN(err, "cannot remove element at slot %d", i); | ||
| 405 | if (err == 0) | ||
| 406 | clear_bit(i, obj_in_table); | ||
| 407 | } else { | ||
| 408 | err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); | ||
| 409 | WARN(err, "failed to insert object %d", i); | ||
| 410 | if (err == 0) | ||
| 411 | set_bit(i, obj_in_table); | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | for (i = 0; i < entries; i++) { | ||
| 416 | cond_resched(); | ||
| 417 | err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); | ||
| 418 | if (test_bit(i, obj_in_table)) { | ||
| 419 | if (WARN(err, "cannot remove element at slot %d", i)) | ||
| 420 | continue; | ||
| 421 | } else { | ||
| 422 | if (WARN(err != -ENOENT, "removed non-existant element, error %d not %d", | ||
| 423 | err, -ENOENT)) | ||
| 424 | continue; | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | rhltable_destroy(&rhlt); | ||
| 429 | out_free: | ||
| 430 | vfree(rhl_test_objects); | ||
| 431 | vfree(obj_in_table); | ||
| 432 | return ret; | ||
| 433 | } | ||
| 434 | |||
| 435 | static int __init test_rhashtable_max(struct test_obj *array, | ||
| 436 | unsigned int entries) | ||
| 437 | { | ||
| 438 | unsigned int i, insert_retries = 0; | ||
| 439 | int err; | ||
| 440 | |||
| 441 | test_rht_params.max_size = roundup_pow_of_two(entries / 8); | ||
| 442 | err = rhashtable_init(&ht, &test_rht_params); | ||
| 443 | if (err) | ||
| 444 | return err; | ||
| 445 | |||
| 446 | for (i = 0; i < ht.max_elems; i++) { | ||
| 447 | struct test_obj *obj = &array[i]; | ||
| 448 | |||
| 449 | obj->value.id = i * 2; | ||
| 450 | err = insert_retry(&ht, obj, test_rht_params); | ||
| 451 | if (err > 0) | ||
| 452 | insert_retries += err; | ||
| 453 | else if (err) | ||
| 454 | return err; | ||
| 455 | } | ||
| 456 | |||
| 457 | err = insert_retry(&ht, &array[ht.max_elems], test_rht_params); | ||
| 458 | if (err == -E2BIG) { | ||
| 459 | err = 0; | ||
| 460 | } else { | ||
| 461 | pr_info("insert element %u should have failed with %d, got %d\n", | ||
| 462 | ht.max_elems, -E2BIG, err); | ||
| 463 | if (err == 0) | ||
| 464 | err = -1; | ||
| 465 | } | ||
| 466 | |||
| 467 | rhashtable_destroy(&ht); | ||
| 468 | |||
| 469 | return err; | ||
| 470 | } | ||
| 247 | 471 | ||
| 248 | static int thread_lookup_test(struct thread_data *tdata) | 472 | static int thread_lookup_test(struct thread_data *tdata) |
| 249 | { | 473 | { |
| 474 | unsigned int entries = tdata->entries; | ||
| 250 | int i, err = 0; | 475 | int i, err = 0; |
| 251 | 476 | ||
| 252 | for (i = 0; i < entries; i++) { | 477 | for (i = 0; i < entries; i++) { |
| @@ -283,10 +508,10 @@ static int threadfunc(void *data) | |||
| 283 | if (down_interruptible(&startup_sem)) | 508 | if (down_interruptible(&startup_sem)) |
| 284 | pr_err(" thread[%d]: down_interruptible failed\n", tdata->id); | 509 | pr_err(" thread[%d]: down_interruptible failed\n", tdata->id); |
| 285 | 510 | ||
| 286 | for (i = 0; i < entries; i++) { | 511 | for (i = 0; i < tdata->entries; i++) { |
| 287 | tdata->objs[i].value.id = i; | 512 | tdata->objs[i].value.id = i; |
| 288 | tdata->objs[i].value.tid = tdata->id; | 513 | tdata->objs[i].value.tid = tdata->id; |
| 289 | err = insert_retry(&ht, &tdata->objs[i].node, test_rht_params); | 514 | err = insert_retry(&ht, &tdata->objs[i], test_rht_params); |
| 290 | if (err > 0) { | 515 | if (err > 0) { |
| 291 | insert_retries += err; | 516 | insert_retries += err; |
| 292 | } else if (err) { | 517 | } else if (err) { |
| @@ -307,7 +532,7 @@ static int threadfunc(void *data) | |||
| 307 | } | 532 | } |
| 308 | 533 | ||
| 309 | for (step = 10; step > 0; step--) { | 534 | for (step = 10; step > 0; step--) { |
| 310 | for (i = 0; i < entries; i += step) { | 535 | for (i = 0; i < tdata->entries; i += step) { |
| 311 | if (tdata->objs[i].value.id == TEST_INSERT_FAIL) | 536 | if (tdata->objs[i].value.id == TEST_INSERT_FAIL) |
| 312 | continue; | 537 | continue; |
| 313 | err = rhashtable_remove_fast(&ht, &tdata->objs[i].node, | 538 | err = rhashtable_remove_fast(&ht, &tdata->objs[i].node, |
| @@ -338,17 +563,25 @@ out: | |||
| 338 | 563 | ||
| 339 | static int __init test_rht_init(void) | 564 | static int __init test_rht_init(void) |
| 340 | { | 565 | { |
| 566 | unsigned int entries; | ||
| 341 | int i, err, started_threads = 0, failed_threads = 0; | 567 | int i, err, started_threads = 0, failed_threads = 0; |
| 342 | u64 total_time = 0; | 568 | u64 total_time = 0; |
| 343 | struct thread_data *tdata; | 569 | struct thread_data *tdata; |
| 344 | struct test_obj *objs; | 570 | struct test_obj *objs; |
| 345 | 571 | ||
| 346 | entries = min(entries, MAX_ENTRIES); | 572 | if (parm_entries < 0) |
| 573 | parm_entries = 1; | ||
| 574 | |||
| 575 | entries = min(parm_entries, MAX_ENTRIES); | ||
| 347 | 576 | ||
| 348 | test_rht_params.automatic_shrinking = shrinking; | 577 | test_rht_params.automatic_shrinking = shrinking; |
| 349 | test_rht_params.max_size = max_size ? : roundup_pow_of_two(entries); | 578 | test_rht_params.max_size = max_size ? : roundup_pow_of_two(entries); |
| 350 | test_rht_params.nelem_hint = size; | 579 | test_rht_params.nelem_hint = size; |
| 351 | 580 | ||
| 581 | objs = vzalloc((test_rht_params.max_size + 1) * sizeof(struct test_obj)); | ||
| 582 | if (!objs) | ||
| 583 | return -ENOMEM; | ||
| 584 | |||
| 352 | pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n", | 585 | pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n", |
| 353 | size, max_size, shrinking); | 586 | size, max_size, shrinking); |
| 354 | 587 | ||
| @@ -356,7 +589,8 @@ static int __init test_rht_init(void) | |||
| 356 | s64 time; | 589 | s64 time; |
| 357 | 590 | ||
| 358 | pr_info("Test %02d:\n", i); | 591 | pr_info("Test %02d:\n", i); |
| 359 | memset(&array, 0, sizeof(array)); | 592 | memset(objs, 0, test_rht_params.max_size * sizeof(struct test_obj)); |
| 593 | |||
| 360 | err = rhashtable_init(&ht, &test_rht_params); | 594 | err = rhashtable_init(&ht, &test_rht_params); |
| 361 | if (err < 0) { | 595 | if (err < 0) { |
| 362 | pr_warn("Test failed: Unable to initialize hashtable: %d\n", | 596 | pr_warn("Test failed: Unable to initialize hashtable: %d\n", |
| @@ -364,9 +598,10 @@ static int __init test_rht_init(void) | |||
| 364 | continue; | 598 | continue; |
| 365 | } | 599 | } |
| 366 | 600 | ||
| 367 | time = test_rhashtable(&ht); | 601 | time = test_rhashtable(&ht, objs, entries); |
| 368 | rhashtable_destroy(&ht); | 602 | rhashtable_destroy(&ht); |
| 369 | if (time < 0) { | 603 | if (time < 0) { |
| 604 | vfree(objs); | ||
| 370 | pr_warn("Test failed: return code %lld\n", time); | 605 | pr_warn("Test failed: return code %lld\n", time); |
| 371 | return -EINVAL; | 606 | return -EINVAL; |
| 372 | } | 607 | } |
| @@ -374,6 +609,11 @@ static int __init test_rht_init(void) | |||
| 374 | total_time += time; | 609 | total_time += time; |
| 375 | } | 610 | } |
| 376 | 611 | ||
| 612 | pr_info("test if its possible to exceed max_size %d: %s\n", | ||
| 613 | test_rht_params.max_size, test_rhashtable_max(objs, entries) == 0 ? | ||
| 614 | "no, ok" : "YES, failed"); | ||
| 615 | vfree(objs); | ||
| 616 | |||
| 377 | do_div(total_time, runs); | 617 | do_div(total_time, runs); |
| 378 | pr_info("Average test time: %llu\n", total_time); | 618 | pr_info("Average test time: %llu\n", total_time); |
| 379 | 619 | ||
| @@ -404,6 +644,7 @@ static int __init test_rht_init(void) | |||
| 404 | } | 644 | } |
| 405 | for (i = 0; i < tcount; i++) { | 645 | for (i = 0; i < tcount; i++) { |
| 406 | tdata[i].id = i; | 646 | tdata[i].id = i; |
| 647 | tdata[i].entries = entries; | ||
| 407 | tdata[i].objs = objs + i * entries; | 648 | tdata[i].objs = objs + i * entries; |
| 408 | tdata[i].task = kthread_run(threadfunc, &tdata[i], | 649 | tdata[i].task = kthread_run(threadfunc, &tdata[i], |
| 409 | "rhashtable_thrad[%d]", i); | 650 | "rhashtable_thrad[%d]", i); |
| @@ -425,11 +666,17 @@ static int __init test_rht_init(void) | |||
| 425 | failed_threads++; | 666 | failed_threads++; |
| 426 | } | 667 | } |
| 427 | } | 668 | } |
| 428 | pr_info("Started %d threads, %d failed\n", | ||
| 429 | started_threads, failed_threads); | ||
| 430 | rhashtable_destroy(&ht); | 669 | rhashtable_destroy(&ht); |
| 431 | vfree(tdata); | 670 | vfree(tdata); |
| 432 | vfree(objs); | 671 | vfree(objs); |
| 672 | |||
| 673 | /* | ||
| 674 | * rhltable_remove is very expensive, default values can cause test | ||
| 675 | * to run for 2 minutes or more, use a smaller number instead. | ||
| 676 | */ | ||
| 677 | err = test_rhltable(entries / 16); | ||
| 678 | pr_info("Started %d threads, %d failed, rhltable test returns %d\n", | ||
| 679 | started_threads, failed_threads, err); | ||
| 433 | return 0; | 680 | return 0; |
| 434 | } | 681 | } |
| 435 | 682 | ||
