X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Fldlm%2Fldlm_test.c;h=5381b5b2d8d0efc5623e010b257d0144c901661b;hb=2dc9c16e770415d56839e1996015fec5fab93f29;hp=fc88099a2828ef9148094fe23fd87ffeb7036700;hpb=142a324303228704ded3a66e20c7755d26f706ad;p=fs%2Flustre-release.git diff --git a/lustre/ldlm/ldlm_test.c b/lustre/ldlm/ldlm_test.c index fc88099..5381b5b 100644 --- a/lustre/ldlm/ldlm_test.c +++ b/lustre/ldlm/ldlm_test.c @@ -1,49 +1,187 @@ /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- * vim:expandtab:shiftwidth=8:tabstop=8: * - * Copyright (C) 2002 Cluster File Systems, Inc. + * Copyright (c) 2002 Cluster File Systems, Inc. + * Copyright (c) 2002 Lawrence Livermore National Laboratory + * Author: James Newsome * - * This code is issued under the GNU General Public License. - * See the file COPYING in this distribution + * This file is part of Lustre, http://www.lustre.org. * - * by Cluster File Systems, Inc. + * Lustre is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * Lustre 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 Lustre; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define DEBUG_SUBSYSTEM S_LDLM +#include +#include +#include + #include +#include + +struct ldlm_test_thread { + struct obd_device *obddev; + struct ldlm_namespace *t_ns; + struct list_head t_link; + __u32 t_flags; + wait_queue_head_t t_ctl_waitq; +}; + +struct ldlm_test_lock { + struct list_head l_link; + struct lustre_handle l_lockh; +}; -static int ldlm_test_callback(struct ldlm_lock *lock, struct ldlm_lock *new, - void *data, __u32 data_len) +static unsigned int max_locks; +static unsigned int num_resources; +static unsigned int num_extents; + +static spinlock_t ctl_lock = SPIN_LOCK_UNLOCKED; +/* protect these with the ctl_lock */ +static LIST_HEAD(ctl_threads); +static int regression_running = 0; +static LIST_HEAD(lock_list); +static int num_locks = 0; + +/* cumulative stats for regression test */ +static atomic_t locks_requested = ATOMIC_INIT(0); +static atomic_t converts_requested = ATOMIC_INIT(0); +static atomic_t locks_granted = ATOMIC_INIT(0); +static atomic_t locks_matched = ATOMIC_INIT(0); + +/* making this a global avoids the problem of having pointers + * to garbage after the test exits. + */ +static struct lustre_handle regress_connh; + +static int ldlm_do_decrement(void); +static int ldlm_do_enqueue(struct ldlm_test_thread *thread); +static int ldlm_do_convert(void); + +/* + * blocking ast for regression test. + * Just cancels lock + */ +static int ldlm_test_blocking_ast(struct ldlm_lock *lock, + struct ldlm_lock_desc *new, + void *data, int flag) { - printk("ldlm_test_callback: lock=%p, new=%p\n", lock, new); - return 0; + int rc; + struct lustre_handle lockh; + ENTRY; + + switch (flag) { + case LDLM_CB_BLOCKING: + LDLM_DEBUG(lock, "We're blocking. Cancelling lock"); + ldlm_lock2handle(lock, &lockh); + rc = ldlm_cli_cancel(&lockh); + if (rc < 0) { + CERROR("ldlm_cli_cancel: %d\n", rc); + LBUG(); + } + break; + case LDLM_CB_CANCELING: + LDLM_DEBUG(lock, "this lock is being cancelled"); + break; + default: + LBUG(); + } + + RETURN(0); +} + +/* blocking ast for basic tests. noop */ +static int ldlm_blocking_ast(struct ldlm_lock *lock, + struct ldlm_lock_desc *new, + void *data, int flag) +{ + ENTRY; + CERROR("ldlm_blocking_ast: lock=%p, new=%p, flag=%d\n", lock, new, + flag); + RETURN(0); +} + +/* Completion ast for regression test. + * Does not sleep when blocked. + */ +static int ldlm_test_completion_ast(struct ldlm_lock *lock, int flags, void *data) +{ + struct ldlm_test_lock *lock_info; + ENTRY; + + if (flags & (LDLM_FL_BLOCK_WAIT | LDLM_FL_BLOCK_GRANTED | + LDLM_FL_BLOCK_CONV)) { + LDLM_DEBUG(lock, "client-side enqueue returned a blocked lock"); + RETURN(0); + } + + if (lock->l_granted_mode != lock->l_req_mode) + CERROR("completion ast called with non-granted lock\n"); + + /* add to list of granted locks */ + + if (flags & LDLM_FL_WAIT_NOREPROC) { + atomic_inc(&locks_matched); + LDLM_DEBUG(lock, "lock matched"); + } else { + atomic_inc(&locks_granted); + LDLM_DEBUG(lock, "lock granted"); + } + + OBD_ALLOC(lock_info, sizeof(*lock_info)); + if (lock_info == NULL) { + LBUG(); + RETURN(-ENOMEM); + } + + ldlm_lock2handle(lock, &lock_info->l_lockh); + + spin_lock(&ctl_lock); + list_add_tail(&lock_info->l_link, &lock_list); + num_locks++; + spin_unlock(&ctl_lock); + + RETURN(0); } int ldlm_test_basics(struct obd_device *obddev) { struct ldlm_namespace *ns; struct ldlm_resource *res; - __u64 res_id[RES_NAME_SIZE] = {1, 2, 3}; + struct ldlm_res_id res_id = { .name = {1, 2, 3} }; ldlm_error_t err; - struct ldlm_handle lockh_1, lockh_2; + struct ldlm_lock *lock1, *lock; int flags; + ENTRY; - ns = ldlm_namespace_new(obddev, 0); + ns = ldlm_namespace_new("test_server", LDLM_NAMESPACE_SERVER); if (ns == NULL) LBUG(); - err = ldlm_local_lock_create(ns, NULL, res_id, LDLM_PLAIN, LCK_CR, - NULL, 0, &lockh_1); - err = ldlm_local_lock_enqueue(&lockh_1, NULL, &flags, - ldlm_test_callback, ldlm_test_callback); + lock1 = ldlm_lock_create(ns, NULL, res_id, LDLM_PLAIN, LCK_CR, NULL, 0); + if (lock1 == NULL) + LBUG(); + err = ldlm_lock_enqueue(ns, lock1, NULL, 0, &flags, + ldlm_completion_ast, ldlm_blocking_ast); if (err != ELDLM_OK) LBUG(); - err = ldlm_local_lock_create(ns, NULL, res_id, LDLM_PLAIN, LCK_EX, - NULL, 0, &lockh_2); - err = ldlm_local_lock_enqueue(&lockh_2, NULL, &flags, - ldlm_test_callback, ldlm_test_callback); + lock = ldlm_lock_create(ns, NULL, res_id, LDLM_PLAIN, LCK_EX, NULL, 0); + if (lock == NULL) + LBUG(); + err = ldlm_lock_enqueue(ns, lock, NULL, 0, &flags, + ldlm_completion_ast, ldlm_blocking_ast); if (err != ELDLM_OK) LBUG(); if (!(flags & LDLM_FL_BLOCK_GRANTED)) @@ -54,53 +192,59 @@ int ldlm_test_basics(struct obd_device *obddev) LBUG(); ldlm_resource_dump(res); - res = ldlm_local_lock_convert(&lockh_1, LCK_NL, &flags); + res = ldlm_lock_convert(lock1, LCK_NL, &flags); if (res != NULL) ldlm_reprocess_all(res); ldlm_resource_dump(res); - ldlm_namespace_free(ns); + ldlm_namespace_free(ns, 0); - return 0; + RETURN(0); } int ldlm_test_extents(struct obd_device *obddev) { struct ldlm_namespace *ns; struct ldlm_resource *res; - struct ldlm_lock *lock; - __u64 res_id[RES_NAME_SIZE] = {0, 0, 0}; + struct ldlm_lock *lock, *lock1, *lock2; + struct ldlm_res_id res_id = { .name = {0} }; struct ldlm_extent ext1 = {4, 6}, ext2 = {6, 9}, ext3 = {10, 11}; - struct ldlm_handle ext1_h, ext2_h, ext3_h; ldlm_error_t err; int flags; + ENTRY; - ns = ldlm_namespace_new(obddev, 0); + ns = ldlm_namespace_new("test_server", LDLM_NAMESPACE_SERVER); if (ns == NULL) LBUG(); flags = 0; - err = ldlm_local_lock_create(ns, NULL, res_id, LDLM_EXTENT, LCK_PR, - NULL, 0, &ext1_h); - err = ldlm_local_lock_enqueue(&ext1_h, &ext1, &flags, NULL, NULL); + lock1 = ldlm_lock_create(ns, NULL, res_id, LDLM_EXTENT, LCK_PR, NULL, + 0); + if (lock1 == NULL) + LBUG(); + err = ldlm_lock_enqueue(ns, lock1, &ext1, sizeof(ext1), &flags, NULL, + NULL); if (err != ELDLM_OK) LBUG(); if (!(flags & LDLM_FL_LOCK_CHANGED)) LBUG(); flags = 0; - err = ldlm_local_lock_create(ns, NULL, res_id, LDLM_EXTENT, LCK_PR, - NULL, 0, &ext2_h); - err = ldlm_local_lock_enqueue(&ext2_h, &ext2, &flags, NULL, NULL); + lock2 = ldlm_lock_create(ns, NULL, res_id, LDLM_EXTENT, LCK_PR, + NULL, 0); + err = ldlm_lock_enqueue(ns, lock2, &ext2, sizeof(ext2), &flags, NULL, + NULL); if (err != ELDLM_OK) LBUG(); if (!(flags & LDLM_FL_LOCK_CHANGED)) LBUG(); flags = 0; - err = ldlm_local_lock_create(ns, NULL, res_id, LDLM_EXTENT, LCK_EX, - NULL, 0, &ext3_h); - err = ldlm_local_lock_enqueue(&ext3_h, &ext3, &flags, NULL, NULL); + lock = ldlm_lock_create(ns, NULL, res_id, LDLM_EXTENT, LCK_EX, NULL, 0); + if (lock == NULL) + LBUG(); + err = ldlm_lock_enqueue(ns, lock, &ext3, sizeof(ext3), &flags, + NULL, NULL); if (err != ELDLM_OK) LBUG(); if (!(flags & LDLM_FL_BLOCK_GRANTED)) @@ -110,13 +254,11 @@ int ldlm_test_extents(struct obd_device *obddev) /* Convert/cancel blocking locks */ flags = 0; - res = ldlm_local_lock_convert(&ext1_h, LCK_NL, &flags); + res = ldlm_lock_convert(lock1, LCK_NL, &flags); if (res != NULL) ldlm_reprocess_all(res); - flags = 0; - lock = ldlm_handle2object(&ext2_h); - res = ldlm_local_lock_cancel(lock); + ldlm_lock_cancel(lock2); if (res != NULL) ldlm_reprocess_all(res); @@ -125,31 +267,372 @@ int ldlm_test_extents(struct obd_device *obddev) if (res == NULL) LBUG(); ldlm_resource_dump(res); - ldlm_namespace_free(ns); + ldlm_namespace_free(ns, 0); - return 0; + RETURN(0); } static int ldlm_test_network(struct obd_device *obddev, - struct ptlrpc_connection *conn) + struct lustre_handle *connh) { - struct ldlm_obd *ldlm = &obddev->u.ldlm; - - __u64 res_id[RES_NAME_SIZE] = {1, 2, 3}; + struct ldlm_res_id res_id = { .name = {1, 2, 3} }; struct ldlm_extent ext = {4, 6}; - struct ldlm_handle lockh1; + struct lustre_handle lockh1; + struct ldlm_lock *lock; int flags = 0; ldlm_error_t err; + ENTRY; + + err = ldlm_cli_enqueue(connh, NULL, obddev->obd_namespace, NULL, res_id, + LDLM_EXTENT, &ext, sizeof(ext), LCK_PR, &flags, + ldlm_completion_ast, NULL, NULL, 0, &lockh1); - err = ldlm_cli_enqueue(ldlm->ldlm_client, conn, obddev->obd_namespace, - NULL, res_id, LDLM_EXTENT, &ext, LCK_PR, &flags, - NULL, 0, &lockh1); CERROR("ldlm_cli_enqueue: %d\n", err); + flags = 0; + err = ldlm_cli_convert(&lockh1, LCK_EX, &flags); + CERROR("ldlm_cli_convert: %d\n", err); + + lock = ldlm_handle2lock(&lockh1); + ldlm_lock_dump(D_OTHER, lock); + ldlm_lock_put(lock); + + /* Need to decrement old mode. Don't bother incrementing new + * mode since the test is done. + */ + if (err == ELDLM_OK) + ldlm_lock_decref(&lockh1, LCK_PR); + RETURN(err); } -int ldlm_test(struct obd_device *obddev, struct ptlrpc_connection *conn) +static int ldlm_do_decrement(void) +{ + struct ldlm_test_lock *lock_info; + struct ldlm_lock *lock; + int rc = 0; + ENTRY; + + spin_lock(&ctl_lock); + if(list_empty(&lock_list)) { + CERROR("lock_list is empty\n"); + spin_unlock(&ctl_lock); + RETURN(0); + } + + /* delete from list */ + lock_info = list_entry(lock_list.next, + struct ldlm_test_lock, l_link); + list_del(lock_list.next); + num_locks--; + spin_unlock(&ctl_lock); + + /* decrement and free the info */ + lock = ldlm_handle2lock(&lock_info->l_lockh); + ldlm_lock_decref(&lock_info->l_lockh, lock->l_granted_mode); + ldlm_lock_put(lock); + + OBD_FREE(lock_info, sizeof(*lock_info)); + + RETURN(rc); +} + +static int ldlm_do_enqueue(struct ldlm_test_thread *thread) +{ + struct lustre_handle lockh; + struct ldlm_res_id res_id = { .name = {0} }; + __u32 lock_mode; + struct ldlm_extent ext; + unsigned char random; + int flags = 0, rc = 0; + ENTRY; + + /* Pick a random resource from 1 to num_resources */ + get_random_bytes(&random, sizeof(random)); + res_id.name[0] = random % num_resources; + + /* Pick a random lock mode */ + get_random_bytes(&random, sizeof(random)); + lock_mode = random % LCK_NL + 1; + + /* Pick a random extent */ + get_random_bytes(&random, sizeof(random)); + ext.start = random % num_extents; + get_random_bytes(&random, sizeof(random)); + ext.end = random % + (num_extents - (int)ext.start) + ext.start; + + LDLM_DEBUG_NOLOCK("about to enqueue with resource "LPX64", mode %d," + " extent "LPX64" -> "LPX64, res_id.name[0], lock_mode, + ext.start, ext.end); + + rc = ldlm_match_or_enqueue(®ress_connh, NULL, + thread->obddev->obd_namespace, + NULL, res_id, LDLM_EXTENT, &ext, + sizeof(ext), lock_mode, &flags, + ldlm_test_completion_ast, + ldlm_test_blocking_ast, + NULL, 0, &lockh); + + atomic_inc(&locks_requested); + + if (rc < 0) { + CERROR("ldlm_cli_enqueue: %d\n", rc); + LBUG(); + } + + RETURN(rc); +} + +static int ldlm_do_convert(void) +{ + __u32 lock_mode; + unsigned char random; + int flags = 0, rc = 0; + struct ldlm_test_lock *lock_info; + struct ldlm_lock *lock; + ENTRY; + + /* delete from list */ + spin_lock(&ctl_lock); + lock_info = list_entry(lock_list.next, struct ldlm_test_lock, l_link); + list_del(lock_list.next); + num_locks--; + spin_unlock(&ctl_lock); + + /* Pick a random lock mode */ + get_random_bytes(&random, sizeof(random)); + lock_mode = random % LCK_NL + 1; + + /* do the conversion */ + rc = ldlm_cli_convert(&lock_info->l_lockh , lock_mode, &flags); + atomic_inc(&converts_requested); + + if (rc < 0) { + CERROR("ldlm_cli_convert: %d\n", rc); + LBUG(); + } + + /* + * Adjust reference counts. + * FIXME: This is technically a bit... wrong, + * since we don't know when/if the convert succeeded + */ + ldlm_lock_addref(&lock_info->l_lockh, lock_mode); + lock = ldlm_handle2lock(&lock_info->l_lockh); + ldlm_lock_decref(&lock_info->l_lockh, lock->l_granted_mode); + ldlm_lock_put(lock); + + OBD_FREE(lock_info, sizeof(*lock_info)); + + RETURN(rc); +} + + + +static int ldlm_test_main(void *data) +{ + struct ldlm_test_thread *thread = data; + unsigned long flags; + ENTRY; + + lock_kernel(); + daemonize(); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + sigfillset(¤t->blocked); + recalc_sigpending(); +#else + spin_lock_irqsave(¤t->sigmask_lock, flags); + sigfillset(¤t->blocked); + recalc_sigpending(current); + spin_unlock_irqrestore(¤t->sigmask_lock, flags); +#endif + + sprintf(current->comm, "ldlm_test"); + unlock_kernel(); + + /* Record that the thread is running */ + thread->t_flags |= SVC_RUNNING; + wake_up(&thread->t_ctl_waitq); + + while (!(thread->t_flags & SVC_STOPPING)) { + unsigned char random; + unsigned char dec_chance, con_chance; + unsigned char chance_left = 100; + + spin_lock(&ctl_lock); + /* probability of decrementing increases linearly + * as more locks are held. + */ + dec_chance = chance_left * num_locks / max_locks; + chance_left -= dec_chance; + + /* FIXME: conversions temporarily disabled + * until they are working correctly. + */ + /* con_chance = chance_left * num_locks / max_locks; */ + con_chance = 0; + chance_left -= con_chance; + spin_unlock(&ctl_lock); + + get_random_bytes(&random, sizeof(random)); + + random = random % 100; + if (random < dec_chance) + ldlm_do_decrement(); + else if (random < (dec_chance + con_chance)) + ldlm_do_convert(); + else + ldlm_do_enqueue(thread); + + LDLM_DEBUG_NOLOCK("locks requested: %d, " + "conversions requested %d", + atomic_read(&locks_requested), + atomic_read(&converts_requested)); + LDLM_DEBUG_NOLOCK("locks granted: %d, " + "locks matched: %d", + atomic_read(&locks_granted), + atomic_read(&locks_matched)); + + spin_lock(&ctl_lock); + LDLM_DEBUG_NOLOCK("lock references currently held: %d, ", + num_locks); + spin_unlock(&ctl_lock); + + /* + * We don't sleep after a lock being blocked, so let's + * make sure other things can run. + */ + schedule(); + } + + thread->t_flags |= SVC_STOPPED; + wake_up(&thread->t_ctl_waitq); + + RETURN(0); +} + +static int ldlm_start_thread(struct obd_device *obddev, + struct lustre_handle *connh) +{ + struct ldlm_test_thread *test; + int rc; + ENTRY; + + OBD_ALLOC(test, sizeof(*test)); + if (test == NULL) { + LBUG(); + RETURN(-ENOMEM); + } + init_waitqueue_head(&test->t_ctl_waitq); + + test->obddev = obddev; + + spin_lock(&ctl_lock); + list_add(&test->t_link, &ctl_threads); + spin_unlock(&ctl_lock); + + rc = kernel_thread(ldlm_test_main, (void *)test, + CLONE_VM | CLONE_FS | CLONE_FILES); + if (rc < 0) { + CERROR("cannot start thread\n"); + RETURN(-EINVAL); + } + wait_event(test->t_ctl_waitq, test->t_flags & SVC_RUNNING); + + RETURN(0); +} + +int ldlm_regression_start(struct obd_device *obddev, + struct lustre_handle *connh, + unsigned int threads, unsigned int max_locks_in, + unsigned int num_resources_in, + unsigned int num_extents_in) +{ + int i, rc = 0; + ENTRY; + + spin_lock(&ctl_lock); + if (regression_running) { + CERROR("You can't start the ldlm regression twice.\n"); + spin_unlock(&ctl_lock); + RETURN(-EINVAL); + } + regression_running = 1; + spin_unlock(&ctl_lock); + + regress_connh = *connh; + max_locks = max_locks_in; + num_resources = num_resources_in; + num_extents = num_extents_in; + + LDLM_DEBUG_NOLOCK("regression test started: threads: %d, max_locks: " + "%d, num_res: %d, num_ext: %d\n", + threads, max_locks_in, num_resources_in, + num_extents_in); + + for (i = 0; i < threads; i++) { + rc = ldlm_start_thread(obddev, connh); + if (rc < 0) + GOTO(cleanup, rc); + } + + cleanup: + if (rc < 0) + ldlm_regression_stop(); + RETURN(rc); +} + +int ldlm_regression_stop(void) +{ + ENTRY; + + spin_lock(&ctl_lock); + if (!regression_running) { + CERROR("The ldlm regression isn't started.\n"); + spin_unlock(&ctl_lock); + RETURN(-EINVAL); + } + + while (!list_empty(&ctl_threads)) { + struct ldlm_test_thread *thread; + thread = list_entry(ctl_threads.next, struct ldlm_test_thread, + t_link); + + thread->t_flags |= SVC_STOPPING; + + spin_unlock(&ctl_lock); + wake_up(&thread->t_ctl_waitq); + wait_event(thread->t_ctl_waitq, thread->t_flags & SVC_STOPPED); + spin_lock(&ctl_lock); + + list_del(&thread->t_link); + OBD_FREE(thread, sizeof(*thread)); + } + + /* decrement all held locks */ + while (!list_empty(&lock_list)) { + struct ldlm_lock *lock; + struct ldlm_test_lock *lock_info = + list_entry(lock_list.next, struct ldlm_test_lock, + l_link); + list_del(lock_list.next); + num_locks--; + + lock = ldlm_handle2lock(&lock_info->l_lockh); + ldlm_lock_decref(&lock_info->l_lockh, lock->l_granted_mode); + ldlm_lock_put(lock); + + OBD_FREE(lock_info, sizeof(*lock_info)); + } + + regression_running = 0; + spin_unlock(&ctl_lock); + + RETURN(0); +} + +int ldlm_test(struct obd_device *obddev, struct lustre_handle *connh) { int rc; rc = ldlm_test_basics(obddev); @@ -160,6 +643,6 @@ int ldlm_test(struct obd_device *obddev, struct ptlrpc_connection *conn) if (rc) RETURN(rc); - rc = ldlm_test_network(obddev, conn); + rc = ldlm_test_network(obddev, connh); RETURN(rc); }