diff options
| author | Peter Maydell <peter.maydell@linaro.org> | 2015-02-24 13:58:18 +0000 |
|---|---|---|
| committer | Peter Maydell <peter.maydell@linaro.org> | 2015-02-24 13:58:18 +0000 |
| commit | 73104fd399c6778112f64fe0d439319f24508d9a (patch) | |
| tree | c097e6a8acd33b8ad8561a2bccacc4247db76085 /tests/test-rcu-list.c | |
| parent | 3dc10613c313a042a111e46a977733411495ea8c (diff) | |
| parent | 0dc3f44aca18b1be8b425f3f4feb4b3e8d68de2e (diff) | |
| download | focaccia-qemu-73104fd399c6778112f64fe0d439319f24508d9a.tar.gz focaccia-qemu-73104fd399c6778112f64fe0d439319f24508d9a.zip | |
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
- vhost-scsi: add bootindex property - RCU: fix MemoryRegion lifetime issues in PCI; document the rules; convert of AddressSpaceDispatch and RAMList - KVM: add kvm_exit reasons for aarch64 # gpg: Signature made Mon Feb 16 16:32:32 2015 GMT using RSA key ID 78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: (21 commits) Convert ram_list to RCU exec: convert ram_list to QLIST cosmetic changes preparing for the following patches exec: protect mru_block with RCU rcu: add g_free_rcu rcu: introduce RCU-enabled QLIST exec: RCUify AddressSpaceDispatch exec: make iotlb RCU-friendly exec: introduce cpu_reload_memory_map docs: clarify memory region lifecycle pci: split shpc_cleanup and shpc_free pcie: remove mmconfig memory leak and wrap mmconfig update with transaction memory: keep the owner of the AddressSpace alive until do_address_space_destroy rcu: run RCU callbacks under the BQL rcu: do not let RCU callbacks pile up indefinitely vhost-scsi: set the bootable value of channel/target/lun vhost-scsi: add a property for booting vhost-scsi: expose the TYPE_FW_PATH_PROVIDER interface vhost-scsi: add bootindex property qdev: support to get a device firmware path directly ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'tests/test-rcu-list.c')
| -rw-r--r-- | tests/test-rcu-list.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/tests/test-rcu-list.c b/tests/test-rcu-list.c new file mode 100644 index 0000000000..46b5e263e5 --- /dev/null +++ b/tests/test-rcu-list.c @@ -0,0 +1,306 @@ +/* + * rcuq_test.c + * + * usage: rcuq_test <readers> <duration> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (c) 2013 Mike D. Day, IBM Corporation. + */ + +#include <glib.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "qemu/atomic.h" +#include "qemu/rcu.h" +#include "qemu/compiler.h" +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "qemu/rcu_queue.h" + +/* + * Test variables. + */ + +long long n_reads = 0LL; +long long n_updates = 0LL; +long long n_reclaims = 0LL; +long long n_nodes_removed = 0LL; +long long n_nodes = 0LL; +int g_test_in_charge = 0; + +int nthreadsrunning; + +char argsbuf[64]; + +#define GOFLAG_INIT 0 +#define GOFLAG_RUN 1 +#define GOFLAG_STOP 2 + +static volatile int goflag = GOFLAG_INIT; + +#define RCU_READ_RUN 1000 +#define RCU_UPDATE_RUN 10 +#define NR_THREADS 100 +#define RCU_Q_LEN 100 + +static QemuThread threads[NR_THREADS]; +static struct rcu_reader_data *data[NR_THREADS]; +static int n_threads; + +static int select_random_el(int max) +{ + return (rand() % max); +} + + +static void create_thread(void *(*func)(void *)) +{ + if (n_threads >= NR_THREADS) { + fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS); + exit(-1); + } + qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads], + QEMU_THREAD_JOINABLE); + n_threads++; +} + +static void wait_all_threads(void) +{ + int i; + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } + n_threads = 0; +} + + +struct list_element { + QLIST_ENTRY(list_element) entry; + struct rcu_head rcu; + long long val; +}; + +static void reclaim_list_el(struct rcu_head *prcu) +{ + struct list_element *el = container_of(prcu, struct list_element, rcu); + g_free(el); + atomic_add(&n_reclaims, 1); +} + +static QLIST_HEAD(q_list_head, list_element) Q_list_head; + +static void *rcu_q_reader(void *arg) +{ + long long j, n_reads_local = 0; + struct list_element *el; + + *(struct rcu_reader_data **)arg = &rcu_reader; + atomic_inc(&nthreadsrunning); + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + + while (goflag == GOFLAG_RUN) { + rcu_read_lock(); + QLIST_FOREACH_RCU(el, &Q_list_head, entry) { + j = atomic_read(&el->val); + (void)j; + n_reads_local++; + if (goflag == GOFLAG_STOP) { + break; + } + } + rcu_read_unlock(); + + g_usleep(100); + } + atomic_add(&n_reads, n_reads_local); + return NULL; +} + + +static void *rcu_q_updater(void *arg) +{ + int j, target_el; + long long n_updates_local = 0; + long long n_removed_local = 0; + struct list_element *el, *prev_el; + + *(struct rcu_reader_data **)arg = &rcu_reader; + atomic_inc(&nthreadsrunning); + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + + while (goflag == GOFLAG_RUN) { + target_el = select_random_el(RCU_Q_LEN); + j = 0; + /* FOREACH_RCU could work here but let's use both macros */ + QLIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { + j++; + if (target_el == j) { + QLIST_REMOVE_RCU(prev_el, entry); + /* may be more than one updater in the future */ + call_rcu1(&prev_el->rcu, reclaim_list_el); + n_removed_local++; + break; + } + } + if (goflag == GOFLAG_STOP) { + break; + } + target_el = select_random_el(RCU_Q_LEN); + j = 0; + QLIST_FOREACH_RCU(el, &Q_list_head, entry) { + j++; + if (target_el == j) { + prev_el = g_new(struct list_element, 1); + atomic_add(&n_nodes, 1); + prev_el->val = atomic_read(&n_nodes); + QLIST_INSERT_BEFORE_RCU(el, prev_el, entry); + break; + } + } + + n_updates_local += 2; + synchronize_rcu(); + } + synchronize_rcu(); + atomic_add(&n_updates, n_updates_local); + atomic_add(&n_nodes_removed, n_removed_local); + return NULL; +} + +static void rcu_qtest_init(void) +{ + struct list_element *new_el; + int i; + nthreadsrunning = 0; + srand(time(0)); + for (i = 0; i < RCU_Q_LEN; i++) { + new_el = g_new(struct list_element, 1); + new_el->val = i; + QLIST_INSERT_HEAD_RCU(&Q_list_head, new_el, entry); + } + atomic_add(&n_nodes, RCU_Q_LEN); +} + +static void rcu_qtest_run(int duration, int nreaders) +{ + int nthreads = nreaders + 1; + while (atomic_read(&nthreadsrunning) < nthreads) { + g_usleep(1000); + } + + goflag = GOFLAG_RUN; + sleep(duration); + goflag = GOFLAG_STOP; + wait_all_threads(); +} + + +static void rcu_qtest(const char *test, int duration, int nreaders) +{ + int i; + long long n_removed_local = 0; + + struct list_element *el, *prev_el; + + rcu_qtest_init(); + for (i = 0; i < nreaders; i++) { + create_thread(rcu_q_reader); + } + create_thread(rcu_q_updater); + rcu_qtest_run(duration, nreaders); + + QLIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { + QLIST_REMOVE_RCU(prev_el, entry); + call_rcu1(&prev_el->rcu, reclaim_list_el); + n_removed_local++; + } + atomic_add(&n_nodes_removed, n_removed_local); + synchronize_rcu(); + while (n_nodes_removed > n_reclaims) { + g_usleep(100); + synchronize_rcu(); + } + if (g_test_in_charge) { + g_assert_cmpint(n_nodes_removed, ==, n_reclaims); + } else { + printf("%s: %d readers; 1 updater; nodes read: " \ + "%lld, nodes removed: %lld; nodes reclaimed: %lld\n", + test, nthreadsrunning - 1, n_reads, n_nodes_removed, n_reclaims); + exit(0); + } +} + +static void usage(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s duration nreaders\n", argv[0]); + exit(-1); +} + +static int gtest_seconds; + +static void gtest_rcuq_one(void) +{ + rcu_qtest("rcuqtest", gtest_seconds / 4, 1); +} + +static void gtest_rcuq_few(void) +{ + rcu_qtest("rcuqtest", gtest_seconds / 4, 5); +} + +static void gtest_rcuq_many(void) +{ + rcu_qtest("rcuqtest", gtest_seconds / 2, 20); +} + + +int main(int argc, char *argv[]) +{ + int duration = 0, readers = 0; + + if (argc >= 2) { + if (argv[1][0] == '-') { + g_test_init(&argc, &argv, NULL); + if (g_test_quick()) { + gtest_seconds = 4; + } else { + gtest_seconds = 20; + } + g_test_add_func("/rcu/qlist/single-threaded", gtest_rcuq_one); + g_test_add_func("/rcu/qlist/short-few", gtest_rcuq_few); + g_test_add_func("/rcu/qlist/long-many", gtest_rcuq_many); + g_test_in_charge = 1; + return g_test_run(); + } + duration = strtoul(argv[1], NULL, 0); + } + if (argc >= 3) { + readers = strtoul(argv[2], NULL, 0); + } + if (duration && readers) { + rcu_qtest(argv[0], duration, readers); + return 0; + } + + usage(argc, argv); + return -1; +} |