X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Fobdclass%2Fcl_object.c;h=e400f308cdf05262bb4e301e2ec845579bfae859;hb=65e067d5d90270d4237a7271008561a4b432b94d;hp=3d6ef8c926d8e681d715fb9f667e0b74fca5467c;hpb=3b4c006b28c9d6a7c3b00535cd3a6292178fa4c6;p=fs%2Flustre-release.git diff --git a/lustre/obdclass/cl_object.c b/lustre/obdclass/cl_object.c index 3d6ef8c..e400f30 100644 --- a/lustre/obdclass/cl_object.c +++ b/lustre/obdclass/cl_object.c @@ -1,6 +1,4 @@ -/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- - * vim:expandtab:shiftwidth=8:tabstop=8: - * +/* * GPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -26,8 +24,10 @@ * GPL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Use is subject to license terms. + * + * Copyright (c) 2011, 2014, Intel Corporation. */ /* * This file is part of Lustre, http://www.lustre.org/ @@ -36,6 +36,7 @@ * Client Lustre Object. * * Author: Nikita Danilov + * Author: Jinshan Xiong */ /* @@ -43,16 +44,11 @@ * * i_mutex * PG_locked - * ->coh_page_guard - * ->coh_lock_guard * ->coh_attr_guard * ->ls_guard */ #define DEBUG_SUBSYSTEM S_CLASS -#ifndef EXPORT_SYMTAB -# define EXPORT_SYMTAB -#endif #include /* class_put_type() */ @@ -61,18 +57,12 @@ #include #include #include /* for cfs_hash stuff */ -/* lu_time_global_{init,fini}() */ -#include - #include +#include #include "cl_internal.h" -static cfs_mem_cache_t *cl_env_kmem; +static struct kmem_cache *cl_env_kmem; -/** Lock class of cl_object_header::coh_page_guard */ -static struct lock_class_key cl_page_guard_class; -/** Lock class of cl_object_header::coh_lock_guard */ -static struct lock_class_key cl_lock_guard_class; /** Lock class of cl_object_header::coh_attr_guard */ static struct lock_class_key cl_attr_guard_class; @@ -81,23 +71,16 @@ static struct lock_class_key cl_attr_guard_class; */ int cl_object_header_init(struct cl_object_header *h) { - int result; - - ENTRY; - result = lu_object_header_init(&h->coh_lu); - if (result == 0) { - spin_lock_init(&h->coh_page_guard); - spin_lock_init(&h->coh_lock_guard); - spin_lock_init(&h->coh_attr_guard); - lockdep_set_class(&h->coh_attr_guard, &cl_page_guard_class); - lockdep_set_class(&h->coh_attr_guard, &cl_lock_guard_class); - lockdep_set_class(&h->coh_attr_guard, &cl_attr_guard_class); - h->coh_pages = 0; - /* XXX hard coded GFP_* mask. */ - INIT_RADIX_TREE(&h->coh_tree, GFP_ATOMIC); - CFS_INIT_LIST_HEAD(&h->coh_locks); - } - RETURN(result); + int result; + + ENTRY; + result = lu_object_header_init(&h->coh_lu); + if (result == 0) { + spin_lock_init(&h->coh_attr_guard); + lockdep_set_class(&h->coh_attr_guard, &cl_attr_guard_class); + h->coh_page_bufsize = 0; + } + RETURN(result); } EXPORT_SYMBOL(cl_object_header_init); @@ -106,7 +89,6 @@ EXPORT_SYMBOL(cl_object_header_init); */ void cl_object_header_fini(struct cl_object_header *h) { - LASSERT(list_empty(&h->coh_locks)); lu_object_header_fini(&h->coh_lu); } EXPORT_SYMBOL(cl_object_header_fini); @@ -123,7 +105,7 @@ struct cl_object *cl_object_find(const struct lu_env *env, struct cl_device *cd, const struct lu_fid *fid, const struct cl_object_conf *c) { - might_sleep(); + might_sleep(); return lu2cl(lu_object_find_slice(env, cl2lu_dev(cd), fid, &c->coc_lu)); } EXPORT_SYMBOL(cl_object_find); @@ -159,7 +141,7 @@ EXPORT_SYMBOL(cl_object_get); /** * Returns the top-object for a given \a o. * - * \see cl_page_top(), cl_io_top() + * \see cl_io_top() */ struct cl_object *cl_object_top(struct cl_object *o) { @@ -186,7 +168,7 @@ EXPORT_SYMBOL(cl_object_top); */ static spinlock_t *cl_object_attr_guard(struct cl_object *o) { - return &cl_object_header(cl_object_top(o))->coh_attr_guard; + return &cl_object_header(cl_object_top(o))->coh_attr_guard; } /** @@ -194,11 +176,12 @@ static spinlock_t *cl_object_attr_guard(struct cl_object *o) * * Prevents data-attributes from changing, until lock is released by * cl_object_attr_unlock(). This has to be called before calls to - * cl_object_attr_get(), cl_object_attr_set(). + * cl_object_attr_get(), cl_object_attr_update(). */ void cl_object_attr_lock(struct cl_object *o) +__acquires(cl_object_attr_guard(o)) { - spin_lock(cl_object_attr_guard(o)); + spin_lock(cl_object_attr_guard(o)); } EXPORT_SYMBOL(cl_object_attr_lock); @@ -206,8 +189,9 @@ EXPORT_SYMBOL(cl_object_attr_lock); * Releases data-attributes lock, acquired by cl_object_attr_lock(). */ void cl_object_attr_unlock(struct cl_object *o) +__releases(cl_object_attr_guard(o)) { - spin_unlock(cl_object_attr_guard(o)); + spin_unlock(cl_object_attr_guard(o)); } EXPORT_SYMBOL(cl_object_attr_unlock); @@ -221,15 +205,15 @@ EXPORT_SYMBOL(cl_object_attr_unlock); int cl_object_attr_get(const struct lu_env *env, struct cl_object *obj, struct cl_attr *attr) { - struct lu_object_header *top; - int result; + struct lu_object_header *top; + int result; - LASSERT_SPIN_LOCKED(cl_object_attr_guard(obj)); - ENTRY; + assert_spin_locked(cl_object_attr_guard(obj)); + ENTRY; top = obj->co_lu.lo_header; result = 0; - list_for_each_entry(obj, &top->loh_layers, co_lu.lo_linkage) { + list_for_each_entry(obj, &top->loh_layers, co_lu.lo_linkage) { if (obj->co_ops->coo_attr_get != NULL) { result = obj->co_ops->coo_attr_get(env, obj, attr); if (result != 0) { @@ -247,33 +231,34 @@ EXPORT_SYMBOL(cl_object_attr_get); * Updates data-attributes of an object \a obj. * * Only attributes, mentioned in a validness bit-mask \a v are - * updated. Calls cl_object_operations::coo_attr_set() on every layer, bottom + * updated. Calls cl_object_operations::coo_upd_attr() on every layer, bottom * to top. */ -int cl_object_attr_set(const struct lu_env *env, struct cl_object *obj, - const struct cl_attr *attr, unsigned v) +int cl_object_attr_update(const struct lu_env *env, struct cl_object *obj, + const struct cl_attr *attr, unsigned v) { - struct lu_object_header *top; - int result; - - LASSERT_SPIN_LOCKED(cl_object_attr_guard(obj)); - ENTRY; - - top = obj->co_lu.lo_header; - result = 0; - list_for_each_entry_reverse(obj, &top->loh_layers, co_lu.lo_linkage) { - if (obj->co_ops->coo_attr_set != NULL) { - result = obj->co_ops->coo_attr_set(env, obj, attr, v); - if (result != 0) { - if (result > 0) - result = 0; - break; - } - } - } - RETURN(result); + struct lu_object_header *top; + int result; + + assert_spin_locked(cl_object_attr_guard(obj)); + ENTRY; + + top = obj->co_lu.lo_header; + result = 0; + list_for_each_entry_reverse(obj, &top->loh_layers, co_lu.lo_linkage) { + if (obj->co_ops->coo_attr_update != NULL) { + result = obj->co_ops->coo_attr_update(env, obj, attr, + v); + if (result != 0) { + if (result > 0) + result = 0; + break; + } + } + } + RETURN(result); } -EXPORT_SYMBOL(cl_object_attr_set); +EXPORT_SYMBOL(cl_object_attr_update); /** * Notifies layers (bottom-to-top) that glimpse AST was received. @@ -292,7 +277,7 @@ int cl_object_glimpse(const struct lu_env *env, struct cl_object *obj, ENTRY; top = obj->co_lu.lo_header; result = 0; - list_for_each_entry_reverse(obj, &top->loh_layers, co_lu.lo_linkage) { + list_for_each_entry_reverse(obj, &top->loh_layers, co_lu.lo_linkage) { if (obj->co_ops->coo_glimpse != NULL) { result = obj->co_ops->coo_glimpse(env, obj, lvb); if (result != 0) @@ -320,7 +305,7 @@ int cl_conf_set(const struct lu_env *env, struct cl_object *obj, ENTRY; top = obj->co_lu.lo_header; result = 0; - list_for_each_entry(obj, &top->loh_layers, co_lu.lo_linkage) { + list_for_each_entry(obj, &top->loh_layers, co_lu.lo_linkage) { if (obj->co_ops->coo_conf_set != NULL) { result = obj->co_ops->coo_conf_set(env, obj, conf); if (result != 0) @@ -332,92 +317,101 @@ int cl_conf_set(const struct lu_env *env, struct cl_object *obj, EXPORT_SYMBOL(cl_conf_set); /** - * Helper function removing all object locks, and marking object for - * deletion. All object pages must have been deleted at this point. - * - * This is called by cl_inode_fini() and lov_object_delete() to destroy top- - * and sub- objects respectively. + * Prunes caches of pages and locks for this object. */ -void cl_object_kill(const struct lu_env *env, struct cl_object *obj) +int cl_object_prune(const struct lu_env *env, struct cl_object *obj) { - struct cl_object_header *hdr; - - hdr = cl_object_header(obj); - LASSERT(hdr->coh_tree.rnode == NULL); - LASSERT(hdr->coh_pages == 0); - - set_bit(LU_OBJECT_HEARD_BANSHEE, &hdr->coh_lu.loh_flags); - /* - * Destroy all locks. Object destruction (including cl_inode_fini()) - * cannot cancel the locks, because in the case of a local client, - * where client and server share the same thread running - * prune_icache(), this can dead-lock with ldlm_cancel_handler() - * waiting on __wait_on_freeing_inode(). - */ - cl_locks_prune(env, obj, 0); + struct lu_object_header *top; + struct cl_object *o; + int result; + ENTRY; + + top = obj->co_lu.lo_header; + result = 0; + list_for_each_entry(o, &top->loh_layers, co_lu.lo_linkage) { + if (o->co_ops->coo_prune != NULL) { + result = o->co_ops->coo_prune(env, o); + if (result != 0) + break; + } + } + + RETURN(result); } -EXPORT_SYMBOL(cl_object_kill); +EXPORT_SYMBOL(cl_object_prune); /** - * Prunes caches of pages and locks for this object. + * Get stripe information of this object. */ -void cl_object_prune(const struct lu_env *env, struct cl_object *obj) +int cl_object_getstripe(const struct lu_env *env, struct cl_object *obj, + struct lov_user_md __user *uarg) { - ENTRY; - cl_pages_prune(env, obj); - cl_locks_prune(env, obj, 1); - EXIT; + struct lu_object_header *top; + int result = 0; + ENTRY; + + top = obj->co_lu.lo_header; + list_for_each_entry(obj, &top->loh_layers, co_lu.lo_linkage) { + if (obj->co_ops->coo_getstripe != NULL) { + result = obj->co_ops->coo_getstripe(env, obj, uarg); + if (result != 0) + break; + } + } + RETURN(result); } -EXPORT_SYMBOL(cl_object_prune); +EXPORT_SYMBOL(cl_object_getstripe); /** - * Check if the object has locks. + * Helper function removing all object locks, and marking object for + * deletion. All object pages must have been deleted at this point. + * + * This is called by cl_inode_fini() and lov_object_delete() to destroy top- + * and sub- objects respectively. */ -int cl_object_has_locks(struct cl_object *obj) +void cl_object_kill(const struct lu_env *env, struct cl_object *obj) { - struct cl_object_header *head = cl_object_header(obj); - int has; - - spin_lock(&head->coh_lock_guard); - has = list_empty(&head->coh_locks); - spin_unlock(&head->coh_lock_guard); + struct cl_object_header *hdr = cl_object_header(obj); - return (has == 0); + set_bit(LU_OBJECT_HEARD_BANSHEE, &hdr->coh_lu.loh_flags); } -EXPORT_SYMBOL(cl_object_has_locks); +EXPORT_SYMBOL(cl_object_kill); void cache_stats_init(struct cache_stats *cs, const char *name) { + int i; + cs->cs_name = name; - atomic_set(&cs->cs_lookup, 0); - atomic_set(&cs->cs_hit, 0); - atomic_set(&cs->cs_total, 0); - atomic_set(&cs->cs_busy, 0); + for (i = 0; i < CS_NR; i++) + atomic_set(&cs->cs_stats[i], 0); } -int cache_stats_print(const struct cache_stats *cs, - char *page, int count, int h) +static int cache_stats_print(const struct cache_stats *cs, + struct seq_file *m, int h) { - int nob = 0; -/* - lookup hit total cached create - env: ...... ...... ...... ...... ...... -*/ - if (h) - nob += snprintf(page, count, - " lookup hit total busy create\n"); - - nob += snprintf(page + nob, count - nob, - "%5.5s: %6u %6u %6u %6u %6u", - cs->cs_name, - atomic_read(&cs->cs_lookup), - atomic_read(&cs->cs_hit), - atomic_read(&cs->cs_total), - atomic_read(&cs->cs_busy), - atomic_read(&cs->cs_created)); - return nob; + int i; + + /* + * lookup hit total cached create + * env: ...... ...... ...... ...... ...... + */ + if (h) { + const char *names[CS_NR] = CS_NAMES; + + seq_printf(m, "%6s", " "); + for (i = 0; i < CS_NR; i++) + seq_printf(m, "%8s", names[i]); + seq_printf(m, "\n"); + } + + seq_printf(m, "%5.5s:", cs->cs_name); + for (i = 0; i < CS_NR; i++) + seq_printf(m, "%8u", atomic_read(&cs->cs_stats[i])); + return 0; } +static void cl_env_percpu_refill(void); + /** * Initialize client site. * @@ -426,19 +420,17 @@ int cache_stats_print(const struct cache_stats *cs, */ int cl_site_init(struct cl_site *s, struct cl_device *d) { - int i; + size_t i; int result; result = lu_site_init(&s->cs_lu, &d->cd_lu_dev); if (result == 0) { cache_stats_init(&s->cs_pages, "pages"); - cache_stats_init(&s->cs_locks, "locks"); for (i = 0; i < ARRAY_SIZE(s->cs_pages_state); ++i) - atomic_set(&s->cs_pages_state[0], 0); - for (i = 0; i < ARRAY_SIZE(s->cs_locks_state); ++i) - atomic_set(&s->cs_locks_state[i], 0); - } - return result; + atomic_set(&s->cs_pages_state[0], 0); + cl_env_percpu_refill(); + } + return result; } EXPORT_SYMBOL(cl_site_init); @@ -453,61 +445,40 @@ EXPORT_SYMBOL(cl_site_fini); static struct cache_stats cl_env_stats = { .cs_name = "envs", - .cs_created = ATOMIC_INIT(0), - .cs_lookup = ATOMIC_INIT(0), - .cs_hit = ATOMIC_INIT(0), - .cs_total = ATOMIC_INIT(0), - .cs_busy = ATOMIC_INIT(0) + .cs_stats = { ATOMIC_INIT(0), } }; /** * Outputs client site statistical counters into a buffer. Suitable for * ll_rd_*()-style functions. */ -int cl_site_stats_print(const struct cl_site *site, char *page, int count) -{ - int nob; - int i; - static const char *pstate[] = { - [CPS_CACHED] = "c", - [CPS_OWNED] = "o", - [CPS_PAGEOUT] = "w", - [CPS_PAGEIN] = "r", - [CPS_FREEING] = "f" - }; - static const char *lstate[] = { - [CLS_NEW] = "n", - [CLS_QUEUING] = "q", - [CLS_ENQUEUED] = "e", - [CLS_HELD] = "h", - [CLS_INTRANSIT] = "t", - [CLS_CACHED] = "c", - [CLS_FREEING] = "f" - }; +int cl_site_stats_print(const struct cl_site *site, struct seq_file *m) +{ + static const char *pstate[] = { + [CPS_CACHED] = "c", + [CPS_OWNED] = "o", + [CPS_PAGEOUT] = "w", + [CPS_PAGEIN] = "r", + [CPS_FREEING] = "f" + }; + size_t i; + /* lookup hit total busy create pages: ...... ...... ...... ...... ...... [...... ...... ...... ......] locks: ...... ...... ...... ...... ...... [...... ...... ...... ...... ......] env: ...... ...... ...... ...... ...... */ - nob = lu_site_stats_print(&site->cs_lu, page, count); - nob += cache_stats_print(&site->cs_pages, page + nob, count - nob, 1); - nob += snprintf(page + nob, count - nob, " ["); - for (i = 0; i < ARRAY_SIZE(site->cs_pages_state); ++i) - nob += snprintf(page + nob, count - nob, "%s: %u ", - pstate[i], - atomic_read(&site->cs_pages_state[i])); - nob += snprintf(page + nob, count - nob, "]\n"); - nob += cache_stats_print(&site->cs_locks, page + nob, count - nob, 0); - nob += snprintf(page + nob, count - nob, " ["); - for (i = 0; i < ARRAY_SIZE(site->cs_locks_state); ++i) - nob += snprintf(page + nob, count - nob, "%s: %u ", - lstate[i], - atomic_read(&site->cs_locks_state[i])); - nob += snprintf(page + nob, count - nob, "]\n"); - nob += cache_stats_print(&cl_env_stats, page + nob, count - nob, 0); - nob += snprintf(page + nob, count - nob, "\n"); - return nob; + lu_site_stats_seq_print(&site->cs_lu, m); + cache_stats_print(&site->cs_pages, m, 1); + seq_printf(m, " ["); + for (i = 0; i < ARRAY_SIZE(site->cs_pages_state); ++i) + seq_printf(m, "%s: %u ", pstate[i], + atomic_read(&site->cs_pages_state[i])); + seq_printf(m, "]\n"); + cache_stats_print(&cl_env_stats, m, 0); + seq_printf(m, "\n"); + return 0; } EXPORT_SYMBOL(cl_site_stats_print); @@ -517,31 +488,62 @@ EXPORT_SYMBOL(cl_site_stats_print); * */ -static CFS_LIST_HEAD(cl_envs); +/** + * The most efficient way is to store cl_env pointer in task specific + * structures. On Linux, it wont' be easy to use task_struct->journal_info + * because Lustre code may call into other fs which has certain assumptions + * about journal_info. Currently following fields in task_struct are identified + * can be used for this purpose: + * - cl_env: for liblustre. + * - tux_info: ony on RedHat kernel. + * - ... + * \note As long as we use task_struct to store cl_env, we assume that once + * called into Lustre, we'll never call into the other part of the kernel + * which will use those fields in task_struct without explicitly exiting + * Lustre. + * + * If there's no space in task_struct is available, hash will be used. + * bz20044, bz22683. + */ + +static struct list_head cl_envs; static unsigned cl_envs_cached_nr = 0; static unsigned cl_envs_cached_max = 128; /* XXX: prototype: arbitrary limit * for now. */ -static spinlock_t cl_envs_guard = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(cl_envs_guard); struct cl_env { void *ce_magic; struct lu_env ce_lu; struct lu_context ce_ses; + +#ifdef LL_TASK_CL_ENV + void *ce_prev; +#else /** * This allows cl_env to be entered into cl_env_hash which implements * the current thread -> client environment lookup. */ - struct hlist_node ce_node; - /** - * Owner for the current cl_env, the key for cfs_hash. - * Now current thread pointer is stored. - */ - void *ce_owner; + struct hlist_node ce_node; +#endif + /** + * Owner for the current cl_env. + * + * If LL_TASK_CL_ENV is defined, this point to the owning current, + * only for debugging purpose ; + * Otherwise hash is used, and this is the key for cfs_hash. + * Now current thread pid is stored. Note using thread pointer would + * lead to unbalanced hash because of its specific allocation locality + * and could be varied for different platforms and OSes, even different + * OS versions. + */ + void *ce_owner; + /* * Linkage into global list of all client environments. Used for * garbage collection. */ - struct list_head ce_linkage; + struct list_head ce_linkage; /* * */ @@ -553,24 +555,39 @@ struct cl_env { void *ce_debug; }; -#define CL_ENV_INC(counter) atomic_inc(&cl_env_stats.counter) +#ifdef CONFIG_DEBUG_PAGESTATE_TRACKING +#define CL_ENV_INC(counter) atomic_inc(&cl_env_stats.cs_stats[CS_##counter]) -#define CL_ENV_DEC(counter) \ - do { \ - LASSERT(atomic_read(&cl_env_stats.counter) > 0); \ - atomic_dec(&cl_env_stats.counter); \ - } while (0) +#define CL_ENV_DEC(counter) do { \ + LASSERT(atomic_read(&cl_env_stats.cs_stats[CS_##counter]) > 0); \ + atomic_dec(&cl_env_stats.cs_stats[CS_##counter]); \ +} while (0) +#else +#define CL_ENV_INC(counter) +#define CL_ENV_DEC(counter) +#endif -/***************************************************************************** - * Routins to use cfs_hash functionality to bind the current thread - * to cl_env +static void cl_env_init0(struct cl_env *cle, void *debug) +{ + LASSERT(cle->ce_ref == 0); + LASSERT(cle->ce_magic == &cl_env_init0); + LASSERT(cle->ce_debug == NULL && cle->ce_owner == NULL); + + cle->ce_ref = 1; + cle->ce_debug = debug; + CL_ENV_INC(busy); +} + + +#ifndef LL_TASK_CL_ENV +/* + * The implementation of using hash table to connect cl_env and thread */ -/** lustre hash to manage the cl_env for current thread */ static cfs_hash_t *cl_env_hash; -static void cl_env_init0(struct cl_env *cle, void *debug); -static unsigned cl_env_hops_hash(cfs_hash_t *lh, void *key, unsigned mask) +static unsigned cl_env_hops_hash(cfs_hash_t *lh, + const void *key, unsigned mask) { #if BITS_PER_LONG == 64 return cfs_hash_u64_hash((__u64)key, mask); @@ -581,12 +598,13 @@ static unsigned cl_env_hops_hash(cfs_hash_t *lh, void *key, unsigned mask) static void *cl_env_hops_obj(struct hlist_node *hn) { - struct cl_env *cle = hlist_entry(hn, struct cl_env, ce_node); - LASSERT(cle->ce_magic == &cl_env_init0); - return (void *)cle; + struct cl_env *cle = hlist_entry(hn, struct cl_env, ce_node); + + LASSERT(cle->ce_magic == &cl_env_init0); + return (void *)cle; } -static int cl_env_hops_compare(void *key, struct hlist_node *hn) +static int cl_env_hops_keycmp(const void *key, struct hlist_node *hn) { struct cl_env *cle = cl_env_hops_obj(hn); @@ -594,98 +612,156 @@ static int cl_env_hops_compare(void *key, struct hlist_node *hn) return (key == cle->ce_owner); } +static void cl_env_hops_noop(cfs_hash_t *hs, struct hlist_node *hn) +{ + struct cl_env *cle = hlist_entry(hn, struct cl_env, ce_node); + LASSERT(cle->ce_magic == &cl_env_init0); +} + static cfs_hash_ops_t cl_env_hops = { - .hs_hash = cl_env_hops_hash, - .hs_compare = cl_env_hops_compare, - .hs_key = cl_env_hops_obj, - .hs_get = cl_env_hops_obj, - .hs_put = cl_env_hops_obj, + .hs_hash = cl_env_hops_hash, + .hs_key = cl_env_hops_obj, + .hs_keycmp = cl_env_hops_keycmp, + .hs_object = cl_env_hops_obj, + .hs_get = cl_env_hops_noop, + .hs_put_locked = cl_env_hops_noop, }; static inline struct cl_env *cl_env_fetch(void) { - struct cl_env *cle; - cle = cfs_hash_lookup(cl_env_hash, cfs_current()); - LASSERT(ergo(cle, cle->ce_magic == &cl_env_init0)); - return cle; + struct cl_env *cle; + + cle = cfs_hash_lookup(cl_env_hash, (void *) (long) current->pid); + LASSERT(ergo(cle, cle->ce_magic == &cl_env_init0)); + return cle; } static inline void cl_env_attach(struct cl_env *cle) { - if (cle) { - int rc; - LASSERT(cle->ce_owner == NULL); - cle->ce_owner = cfs_current(); - rc = cfs_hash_add_unique(cl_env_hash, cle->ce_owner, - &cle->ce_node); - LASSERT(rc == 0); - } + if (cle) { + int rc; + + LASSERT(cle->ce_owner == NULL); + cle->ce_owner = (void *) (long) current->pid; + rc = cfs_hash_add_unique(cl_env_hash, cle->ce_owner, + &cle->ce_node); + LASSERT(rc == 0); + } } -static inline struct cl_env *cl_env_detach(struct cl_env *cle) +static inline void cl_env_do_detach(struct cl_env *cle) { - if (cle == NULL) - cle = cl_env_fetch(); - if (cle && cle->ce_owner) { - void *cookie; - LASSERT(cle->ce_owner == cfs_current()); - cookie = cfs_hash_del(cl_env_hash, cle->ce_owner, - &cle->ce_node); - cle->ce_owner = NULL; - LASSERT(cookie == cle); - } - return cle; + void *cookie; + + LASSERT(cle->ce_owner == (void *) (long) current->pid); + cookie = cfs_hash_del(cl_env_hash, cle->ce_owner, + &cle->ce_node); + LASSERT(cookie == cle); + cle->ce_owner = NULL; } -/* ----------------------- hash routines end ---------------------------- */ -static void cl_env_init0(struct cl_env *cle, void *debug) +static int cl_env_store_init(void) { + cl_env_hash = cfs_hash_create("cl_env", + HASH_CL_ENV_BITS, HASH_CL_ENV_BITS, + HASH_CL_ENV_BKT_BITS, 0, + CFS_HASH_MIN_THETA, + CFS_HASH_MAX_THETA, + &cl_env_hops, + CFS_HASH_RW_BKTLOCK); + return cl_env_hash != NULL ? 0 :-ENOMEM; +} + +static void cl_env_store_fini(void) { + cfs_hash_putref(cl_env_hash); +} + +#else /* LL_TASK_CL_ENV */ +/* + * The implementation of store cl_env directly in thread structure. + */ + +static inline struct cl_env *cl_env_fetch(void) { - LASSERT(cle->ce_ref == 0); - LASSERT(cle->ce_magic == &cl_env_init0); - LASSERT(cle->ce_debug == NULL && cle->ce_owner == NULL); + struct cl_env *cle; - cle->ce_ref = 1; - cle->ce_debug = debug; - CL_ENV_INC(cs_busy); + cle = current->LL_TASK_CL_ENV; + if (cle && cle->ce_magic != &cl_env_init0) + cle = NULL; + return cle; } -static struct lu_env *cl_env_new(__u32 tags, void *debug) +static inline void cl_env_attach(struct cl_env *cle) { - struct lu_env *env; - struct cl_env *cle; + if (cle) { + LASSERT(cle->ce_owner == NULL); + cle->ce_owner = current; + cle->ce_prev = current->LL_TASK_CL_ENV; + current->LL_TASK_CL_ENV = cle; + } +} - OBD_SLAB_ALLOC_PTR_GFP(cle, cl_env_kmem, CFS_ALLOC_IO); - if (cle != NULL) { - int rc; +static inline void cl_env_do_detach(struct cl_env *cle) +{ + LASSERT(cle->ce_owner == current); + LASSERT(current->LL_TASK_CL_ENV == cle); + current->LL_TASK_CL_ENV = cle->ce_prev; + cle->ce_owner = NULL; +} - CFS_INIT_LIST_HEAD(&cle->ce_linkage); - cle->ce_magic = &cl_env_init0; - env = &cle->ce_lu; - rc = lu_env_init(env, LCT_CL_THREAD|tags); - if (rc == 0) { - rc = lu_context_init(&cle->ce_ses, LCT_SESSION|tags); - if (rc == 0) { - lu_context_enter(&cle->ce_ses); - env->le_ses = &cle->ce_ses; - cl_env_init0(cle, debug); - } else - lu_env_fini(env); - } - if (rc != 0) { - OBD_SLAB_FREE_PTR(cle, cl_env_kmem); - env = ERR_PTR(rc); - } else { - CL_ENV_INC(cs_created); - CL_ENV_INC(cs_total); - } - } else - env = ERR_PTR(-ENOMEM); - return env; +static int cl_env_store_init(void) { return 0; } +static void cl_env_store_fini(void) { } + +#endif /* LL_TASK_CL_ENV */ + +static inline struct cl_env *cl_env_detach(struct cl_env *cle) +{ + if (cle == NULL) + cle = cl_env_fetch(); + + if (cle && cle->ce_owner) + cl_env_do_detach(cle); + + return cle; +} + +static struct lu_env *cl_env_new(__u32 ctx_tags, __u32 ses_tags, void *debug) +{ + struct lu_env *env; + struct cl_env *cle; + + OBD_SLAB_ALLOC_PTR_GFP(cle, cl_env_kmem, GFP_NOFS); + if (cle != NULL) { + int rc; + + INIT_LIST_HEAD(&cle->ce_linkage); + cle->ce_magic = &cl_env_init0; + env = &cle->ce_lu; + rc = lu_env_init(env, LCT_CL_THREAD|ctx_tags); + if (rc == 0) { + rc = lu_context_init(&cle->ce_ses, + LCT_SESSION | ses_tags); + if (rc == 0) { + lu_context_enter(&cle->ce_ses); + env->le_ses = &cle->ce_ses; + cl_env_init0(cle, debug); + } else + lu_env_fini(env); + } + if (rc != 0) { + OBD_SLAB_FREE_PTR(cle, cl_env_kmem); + env = ERR_PTR(rc); + } else { + CL_ENV_INC(create); + CL_ENV_INC(total); + } + } else + env = ERR_PTR(-ENOMEM); + return env; } static void cl_env_fini(struct cl_env *cle) { - CL_ENV_DEC(cs_total); + CL_ENV_DEC(total); lu_context_fini(&cle->ce_lu.le_ctx); lu_context_fini(&cle->ce_ses); OBD_SLAB_FREE_PTR(cle, cl_env_kmem); @@ -693,19 +769,19 @@ static void cl_env_fini(struct cl_env *cle) static struct lu_env *cl_env_obtain(void *debug) { - struct cl_env *cle; - struct lu_env *env; + struct cl_env *cle; + struct lu_env *env; - ENTRY; - spin_lock(&cl_envs_guard); - LASSERT(equi(cl_envs_cached_nr == 0, list_empty(&cl_envs))); - if (cl_envs_cached_nr > 0) { - int rc; + ENTRY; + spin_lock(&cl_envs_guard); + LASSERT(equi(cl_envs_cached_nr == 0, list_empty(&cl_envs))); + if (cl_envs_cached_nr > 0) { + int rc; - cle = container_of(cl_envs.next, struct cl_env, ce_linkage); - list_del_init(&cle->ce_linkage); - cl_envs_cached_nr--; - spin_unlock(&cl_envs_guard); + cle = container_of(cl_envs.next, struct cl_env, ce_linkage); + list_del_init(&cle->ce_linkage); + cl_envs_cached_nr--; + spin_unlock(&cl_envs_guard); env = &cle->ce_lu; rc = lu_env_refill(env); @@ -718,10 +794,11 @@ static struct lu_env *cl_env_obtain(void *debug) env = ERR_PTR(rc); } } else { - spin_unlock(&cl_envs_guard); - env = cl_env_new(0, debug); - } - RETURN(env); + spin_unlock(&cl_envs_guard); + env = cl_env_new(lu_context_tags_default, + lu_session_tags_default, debug); + } + RETURN(env); } static inline struct cl_env *cl_env_container(struct lu_env *env) @@ -734,7 +811,7 @@ struct lu_env *cl_env_peek(int *refcheck) struct lu_env *env; struct cl_env *cle; - CL_ENV_INC(cs_lookup); + CL_ENV_INC(lookup); /* check that we don't go far from untrusted pointer */ CLASSERT(offsetof(struct cl_env, ce_magic) == 0); @@ -742,11 +819,11 @@ struct lu_env *cl_env_peek(int *refcheck) env = NULL; cle = cl_env_fetch(); if (cle != NULL) { - CL_ENV_INC(cs_hit); + CL_ENV_INC(hit); env = &cle->ce_lu; *refcheck = ++cle->ce_ref; } - CDEBUG(D_OTHER, "%i@%p\n", cle ? cle->ce_ref : 0, cle); + CDEBUG(D_OTHER, "%d@%p\n", cle ? cle->ce_ref : 0, cle); return env; } EXPORT_SYMBOL(cl_env_peek); @@ -777,7 +854,7 @@ struct lu_env *cl_env_get(int *refcheck) cle = cl_env_container(env); cl_env_attach(cle); *refcheck = cle->ce_ref; - CDEBUG(D_OTHER, "%i@%p\n", cle->ce_ref, cle); + CDEBUG(D_OTHER, "%d@%p\n", cle->ce_ref, cle); } } return env; @@ -794,13 +871,13 @@ struct lu_env *cl_env_alloc(int *refcheck, __u32 tags) struct lu_env *env; LASSERT(cl_env_peek(refcheck) == NULL); - env = cl_env_new(tags, __builtin_return_address(0)); + env = cl_env_new(tags, tags, __builtin_return_address(0)); if (!IS_ERR(env)) { struct cl_env *cle; cle = cl_env_container(env); *refcheck = cle->ce_ref; - CDEBUG(D_OTHER, "%i@%p\n", cle->ce_ref, cle); + CDEBUG(D_OTHER, "%d@%p\n", cle->ce_ref, cle); } return env; } @@ -820,23 +897,23 @@ static void cl_env_exit(struct cl_env *cle) */ unsigned cl_env_cache_purge(unsigned nr) { - struct cl_env *cle; - - ENTRY; - spin_lock(&cl_envs_guard); - for (; !list_empty(&cl_envs) && nr > 0; --nr) { - cle = container_of(cl_envs.next, struct cl_env, ce_linkage); - list_del_init(&cle->ce_linkage); - LASSERT(cl_envs_cached_nr > 0); - cl_envs_cached_nr--; - spin_unlock(&cl_envs_guard); - - cl_env_fini(cle); - spin_lock(&cl_envs_guard); - } - LASSERT(equi(cl_envs_cached_nr == 0, list_empty(&cl_envs))); - spin_unlock(&cl_envs_guard); - RETURN(nr); + struct cl_env *cle; + + ENTRY; + spin_lock(&cl_envs_guard); + for (; !list_empty(&cl_envs) && nr > 0; --nr) { + cle = container_of(cl_envs.next, struct cl_env, ce_linkage); + list_del_init(&cle->ce_linkage); + LASSERT(cl_envs_cached_nr > 0); + cl_envs_cached_nr--; + spin_unlock(&cl_envs_guard); + + cl_env_fini(cle); + spin_lock(&cl_envs_guard); + } + LASSERT(equi(cl_envs_cached_nr == 0, list_empty(&cl_envs))); + spin_unlock(&cl_envs_guard); + RETURN(nr); } EXPORT_SYMBOL(cl_env_cache_purge); @@ -856,9 +933,9 @@ void cl_env_put(struct lu_env *env, int *refcheck) LASSERT(cle->ce_ref > 0); LASSERT(ergo(refcheck != NULL, cle->ce_ref == *refcheck)); - CDEBUG(D_OTHER, "%i@%p\n", cle->ce_ref, cle); + CDEBUG(D_OTHER, "%d@%p\n", cle->ce_ref, cle); if (--cle->ce_ref == 0) { - CL_ENV_DEC(cs_busy); + CL_ENV_DEC(busy); cl_env_detach(cle); cle->ce_debug = NULL; cl_env_exit(cle); @@ -871,13 +948,13 @@ void cl_env_put(struct lu_env *env, int *refcheck) if (cl_envs_cached_nr < cl_envs_cached_max && (env->le_ctx.lc_tags & ~LCT_HAS_EXIT) == LCT_CL_THREAD && (env->le_ses->lc_tags & ~LCT_HAS_EXIT) == LCT_SESSION) { - spin_lock(&cl_envs_guard); - list_add(&cle->ce_linkage, &cl_envs); - cl_envs_cached_nr++; - spin_unlock(&cl_envs_guard); - } else - cl_env_fini(cle); - } + spin_lock(&cl_envs_guard); + list_add(&cle->ce_linkage, &cl_envs); + cl_envs_cached_nr++; + spin_unlock(&cl_envs_guard); + } else + cl_env_fini(cle); + } } EXPORT_SYMBOL(cl_env_put); @@ -917,7 +994,7 @@ void cl_env_implant(struct lu_env *env, int *refcheck) cl_env_attach(cle); cl_env_get(refcheck); - CDEBUG(D_OTHER, "%i@%p\n", cle->ce_ref, cle); + CDEBUG(D_OTHER, "%d@%p\n", cle->ce_ref, cle); } EXPORT_SYMBOL(cl_env_implant); @@ -930,7 +1007,7 @@ void cl_env_unplant(struct lu_env *env, int *refcheck) LASSERT(cle->ce_ref > 1); - CDEBUG(D_OTHER, "%i@%p\n", cle->ce_ref, cle); + CDEBUG(D_OTHER, "%d@%p\n", cle->ce_ref, cle); cl_env_detach(cle); cl_env_put(env, refcheck); @@ -1003,6 +1080,103 @@ void cl_lvb2attr(struct cl_attr *attr, const struct ost_lvb *lvb) } EXPORT_SYMBOL(cl_lvb2attr); +static struct cl_env cl_env_percpu[NR_CPUS]; + +static int cl_env_percpu_init(void) +{ + struct cl_env *cle; + int tags = LCT_REMEMBER | LCT_NOREF; + int i, j; + int rc = 0; + + for_each_possible_cpu(i) { + struct lu_env *env; + + cle = &cl_env_percpu[i]; + env = &cle->ce_lu; + + INIT_LIST_HEAD(&cle->ce_linkage); + cle->ce_magic = &cl_env_init0; + rc = lu_env_init(env, LCT_CL_THREAD | tags); + if (rc == 0) { + rc = lu_context_init(&cle->ce_ses, LCT_SESSION | tags); + if (rc == 0) { + lu_context_enter(&cle->ce_ses); + env->le_ses = &cle->ce_ses; + } else { + lu_env_fini(env); + } + } + if (rc != 0) + break; + } + if (rc != 0) { + /* Indices 0 to i (excluding i) were correctly initialized, + * thus we must uninitialize up to i, the rest are undefined. */ + for (j = 0; j < i; j++) { + cle = &cl_env_percpu[i]; + lu_context_exit(&cle->ce_ses); + lu_context_fini(&cle->ce_ses); + lu_env_fini(&cle->ce_lu); + } + } + + return rc; +} + +static void cl_env_percpu_fini(void) +{ + int i; + + for_each_possible_cpu(i) { + struct cl_env *cle = &cl_env_percpu[i]; + + lu_context_exit(&cle->ce_ses); + lu_context_fini(&cle->ce_ses); + lu_env_fini(&cle->ce_lu); + } +} + +static void cl_env_percpu_refill(void) +{ + int i; + + for_each_possible_cpu(i) + lu_env_refill(&cl_env_percpu[i].ce_lu); +} + +void cl_env_percpu_put(struct lu_env *env) +{ + struct cl_env *cle; + int cpu; + + cpu = smp_processor_id(); + cle = cl_env_container(env); + LASSERT(cle == &cl_env_percpu[cpu]); + + cle->ce_ref--; + LASSERT(cle->ce_ref == 0); + + CL_ENV_DEC(busy); + cl_env_detach(cle); + cle->ce_debug = NULL; + + put_cpu(); +} +EXPORT_SYMBOL(cl_env_percpu_put); + +struct lu_env *cl_env_percpu_get() +{ + struct cl_env *cle; + + cle = &cl_env_percpu[get_cpu()]; + cl_env_init0(cle, __builtin_return_address(0)); + + cl_env_attach(cle); + return &cle->ce_lu; +} +EXPORT_SYMBOL(cl_env_percpu_get); + /***************************************************************************** * * Temporary prototype thing: mirror obd-devices into cl devices. @@ -1050,12 +1224,6 @@ void cl_stack_fini(const struct lu_env *env, struct cl_device *cl) } EXPORT_SYMBOL(cl_stack_fini); -int cl_lock_init(void); -void cl_lock_fini(void); - -int cl_page_init(void); -void cl_page_fini(void); - static struct lu_context_key cl_key; struct cl_thread_info *cl_env_info(const struct lu_env *env) @@ -1073,7 +1241,7 @@ static void *cl_key_init(const struct lu_context *ctx, info = cl0_key_init(ctx, key); if (!IS_ERR(info)) { - int i; + size_t i; for (i = 0; i < ARRAY_SIZE(info->clt_counters); ++i) lu_ref_init(&info->clt_counters[i].ctc_locks_locked); @@ -1084,8 +1252,8 @@ static void *cl_key_init(const struct lu_context *ctx, static void cl_key_fini(const struct lu_context *ctx, struct lu_context_key *key, void *data) { - struct cl_thread_info *info; - int i; + struct cl_thread_info *info; + size_t i; info = data; for (i = 0; i < ARRAY_SIZE(info->clt_counters); ++i) @@ -1096,8 +1264,8 @@ static void cl_key_fini(const struct lu_context *ctx, static void cl_key_exit(const struct lu_context *ctx, struct lu_context_key *key, void *data) { - struct cl_thread_info *info = data; - int i; + struct cl_thread_info *info = data; + size_t i; for (i = 0; i < ARRAY_SIZE(info->clt_counters); ++i) { LASSERT(info->clt_counters[i].ctc_nr_held == 0); @@ -1135,25 +1303,36 @@ static struct lu_kmem_descr cl_object_caches[] = { */ int cl_global_init(void) { - int result; + int result; + + INIT_LIST_HEAD(&cl_envs); - cl_env_hash = cfs_hash_create("cl_env", 8, 10, &cl_env_hops, - CFS_HASH_REHASH); - if (cl_env_hash == NULL) - return -ENOMEM; + result = cl_env_store_init(); + if (result) + return result; result = lu_kmem_init(cl_object_caches); - if (result == 0) { - LU_CONTEXT_KEY_INIT(&cl_key); - result = lu_context_key_register(&cl_key); - if (result == 0) { - result = cl_lock_init(); - if (result == 0) - result = cl_page_init(); - } - } if (result) - cfs_hash_destroy(cl_env_hash); + goto out_store; + + LU_CONTEXT_KEY_INIT(&cl_key); + result = lu_context_key_register(&cl_key); + if (result) + goto out_kmem; + + result = cl_env_percpu_init(); + if (result) + /* no cl_env_percpu_fini on error */ + goto out_context; + + return 0; + +out_context: + lu_context_key_degister(&cl_key); +out_kmem: + lu_kmem_fini(cl_object_caches); +out_store: + cl_env_store_fini(); return result; } @@ -1162,9 +1341,8 @@ int cl_global_init(void) */ void cl_global_fini(void) { - cl_lock_fini(); - cl_page_fini(); + cl_env_percpu_fini(); lu_context_key_degister(&cl_key); lu_kmem_fini(cl_object_caches); - cfs_hash_destroy(cl_env_hash); + cl_env_store_fini(); }