diff --git a/autogen.sh b/autogen.sh index 340b2c673d..2e5965e730 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,6 +1,8 @@ #!/bin/sh find . -type d -name .deps | xargs rm -rf +rm -rf config.guess config.sub ltmain.sh +libtoolize aclocal 2>/dev/null && autoheader && automake --add-missing --include-deps # 2>/dev/null && diff --git a/configure.ac b/configure.ac index 52bc4f593b..833514dc11 100644 --- a/configure.ac +++ b/configure.ac @@ -6,6 +6,7 @@ AC_CONFIG_HEADERS([config.h]) AC_PROG_INSTALL AC_PROG_CC +AC_PROG_LIBTOOL ver=`uname -r` KERNELCFLAGS= @@ -88,21 +89,6 @@ else AC_MSG_RESULT([no]) fi -AC_MSG_CHECKING([if inode has i_private field]) -if egrep -qw "i_private" $kernelsrc/include/linux/fs.h; then - AC_DEFINE(HAVE_I_PRIVATE, 1, [inode has i_private field]) - AC_MSG_RESULT([yes]) -else - AC_MSG_RESULT([no]) -fi - -AC_MSG_CHECKING([if inode has i_mutex field ]) -if egrep -qw "i_mutex" $kernelsrc/include/linux/fs.h; then - AC_DEFINE(HAVE_I_MUTEX, 1, [inode has i_mutex field]) - AC_MSG_RESULT([yes]) -else - AC_MSG_RESULT([no]) -fi AC_MSG_CHECKING([if kernel has mutex.h ]) if test -f $kernelsrc/include/linux/mutex.h; then @@ -122,6 +108,7 @@ AC_SUBST(KERNELCFLAGS) AC_CONFIG_FILES([ Makefile src/Makefile + src/lib/Makefile src/cmd/Makefile src/spl/Makefile src/splat/Makefile diff --git a/include/Makefile.am b/include/Makefile.am index 42bbb23991..5b063fd094 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,5 +1,7 @@ -EXTRA_DIST = spl.h splat.h splat-ctl.h +EXTRA_DIST = spl.h +EXTRA_DIST += splat.h splat-ctl.h EXTRA_DIST += linux-condvar.h linux-kmem.h linux-random.h linux-thread.h EXTRA_DIST += linux-types.h linux-cred.h linux-kstat.h linux-rwlock.h EXTRA_DIST += linux-time.h linux-callb.h linux-generic.h linux-mutex.h EXTRA_DIST += linux-taskq.h linux-timer.h +EXTRA_DIST += list.h diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000000..fe3fd006b4 --- /dev/null +++ b/include/list.h @@ -0,0 +1,281 @@ +/***************************************************************************** + * $Id: list.h 2899 2002-12-11 19:00:36Z dun $ + ***************************************************************************** + * Copyright (C) 2001-2002 The Regents of the University of California. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by Chris Dunlap . + * + * This file is from LSD-Tools, the LLNL Software Development Toolbox. + * + * LSD-Tools 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. + * + * LSD-Tools 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 LSD-Tools; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + + +#ifndef LSD_LIST_H +#define LSD_LIST_H + + +/*********** + * Notes * + ***********/ +/* + * If NDEBUG is not defined, internal debug code will be enabled. This is + * intended for development use only and production code should define NDEBUG. + * + * If WITH_LSD_FATAL_ERROR_FUNC is defined, the linker will expect to + * find an external lsd_fatal_error(file,line,mesg) function. By default, + * lsd_fatal_error(file,line,mesg) is a macro definition that outputs an + * error message to stderr. This macro may be redefined to invoke another + * routine instead. + * + * If WITH_LSD_NOMEM_ERROR_FUNC is defined, the linker will expect to + * find an external lsd_nomem_error(file,line,mesg) function. By default, + * lsd_nomem_error(file,line,mesg) is a macro definition that returns NULL. + * This macro may be redefined to invoke another routine instead. + * + * If WITH_PTHREADS is defined, these routines will be thread-safe. + */ + + +/**************** + * Data Types * + ****************/ + +typedef struct list * List; +/* + * List opaque data type. + */ + +typedef struct listIterator * ListIterator; +/* + * List Iterator opaque data type. + */ + +typedef void (*ListDelF) (void *x); +/* + * Function prototype to deallocate data stored in a list. + * This function is responsible for freeing all memory associated + * with an item, including all subordinate items (if applicable). + */ + +typedef int (*ListCmpF) (void *x, void *y); +/* + * Function prototype for comparing two items in a list. + * Returns less-than-zero if (xy). + */ + +typedef int (*ListFindF) (void *x, void *key); +/* + * Function prototype for matching items in a list. + * Returns non-zero if (x==key); o/w returns zero. + */ + +typedef int (*ListForF) (void *x, void *arg); +/* + * Function prototype for operating on each item in a list. + * Returns less-than-zero on error. + */ + + +/******************************* + * General-Purpose Functions * + *******************************/ + +List list_create (ListDelF f); +/* + * Creates and returns a new empty list, or lsd_nomem_error() on failure. + * The deletion function [f] is used to deallocate memory used by items + * in the list; if this is NULL, memory associated with these items + * will not be freed when the list is destroyed. + * Note: Abandoning a list without calling list_destroy() will result + * in a memory leak. + */ + +void list_destroy (List l); +/* + * Destroys list [l], freeing memory used for list iterators and the + * list itself; if a deletion function was specified when the list + * was created, it will be called for each item in the list. + */ + +int list_is_empty (List l); +/* + * Returns non-zero if list [l] is empty; o/w returns zero. + */ + +int list_count (List l); +/* + * Returns the number of items in list [l]. + */ + + +/*************************** + * List Access Functions * + ***************************/ + +void * list_append (List l, void *x); +/* + * Inserts data [x] at the end of list [l]. + * Returns the data's ptr, or lsd_nomem_error() if insertion failed. + */ + +void * list_prepend (List l, void *x); +/* + * Inserts data [x] at the beginning of list [l]. + * Returns the data's ptr, or lsd_nomem_error() if insertion failed. + */ + +void * list_find_first (List l, ListFindF f, void *key); +/* + * Traverses list [l] using [f] to match each item with [key]. + * Returns a ptr to the first item for which the function [f] + * returns non-zero, or NULL if no such item is found. + * Note: This function differs from list_find() in that it does not require + * a list iterator; it should only be used when all list items are known + * to be unique (according to the function [f]). + */ + +int list_delete_all (List l, ListFindF f, void *key); +/* + * Traverses list [l] using [f] to match each item with [key]. + * Removes all items from the list for which the function [f] returns + * non-zero; if a deletion function was specified when the list was + * created, it will be called to deallocate each item being removed. + * Returns a count of the number of items removed from the list. + */ + +int list_for_each (List l, ListForF f, void *arg); +/* + * For each item in list [l], invokes the function [f] with [arg]. + * Returns a count of the number of items on which [f] was invoked. + * If [f] returns <0 for a given item, the iteration is aborted and the + * function returns the negative of that item's position in the list. + */ + +void list_sort (List l, ListCmpF f); +/* + * Sorts list [l] into ascending order according to the function [f]. + * Note: Sorting a list resets all iterators associated with the list. + * Note: The sort algorithm is stable. + */ + + +/**************************** + * Stack Access Functions * + ****************************/ + +void * list_push (List l, void *x); +/* + * Pushes data [x] onto the top of stack [l]. + * Returns the data's ptr, or lsd_nomem_error() if insertion failed. + */ + +void * list_pop (List l); +/* + * Pops the data item at the top of the stack [l]. + * Returns the data's ptr, or NULL if the stack is empty. + */ + +void * list_peek (List l); +/* + * Peeks at the data item at the top of the stack (or head of the queue) [l]. + * Returns the data's ptr, or NULL if the stack (or queue) is empty. + * Note: The item is not removed from the list. + */ + + +/**************************** + * Queue Access Functions * + ****************************/ + +void * list_enqueue (List l, void *x); +/* + * Enqueues data [x] at the tail of queue [l]. + * Returns the data's ptr, or lsd_nomem_error() if insertion failed. + */ + +void * list_dequeue (List l); +/* + * Dequeues the data item at the head of the queue [l]. + * Returns the data's ptr, or NULL if the queue is empty. + */ + + +/***************************** + * List Iterator Functions * + *****************************/ + +ListIterator list_iterator_create (List l); +/* + * Creates and returns a list iterator for non-destructively traversing + * list [l], or lsd_nomem_error() on failure. + */ + +void list_iterator_reset (ListIterator i); +/* + * Resets the list iterator [i] to start traversal at the beginning + * of the list. + */ + +void list_iterator_destroy (ListIterator i); +/* + * Destroys the list iterator [i]; list iterators not explicitly destroyed + * in this manner will be destroyed when the list is deallocated via + * list_destroy(). + */ + +void * list_next (ListIterator i); +/* + * Returns a ptr to the next item's data, + * or NULL once the end of the list is reached. + * Example: i=list_iterator_create(i); while ((x=list_next(i))) {...} + */ + +void * list_insert (ListIterator i, void *x); +/* + * Inserts data [x] immediately before the last item returned via list + * iterator [i]; once the list iterator reaches the end of the list, + * insertion is made at the list's end. + * Returns the data's ptr, or lsd_nomem_error() if insertion failed. + */ + +void * list_find (ListIterator i, ListFindF f, void *key); +/* + * Traverses the list from the point of the list iterator [i] + * using [f] to match each item with [key]. + * Returns a ptr to the next item for which the function [f] + * returns non-zero, or NULL once the end of the list is reached. + * Example: i=list_iterator_reset(i); while ((x=list_find(i,f,k))) {...} + */ + +void * list_remove (ListIterator i); +/* + * Removes from the list the last item returned via list iterator [i] + * and returns the data's ptr. + * Note: The client is responsible for freeing the returned data. + */ + +int list_delete (ListIterator i); +/* + * Removes from the list the last item returned via list iterator [i]; + * if a deletion function was specified when the list was created, + * it will be called to deallocate the item being removed. + * Returns a count of the number of items removed from the list + * (ie, '1' if the item was removed, and '0' otherwise). + */ + + +#endif /* !LSD_LIST_H */ diff --git a/include/splat.h b/include/splat.h index 69cb387ad1..f9341b5d9d 100644 --- a/include/splat.h +++ b/include/splat.h @@ -1,7 +1,8 @@ -#ifndef _KZT_H -#define _KZT_H +#ifndef _SPLAT_H +#define _SPLAT_H -#include +#include "list.h" +#include "splat-ctl.h" #define DEV_NAME "/dev/kztctl" #define COLOR_BLACK "\033[0;30m" @@ -23,13 +24,11 @@ #define COLOR_RESET "\033[0m" typedef struct subsystem { - uu_list_node_t sub_node; /* Linkage for global subsystem list */ kzt_user_t sub_desc; /* Subsystem description */ - uu_list_t *sub_tests; /* Assocated subsystem tests list */ + List sub_tests; /* Assocated subsystem tests list */ } subsystem_t; typedef struct test { - uu_list_node_t test_node; /* Linkage for globals test list */ kzt_user_t test_desc; /* Test description */ subsystem_t *test_sub; /* Parent subsystem */ } test_t; @@ -40,8 +39,8 @@ typedef struct cmd_args { int args_do_all; /* Run all tests flag */ int args_do_color; /* Colorize output */ int args_exit_on_error; /* Exit on first error flag */ - uu_list_t *args_tests; /* Requested subsystems/tests */ + List args_tests; /* Requested subsystems/tests */ } cmd_args_t; -#endif /* _KZT_H */ +#endif /* _SPLAT_H */ diff --git a/src/Makefile.am b/src/Makefile.am index 86f519112a..51013b8d12 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1 +1 @@ -SUBDIRS = cmd spl splat +SUBDIRS = lib cmd spl splat diff --git a/src/cmd/Makefile.am b/src/cmd/Makefile.am index 38a21099c9..ae3961a977 100644 --- a/src/cmd/Makefile.am +++ b/src/cmd/Makefile.am @@ -1,3 +1,5 @@ -AM_CFLAGS = @EXTRA_CFLAGS@ -g -O2 -W -Wall -Wstrict-prototypes -Wshadow +AM_CFLAGS = -g -O2 -W -Wall -Wstrict-prototypes -Wshadow +INCLUDES = -I$(top_srcdir)/include sbin_PROGRAMS = splat splat_SOURCES = splat.c +splat_LDFLAGS = $(top_builddir)/src/lib/libcommon.la diff --git a/src/cmd/splat.c b/src/cmd/splat.c index 0ad65490c9..56181af3d3 100644 --- a/src/cmd/splat.c +++ b/src/cmd/splat.c @@ -1,4 +1,4 @@ -/* Kernel ZFS Test (KZT) user space command interface */ +/* Solaris Porting Layer Aggressive Test (SPLAT) userspace interface */ #include #include @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -29,17 +28,17 @@ static const struct option longOpts[] = { { 0, 0, 0, 0 } }; -static uu_list_t *subsystems; /* Subsystem/tests */ -static uu_list_pool_t *subsystem_pool; /* Subsystem pool */ -static uu_list_pool_t *test_pool; /* Test pool */ +static List subsystems; /* Subsystem/tests */ static int kztctl_fd; /* Control file descriptor */ static char kzt_version[KZT_VERSION_SIZE]; /* Kernel version string */ static char *kzt_buffer = NULL; /* Scratch space area */ static int kzt_buffer_size = 0; /* Scratch space size */ -static void test_list(uu_list_t *, int); +static void test_list(List, int); static int dev_clear(void); +static void subsystem_fini(subsystem_t *); +static void test_fini(test_t *); static int usage(void) { @@ -69,9 +68,8 @@ static subsystem_t *subsystem_init(kzt_user_t *desc) return NULL; memcpy(&sub->sub_desc, desc, sizeof(*desc)); - uu_list_node_init(sub, &sub->sub_node, subsystem_pool); - sub->sub_tests = uu_list_create(test_pool, NULL, 0); + sub->sub_tests = list_create((ListDelF)test_fini); if (sub->sub_tests == NULL) { free(sub); return NULL; @@ -83,8 +81,6 @@ static subsystem_t *subsystem_init(kzt_user_t *desc) static void subsystem_fini(subsystem_t *sub) { assert(sub != NULL); - - uu_list_node_fini(sub, &sub->sub_node, subsystem_pool); free(sub); } @@ -149,13 +145,15 @@ static int subsystem_setup(void) return -ENOMEM; } - uu_list_insert(subsystems, sub, 0); + list_append(subsystems, sub); } free(cfg); return 0; } +/* XXX - Commented out until we sort the lists */ +#if 0 static int subsystem_compare(const void *l_arg, const void *r_arg, void *private) { const subsystem_t *l = l_arg; @@ -169,9 +167,11 @@ static int subsystem_compare(const void *l_arg, const void *r_arg, void *private return 0; } +#endif -static void subsystem_list(uu_list_t *list, int indent) +static void subsystem_list(List l, int indent) { + ListIterator i; subsystem_t *sub; fprintf(stdout, @@ -179,8 +179,9 @@ static void subsystem_list(uu_list_t *list, int indent) "Available KZT Tests " "-------------------------------\n"); - for (sub = uu_list_first(list); sub != NULL; - sub = uu_list_next(list, sub)) { + i = list_iterator_create(l); + + while ((sub = list_next(i))) { fprintf(stdout, "%*s0x%0*x %-*s ---- %s ----\n", indent, "", 4, sub->sub_desc.id, @@ -188,6 +189,8 @@ static void subsystem_list(uu_list_t *list, int indent) sub->sub_desc.desc); test_list(sub->sub_tests, indent + 7); } + + list_iterator_destroy(i); } static test_t *test_init(subsystem_t *sub, kzt_user_t *desc) @@ -200,7 +203,6 @@ static test_t *test_init(subsystem_t *sub, kzt_user_t *desc) test->test_sub = sub; memcpy(&test->test_desc, desc, sizeof(*desc)); - uu_list_node_init(test, &test->test_node, test_pool); return test; } @@ -208,8 +210,6 @@ static test_t *test_init(subsystem_t *sub, kzt_user_t *desc) static void test_fini(test_t *test) { assert(test != NULL); - - uu_list_node_fini(test, &test->test_node, test_pool); free(test); } @@ -274,13 +274,15 @@ static int test_setup(subsystem_t *sub) return -ENOMEM; } - uu_list_insert(sub->sub_tests, test, 0); + list_append(sub->sub_tests, test); } free(cfg); return 0; } +/* XXX - Commented out until we sort the lists */ +#if 0 static int test_compare(const void *l_arg, const void *r_arg, void *private) { const test_t *l = l_arg; @@ -294,27 +296,33 @@ static int test_compare(const void *l_arg, const void *r_arg, void *private) return 0; } +#endif static test_t *test_copy(test_t *test) { return test_init(test->test_sub, &test->test_desc); } -static void test_list(uu_list_t *list, int indent) +static void test_list(List l, int indent) { + ListIterator i; test_t *test; - for (test = uu_list_first(list); test != NULL; - test = uu_list_next(list, test)) + i = list_iterator_create(l); + + while ((test = list_next(i))) fprintf(stdout, "%*s0x%0*x %-*s %-*s\n", indent, "", 04, test->test_desc.id, KZT_NAME_SIZE, test->test_desc.name, KZT_DESC_SIZE, test->test_desc.desc); + + list_iterator_destroy(i); } static test_t *test_find(char *sub_str, char *test_str) { + ListIterator si, ti; subsystem_t *sub; test_t *test; int sub_num, test_num; @@ -326,22 +334,31 @@ static test_t *test_find(char *sub_str, char *test_str) sub_num = strtol(sub_str, NULL, 0); test_num = strtol(test_str, NULL, 0); - for (sub = uu_list_first(subsystems); sub != NULL; - sub = uu_list_next(subsystems, sub)) { + si = list_iterator_create(subsystems); + + while ((sub = list_next(si))) { if (strncmp(sub->sub_desc.name, sub_str, KZT_NAME_SIZE) && sub->sub_desc.id != sub_num) continue; - for (test = uu_list_first(sub->sub_tests); test != NULL; - test = uu_list_next(sub->sub_tests, test)) { + ti = list_iterator_create(sub->sub_tests); + + while ((test = list_next(ti))) { if (!strncmp(test->test_desc.name, test_str, - KZT_NAME_SIZE) || test->test_desc.id == test_num) + KZT_NAME_SIZE) || test->test_desc.id == test_num) { + list_iterator_destroy(ti); + list_iterator_destroy(si); return test; + } } + + list_iterator_destroy(ti); } + list_iterator_destroy(si); + return NULL; } @@ -353,27 +370,35 @@ static int test_add(cmd_args_t *args, test_t *test) if (tmp == NULL) return -ENOMEM; - uu_list_insert(args->args_tests, tmp, 0); + list_append(args->args_tests, tmp); return 0; } static int test_add_all(cmd_args_t *args) { + ListIterator si, ti; subsystem_t *sub; test_t *test; int rc; - for (sub = uu_list_first(subsystems); sub != NULL; - sub = uu_list_next(subsystems, sub)) { + si = list_iterator_create(subsystems); - for (test = uu_list_first(sub->sub_tests); test != NULL; - test = uu_list_next(sub->sub_tests, test)) { + while ((sub = list_next(si))) { + ti = list_iterator_create(sub->sub_tests); - if (rc = test_add(args, test)) + while ((test = list_next(ti))) { + if ((rc = test_add(args, test))) { + list_iterator_destroy(ti); + list_iterator_destroy(si); return rc; + } } + + list_iterator_destroy(ti); } + list_iterator_destroy(si); + return 0; } @@ -428,6 +453,7 @@ static int test_run(cmd_args_t *args, test_t *test) static int tests_run(cmd_args_t *args) { + ListIterator i; test_t *test; int rc; @@ -436,19 +462,23 @@ static int tests_run(cmd_args_t *args) "Running KZT Tests " "-------------------------------\n"); - for (test = uu_list_first(args->args_tests); test != NULL; - test = uu_list_next(args->args_tests, test)) { + i = list_iterator_create(args->args_tests); + while ((test = list_next(i))) { rc = test_run(args, test); - if (rc && args->args_exit_on_error) + if (rc && args->args_exit_on_error) { + list_iterator_destroy(i); return rc; + } } + list_iterator_destroy(i); return 0; } static int args_parse_test(cmd_args_t *args, char *str) { + ListIterator si, ti; subsystem_t *s; test_t *t; char *sub_str, *test_str; @@ -476,21 +506,26 @@ static int args_parse_test(cmd_args_t *args, char *str) if (!strncasecmp(test_str, "all", strlen(test_str)) || (test_num == -1)) test_all = 1; + si = list_iterator_create(subsystems); + if (sub_all) { if (test_all) { /* Add all tests from all subsystems */ - for (s = uu_list_first(subsystems); s != NULL; - s = uu_list_next(subsystems, s)) - for (t = uu_list_first(s->sub_tests);t != NULL; - t = uu_list_next(s->sub_tests, t)) - if (rc = test_add(args, t)) + while ((s = list_next(si))) { + ti = list_iterator_create(s->sub_tests); + while ((t = list_next(ti))) { + if ((rc = test_add(args, t))) { + list_iterator_destroy(ti); goto error_run; + } + } + list_iterator_destroy(ti); + } } else { /* Add a specific test from all subsystems */ - for (s = uu_list_first(subsystems); s != NULL; - s = uu_list_next(subsystems, s)) { - if (t = test_find(s->sub_desc.name,test_str)) { - if (rc = test_add(args, t)) + while ((s = list_next(si))) { + if ((t = test_find(s->sub_desc.name,test_str))) { + if ((rc = test_add(args, t))) goto error_run; flag = 1; @@ -504,21 +539,24 @@ static int args_parse_test(cmd_args_t *args, char *str) } else { if (test_all) { /* Add all tests from a specific subsystem */ - for (s = uu_list_first(subsystems); s != NULL; - s = uu_list_next(subsystems, s)) { + while ((s = list_next(si))) { if (strncasecmp(sub_str, s->sub_desc.name, strlen(sub_str))) continue; - for (t = uu_list_first(s->sub_tests);t != NULL; - t = uu_list_next(s->sub_tests, t)) - if (rc = test_add(args, t)) + ti = list_iterator_create(s->sub_tests); + while ((t = list_next(ti))) { + if ((rc = test_add(args, t))) { + list_iterator_destroy(ti); goto error_run; + } + } + list_iterator_destroy(ti); } } else { /* Add a specific test from a specific subsystem */ - if (t = test_find(sub_str, test_str)) { - if (rc = test_add(args, t)) + if ((t = test_find(sub_str, test_str))) { + if ((rc = test_add(args, t))) goto error_run; } else { fprintf(stderr, "Test '%s:%s' could not be " @@ -528,25 +566,25 @@ static int args_parse_test(cmd_args_t *args, char *str) } } + list_iterator_destroy(si); + return 0; error_run: + list_iterator_destroy(si); + fprintf(stderr, "Test '%s:%s' not added to run list: %d\n", sub_str, test_str, rc); + return rc; } static void args_fini(cmd_args_t *args) { - struct cmd_test *ptr1, *ptr2; - assert(args != NULL); - - - if (args->args_tests != NULL) { - uu_list_destroy(args->args_tests); - } + if (args->args_tests != NULL) + list_destroy(args->args_tests); free(args); } @@ -573,7 +611,7 @@ args_init(int argc, char **argv) args->args_do_all = 0; args->args_do_color = 1; args->args_exit_on_error = 0; - args->args_tests = uu_list_create(test_pool, NULL, 0); + args->args_tests = list_create((ListDelF)test_fini); if (args->args_tests == NULL) { args_fini(args); return NULL; @@ -674,6 +712,7 @@ dev_fini(void) static int dev_init(void) { + ListIterator i; subsystem_t *sub; int rc; @@ -690,7 +729,7 @@ dev_init(void) if ((rc = read(kztctl_fd, kzt_version, KZT_VERSION_SIZE - 1)) == -1) goto error; - if (rc = dev_clear()) + if ((rc = dev_clear())) goto error; if ((rc = dev_size(0)) < 0) @@ -710,11 +749,16 @@ dev_init(void) goto error; /* Determine available tests for all subsystems */ - for (sub = uu_list_first(subsystems); sub != NULL; - sub = uu_list_next(subsystems, sub)) - if ((rc = test_setup(sub)) != 0) - goto error; + i = list_iterator_create(subsystems); + while ((sub = list_next(i))) { + if ((rc = test_setup(sub)) != 0) { + list_iterator_destroy(i); + goto error; + } + } + + list_iterator_destroy(i); return 0; error: @@ -731,42 +775,20 @@ error: int init(void) { - int rc; - - /* Configure the subsystem pool */ - subsystem_pool = uu_list_pool_create("sub_pool", sizeof(subsystem_t), - offsetof(subsystem_t, sub_node), - subsystem_compare, 0); - if (subsystem_pool == NULL) - return -ENOMEM; - - /* Configure the test pool */ - test_pool = uu_list_pool_create("test_pool", sizeof(test_t), - offsetof(test_t, test_node), - test_compare, 0); - if (test_pool == NULL) { - uu_list_pool_destroy(subsystem_pool); - return -ENOMEM; - } + int rc = 0; /* Allocate the subsystem list */ - subsystems = uu_list_create(subsystem_pool, NULL, 0); - if (subsystems == NULL) { - uu_list_pool_destroy(test_pool); - uu_list_pool_destroy(subsystem_pool); - return -ENOMEM; - } + subsystems = list_create((ListDelF)subsystem_fini); + if (subsystems == NULL) + rc = ENOMEM; - return 0; + return rc; } void fini(void) { - /* XXX - Cleanup destroy lists release memory */ - - /* XXX - Remove contents of list first */ - uu_list_destroy(subsystems); + list_destroy(subsystems); } @@ -777,11 +799,11 @@ main(int argc, char **argv) int rc = 0; /* General init */ - if (rc = init()) + if ((rc = init())) return rc; /* Device specific init */ - if (rc = dev_init()) + if ((rc = dev_init())) goto out; /* Argument init and parsing */ @@ -802,12 +824,12 @@ main(int argc, char **argv) /* Add all available test to the list of tests to run */ if (args->args_do_all) { - if (rc = test_add_all(args)) + if ((rc = test_add_all(args))) goto out; } /* Run all the requested tests */ - if (rc = tests_run(args)) + if ((rc = tests_run(args))) goto out; out: diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am new file mode 100644 index 0000000000..91c228fb0c --- /dev/null +++ b/src/lib/Makefile.am @@ -0,0 +1,3 @@ +INCLUDES = -I$(top_srcdir)/include +noinst_LTLIBRARIES = libcommon.la +libcommon_la_SOURCES = list.c diff --git a/src/lib/list.c b/src/lib/list.c new file mode 100644 index 0000000000..6548cacbca --- /dev/null +++ b/src/lib/list.c @@ -0,0 +1,833 @@ +/***************************************************************************** + * $Id: list.c 3709 2006-11-29 00:51:22Z dun $ + ***************************************************************************** + * Copyright (C) 2001-2002 The Regents of the University of California. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by Chris Dunlap . + * + * This file is from LSD-Tools, the LLNL Software Development Toolbox. + * + * LSD-Tools 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. + * + * LSD-Tools 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 LSD-Tools; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + ***************************************************************************** + * Refer to "list.h" for documentation on public functions. + *****************************************************************************/ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef WITH_PTHREADS +# include +#endif /* WITH_PTHREADS */ + +#include +#include +#include +#include +#include "list.h" + + +/********************* + * lsd_fatal_error * + *********************/ + +#ifdef WITH_LSD_FATAL_ERROR_FUNC +# undef lsd_fatal_error + extern void lsd_fatal_error(char *file, int line, char *mesg); +#else /* !WITH_LSD_FATAL_ERROR_FUNC */ +# ifndef lsd_fatal_error +# include +# include +# include +# define lsd_fatal_error(file, line, mesg) \ + do { \ + fprintf(stderr, "ERROR: [%s:%d] %s: %s\n", \ + file, line, mesg, strerror(errno)); \ + } while (0) +# endif /* !lsd_fatal_error */ +#endif /* !WITH_LSD_FATAL_ERROR_FUNC */ + + +/********************* + * lsd_nomem_error * + *********************/ + +#ifdef WITH_LSD_NOMEM_ERROR_FUNC +# undef lsd_nomem_error + extern void * lsd_nomem_error(char *file, int line, char *mesg); +#else /* !WITH_LSD_NOMEM_ERROR_FUNC */ +# ifndef lsd_nomem_error +# define lsd_nomem_error(file, line, mesg) (NULL) +# endif /* !lsd_nomem_error */ +#endif /* !WITH_LSD_NOMEM_ERROR_FUNC */ + + +/*************** + * Constants * + ***************/ + +#define LIST_ALLOC 32 +#define LIST_MAGIC 0xDEADBEEF + + +/**************** + * Data Types * + ****************/ + +struct listNode { + void *data; /* node's data */ + struct listNode *next; /* next node in list */ +}; + +struct listIterator { + struct list *list; /* the list being iterated */ + struct listNode *pos; /* the next node to be iterated */ + struct listNode **prev; /* addr of 'next' ptr to prv It node */ + struct listIterator *iNext; /* iterator chain for list_destroy() */ +#ifndef NDEBUG + unsigned int magic; /* sentinel for asserting validity */ +#endif /* !NDEBUG */ +}; + +struct list { + struct listNode *head; /* head of the list */ + struct listNode **tail; /* addr of last node's 'next' ptr */ + struct listIterator *iNext; /* iterator chain for list_destroy() */ + ListDelF fDel; /* function to delete node data */ + int count; /* number of nodes in list */ +#ifdef WITH_PTHREADS + pthread_mutex_t mutex; /* mutex to protect access to list */ +#endif /* WITH_PTHREADS */ +#ifndef NDEBUG + unsigned int magic; /* sentinel for asserting validity */ +#endif /* !NDEBUG */ +}; + +typedef struct listNode * ListNode; + + +/**************** + * Prototypes * + ****************/ + +static void * list_node_create (List l, ListNode *pp, void *x); +static void * list_node_destroy (List l, ListNode *pp); +static List list_alloc (void); +static void list_free (List l); +static ListNode list_node_alloc (void); +static void list_node_free (ListNode p); +static ListIterator list_iterator_alloc (void); +static void list_iterator_free (ListIterator i); +static void * list_alloc_aux (int size, void *pfreelist); +static void list_free_aux (void *x, void *pfreelist); + + +/*************** + * Variables * + ***************/ + +static List list_free_lists = NULL; +static ListNode list_free_nodes = NULL; +static ListIterator list_free_iterators = NULL; + +#ifdef WITH_PTHREADS +static pthread_mutex_t list_free_lock = PTHREAD_MUTEX_INITIALIZER; +#endif /* WITH_PTHREADS */ + + +/************ + * Macros * + ************/ + +#ifdef WITH_PTHREADS + +# define list_mutex_init(mutex) \ + do { \ + int e = pthread_mutex_init(mutex, NULL); \ + if (e != 0) { \ + errno = e; \ + lsd_fatal_error(__FILE__, __LINE__, "list mutex init"); \ + abort(); \ + } \ + } while (0) + +# define list_mutex_lock(mutex) \ + do { \ + int e = pthread_mutex_lock(mutex); \ + if (e != 0) { \ + errno = e; \ + lsd_fatal_error(__FILE__, __LINE__, "list mutex lock"); \ + abort(); \ + } \ + } while (0) + +# define list_mutex_unlock(mutex) \ + do { \ + int e = pthread_mutex_unlock(mutex); \ + if (e != 0) { \ + errno = e; \ + lsd_fatal_error(__FILE__, __LINE__, "list mutex unlock"); \ + abort(); \ + } \ + } while (0) + +# define list_mutex_destroy(mutex) \ + do { \ + int e = pthread_mutex_destroy(mutex); \ + if (e != 0) { \ + errno = e; \ + lsd_fatal_error(__FILE__, __LINE__, "list mutex destroy"); \ + abort(); \ + } \ + } while (0) + +# ifndef NDEBUG + static int list_mutex_is_locked (pthread_mutex_t *mutex); +# endif /* !NDEBUG */ + +#else /* !WITH_PTHREADS */ + +# define list_mutex_init(mutex) +# define list_mutex_lock(mutex) +# define list_mutex_unlock(mutex) +# define list_mutex_destroy(mutex) +# define list_mutex_is_locked(mutex) (1) + +#endif /* !WITH_PTHREADS */ + + +/*************** + * Functions * + ***************/ + +List +list_create (ListDelF f) +{ + List l; + + if (!(l = list_alloc())) + return(lsd_nomem_error(__FILE__, __LINE__, "list create")); + l->head = NULL; + l->tail = &l->head; + l->iNext = NULL; + l->fDel = f; + l->count = 0; + list_mutex_init(&l->mutex); + assert(l->magic = LIST_MAGIC); /* set magic via assert abuse */ + return(l); +} + + +void +list_destroy (List l) +{ + ListIterator i, iTmp; + ListNode p, pTmp; + + assert(l != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + i = l->iNext; + while (i) { + assert(i->magic == LIST_MAGIC); + iTmp = i->iNext; + assert(i->magic = ~LIST_MAGIC); /* clear magic via assert abuse */ + list_iterator_free(i); + i = iTmp; + } + p = l->head; + while (p) { + pTmp = p->next; + if (p->data && l->fDel) + l->fDel(p->data); + list_node_free(p); + p = pTmp; + } + assert(l->magic = ~LIST_MAGIC); /* clear magic via assert abuse */ + list_mutex_unlock(&l->mutex); + list_mutex_destroy(&l->mutex); + list_free(l); + return; +} + + +int +list_is_empty (List l) +{ + int n; + + assert(l != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + n = l->count; + list_mutex_unlock(&l->mutex); + return(n == 0); +} + + +int +list_count (List l) +{ + int n; + + assert(l != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + n = l->count; + list_mutex_unlock(&l->mutex); + return(n); +} + + +void * +list_append (List l, void *x) +{ + void *v; + + assert(l != NULL); + assert(x != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + v = list_node_create(l, l->tail, x); + list_mutex_unlock(&l->mutex); + return(v); +} + + +void * +list_prepend (List l, void *x) +{ + void *v; + + assert(l != NULL); + assert(x != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + v = list_node_create(l, &l->head, x); + list_mutex_unlock(&l->mutex); + return(v); +} + + +void * +list_find_first (List l, ListFindF f, void *key) +{ + ListNode p; + void *v = NULL; + + assert(l != NULL); + assert(f != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + for (p=l->head; p; p=p->next) { + if (f(p->data, key)) { + v = p->data; + break; + } + } + list_mutex_unlock(&l->mutex); + return(v); +} + + +int +list_delete_all (List l, ListFindF f, void *key) +{ + ListNode *pp; + void *v; + int n = 0; + + assert(l != NULL); + assert(f != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + pp = &l->head; + while (*pp) { + if (f((*pp)->data, key)) { + if ((v = list_node_destroy(l, pp))) { + if (l->fDel) + l->fDel(v); + n++; + } + } + else { + pp = &(*pp)->next; + } + } + list_mutex_unlock(&l->mutex); + return(n); +} + + +int +list_for_each (List l, ListForF f, void *arg) +{ + ListNode p; + int n = 0; + + assert(l != NULL); + assert(f != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + for (p=l->head; p; p=p->next) { + n++; + if (f(p->data, arg) < 0) { + n = -n; + break; + } + } + list_mutex_unlock(&l->mutex); + return(n); +} + + +void +list_sort (List l, ListCmpF f) +{ +/* Note: Time complexity O(n^2). + */ + ListNode *pp, *ppPrev, *ppPos, pTmp; + ListIterator i; + + assert(l != NULL); + assert(f != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + if (l->count > 1) { + ppPrev = &l->head; + pp = &(*ppPrev)->next; + while (*pp) { + if (f((*pp)->data, (*ppPrev)->data) < 0) { + ppPos = &l->head; + while (f((*pp)->data, (*ppPos)->data) >= 0) + ppPos = &(*ppPos)->next; + pTmp = (*pp)->next; + (*pp)->next = *ppPos; + *ppPos = *pp; + *pp = pTmp; + if (ppPrev == ppPos) + ppPrev = &(*ppPrev)->next; + } + else { + ppPrev = pp; + pp = &(*pp)->next; + } + } + l->tail = pp; + + for (i=l->iNext; i; i=i->iNext) { + assert(i->magic == LIST_MAGIC); + i->pos = i->list->head; + i->prev = &i->list->head; + } + } + list_mutex_unlock(&l->mutex); + return; +} + + +void * +list_push (List l, void *x) +{ + void *v; + + assert(l != NULL); + assert(x != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + v = list_node_create(l, &l->head, x); + list_mutex_unlock(&l->mutex); + return(v); +} + + +void * +list_pop (List l) +{ + void *v; + + assert(l != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + v = list_node_destroy(l, &l->head); + list_mutex_unlock(&l->mutex); + return(v); +} + + +void * +list_peek (List l) +{ + void *v; + + assert(l != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + v = (l->head) ? l->head->data : NULL; + list_mutex_unlock(&l->mutex); + return(v); +} + + +void * +list_enqueue (List l, void *x) +{ + void *v; + + assert(l != NULL); + assert(x != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + v = list_node_create(l, l->tail, x); + list_mutex_unlock(&l->mutex); + return(v); +} + + +void * +list_dequeue (List l) +{ + void *v; + + assert(l != NULL); + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + v = list_node_destroy(l, &l->head); + list_mutex_unlock(&l->mutex); + return(v); +} + + +ListIterator +list_iterator_create (List l) +{ + ListIterator i; + + assert(l != NULL); + if (!(i = list_iterator_alloc())) + return(lsd_nomem_error(__FILE__, __LINE__, "list iterator create")); + i->list = l; + list_mutex_lock(&l->mutex); + assert(l->magic == LIST_MAGIC); + i->pos = l->head; + i->prev = &l->head; + i->iNext = l->iNext; + l->iNext = i; + assert(i->magic = LIST_MAGIC); /* set magic via assert abuse */ + list_mutex_unlock(&l->mutex); + return(i); +} + + +void +list_iterator_reset (ListIterator i) +{ + assert(i != NULL); + assert(i->magic == LIST_MAGIC); + list_mutex_lock(&i->list->mutex); + assert(i->list->magic == LIST_MAGIC); + i->pos = i->list->head; + i->prev = &i->list->head; + list_mutex_unlock(&i->list->mutex); + return; +} + + +void +list_iterator_destroy (ListIterator i) +{ + ListIterator *pi; + + assert(i != NULL); + assert(i->magic == LIST_MAGIC); + list_mutex_lock(&i->list->mutex); + assert(i->list->magic == LIST_MAGIC); + for (pi=&i->list->iNext; *pi; pi=&(*pi)->iNext) { + assert((*pi)->magic == LIST_MAGIC); + if (*pi == i) { + *pi = (*pi)->iNext; + break; + } + } + list_mutex_unlock(&i->list->mutex); + assert(i->magic = ~LIST_MAGIC); /* clear magic via assert abuse */ + list_iterator_free(i); + return; +} + + +void * +list_next (ListIterator i) +{ + ListNode p; + + assert(i != NULL); + assert(i->magic == LIST_MAGIC); + list_mutex_lock(&i->list->mutex); + assert(i->list->magic == LIST_MAGIC); + if ((p = i->pos)) + i->pos = p->next; + if (*i->prev != p) + i->prev = &(*i->prev)->next; + list_mutex_unlock(&i->list->mutex); + return(p ? p->data : NULL); +} + + +void * +list_insert (ListIterator i, void *x) +{ + void *v; + + assert(i != NULL); + assert(x != NULL); + assert(i->magic == LIST_MAGIC); + list_mutex_lock(&i->list->mutex); + assert(i->list->magic == LIST_MAGIC); + v = list_node_create(i->list, i->prev, x); + list_mutex_unlock(&i->list->mutex); + return(v); +} + + +void * +list_find (ListIterator i, ListFindF f, void *key) +{ + void *v; + + assert(i != NULL); + assert(f != NULL); + assert(i->magic == LIST_MAGIC); + while ((v=list_next(i)) && !f(v,key)) {;} + return(v); +} + + +void * +list_remove (ListIterator i) +{ + void *v = NULL; + + assert(i != NULL); + assert(i->magic == LIST_MAGIC); + list_mutex_lock(&i->list->mutex); + assert(i->list->magic == LIST_MAGIC); + if (*i->prev != i->pos) + v = list_node_destroy(i->list, i->prev); + list_mutex_unlock(&i->list->mutex); + return(v); +} + + +int +list_delete (ListIterator i) +{ + void *v; + + assert(i != NULL); + assert(i->magic == LIST_MAGIC); + if ((v = list_remove(i))) { + if (i->list->fDel) + i->list->fDel(v); + return(1); + } + return(0); +} + + +static void * +list_node_create (List l, ListNode *pp, void *x) +{ +/* Inserts data pointed to by [x] into list [l] after [pp], + * the address of the previous node's "next" ptr. + * Returns a ptr to data [x], or NULL if insertion fails. + * This routine assumes the list is already locked upon entry. + */ + ListNode p; + ListIterator i; + + assert(l != NULL); + assert(l->magic == LIST_MAGIC); + assert(list_mutex_is_locked(&l->mutex)); + assert(pp != NULL); + assert(x != NULL); + if (!(p = list_node_alloc())) + return(lsd_nomem_error(__FILE__, __LINE__, "list node create")); + p->data = x; + if (!(p->next = *pp)) + l->tail = &p->next; + *pp = p; + l->count++; + for (i=l->iNext; i; i=i->iNext) { + assert(i->magic == LIST_MAGIC); + if (i->prev == pp) + i->prev = &p->next; + else if (i->pos == p->next) + i->pos = p; + assert((i->pos == *i->prev) || (i->pos == (*i->prev)->next)); + } + return(x); +} + + +static void * +list_node_destroy (List l, ListNode *pp) +{ +/* Removes the node pointed to by [*pp] from from list [l], + * where [pp] is the address of the previous node's "next" ptr. + * Returns the data ptr associated with list item being removed, + * or NULL if [*pp] points to the NULL element. + * This routine assumes the list is already locked upon entry. + */ + void *v; + ListNode p; + ListIterator i; + + assert(l != NULL); + assert(l->magic == LIST_MAGIC); + assert(list_mutex_is_locked(&l->mutex)); + assert(pp != NULL); + if (!(p = *pp)) + return(NULL); + v = p->data; + if (!(*pp = p->next)) + l->tail = pp; + l->count--; + for (i=l->iNext; i; i=i->iNext) { + assert(i->magic == LIST_MAGIC); + if (i->pos == p) + i->pos = p->next, i->prev = pp; + else if (i->prev == &p->next) + i->prev = pp; + assert((i->pos == *i->prev) || (i->pos == (*i->prev)->next)); + } + list_node_free(p); + return(v); +} + + +static List +list_alloc (void) +{ + return(list_alloc_aux(sizeof(struct list), &list_free_lists)); +} + + +static void +list_free (List l) +{ + list_free_aux(l, &list_free_lists); + return; +} + + +static ListNode +list_node_alloc (void) +{ + return(list_alloc_aux(sizeof(struct listNode), &list_free_nodes)); +} + + +static void +list_node_free (ListNode p) +{ + list_free_aux(p, &list_free_nodes); + return; +} + + +static ListIterator +list_iterator_alloc (void) +{ + return(list_alloc_aux(sizeof(struct listIterator), &list_free_iterators)); +} + + +static void +list_iterator_free (ListIterator i) +{ + list_free_aux(i, &list_free_iterators); + return; +} + + +static void * +list_alloc_aux (int size, void *pfreelist) +{ +/* Allocates an object of [size] bytes from the freelist [*pfreelist]. + * Memory is added to the freelist in chunks of size LIST_ALLOC. + * Returns a ptr to the object, or NULL if the memory request fails. + */ + void **px; + void **pfree = pfreelist; + void **plast; + + assert(sizeof(char) == 1); + assert(size >= sizeof(void *)); + assert(pfreelist != NULL); + assert(LIST_ALLOC > 0); + list_mutex_lock(&list_free_lock); + if (!*pfree) { + if ((*pfree = malloc(LIST_ALLOC * size))) { + px = *pfree; + plast = (void **) ((char *) *pfree + ((LIST_ALLOC - 1) * size)); + while (px < plast) + *px = (char *) px + size, px = *px; + *plast = NULL; + } + } + if ((px = *pfree)) + *pfree = *px; + else + errno = ENOMEM; + list_mutex_unlock(&list_free_lock); + return(px); +} + + +static void +list_free_aux (void *x, void *pfreelist) +{ +/* Frees the object [x], returning it to the freelist [*pfreelist]. + */ + void **px = x; + void **pfree = pfreelist; + + assert(x != NULL); + assert(pfreelist != NULL); + list_mutex_lock(&list_free_lock); + *px = *pfree; + *pfree = px; + list_mutex_unlock(&list_free_lock); + return; +} + + +#ifndef NDEBUG +#ifdef WITH_PTHREADS +static int +list_mutex_is_locked (pthread_mutex_t *mutex) +{ +/* Returns true if the mutex is locked; o/w, returns false. + */ + int rc; + + assert(mutex != NULL); + rc = pthread_mutex_trylock(mutex); + return(rc == EBUSY ? 1 : 0); +} +#endif /* WITH_PTHREADS */ +#endif /* !NDEBUG */ diff --git a/src/spl/Makefile.in b/src/spl/Makefile.in index d052fc3701..8a8674d6e6 100644 --- a/src/spl/Makefile.in +++ b/src/spl/Makefile.in @@ -5,8 +5,9 @@ MODULES := spl DISTFILES = Makefile.in \ linux-kmem.c linux-rwlock.c linux-taskq.c linux-thread.c +EXTRA_CFLAGS += -I$(src) + # Removed '-std=gnu99' does to compile issues with i386 SPIN_LOCK_UNLOCKED -# EXTRA_CFLAGS += -I$(src) # EXTRA_CFLAGS += -Wall -Wno-unknown-pragmas -Wno-missing-braces \ # -Wno-sign-compare -Wno-parentheses -Wno-uninitialized \ # -Wno-implicit-function-declaration -Wno-unused -Wno-trigraphs \ diff --git a/src/spl/linux-kmem.c b/src/spl/linux-kmem.c index 7de2b211d2..e5ebdc1753 100644 --- a/src/spl/linux-kmem.c +++ b/src/spl/linux-kmem.c @@ -1,4 +1,4 @@ -#include +#include "linux-kmem.h" /* * Memory allocation interfaces