X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=blobdiff_plain;f=lustre%2Fobdclass%2Flu_object.c;h=264caca734d56b753caee7ab70aaf8e838073160;hp=31c4d722faedd0912f253b11d66f3d73ef2a1273;hb=7a8fafe2a10674dae7dc490c45d7bd2e49e1926e;hpb=e089a515efae3391709b997be889ebe0f3306e9d diff --git a/lustre/obdclass/lu_object.c b/lustre/obdclass/lu_object.c index 31c4d72..264caca 100644 --- a/lustre/obdclass/lu_object.c +++ b/lustre/obdclass/lu_object.c @@ -15,11 +15,7 @@ * * You should have received a copy of the GNU General Public License * version 2 along with this program; If not, see - * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. + * http://www.gnu.org/licenses/gpl-2.0.html * * GPL HEADER END */ @@ -27,7 +23,7 @@ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Use is subject to license terms. * - * Copyright (c) 2011, 2013, Intel Corporation. + * Copyright (c) 2011, 2017, Intel Corporation. */ /* * This file is part of Lustre, http://www.lustre.org/ @@ -44,23 +40,109 @@ #define DEBUG_SUBSYSTEM S_CLASS -#include - -#ifdef __KERNEL__ -# include -#endif +#include +#include +#include +#include +#include -/* hash_long() */ -#include +#include +#include +#include #include #include #include #include #include #include -#include + +struct lu_site_bkt_data { + /** + * LRU list, updated on each access to object. Protected by + * lsb_waitq.lock. + * + * "Cold" end of LRU is lu_site::ls_lru.next. Accessed object are + * moved to the lu_site::ls_lru.prev + */ + struct list_head lsb_lru; + /** + * Wait-queue signaled when an object in this site is ultimately + * destroyed (lu_object_free()) or initialized (lu_object_start()). + * It is used by lu_object_find() to wait before re-trying when + * object in the process of destruction is found in the hash table; + * or wait object to be initialized by the allocator. + * + * \see htable_lookup(). + */ + wait_queue_head_t lsb_waitq; +}; + +enum { + LU_CACHE_PERCENT_MAX = 50, + LU_CACHE_PERCENT_DEFAULT = 20 +}; + +#define LU_CACHE_NR_MAX_ADJUST 512 +#define LU_CACHE_NR_UNLIMITED -1 +#define LU_CACHE_NR_DEFAULT LU_CACHE_NR_UNLIMITED +/** This is set to roughly (20 * OSS_NTHRS_MAX) to prevent thrashing */ +#define LU_CACHE_NR_ZFS_LIMIT 10240 + +#define LU_CACHE_NR_MIN 4096 +#define LU_CACHE_NR_MAX 0x80000000UL + +/** + * Max 256 buckets, we don't want too many buckets because: + * - consume too much memory (currently max 16K) + * - avoid unbalanced LRU list + * With few cpus there is little gain from extra buckets, so + * we treat this as a maximum in lu_site_init(). + */ +#define LU_SITE_BKT_BITS 8 + +static unsigned int lu_cache_percent = LU_CACHE_PERCENT_DEFAULT; +module_param(lu_cache_percent, int, 0644); +MODULE_PARM_DESC(lu_cache_percent, "Percentage of memory to be used as lu_object cache"); + +static long lu_cache_nr = LU_CACHE_NR_DEFAULT; +module_param(lu_cache_nr, long, 0644); +MODULE_PARM_DESC(lu_cache_nr, "Maximum number of objects in lu_object cache"); static void lu_object_free(const struct lu_env *env, struct lu_object *o); +static __u32 ls_stats_read(struct lprocfs_stats *stats, int idx); + +static u32 lu_fid_hash(const void *data, u32 len, u32 seed) +{ + const struct lu_fid *fid = data; + + seed = cfs_hash_32(seed ^ fid->f_oid, 32); + seed ^= cfs_hash_64(fid->f_seq, 32); + return seed; +} + +static const struct rhashtable_params obj_hash_params = { + .key_len = sizeof(struct lu_fid), + .key_offset = offsetof(struct lu_object_header, loh_fid), + .head_offset = offsetof(struct lu_object_header, loh_hash), + .hashfn = lu_fid_hash, + .automatic_shrinking = true, +}; + +static inline int lu_bkt_hash(struct lu_site *s, const struct lu_fid *fid) +{ + return lu_fid_hash(fid, sizeof(*fid), s->ls_bkt_seed) & + (s->ls_bkt_cnt - 1); +} + +wait_queue_head_t * +lu_site_wq_from_fid(struct lu_site *site, struct lu_fid *fid) +{ + struct lu_site_bkt_data *bkt; + + bkt = &site->ls_bkts[lu_bkt_hash(site, fid)]; + return &bkt->lsb_waitq; +} +EXPORT_SYMBOL(lu_site_wq_from_fid); /** * Decrease reference counter on object. If last reference is freed, return @@ -69,30 +151,22 @@ static void lu_object_free(const struct lu_env *env, struct lu_object *o); */ void lu_object_put(const struct lu_env *env, struct lu_object *o) { - struct lu_site_bkt_data *bkt; - struct lu_object_header *top; - struct lu_site *site; - struct lu_object *orig; - cfs_hash_bd_t bd; - const struct lu_fid *fid; - - top = o->lo_header; - site = o->lo_dev->ld_site; - orig = o; + struct lu_site_bkt_data *bkt; + struct lu_object_header *top = o->lo_header; + struct lu_site *site = o->lo_dev->ld_site; + struct lu_object *orig = o; + const struct lu_fid *fid = lu_object_fid(o); /* * till we have full fids-on-OST implemented anonymous objects * are possible in OSP. such an object isn't listed in the site * so we should not remove it from the site. */ - fid = lu_object_fid(o); if (fid_is_zero(fid)) { - LASSERT(top->loh_hash.next == NULL - && top->loh_hash.pprev == NULL); - LASSERT(cfs_list_empty(&top->loh_lru)); - if (!cfs_atomic_dec_and_test(&top->loh_ref)) + LASSERT(list_empty(&top->loh_lru)); + if (!atomic_dec_and_test(&top->loh_ref)) return; - cfs_list_for_each_entry_reverse(o, &top->loh_layers, lo_linkage) { + list_for_each_entry_reverse(o, &top->loh_layers, lo_linkage) { if (o->lo_ops->loo_object_release != NULL) o->lo_ops->loo_object_release(env, o); } @@ -100,58 +174,75 @@ void lu_object_put(const struct lu_env *env, struct lu_object *o) return; } - cfs_hash_bd_get(site->ls_obj_hash, &top->loh_fid, &bd); - bkt = cfs_hash_bd_extra_get(site->ls_obj_hash, &bd); - - if (!cfs_hash_bd_dec_and_lock(site->ls_obj_hash, &bd, &top->loh_ref)) { - if (lu_object_is_dying(top)) { + bkt = &site->ls_bkts[lu_bkt_hash(site, &top->loh_fid)]; + if (atomic_add_unless(&top->loh_ref, -1, 1)) { +still_active: + /* + * At this point the object reference is dropped and lock is + * not taken, so lu_object should not be touched because it + * can be freed by concurrent thread. + * + * Somebody may be waiting for this, currently only used for + * cl_object, see cl_object_put_last(). + */ + wake_up(&bkt->lsb_waitq); - /* - * somebody may be waiting for this, currently only - * used for cl_object, see cl_object_put_last(). - */ - wake_up_all(&bkt->lsb_marche_funebre); - } return; } - LASSERT(bkt->lsb_busy > 0); - bkt->lsb_busy--; - /* - * When last reference is released, iterate over object - * layers, and notify them that object is no longer busy. - */ - cfs_list_for_each_entry_reverse(o, &top->loh_layers, lo_linkage) { - if (o->lo_ops->loo_object_release != NULL) - o->lo_ops->loo_object_release(env, o); - } + spin_lock(&bkt->lsb_waitq.lock); + if (!atomic_dec_and_test(&top->loh_ref)) { + spin_unlock(&bkt->lsb_waitq.lock); + goto still_active; + } - if (!lu_object_is_dying(top)) { - LASSERT(cfs_list_empty(&top->loh_lru)); - cfs_list_add_tail(&top->loh_lru, &bkt->lsb_lru); - cfs_hash_bd_unlock(site->ls_obj_hash, &bd, 1); - return; - } + /* + * Refcount is zero, and cannot be incremented without taking the bkt + * lock, so object is stable. + */ - /* - * If object is dying (will not be cached), removed it - * from hash table and LRU. - * - * This is done with hash table and LRU lists locked. As the only - * way to acquire first reference to previously unreferenced - * object is through hash-table lookup (lu_object_find()), - * or LRU scanning (lu_site_purge()), that are done under hash-table - * and LRU lock, no race with concurrent object lookup is possible - * and we can safely destroy object below. - */ + /* + * When last reference is released, iterate over object layers, and + * notify them that object is no longer busy. + */ + list_for_each_entry_reverse(o, &top->loh_layers, lo_linkage) { + if (o->lo_ops->loo_object_release != NULL) + o->lo_ops->loo_object_release(env, o); + } + + /* + * Don't use local 'is_dying' here because if was taken without lock but + * here we need the latest actual value of it so check lu_object + * directly here. + */ + if (!lu_object_is_dying(top) && + (lu_object_exists(orig) || lu_object_is_cl(orig))) { + LASSERT(list_empty(&top->loh_lru)); + list_add_tail(&top->loh_lru, &bkt->lsb_lru); + spin_unlock(&bkt->lsb_waitq.lock); + percpu_counter_inc(&site->ls_lru_len_counter); + CDEBUG(D_INODE, "Add %p/%p to site lru. bkt: %p\n", + orig, top, bkt); + return; + } + + /* + * If object is dying (will not be cached) then remove it from hash + * table (it is already not on the LRU). + * + * This is done with bucket lock held. As the only way to acquire first + * reference to previously unreferenced object is through hash-table + * lookup (lu_object_find()) which takes the lock for first reference, + * no race with concurrent object lookup is possible and we can safely + * destroy object below. + */ if (!test_and_set_bit(LU_OBJECT_UNHASHED, &top->loh_flags)) - cfs_hash_bd_del_locked(site->ls_obj_hash, &bd, &top->loh_hash); - cfs_hash_bd_unlock(site->ls_obj_hash, &bd, 1); - /* - * Object was already removed from hash and lru above, can - * kill it. - */ - lu_object_free(env, orig); + rhashtable_remove_fast(&site->ls_obj_hash, &top->loh_hash, + obj_hash_params); + + spin_unlock(&bkt->lsb_waitq.lock); + /* Object was already removed from hash above, can kill it. */ + lu_object_free(env, orig); } EXPORT_SYMBOL(lu_object_put); @@ -177,13 +268,20 @@ void lu_object_unhash(const struct lu_env *env, struct lu_object *o) top = o->lo_header; set_bit(LU_OBJECT_HEARD_BANSHEE, &top->loh_flags); if (!test_and_set_bit(LU_OBJECT_UNHASHED, &top->loh_flags)) { - cfs_hash_t *obj_hash = o->lo_dev->ld_site->ls_obj_hash; - cfs_hash_bd_t bd; + struct lu_site *site = o->lo_dev->ld_site; + struct rhashtable *obj_hash = &site->ls_obj_hash; + struct lu_site_bkt_data *bkt; + + bkt = &site->ls_bkts[lu_bkt_hash(site, &top->loh_fid)]; + spin_lock(&bkt->lsb_waitq.lock); + if (!list_empty(&top->loh_lru)) { + list_del_init(&top->loh_lru); + percpu_counter_dec(&site->ls_lru_len_counter); + } + spin_unlock(&bkt->lsb_waitq.lock); - cfs_hash_bd_get_and_lock(obj_hash, &top->loh_fid, &bd, 1); - cfs_list_del_init(&top->loh_lru); - cfs_hash_bd_del_locked(obj_hash, &bd, &top->loh_hash); - cfs_hash_bd_unlock(obj_hash, &bd, 1); + rhashtable_remove_fast(obj_hash, &top->loh_hash, + obj_hash_params); } } EXPORT_SYMBOL(lu_object_unhash); @@ -196,17 +294,9 @@ EXPORT_SYMBOL(lu_object_unhash); */ static struct lu_object *lu_object_alloc(const struct lu_env *env, struct lu_device *dev, - const struct lu_fid *f, - const struct lu_object_conf *conf) + const struct lu_fid *f) { - struct lu_object *scan; struct lu_object *top; - cfs_list_t *layers; - unsigned int init_mask = 0; - unsigned int init_flag; - int clean; - int result; - ENTRY; /* * Create top-level object slice. This will also create @@ -214,15 +304,36 @@ static struct lu_object *lu_object_alloc(const struct lu_env *env, */ top = dev->ld_ops->ldo_object_alloc(env, NULL, dev); if (top == NULL) - RETURN(ERR_PTR(-ENOMEM)); + return ERR_PTR(-ENOMEM); if (IS_ERR(top)) - RETURN(top); - /* - * This is the only place where object fid is assigned. It's constant - * after this point. - */ - top->lo_header->loh_fid = *f; - layers = &top->lo_header->loh_layers; + return top; + /* + * This is the only place where object fid is assigned. It's constant + * after this point. + */ + top->lo_header->loh_fid = *f; + + return top; +} + +/** + * Initialize object. + * + * This is called after object hash insertion to avoid returning an object with + * stale attributes. + */ +static int lu_object_start(const struct lu_env *env, struct lu_device *dev, + struct lu_object *top, + const struct lu_object_conf *conf) +{ + struct lu_object *scan; + struct list_head *layers; + unsigned int init_mask = 0; + unsigned int init_flag; + int clean; + int result; + + layers = &top->lo_header->loh_layers; do { /* @@ -231,34 +342,34 @@ static struct lu_object *lu_object_alloc(const struct lu_env *env, */ clean = 1; init_flag = 1; - cfs_list_for_each_entry(scan, layers, lo_linkage) { + list_for_each_entry(scan, layers, lo_linkage) { if (init_mask & init_flag) goto next; clean = 0; scan->lo_header = top->lo_header; result = scan->lo_ops->loo_object_init(env, scan, conf); - if (result != 0) { - lu_object_free(env, top); - RETURN(ERR_PTR(result)); - } + if (result) + return result; + init_mask |= init_flag; next: init_flag <<= 1; } } while (!clean); - cfs_list_for_each_entry_reverse(scan, layers, lo_linkage) { - if (scan->lo_ops->loo_object_start != NULL) { - result = scan->lo_ops->loo_object_start(env, scan); - if (result != 0) { - lu_object_free(env, top); - RETURN(ERR_PTR(result)); - } - } - } + list_for_each_entry_reverse(scan, layers, lo_linkage) { + if (scan->lo_ops->loo_object_start != NULL) { + result = scan->lo_ops->loo_object_start(env, scan); + if (result) + return result; + } + } + + lprocfs_counter_incr(dev->ld_site->ls_stats, LU_SS_CREATED); - lprocfs_counter_incr(dev->ld_site->ls_stats, LU_SS_CREATED); - RETURN(top); + set_bit(LU_OBJECT_INITED, &top->lo_header->loh_flags); + + return 0; } /** @@ -266,19 +377,19 @@ next: */ static void lu_object_free(const struct lu_env *env, struct lu_object *o) { - struct lu_site_bkt_data *bkt; - struct lu_site *site; - struct lu_object *scan; - cfs_list_t *layers; - cfs_list_t splice; + wait_queue_head_t *wq; + struct lu_site *site; + struct lu_object *scan; + struct list_head *layers; + LIST_HEAD(splice); - site = o->lo_dev->ld_site; - layers = &o->lo_header->loh_layers; - bkt = lu_site_bkt_from_fid(site, &o->lo_header->loh_fid); + site = o->lo_dev->ld_site; + layers = &o->lo_header->loh_layers; + wq = lu_site_wq_from_fid(site, &o->lo_header->loh_fid); /* * First call ->loo_object_delete() method to release all resources. */ - cfs_list_for_each_entry_reverse(scan, layers, lo_linkage) { + list_for_each_entry_reverse(scan, layers, lo_linkage) { if (scan->lo_ops->loo_object_delete != NULL) scan->lo_ops->loo_object_delete(env, scan); } @@ -289,107 +400,116 @@ static void lu_object_free(const struct lu_env *env, struct lu_object *o) * necessary, because lu_object_header is freed together with the * top-level slice. */ - CFS_INIT_LIST_HEAD(&splice); - cfs_list_splice_init(layers, &splice); - while (!cfs_list_empty(&splice)) { + list_splice_init(layers, &splice); + while (!list_empty(&splice)) { /* * Free layers in bottom-to-top order, so that object header * lives as long as possible and ->loo_object_free() methods * can look at its contents. */ - o = container_of0(splice.prev, struct lu_object, lo_linkage); - cfs_list_del_init(&o->lo_linkage); + o = container_of(splice.prev, struct lu_object, lo_linkage); + list_del_init(&o->lo_linkage); LASSERT(o->lo_ops->loo_object_free != NULL); o->lo_ops->loo_object_free(env, o); } - if (waitqueue_active(&bkt->lsb_marche_funebre)) - wake_up_all(&bkt->lsb_marche_funebre); + if (waitqueue_active(wq)) + wake_up_all(wq); } /** * Free \a nr objects from the cold end of the site LRU list. + * if canblock is 0, then don't block awaiting for another + * instance of lu_site_purge() to complete */ -int lu_site_purge(const struct lu_env *env, struct lu_site *s, int nr) +int lu_site_purge_objects(const struct lu_env *env, struct lu_site *s, + int nr, int canblock) { - struct lu_object_header *h; - struct lu_object_header *temp; - struct lu_site_bkt_data *bkt; - cfs_hash_bd_t bd; - cfs_hash_bd_t bd2; - cfs_list_t dispose; - int did_sth; - int start; - int count; - int bnr; - int i; + struct lu_object_header *h; + struct lu_object_header *temp; + struct lu_site_bkt_data *bkt; + LIST_HEAD(dispose); + int did_sth; + unsigned int start = 0; + int count; + int bnr; + unsigned int i; if (OBD_FAIL_CHECK(OBD_FAIL_OBD_NO_LRU)) RETURN(0); - CFS_INIT_LIST_HEAD(&dispose); - /* - * Under LRU list lock, scan LRU list and move unreferenced objects to - * the dispose list, removing them from LRU and hash table. - */ - start = s->ls_purge_start; - bnr = (nr == ~0) ? -1 : nr / CFS_HASH_NBKT(s->ls_obj_hash) + 1; - again: - did_sth = 0; - cfs_hash_for_each_bucket(s->ls_obj_hash, &bd, i) { - if (i < start) - continue; - count = bnr; - cfs_hash_bd_lock(s->ls_obj_hash, &bd, 1); - bkt = cfs_hash_bd_extra_get(s->ls_obj_hash, &bd); - - cfs_list_for_each_entry_safe(h, temp, &bkt->lsb_lru, loh_lru) { - LASSERT(cfs_atomic_read(&h->loh_ref) == 0); - - cfs_hash_bd_get(s->ls_obj_hash, &h->loh_fid, &bd2); - LASSERT(bd.bd_bucket == bd2.bd_bucket); - - cfs_hash_bd_del_locked(s->ls_obj_hash, - &bd2, &h->loh_hash); - cfs_list_move(&h->loh_lru, &dispose); - if (did_sth == 0) - did_sth = 1; - - if (nr != ~0 && --nr == 0) - break; - - if (count > 0 && --count == 0) - break; + /* + * Under LRU list lock, scan LRU list and move unreferenced objects to + * the dispose list, removing them from LRU and hash table. + */ + if (nr != ~0) + start = s->ls_purge_start; + bnr = (nr == ~0) ? -1 : nr / s->ls_bkt_cnt + 1; +again: + /* + * It doesn't make any sense to make purge threads parallel, that can + * only bring troubles to us. See LU-5331. + */ + if (canblock != 0) + mutex_lock(&s->ls_purge_mutex); + else if (mutex_trylock(&s->ls_purge_mutex) == 0) + goto out; + + did_sth = 0; + for (i = start; i < s->ls_bkt_cnt ; i++) { + count = bnr; + bkt = &s->ls_bkts[i]; + spin_lock(&bkt->lsb_waitq.lock); + + list_for_each_entry_safe(h, temp, &bkt->lsb_lru, loh_lru) { + LASSERT(atomic_read(&h->loh_ref) == 0); + + LINVRNT(lu_bkt_hash(s, &h->loh_fid) == i); + + set_bit(LU_OBJECT_UNHASHED, &h->loh_flags); + rhashtable_remove_fast(&s->ls_obj_hash, &h->loh_hash, + obj_hash_params); + list_move(&h->loh_lru, &dispose); + percpu_counter_dec(&s->ls_lru_len_counter); + if (did_sth == 0) + did_sth = 1; + + if (nr != ~0 && --nr == 0) + break; + + if (count > 0 && --count == 0) + break; } - cfs_hash_bd_unlock(s->ls_obj_hash, &bd, 1); + spin_unlock(&bkt->lsb_waitq.lock); cond_resched(); /* * Free everything on the dispose list. This is safe against * races due to the reasons described in lu_object_put(). */ - while (!cfs_list_empty(&dispose)) { - h = container_of0(dispose.next, - struct lu_object_header, loh_lru); - cfs_list_del_init(&h->loh_lru); - lu_object_free(env, lu_object_top(h)); - lprocfs_counter_incr(s->ls_stats, LU_SS_LRU_PURGED); - } - - if (nr == 0) - break; - } + while ((h = list_first_entry_or_null(&dispose, + struct lu_object_header, + loh_lru)) != NULL) { + list_del_init(&h->loh_lru); + lu_object_free(env, lu_object_top(h)); + lprocfs_counter_incr(s->ls_stats, LU_SS_LRU_PURGED); + } - if (nr != 0 && did_sth && start != 0) { - start = 0; /* restart from the first bucket */ - goto again; - } - /* race on s->ls_purge_start, but nobody cares */ - s->ls_purge_start = i % CFS_HASH_NBKT(s->ls_obj_hash); + if (nr == 0) + break; + } + mutex_unlock(&s->ls_purge_mutex); - return nr; + if (nr != 0 && did_sth && start != 0) { + start = 0; /* restart from the first bucket */ + goto again; + } + /* race on s->ls_purge_start, but nobody cares */ + s->ls_purge_start = i & (s->ls_bkt_cnt - 1); +out: + return nr; } -EXPORT_SYMBOL(lu_site_purge); +EXPORT_SYMBOL(lu_site_purge_objects); /* * Object printing. @@ -430,7 +550,7 @@ LU_KEY_INIT_FINI(lu_global, struct lu_cdebug_data); * Key, holding temporary buffer. This key is registered very early by * lu_global_init(). */ -struct lu_context_key lu_global_key = { +static struct lu_context_key lu_global_key = { .lct_tags = LCT_MD_THREAD | LCT_DT_THREAD | LCT_MG_THREAD | LCT_CL_THREAD | LCT_LOCAL, .lct_init = lu_global_key_init, @@ -462,8 +582,8 @@ int lu_cdebug_printer(const struct lu_env *env, vsnprintf(key->lck_area + used, ARRAY_SIZE(key->lck_area) - used, format, args); if (complete) { - if (cfs_cdebug_show(msgdata->msg_mask, msgdata->msg_subsys)) - libcfs_debug_msg(msgdata, "%s", key->lck_area); + if (cfs_cdebug_show(msgdata->msg_mask, msgdata->msg_subsys)) + libcfs_debug_msg(msgdata, "%s\n", key->lck_area); key->lck_area[0] = 0; } va_end(args); @@ -478,13 +598,13 @@ void lu_object_header_print(const struct lu_env *env, void *cookie, lu_printer_t printer, const struct lu_object_header *hdr) { - (*printer)(env, cookie, "header@%p[%#lx, %d, "DFID"%s%s%s]", - hdr, hdr->loh_flags, cfs_atomic_read(&hdr->loh_ref), - PFID(&hdr->loh_fid), - cfs_hlist_unhashed(&hdr->loh_hash) ? "" : " hash", - cfs_list_empty((cfs_list_t *)&hdr->loh_lru) ? \ - "" : " lru", - hdr->loh_attr & LOHA_EXISTS ? " exist":""); + (*printer)(env, cookie, "header@%p[%#lx, %d, "DFID"%s%s%s]", + hdr, hdr->loh_flags, atomic_read(&hdr->loh_ref), + PFID(&hdr->loh_fid), + test_bit(LU_OBJECT_UNHASHED, + &hdr->loh_flags) ? "" : " hash", + list_empty(&hdr->loh_lru) ? "" : " lru", + hdr->loh_attr & LOHA_EXISTS ? " exist" : ""); } EXPORT_SYMBOL(lu_object_header_print); @@ -502,7 +622,7 @@ void lu_object_print(const struct lu_env *env, void *cookie, lu_object_header_print(env, cookie, printer, top); (*printer)(env, cookie, "{\n"); - cfs_list_for_each_entry(o, &top->loh_layers, lo_linkage) { + list_for_each_entry(o, &top->loh_layers, lo_linkage) { /* * print `.' \a depth times followed by type name and address */ @@ -527,82 +647,105 @@ int lu_object_invariant(const struct lu_object *o) struct lu_object_header *top; top = o->lo_header; - cfs_list_for_each_entry(o, &top->loh_layers, lo_linkage) { + list_for_each_entry(o, &top->loh_layers, lo_linkage) { if (o->lo_ops->loo_object_invariant != NULL && !o->lo_ops->loo_object_invariant(o)) return 0; } return 1; } -EXPORT_SYMBOL(lu_object_invariant); -static struct lu_object *htable_lookup(struct lu_site *s, - cfs_hash_bd_t *bd, - const struct lu_fid *f, - wait_queue_t *waiter, - __u64 *version) +/* + * Limit the lu_object cache to a maximum of lu_cache_nr objects. Because the + * calculation for the number of objects to reclaim is not covered by a lock the + * maximum number of objects is capped by LU_CACHE_MAX_ADJUST. This ensures + * that many concurrent threads will not accidentally purge the entire cache. + */ +static void lu_object_limit(const struct lu_env *env, + struct lu_device *dev) { - struct lu_site_bkt_data *bkt; - struct lu_object_header *h; - cfs_hlist_node_t *hnode; - __u64 ver = cfs_hash_bd_version_get(bd); - - if (*version == ver) - return ERR_PTR(-ENOENT); - - *version = ver; - bkt = cfs_hash_bd_extra_get(s->ls_obj_hash, bd); - /* cfs_hash_bd_peek_locked is a somehow "internal" function - * of cfs_hash, it doesn't add refcount on object. */ - hnode = cfs_hash_bd_peek_locked(s->ls_obj_hash, bd, (void *)f); - if (hnode == NULL) { - lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_MISS); - return ERR_PTR(-ENOENT); - } + u64 size, nr; - h = container_of0(hnode, struct lu_object_header, loh_hash); - if (likely(!lu_object_is_dying(h))) { - cfs_hash_get(s->ls_obj_hash, hnode); - lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_HIT); - cfs_list_del_init(&h->loh_lru); - return lu_object_top(h); - } + if (lu_cache_nr == LU_CACHE_NR_UNLIMITED) + return; - /* - * Lookup found an object being destroyed this object cannot be - * returned (to assure that references to dying objects are eventually - * drained), and moreover, lookup has to wait until object is freed. - */ + size = atomic_read(&dev->ld_site->ls_obj_hash.nelems); + nr = (u64)lu_cache_nr; + if (size <= nr) + return; - init_waitqueue_entry_current(waiter); - add_wait_queue(&bkt->lsb_marche_funebre, waiter); - set_current_state(TASK_UNINTERRUPTIBLE); - lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_DEATH_RACE); - return ERR_PTR(-EAGAIN); + lu_site_purge_objects(env, dev->ld_site, + min_t(u64, size - nr, LU_CACHE_NR_MAX_ADJUST), + 0); } -static struct lu_object *htable_lookup_nowait(struct lu_site *s, - cfs_hash_bd_t *bd, - const struct lu_fid *f) -{ - cfs_hlist_node_t *hnode; - struct lu_object_header *h; +static struct lu_object *htable_lookup(const struct lu_env *env, + struct lu_device *dev, + struct lu_site_bkt_data *bkt, + const struct lu_fid *f, + struct lu_object_header *new) +{ + struct lu_site *s = dev->ld_site; + struct lu_object_header *h; + +try_again: + rcu_read_lock(); + if (new) + h = rhashtable_lookup_get_insert_fast(&s->ls_obj_hash, + &new->loh_hash, + obj_hash_params); + else + h = rhashtable_lookup(&s->ls_obj_hash, f, obj_hash_params); + + if (IS_ERR_OR_NULL(h)) { + /* Not found */ + if (!new) + lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_MISS); + rcu_read_unlock(); + if (PTR_ERR(h) == -ENOMEM) { + msleep(20); + goto try_again; + } + lu_object_limit(env, dev); + if (PTR_ERR(h) == -E2BIG) + goto try_again; - /* cfs_hash_bd_peek_locked is a somehow "internal" function - * of cfs_hash, it doesn't add refcount on object. */ - hnode = cfs_hash_bd_peek_locked(s->ls_obj_hash, bd, (void *)f); - if (hnode == NULL) { - lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_MISS); return ERR_PTR(-ENOENT); } - h = container_of0(hnode, struct lu_object_header, loh_hash); - if (unlikely(lu_object_is_dying(h))) + if (atomic_inc_not_zero(&h->loh_ref)) { + rcu_read_unlock(); + return lu_object_top(h); + } + + spin_lock(&bkt->lsb_waitq.lock); + if (lu_object_is_dying(h) || + test_bit(LU_OBJECT_UNHASHED, &h->loh_flags)) { + spin_unlock(&bkt->lsb_waitq.lock); + rcu_read_unlock(); + if (new) { + /* + * Old object might have already been removed, or will + * be soon. We need to insert our new object, so + * remove the old one just in case it is still there. + */ + rhashtable_remove_fast(&s->ls_obj_hash, &h->loh_hash, + obj_hash_params); + goto try_again; + } + lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_MISS); return ERR_PTR(-ENOENT); + } + /* Now protected by spinlock */ + rcu_read_unlock(); - cfs_hash_get(s->ls_obj_hash, hnode); + if (!list_empty(&h->loh_lru)) { + list_del_init(&h->loh_lru); + percpu_counter_dec(&s->ls_lru_len_counter); + } + atomic_inc(&h->loh_ref); + spin_unlock(&bkt->lsb_waitq.lock); lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_HIT); - cfs_list_del_init(&h->loh_lru); return lu_object_top(h); } @@ -619,106 +762,42 @@ struct lu_object *lu_object_find(const struct lu_env *env, } EXPORT_SYMBOL(lu_object_find); -static struct lu_object *lu_object_new(const struct lu_env *env, - struct lu_device *dev, - const struct lu_fid *f, - const struct lu_object_conf *conf) -{ - struct lu_object *o; - cfs_hash_t *hs; - cfs_hash_bd_t bd; - struct lu_site_bkt_data *bkt; - - o = lu_object_alloc(env, dev, f, conf); - if (unlikely(IS_ERR(o))) - return o; - - hs = dev->ld_site->ls_obj_hash; - cfs_hash_bd_get_and_lock(hs, (void *)f, &bd, 1); - bkt = cfs_hash_bd_extra_get(hs, &bd); - cfs_hash_bd_add_locked(hs, &bd, &o->lo_header->loh_hash); - bkt->lsb_busy++; - cfs_hash_bd_unlock(hs, &bd, 1); - return o; -} - -/** - * Core logic of lu_object_find*() functions. +/* + * Get a 'first' reference to an object that was found while looking through the + * hash table. */ -static struct lu_object *lu_object_find_try(const struct lu_env *env, - struct lu_device *dev, - const struct lu_fid *f, - const struct lu_object_conf *conf, - wait_queue_t *waiter) +struct lu_object *lu_object_get_first(struct lu_object_header *h, + struct lu_device *dev) { - struct lu_object *o; - struct lu_object *shadow; - struct lu_site *s; - cfs_hash_t *hs; - cfs_hash_bd_t bd; - __u64 version = 0; - - /* - * This uses standard index maintenance protocol: - * - * - search index under lock, and return object if found; - * - otherwise, unlock index, allocate new object; - * - lock index and search again; - * - if nothing is found (usual case), insert newly created - * object into index; - * - otherwise (race: other thread inserted object), free - * object just allocated. - * - unlock index; - * - return object. - * - * For "LOC_F_NEW" case, we are sure the object is new established. - * It is unnecessary to perform lookup-alloc-lookup-insert, instead, - * just alloc and insert directly. - * - * If dying object is found during index search, add @waiter to the - * site wait-queue and return ERR_PTR(-EAGAIN). - */ - if (conf != NULL && conf->loc_flags & LOC_F_NEW) - return lu_object_new(env, dev, f, conf); - - s = dev->ld_site; - hs = s->ls_obj_hash; - cfs_hash_bd_get_and_lock(hs, (void *)f, &bd, 1); - o = htable_lookup(s, &bd, f, waiter, &version); - cfs_hash_bd_unlock(hs, &bd, 1); - if (!IS_ERR(o) || PTR_ERR(o) != -ENOENT) - return o; + struct lu_site *s = dev->ld_site; + struct lu_object *ret; - /* - * Allocate new object. This may result in rather complicated - * operations, including fld queries, inode loading, etc. - */ - o = lu_object_alloc(env, dev, f, conf); - if (unlikely(IS_ERR(o))) - return o; + if (IS_ERR_OR_NULL(h) || lu_object_is_dying(h)) + return NULL; - LASSERT(lu_fid_eq(lu_object_fid(o), f)); + ret = lu_object_locate(h, dev->ld_type); + if (!ret) + return ret; - cfs_hash_bd_lock(hs, &bd, 1); + if (!atomic_inc_not_zero(&h->loh_ref)) { + struct lu_site_bkt_data *bkt; - shadow = htable_lookup(s, &bd, f, waiter, &version); - if (likely(IS_ERR(shadow) && PTR_ERR(shadow) == -ENOENT)) { - struct lu_site_bkt_data *bkt; - - bkt = cfs_hash_bd_extra_get(hs, &bd); - cfs_hash_bd_add_locked(hs, &bd, &o->lo_header->loh_hash); - bkt->lsb_busy++; - cfs_hash_bd_unlock(hs, &bd, 1); - return o; - } - - lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_RACE); - cfs_hash_bd_unlock(hs, &bd, 1); - lu_object_free(env, o); - return shadow; + bkt = &s->ls_bkts[lu_bkt_hash(s, &h->loh_fid)]; + spin_lock(&bkt->lsb_waitq.lock); + if (!lu_object_is_dying(h) && + !test_bit(LU_OBJECT_UNHASHED, &h->loh_flags)) + atomic_inc(&h->loh_ref); + else + ret = NULL; + spin_unlock(&bkt->lsb_waitq.lock); + } + return ret; } +EXPORT_SYMBOL(lu_object_get_first); /** + * Core logic of lu_object_find*() functions. + * * Much like lu_object_find(), but top level device of object is specifically * \a dev rather than top level device of the site. This interface allows * objects of different "stacking" to be created within the same site. @@ -728,48 +807,135 @@ struct lu_object *lu_object_find_at(const struct lu_env *env, const struct lu_fid *f, const struct lu_object_conf *conf) { + struct lu_object *o; + struct lu_object *shadow; + struct lu_site *s; struct lu_site_bkt_data *bkt; - struct lu_object *obj; - wait_queue_t wait; + struct rhashtable *hs; + int rc; + + ENTRY; + + /* FID is from disk or network, zero FID is meaningless, return error + * early to avoid assertion in lu_object_put. If a zero FID is wanted, + * it should be allocated via lu_object_anon(). + */ + if (fid_is_zero(f)) + RETURN(ERR_PTR(-EINVAL)); + + /* + * This uses standard index maintenance protocol: + * + * - search index under lock, and return object if found; + * - otherwise, unlock index, allocate new object; + * - lock index and search again; + * - if nothing is found (usual case), insert newly created + * object into index; + * - otherwise (race: other thread inserted object), free + * object just allocated. + * - unlock index; + * - return object. + * + * For "LOC_F_NEW" case, we are sure the object is new established. + * It is unnecessary to perform lookup-alloc-lookup-insert, instead, + * just alloc and insert directly. + * + */ + s = dev->ld_site; + hs = &s->ls_obj_hash; + + if (unlikely(OBD_FAIL_PRECHECK(OBD_FAIL_OBD_ZERO_NLINK_RACE))) + lu_site_purge(env, s, -1); + + bkt = &s->ls_bkts[lu_bkt_hash(s, f)]; + if (!(conf && conf->loc_flags & LOC_F_NEW)) { + o = htable_lookup(env, dev, bkt, f, NULL); - while (1) { - obj = lu_object_find_try(env, dev, f, conf, &wait); - if (obj != ERR_PTR(-EAGAIN)) - return obj; + if (!IS_ERR(o)) { + if (likely(lu_object_is_inited(o->lo_header))) + RETURN(o); + + wait_event_idle(bkt->lsb_waitq, + lu_object_is_inited(o->lo_header) || + lu_object_is_dying(o->lo_header)); + + if (lu_object_is_dying(o->lo_header)) { + lu_object_put(env, o); + + RETURN(ERR_PTR(-ENOENT)); + } + + RETURN(o); + } + + if (PTR_ERR(o) != -ENOENT) + RETURN(o); + } + + /* + * Allocate new object, NB, object is unitialized in case object + * is changed between allocation and hash insertion, thus the object + * with stale attributes is returned. + */ + o = lu_object_alloc(env, dev, f); + if (IS_ERR(o)) + RETURN(o); + + LASSERT(lu_fid_eq(lu_object_fid(o), f)); + + CFS_RACE_WAIT(OBD_FAIL_OBD_ZERO_NLINK_RACE); + + if (conf && conf->loc_flags & LOC_F_NEW) { + int status = rhashtable_insert_fast(hs, &o->lo_header->loh_hash, + obj_hash_params); + if (status) + /* Strange error - go the slow way */ + shadow = htable_lookup(env, dev, bkt, f, o->lo_header); + else + shadow = ERR_PTR(-ENOENT); + } else { + shadow = htable_lookup(env, dev, bkt, f, o->lo_header); + } + if (likely(PTR_ERR(shadow) == -ENOENT)) { /* - * lu_object_find_try() already added waiter into the - * wait queue. + * The new object has been successfully inserted. + * + * This may result in rather complicated operations, including + * fld queries, inode loading, etc. */ - waitq_wait(&wait, TASK_UNINTERRUPTIBLE); - bkt = lu_site_bkt_from_fid(dev->ld_site, (void *)f); - remove_wait_queue(&bkt->lsb_marche_funebre, &wait); + rc = lu_object_start(env, dev, o, conf); + if (rc) { + lu_object_put_nocache(env, o); + RETURN(ERR_PTR(rc)); + } + + wake_up(&bkt->lsb_waitq); + + lu_object_limit(env, dev); + + RETURN(o); } -} -EXPORT_SYMBOL(lu_object_find_at); -/** - * Try to find the object in cache without waiting for the dead object - * to be released nor allocating object if no cached one was found. - * - * The found object will be set as LU_OBJECT_HEARD_BANSHEE for purging. - */ -void lu_object_purge(const struct lu_env *env, struct lu_device *dev, - const struct lu_fid *f) -{ - struct lu_site *s = dev->ld_site; - cfs_hash_t *hs = s->ls_obj_hash; - cfs_hash_bd_t bd; - struct lu_object *o; + lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_RACE); + lu_object_free(env, o); - cfs_hash_bd_get_and_lock(hs, f, &bd, 1); - o = htable_lookup_nowait(s, &bd, f); - cfs_hash_bd_unlock(hs, &bd, 1); - if (!IS_ERR(o)) { - set_bit(LU_OBJECT_HEARD_BANSHEE, &o->lo_header->loh_flags); - lu_object_put(env, o); + if (!(conf && conf->loc_flags & LOC_F_NEW) && + !IS_ERR(shadow) && + !lu_object_is_inited(shadow->lo_header)) { + wait_event_idle(bkt->lsb_waitq, + lu_object_is_inited(shadow->lo_header) || + lu_object_is_dying(shadow->lo_header)); + + if (lu_object_is_dying(shadow->lo_header)) { + lu_object_put(env, shadow); + + RETURN(ERR_PTR(-ENOENT)); + } } + + RETURN(shadow); } -EXPORT_SYMBOL(lu_object_purge); +EXPORT_SYMBOL(lu_object_find_at); /** * Find object with given fid, and return its slice belonging to given device. @@ -779,62 +945,47 @@ struct lu_object *lu_object_find_slice(const struct lu_env *env, const struct lu_fid *f, const struct lu_object_conf *conf) { - struct lu_object *top; - struct lu_object *obj; + struct lu_object *top; + struct lu_object *obj; + + top = lu_object_find(env, dev, f, conf); + if (IS_ERR(top)) + return top; + + obj = lu_object_locate(top->lo_header, dev->ld_type); + if (unlikely(obj == NULL)) { + lu_object_put(env, top); + obj = ERR_PTR(-ENOENT); + } - top = lu_object_find(env, dev, f, conf); - if (!IS_ERR(top)) { - obj = lu_object_locate(top->lo_header, dev->ld_type); - if (obj == NULL) - lu_object_put(env, top); - } else - obj = top; - return obj; + return obj; } EXPORT_SYMBOL(lu_object_find_slice); -/** - * Global list of all device types. - */ -static CFS_LIST_HEAD(lu_device_types); - int lu_device_type_init(struct lu_device_type *ldt) { int result = 0; - CFS_INIT_LIST_HEAD(&ldt->ldt_linkage); + atomic_set(&ldt->ldt_device_nr, 0); if (ldt->ldt_ops->ldto_init) result = ldt->ldt_ops->ldto_init(ldt); - if (result == 0) - cfs_list_add(&ldt->ldt_linkage, &lu_device_types); + return result; } EXPORT_SYMBOL(lu_device_type_init); void lu_device_type_fini(struct lu_device_type *ldt) { - cfs_list_del_init(&ldt->ldt_linkage); if (ldt->ldt_ops->ldto_fini) ldt->ldt_ops->ldto_fini(ldt); } EXPORT_SYMBOL(lu_device_type_fini); -void lu_types_stop(void) -{ - struct lu_device_type *ldt; - - cfs_list_for_each_entry(ldt, &lu_device_types, ldt_linkage) { - if (ldt->ldt_device_nr == 0 && ldt->ldt_ops->ldto_stop) - ldt->ldt_ops->ldto_stop(ldt); - } -} -EXPORT_SYMBOL(lu_types_stop); - /** * Global list of all sites on this node */ -static CFS_LIST_HEAD(lu_sites); -static DEFINE_MUTEX(lu_sites_guard); +static LIST_HEAD(lu_sites); +static DECLARE_RWSEM(lu_sites_guard); /** * Global environment used by site shrinker. @@ -847,167 +998,105 @@ struct lu_site_print_arg { lu_printer_t lsp_printer; }; -static int -lu_site_obj_print(cfs_hash_t *hs, cfs_hash_bd_t *bd, - cfs_hlist_node_t *hnode, void *data) +static void +lu_site_obj_print(struct lu_object_header *h, struct lu_site_print_arg *arg) { - struct lu_site_print_arg *arg = (struct lu_site_print_arg *)data; - struct lu_object_header *h; + if (!list_empty(&h->loh_layers)) { + const struct lu_object *o; - h = cfs_hlist_entry(hnode, struct lu_object_header, loh_hash); - if (!cfs_list_empty(&h->loh_layers)) { - const struct lu_object *o; - - o = lu_object_top(h); - lu_object_print(arg->lsp_env, arg->lsp_cookie, - arg->lsp_printer, o); - } else { - lu_object_header_print(arg->lsp_env, arg->lsp_cookie, - arg->lsp_printer, h); - } - return 0; + o = lu_object_top(h); + lu_object_print(arg->lsp_env, arg->lsp_cookie, + arg->lsp_printer, o); + } else { + lu_object_header_print(arg->lsp_env, arg->lsp_cookie, + arg->lsp_printer, h); + } } /** * Print all objects in \a s. */ -void lu_site_print(const struct lu_env *env, struct lu_site *s, void *cookie, - lu_printer_t printer) +void lu_site_print(const struct lu_env *env, struct lu_site *s, atomic_t *ref, + int msg_flag, lu_printer_t printer) { - struct lu_site_print_arg arg = { - .lsp_env = (struct lu_env *)env, - .lsp_cookie = cookie, - .lsp_printer = printer, - }; + struct lu_site_print_arg arg = { + .lsp_env = (struct lu_env *)env, + .lsp_printer = printer, + }; + struct rhashtable_iter iter; + struct lu_object_header *h; + LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, msg_flag, NULL); - cfs_hash_for_each(s->ls_obj_hash, lu_site_obj_print, &arg); -} -EXPORT_SYMBOL(lu_site_print); + if (!s || !atomic_read(ref)) + return; -enum { - LU_CACHE_PERCENT_MAX = 50, - LU_CACHE_PERCENT_DEFAULT = 20 -}; + arg.lsp_cookie = (void *)&msgdata; -static unsigned int lu_cache_percent = LU_CACHE_PERCENT_DEFAULT; -CFS_MODULE_PARM(lu_cache_percent, "i", int, 0644, - "Percentage of memory to be used as lu_object cache"); + rhashtable_walk_enter(&s->ls_obj_hash, &iter); + rhashtable_walk_start(&iter); + while ((h = rhashtable_walk_next(&iter)) != NULL) { + if (IS_ERR(h)) + continue; + lu_site_obj_print(h, &arg); + } + rhashtable_walk_stop(&iter); + rhashtable_walk_exit(&iter); +} +EXPORT_SYMBOL(lu_site_print); /** * Return desired hash table order. */ -static int lu_htable_order(void) +static void lu_htable_limits(struct lu_device *top) { - unsigned long cache_size; - int bits; + unsigned long cache_size; - /* - * Calculate hash table size, assuming that we want reasonable - * performance when 20% of total memory is occupied by cache of - * lu_objects. - * - * Size of lu_object is (arbitrary) taken as 1K (together with inode). - */ - cache_size = num_physpages; + /* + * For ZFS based OSDs the cache should be disabled by default. This + * allows the ZFS ARC maximum flexibility in determining what buffers + * to cache. If Lustre has objects or buffer which it wants to ensure + * always stay cached it must maintain a hold on them. + */ + if (strcmp(top->ld_type->ldt_name, LUSTRE_OSD_ZFS_NAME) == 0) { + lu_cache_nr = LU_CACHE_NR_ZFS_LIMIT; + return; + } + + /* + * Calculate hash table size, assuming that we want reasonable + * performance when 20% of total memory is occupied by cache of + * lu_objects. + * + * Size of lu_object is (arbitrary) taken as 1K (together with inode). + */ + cache_size = cfs_totalram_pages(); #if BITS_PER_LONG == 32 - /* limit hashtable size for lowmem systems to low RAM */ - if (cache_size > 1 << (30 - PAGE_CACHE_SHIFT)) - cache_size = 1 << (30 - PAGE_CACHE_SHIFT) * 3 / 4; + /* limit hashtable size for lowmem systems to low RAM */ + if (cache_size > 1 << (30 - PAGE_SHIFT)) + cache_size = 1 << (30 - PAGE_SHIFT) * 3 / 4; #endif - /* clear off unreasonable cache setting. */ - if (lu_cache_percent == 0 || lu_cache_percent > LU_CACHE_PERCENT_MAX) { - CWARN("obdclass: invalid lu_cache_percent: %u, it must be in" - " the range of (0, %u]. Will use default value: %u.\n", - lu_cache_percent, LU_CACHE_PERCENT_MAX, - LU_CACHE_PERCENT_DEFAULT); - - lu_cache_percent = LU_CACHE_PERCENT_DEFAULT; - } - cache_size = cache_size / 100 * lu_cache_percent * - (PAGE_CACHE_SIZE / 1024); - - for (bits = 1; (1 << bits) < cache_size; ++bits) { - ; - } - return bits; -} - -static unsigned lu_obj_hop_hash(cfs_hash_t *hs, - const void *key, unsigned mask) -{ - struct lu_fid *fid = (struct lu_fid *)key; - __u32 hash; - - hash = fid_flatten32(fid); - hash += (hash >> 4) + (hash << 12); /* mixing oid and seq */ - hash = cfs_hash_long(hash, hs->hs_bkt_bits); - - /* give me another random factor */ - hash -= cfs_hash_long((unsigned long)hs, fid_oid(fid) % 11 + 3); - - hash <<= hs->hs_cur_bits - hs->hs_bkt_bits; - hash |= (fid_seq(fid) + fid_oid(fid)) & (CFS_HASH_NBKT(hs) - 1); - - return hash & mask; -} - -static void *lu_obj_hop_object(cfs_hlist_node_t *hnode) -{ - return cfs_hlist_entry(hnode, struct lu_object_header, loh_hash); -} - -static void *lu_obj_hop_key(cfs_hlist_node_t *hnode) -{ - struct lu_object_header *h; - - h = cfs_hlist_entry(hnode, struct lu_object_header, loh_hash); - return &h->loh_fid; -} - -static int lu_obj_hop_keycmp(const void *key, cfs_hlist_node_t *hnode) -{ - struct lu_object_header *h; + /* clear off unreasonable cache setting. */ + if (lu_cache_percent == 0 || lu_cache_percent > LU_CACHE_PERCENT_MAX) { + CWARN("obdclass: invalid lu_cache_percent: %u, it must be in the range of (0, %u]. Will use default value: %u.\n", + lu_cache_percent, LU_CACHE_PERCENT_MAX, + LU_CACHE_PERCENT_DEFAULT); - h = cfs_hlist_entry(hnode, struct lu_object_header, loh_hash); - return lu_fid_eq(&h->loh_fid, (struct lu_fid *)key); -} - -static void lu_obj_hop_get(cfs_hash_t *hs, cfs_hlist_node_t *hnode) -{ - struct lu_object_header *h; - - h = cfs_hlist_entry(hnode, struct lu_object_header, loh_hash); - if (cfs_atomic_add_return(1, &h->loh_ref) == 1) { - struct lu_site_bkt_data *bkt; - cfs_hash_bd_t bd; - - cfs_hash_bd_get(hs, &h->loh_fid, &bd); - bkt = cfs_hash_bd_extra_get(hs, &bd); - bkt->lsb_busy++; - } -} + lu_cache_percent = LU_CACHE_PERCENT_DEFAULT; + } + cache_size = cache_size / 100 * lu_cache_percent * + (PAGE_SIZE / 1024); -static void lu_obj_hop_put_locked(cfs_hash_t *hs, cfs_hlist_node_t *hnode) -{ - LBUG(); /* we should never called it */ + lu_cache_nr = clamp_t(typeof(cache_size), cache_size, + LU_CACHE_NR_MIN, LU_CACHE_NR_MAX); } -cfs_hash_ops_t lu_site_hash_ops = { - .hs_hash = lu_obj_hop_hash, - .hs_key = lu_obj_hop_key, - .hs_keycmp = lu_obj_hop_keycmp, - .hs_object = lu_obj_hop_object, - .hs_get = lu_obj_hop_get, - .hs_put_locked = lu_obj_hop_put_locked, -}; - void lu_dev_add_linkage(struct lu_site *s, struct lu_device *d) { spin_lock(&s->ls_ld_lock); - if (cfs_list_empty(&d->ld_linkage)) - cfs_list_add(&d->ld_linkage, &s->ls_ld_linkage); + if (list_empty(&d->ld_linkage)) + list_add(&d->ld_linkage, &s->ls_ld_linkage); spin_unlock(&s->ls_ld_lock); } EXPORT_SYMBOL(lu_dev_add_linkage); @@ -1015,66 +1104,62 @@ EXPORT_SYMBOL(lu_dev_add_linkage); void lu_dev_del_linkage(struct lu_site *s, struct lu_device *d) { spin_lock(&s->ls_ld_lock); - cfs_list_del_init(&d->ld_linkage); + list_del_init(&d->ld_linkage); spin_unlock(&s->ls_ld_lock); } EXPORT_SYMBOL(lu_dev_del_linkage); /** - * Initialize site \a s, with \a d as the top level device. - */ -#define LU_SITE_BITS_MIN 12 -#define LU_SITE_BITS_MAX 24 -/** - * total 256 buckets, we don't want too many buckets because: - * - consume too much memory - * - avoid unbalanced LRU list - */ -#define LU_SITE_BKT_BITS 8 - + * Initialize site \a s, with \a d as the top level device. + */ int lu_site_init(struct lu_site *s, struct lu_device *top) { - struct lu_site_bkt_data *bkt; - cfs_hash_bd_t bd; - char name[16]; - int bits; - int i; - ENTRY; - - memset(s, 0, sizeof *s); - bits = lu_htable_order(); - snprintf(name, 16, "lu_site_%s", top->ld_type->ldt_name); - for (bits = min(max(LU_SITE_BITS_MIN, bits), LU_SITE_BITS_MAX); - bits >= LU_SITE_BITS_MIN; bits--) { - s->ls_obj_hash = cfs_hash_create(name, bits, bits, - bits - LU_SITE_BKT_BITS, - sizeof(*bkt), 0, 0, - &lu_site_hash_ops, - CFS_HASH_SPIN_BKTLOCK | - CFS_HASH_NO_ITEMREF | - CFS_HASH_DEPTH | - CFS_HASH_ASSERT_EMPTY); - if (s->ls_obj_hash != NULL) - break; - } + struct lu_site_bkt_data *bkt; + unsigned int i; + int rc; + ENTRY; - if (s->ls_obj_hash == NULL) { - CERROR("failed to create lu_site hash with bits: %d\n", bits); - return -ENOMEM; - } + memset(s, 0, sizeof *s); + mutex_init(&s->ls_purge_mutex); + lu_htable_limits(top); - cfs_hash_for_each_bucket(s->ls_obj_hash, &bd, i) { - bkt = cfs_hash_bd_extra_get(s->ls_obj_hash, &bd); - CFS_INIT_LIST_HEAD(&bkt->lsb_lru); - init_waitqueue_head(&bkt->lsb_marche_funebre); +#ifdef HAVE_PERCPU_COUNTER_INIT_GFP_FLAG + rc = percpu_counter_init(&s->ls_lru_len_counter, 0, GFP_NOFS); +#else + rc = percpu_counter_init(&s->ls_lru_len_counter, 0); +#endif + if (rc) + return -ENOMEM; + + if (rhashtable_init(&s->ls_obj_hash, &obj_hash_params) != 0) { + CERROR("failed to create lu_site hash\n"); + return -ENOMEM; } - s->ls_stats = lprocfs_alloc_stats(LU_SS_LAST_STAT, 0); - if (s->ls_stats == NULL) { - cfs_hash_putref(s->ls_obj_hash); - s->ls_obj_hash = NULL; - return -ENOMEM; - } + s->ls_bkt_seed = prandom_u32(); + s->ls_bkt_cnt = max_t(long, 1 << LU_SITE_BKT_BITS, + 2 * num_possible_cpus()); + s->ls_bkt_cnt = roundup_pow_of_two(s->ls_bkt_cnt); + OBD_ALLOC_PTR_ARRAY_LARGE(s->ls_bkts, s->ls_bkt_cnt); + if (!s->ls_bkts) { + rhashtable_destroy(&s->ls_obj_hash); + s->ls_bkts = NULL; + return -ENOMEM; + } + + for (i = 0; i < s->ls_bkt_cnt; i++) { + bkt = &s->ls_bkts[i]; + INIT_LIST_HEAD(&bkt->lsb_lru); + init_waitqueue_head(&bkt->lsb_waitq); + } + + s->ls_stats = lprocfs_alloc_stats(LU_SS_LAST_STAT, 0); + if (s->ls_stats == NULL) { + OBD_FREE_PTR_ARRAY_LARGE(s->ls_bkts, s->ls_bkt_cnt); + s->ls_bkts = NULL; + rhashtable_destroy(&s->ls_obj_hash); + return -ENOMEM; + } lprocfs_counter_init(s->ls_stats, LU_SS_CREATED, 0, "created", "created"); @@ -1089,13 +1174,13 @@ int lu_site_init(struct lu_site *s, struct lu_device *top) lprocfs_counter_init(s->ls_stats, LU_SS_LRU_PURGED, 0, "lru_purged", "lru_purged"); - CFS_INIT_LIST_HEAD(&s->ls_linkage); + INIT_LIST_HEAD(&s->ls_linkage); s->ls_top_dev = top; top->ld_site = s; lu_device_get(top); lu_ref_add(&top->ld_reference, "site-top", s); - CFS_INIT_LIST_HEAD(&s->ls_ld_linkage); + INIT_LIST_HEAD(&s->ls_ld_linkage); spin_lock_init(&s->ls_ld_lock); lu_dev_add_linkage(s, top); @@ -1109,14 +1194,17 @@ EXPORT_SYMBOL(lu_site_init); */ void lu_site_fini(struct lu_site *s) { - mutex_lock(&lu_sites_guard); - cfs_list_del_init(&s->ls_linkage); - mutex_unlock(&lu_sites_guard); + down_write(&lu_sites_guard); + list_del_init(&s->ls_linkage); + up_write(&lu_sites_guard); - if (s->ls_obj_hash != NULL) { - cfs_hash_putref(s->ls_obj_hash); - s->ls_obj_hash = NULL; - } + percpu_counter_destroy(&s->ls_lru_len_counter); + + if (s->ls_bkts) { + rhashtable_destroy(&s->ls_obj_hash); + OBD_FREE_PTR_ARRAY_LARGE(s->ls_bkts, s->ls_bkt_cnt); + s->ls_bkts = NULL; + } if (s->ls_top_dev != NULL) { s->ls_top_dev->ld_site = NULL; @@ -1136,11 +1224,11 @@ EXPORT_SYMBOL(lu_site_fini); int lu_site_init_finish(struct lu_site *s) { int result; - mutex_lock(&lu_sites_guard); + down_write(&lu_sites_guard); result = lu_context_refill(&lu_shrink_env.le_ctx); if (result == 0) - cfs_list_add(&s->ls_linkage, &lu_sites); - mutex_unlock(&lu_sites_guard); + list_add(&s->ls_linkage, &lu_sites); + up_write(&lu_sites_guard); return result; } EXPORT_SYMBOL(lu_site_init_finish); @@ -1150,7 +1238,7 @@ EXPORT_SYMBOL(lu_site_init_finish); */ void lu_device_get(struct lu_device *d) { - cfs_atomic_inc(&d->ld_ref); + atomic_inc(&d->ld_ref); } EXPORT_SYMBOL(lu_device_get); @@ -1159,24 +1247,37 @@ EXPORT_SYMBOL(lu_device_get); */ void lu_device_put(struct lu_device *d) { - LASSERT(cfs_atomic_read(&d->ld_ref) > 0); - cfs_atomic_dec(&d->ld_ref); + LASSERT(atomic_read(&d->ld_ref) > 0); + atomic_dec(&d->ld_ref); } EXPORT_SYMBOL(lu_device_put); +enum { /* Maximal number of tld slots. */ + LU_CONTEXT_KEY_NR = 40 +}; +static struct lu_context_key *lu_keys[LU_CONTEXT_KEY_NR] = { NULL, }; +static DECLARE_RWSEM(lu_key_initing); + /** * Initialize device \a d of type \a t. */ int lu_device_init(struct lu_device *d, struct lu_device_type *t) { - if (t->ldt_device_nr++ == 0 && t->ldt_ops->ldto_start != NULL) - t->ldt_ops->ldto_start(t); - memset(d, 0, sizeof *d); - cfs_atomic_set(&d->ld_ref, 0); - d->ld_type = t; - lu_ref_init(&d->ld_reference); - CFS_INIT_LIST_HEAD(&d->ld_linkage); - return 0; + if (atomic_add_unless(&t->ldt_device_nr, 1, 0) == 0) { + down_write(&lu_key_initing); + if (t->ldt_ops->ldto_start && + atomic_read(&t->ldt_device_nr) == 0) + t->ldt_ops->ldto_start(t); + atomic_inc(&t->ldt_device_nr); + up_write(&lu_key_initing); + } + + memset(d, 0, sizeof *d); + d->ld_type = t; + lu_ref_init(&d->ld_reference); + INIT_LIST_HEAD(&d->ld_linkage); + + return 0; } EXPORT_SYMBOL(lu_device_init); @@ -1185,20 +1286,21 @@ EXPORT_SYMBOL(lu_device_init); */ void lu_device_fini(struct lu_device *d) { - struct lu_device_type *t; + struct lu_device_type *t = d->ld_type; - t = d->ld_type; - if (d->ld_obd != NULL) { - d->ld_obd->obd_lu_dev = NULL; - d->ld_obd = NULL; - } + if (d->ld_obd != NULL) { + d->ld_obd->obd_lu_dev = NULL; + d->ld_obd = NULL; + } - lu_ref_fini(&d->ld_reference); - LASSERTF(cfs_atomic_read(&d->ld_ref) == 0, - "Refcount is %u\n", cfs_atomic_read(&d->ld_ref)); - LASSERT(t->ldt_device_nr > 0); - if (--t->ldt_device_nr == 0 && t->ldt_ops->ldto_stop != NULL) - t->ldt_ops->ldto_stop(t); + lu_ref_fini(&d->ld_reference); + LASSERTF(atomic_read(&d->ld_ref) == 0, + "Refcount is %u\n", atomic_read(&d->ld_ref)); + LASSERT(atomic_read(&t->ldt_device_nr) > 0); + + if (atomic_dec_and_test(&t->ldt_device_nr) && + t->ldt_ops->ldto_stop != NULL) + t->ldt_ops->ldto_stop(t); } EXPORT_SYMBOL(lu_device_fini); @@ -1214,7 +1316,7 @@ int lu_object_init(struct lu_object *o, struct lu_object_header *h, o->lo_dev = d; lu_device_get(d); lu_ref_add_at(&d->ld_reference, &o->lo_dev_ref, "lu_object", o); - CFS_INIT_LIST_HEAD(&o->lo_linkage); + INIT_LIST_HEAD(&o->lo_linkage); return 0; } @@ -1227,7 +1329,7 @@ void lu_object_fini(struct lu_object *o) { struct lu_device *dev = o->lo_dev; - LASSERT(cfs_list_empty(&o->lo_linkage)); + LASSERT(list_empty(&o->lo_linkage)); if (dev != NULL) { lu_ref_del_at(&dev->ld_reference, &o->lo_dev_ref, @@ -1246,7 +1348,7 @@ EXPORT_SYMBOL(lu_object_fini); */ void lu_object_add_top(struct lu_object_header *h, struct lu_object *o) { - cfs_list_move(&o->lo_linkage, &h->loh_layers); + list_move(&o->lo_linkage, &h->loh_layers); } EXPORT_SYMBOL(lu_object_add_top); @@ -1258,7 +1360,7 @@ EXPORT_SYMBOL(lu_object_add_top); */ void lu_object_add(struct lu_object *before, struct lu_object *o) { - cfs_list_move(&o->lo_linkage, &before->lo_linkage); + list_move(&o->lo_linkage, &before->lo_linkage); } EXPORT_SYMBOL(lu_object_add); @@ -1268,10 +1370,9 @@ EXPORT_SYMBOL(lu_object_add); int lu_object_header_init(struct lu_object_header *h) { memset(h, 0, sizeof *h); - cfs_atomic_set(&h->loh_ref, 1); - CFS_INIT_HLIST_NODE(&h->loh_hash); - CFS_INIT_LIST_HEAD(&h->loh_lru); - CFS_INIT_LIST_HEAD(&h->loh_layers); + atomic_set(&h->loh_ref, 1); + INIT_LIST_HEAD(&h->loh_lru); + INIT_LIST_HEAD(&h->loh_layers); lu_ref_init(&h->loh_reference); return 0; } @@ -1282,32 +1383,40 @@ EXPORT_SYMBOL(lu_object_header_init); */ void lu_object_header_fini(struct lu_object_header *h) { - LASSERT(cfs_list_empty(&h->loh_layers)); - LASSERT(cfs_list_empty(&h->loh_lru)); - LASSERT(cfs_hlist_unhashed(&h->loh_hash)); + LASSERT(list_empty(&h->loh_layers)); + LASSERT(list_empty(&h->loh_lru)); lu_ref_fini(&h->loh_reference); } EXPORT_SYMBOL(lu_object_header_fini); /** + * Free lu_object_header with proper RCU handling + */ +void lu_object_header_free(struct lu_object_header *h) +{ + lu_object_header_fini(h); + OBD_FREE_PRE(h, sizeof(*h), "kfreed"); + kfree_rcu(h, loh_rcu); +} +EXPORT_SYMBOL(lu_object_header_free); + +/** * Given a compound object, find its slice, corresponding to the device type * \a dtype. */ struct lu_object *lu_object_locate(struct lu_object_header *h, const struct lu_device_type *dtype) { - struct lu_object *o; + struct lu_object *o; - cfs_list_for_each_entry(o, &h->loh_layers, lo_linkage) { - if (o->lo_dev->ld_type == dtype) - return o; - } - return NULL; + list_for_each_entry(o, &h->loh_layers, lo_linkage) { + if (o->lo_dev->ld_type == dtype) + return o; + } + return NULL; } EXPORT_SYMBOL(lu_object_locate); - - /** * Finalize and free devices in the device stack. * @@ -1333,28 +1442,10 @@ void lu_stack_fini(const struct lu_env *env, struct lu_device *top) for (scan = top; scan != NULL; scan = next) { const struct lu_device_type *ldt = scan->ld_type; - struct obd_type *type; next = ldt->ldt_ops->ldto_device_free(env, scan); - type = ldt->ldt_obd_type; - if (type != NULL) { - type->typ_refcnt--; - class_put_type(type); - } } } -EXPORT_SYMBOL(lu_stack_fini); - -enum { - /** - * Maximal number of tld slots. - */ - LU_CONTEXT_KEY_NR = 40 -}; - -static struct lu_context_key *lu_keys[LU_CONTEXT_KEY_NR] = { NULL, }; - -static DEFINE_SPINLOCK(lu_keys_guard); /** * Global counter incremented whenever key is registered, unregistered, @@ -1362,15 +1453,15 @@ static DEFINE_SPINLOCK(lu_keys_guard); * lu_context_refill(). No locking is provided, as initialization and shutdown * are supposed to be externally serialized. */ -static unsigned key_set_version = 0; +static atomic_t key_set_version = ATOMIC_INIT(0); /** * Register new key. */ int lu_context_key_register(struct lu_context_key *key) { - int result; - int i; + int result; + unsigned int i; LASSERT(key->lct_init != NULL); LASSERT(key->lct_fini != NULL); @@ -1378,19 +1469,27 @@ int lu_context_key_register(struct lu_context_key *key) LASSERT(key->lct_owner != NULL); result = -ENFILE; - spin_lock(&lu_keys_guard); + atomic_set(&key->lct_used, 1); + lu_ref_init(&key->lct_reference); for (i = 0; i < ARRAY_SIZE(lu_keys); ++i) { - if (lu_keys[i] == NULL) { - key->lct_index = i; - cfs_atomic_set(&key->lct_used, 1); - lu_keys[i] = key; - lu_ref_init(&key->lct_reference); - result = 0; - ++key_set_version; - break; - } + if (lu_keys[i]) + continue; + key->lct_index = i; + + if (strncmp("osd_", module_name(key->lct_owner), 4) == 0) + CFS_RACE_WAIT(OBD_FAIL_OBD_SETUP); + + if (cmpxchg(&lu_keys[i], NULL, key) != NULL) + continue; + + result = 0; + atomic_inc(&key_set_version); + break; } - spin_unlock(&lu_keys_guard); + if (result) { + lu_ref_fini(&key->lct_reference); + atomic_set(&key->lct_used, 0); + } return result; } EXPORT_SYMBOL(lu_context_key_register); @@ -1403,11 +1502,12 @@ static void key_fini(struct lu_context *ctx, int index) key = lu_keys[index]; LASSERT(key != NULL); LASSERT(key->lct_fini != NULL); - LASSERT(cfs_atomic_read(&key->lct_used) > 1); + LASSERT(atomic_read(&key->lct_used) > 0); key->lct_fini(ctx, key, ctx->lc_value[index]); lu_ref_del(&key->lct_reference, "ctx", ctx); - cfs_atomic_dec(&key->lct_used); + if (atomic_dec_and_test(&key->lct_used)) + wake_up_var(&key->lct_used); LASSERT(key->lct_owner != NULL); if ((ctx->lc_tags & LCT_NOREF) == 0) { @@ -1423,23 +1523,24 @@ static void key_fini(struct lu_context *ctx, int index) */ void lu_context_key_degister(struct lu_context_key *key) { - LASSERT(cfs_atomic_read(&key->lct_used) >= 1); + LASSERT(atomic_read(&key->lct_used) >= 1); LINVRNT(0 <= key->lct_index && key->lct_index < ARRAY_SIZE(lu_keys)); - lu_context_key_quiesce(key); + lu_context_key_quiesce(NULL, key); - ++key_set_version; - spin_lock(&lu_keys_guard); key_fini(&lu_shrink_env.le_ctx, key->lct_index); - if (lu_keys[key->lct_index]) { - lu_keys[key->lct_index] = NULL; + + /** + * Wait until all transient contexts referencing this key have + * run lu_context_key::lct_fini() method. + */ + atomic_dec(&key->lct_used); + wait_var_event(&key->lct_used, atomic_read(&key->lct_used) == 0); + + if (!WARN_ON(lu_keys[key->lct_index] == NULL)) lu_ref_fini(&key->lct_reference); - } - spin_unlock(&lu_keys_guard); - LASSERTF(cfs_atomic_read(&key->lct_used) == 1, - "key has instances: %d\n", - cfs_atomic_read(&key->lct_used)); + smp_store_release(&lu_keys[key->lct_index], NULL); } EXPORT_SYMBOL(lu_context_key_degister); @@ -1511,16 +1612,17 @@ EXPORT_SYMBOL(lu_context_key_revive_many); /** * Quiescent a number of keys. */ -void lu_context_key_quiesce_many(struct lu_context_key *k, ...) +void lu_context_key_quiesce_many(struct lu_device_type *t, + struct lu_context_key *k, ...) { - va_list args; + va_list args; - va_start(args, k); - do { - lu_context_key_quiesce(k); - k = va_arg(args, struct lu_context_key*); - } while (k != NULL); - va_end(args); + va_start(args, k); + do { + lu_context_key_quiesce(t, k); + k = va_arg(args, struct lu_context_key*); + } while (k != NULL); + va_end(args); } EXPORT_SYMBOL(lu_context_key_quiesce_many); @@ -1540,45 +1642,53 @@ EXPORT_SYMBOL(lu_context_key_get); /** * List of remembered contexts. XXX document me. */ -static CFS_LIST_HEAD(lu_context_remembered); +static LIST_HEAD(lu_context_remembered); +static DEFINE_SPINLOCK(lu_context_remembered_guard); /** * Destroy \a key in all remembered contexts. This is used to destroy key * values in "shared" contexts (like service threads), when a module owning * the key is about to be unloaded. */ -void lu_context_key_quiesce(struct lu_context_key *key) -{ - struct lu_context *ctx; - - if (!(key->lct_tags & LCT_QUIESCENT)) { - /* - * XXX layering violation. - */ - key->lct_tags |= LCT_QUIESCENT; - /* - * XXX memory barrier has to go here. - */ - spin_lock(&lu_keys_guard); - cfs_list_for_each_entry(ctx, &lu_context_remembered, - lc_remember) +void lu_context_key_quiesce(struct lu_device_type *t, + struct lu_context_key *key) +{ + struct lu_context *ctx; + + if (key->lct_tags & LCT_QUIESCENT) + return; + /* + * The write-lock on lu_key_initing will ensure that any + * keys_fill() which didn't see LCT_QUIESCENT will have + * finished before we call key_fini(). + */ + down_write(&lu_key_initing); + if (!(key->lct_tags & LCT_QUIESCENT)) { + if (t == NULL || atomic_read(&t->ldt_device_nr) == 0) + key->lct_tags |= LCT_QUIESCENT; + up_write(&lu_key_initing); + + spin_lock(&lu_context_remembered_guard); + list_for_each_entry(ctx, &lu_context_remembered, lc_remember) { + spin_until_cond(READ_ONCE(ctx->lc_state) != LCS_LEAVING); key_fini(ctx, key->lct_index); - spin_unlock(&lu_keys_guard); - ++key_set_version; + } + spin_unlock(&lu_context_remembered_guard); + + return; } + up_write(&lu_key_initing); } -EXPORT_SYMBOL(lu_context_key_quiesce); void lu_context_key_revive(struct lu_context_key *key) { - key->lct_tags &= ~LCT_QUIESCENT; - ++key_set_version; + key->lct_tags &= ~LCT_QUIESCENT; + atomic_inc(&key_set_version); } -EXPORT_SYMBOL(lu_context_key_revive); static void keys_fini(struct lu_context *ctx) { - int i; + unsigned int i; if (ctx->lc_value == NULL) return; @@ -1586,57 +1696,75 @@ static void keys_fini(struct lu_context *ctx) for (i = 0; i < ARRAY_SIZE(lu_keys); ++i) key_fini(ctx, i); - OBD_FREE(ctx->lc_value, ARRAY_SIZE(lu_keys) * sizeof ctx->lc_value[0]); + OBD_FREE_PTR_ARRAY(ctx->lc_value, ARRAY_SIZE(lu_keys)); ctx->lc_value = NULL; } static int keys_fill(struct lu_context *ctx) { - int i; + unsigned int i; + int rc = 0; - LINVRNT(ctx->lc_value != NULL); - for (i = 0; i < ARRAY_SIZE(lu_keys); ++i) { - struct lu_context_key *key; - - key = lu_keys[i]; - if (ctx->lc_value[i] == NULL && key != NULL && - (key->lct_tags & ctx->lc_tags) && - /* - * Don't create values for a LCT_QUIESCENT key, as this - * will pin module owning a key. - */ - !(key->lct_tags & LCT_QUIESCENT)) { - void *value; - - LINVRNT(key->lct_init != NULL); - LINVRNT(key->lct_index == i); - - value = key->lct_init(ctx, key); - if (unlikely(IS_ERR(value))) - return PTR_ERR(value); + /* + * A serialisation with lu_context_key_quiesce() is needed, to + * ensure we see LCT_QUIESCENT and don't allocate a new value + * after it freed one. The rwsem provides this. As down_read() + * does optimistic spinning while the writer is active, this is + * unlikely to ever sleep. + */ + down_read(&lu_key_initing); + ctx->lc_version = atomic_read(&key_set_version); + + LINVRNT(ctx->lc_value); + for (i = 0; i < ARRAY_SIZE(lu_keys); ++i) { + struct lu_context_key *key; + + key = lu_keys[i]; + if (!ctx->lc_value[i] && key && + (key->lct_tags & ctx->lc_tags) && + /* + * Don't create values for a LCT_QUIESCENT key, as this + * will pin module owning a key. + */ + !(key->lct_tags & LCT_QUIESCENT)) { + void *value; + + LINVRNT(key->lct_init != NULL); + LINVRNT(key->lct_index == i); LASSERT(key->lct_owner != NULL); - if (!(ctx->lc_tags & LCT_NOREF)) - try_module_get(key->lct_owner); + if (!(ctx->lc_tags & LCT_NOREF) && + try_module_get(key->lct_owner) == 0) { + /* module is unloading, skip this key */ + continue; + } + + value = key->lct_init(ctx, key); + if (unlikely(IS_ERR(value))) { + rc = PTR_ERR(value); + break; + } + lu_ref_add_atomic(&key->lct_reference, "ctx", ctx); - cfs_atomic_inc(&key->lct_used); - /* - * This is the only place in the code, where an - * element of ctx->lc_value[] array is set to non-NULL - * value. - */ - ctx->lc_value[i] = value; - if (key->lct_exit != NULL) - ctx->lc_tags |= LCT_HAS_EXIT; - } - ctx->lc_version = key_set_version; - } - return 0; + atomic_inc(&key->lct_used); + /* + * This is the only place in the code, where an + * element of ctx->lc_value[] array is set to non-NULL + * value. + */ + ctx->lc_value[i] = value; + if (key->lct_exit != NULL) + ctx->lc_tags |= LCT_HAS_EXIT; + } + } + + up_read(&lu_key_initing); + return rc; } static int keys_init(struct lu_context *ctx) { - OBD_ALLOC(ctx->lc_value, ARRAY_SIZE(lu_keys) * sizeof ctx->lc_value[0]); + OBD_ALLOC_PTR_ARRAY(ctx->lc_value, ARRAY_SIZE(lu_keys)); if (likely(ctx->lc_value != NULL)) return keys_fill(ctx); @@ -1654,11 +1782,11 @@ int lu_context_init(struct lu_context *ctx, __u32 tags) ctx->lc_state = LCS_INITIALIZED; ctx->lc_tags = tags; if (tags & LCT_REMEMBER) { - spin_lock(&lu_keys_guard); - cfs_list_add(&ctx->lc_remember, &lu_context_remembered); - spin_unlock(&lu_keys_guard); + spin_lock(&lu_context_remembered_guard); + list_add(&ctx->lc_remember, &lu_context_remembered); + spin_unlock(&lu_context_remembered_guard); } else { - CFS_INIT_LIST_HEAD(&ctx->lc_remember); + INIT_LIST_HEAD(&ctx->lc_remember); } rc = keys_init(ctx); @@ -1678,15 +1806,14 @@ void lu_context_fini(struct lu_context *ctx) ctx->lc_state = LCS_FINALIZED; if ((ctx->lc_tags & LCT_REMEMBER) == 0) { - LASSERT(cfs_list_empty(&ctx->lc_remember)); - keys_fini(ctx); - - } else { /* could race with key degister */ - spin_lock(&lu_keys_guard); - keys_fini(ctx); - cfs_list_del_init(&ctx->lc_remember); - spin_unlock(&lu_keys_guard); + LASSERT(list_empty(&ctx->lc_remember)); + } else { + /* could race with key degister */ + spin_lock(&lu_context_remembered_guard); + list_del_init(&ctx->lc_remember); + spin_unlock(&lu_context_remembered_guard); } + keys_fini(ctx); } EXPORT_SYMBOL(lu_context_fini); @@ -1705,23 +1832,37 @@ EXPORT_SYMBOL(lu_context_enter); */ void lu_context_exit(struct lu_context *ctx) { - int i; + unsigned int i; - LINVRNT(ctx->lc_state == LCS_ENTERED); - ctx->lc_state = LCS_LEFT; - if (ctx->lc_tags & LCT_HAS_EXIT && ctx->lc_value != NULL) { + LINVRNT(ctx->lc_state == LCS_ENTERED); + /* + * Disable preempt to ensure we get a warning if + * any lct_exit ever tries to sleep. That would hurt + * lu_context_key_quiesce() which spins waiting for us. + * This also ensure we aren't preempted while the state + * is LCS_LEAVING, as that too would cause problems for + * lu_context_key_quiesce(). + */ + preempt_disable(); + /* + * Ensure lu_context_key_quiesce() sees LCS_LEAVING + * or we see LCT_QUIESCENT + */ + smp_store_mb(ctx->lc_state, LCS_LEAVING); + if (ctx->lc_tags & LCT_HAS_EXIT && ctx->lc_value) { for (i = 0; i < ARRAY_SIZE(lu_keys); ++i) { - if (ctx->lc_value[i] != NULL) { - struct lu_context_key *key; - - key = lu_keys[i]; - LASSERT(key != NULL); - if (key->lct_exit != NULL) - key->lct_exit(ctx, - key, ctx->lc_value[i]); - } - } + struct lu_context_key *key; + + key = lu_keys[i]; + if (ctx->lc_value[i] && + !(key->lct_tags & LCT_QUIESCENT) && + key->lct_exit) + key->lct_exit(ctx, key, ctx->lc_value[i]); + } } + + smp_store_release(&ctx->lc_state, LCS_LEFT); + preempt_enable(); } EXPORT_SYMBOL(lu_context_exit); @@ -1732,9 +1873,11 @@ EXPORT_SYMBOL(lu_context_exit); */ int lu_context_refill(struct lu_context *ctx) { - return likely(ctx->lc_version == key_set_version) ? 0 : keys_fill(ctx); + if (likely(ctx->lc_version == atomic_read(&key_set_version))) + return 0; + + return keys_fill(ctx); } -EXPORT_SYMBOL(lu_context_refill); /** * lu_ctx_tags/lu_ses_tags will be updated if there are new types of @@ -1743,42 +1886,42 @@ EXPORT_SYMBOL(lu_context_refill); * predefined when the lu_device type are registered, during the module probe * phase. */ -__u32 lu_context_tags_default = 0; -__u32 lu_session_tags_default = 0; +u32 lu_context_tags_default = LCT_CL_THREAD; +u32 lu_session_tags_default = LCT_SESSION; void lu_context_tags_update(__u32 tags) { - spin_lock(&lu_keys_guard); + spin_lock(&lu_context_remembered_guard); lu_context_tags_default |= tags; - key_set_version++; - spin_unlock(&lu_keys_guard); + atomic_inc(&key_set_version); + spin_unlock(&lu_context_remembered_guard); } EXPORT_SYMBOL(lu_context_tags_update); void lu_context_tags_clear(__u32 tags) { - spin_lock(&lu_keys_guard); + spin_lock(&lu_context_remembered_guard); lu_context_tags_default &= ~tags; - key_set_version++; - spin_unlock(&lu_keys_guard); + atomic_inc(&key_set_version); + spin_unlock(&lu_context_remembered_guard); } EXPORT_SYMBOL(lu_context_tags_clear); void lu_session_tags_update(__u32 tags) { - spin_lock(&lu_keys_guard); + spin_lock(&lu_context_remembered_guard); lu_session_tags_default |= tags; - key_set_version++; - spin_unlock(&lu_keys_guard); + atomic_inc(&key_set_version); + spin_unlock(&lu_context_remembered_guard); } EXPORT_SYMBOL(lu_session_tags_update); void lu_session_tags_clear(__u32 tags) { - spin_lock(&lu_keys_guard); + spin_lock(&lu_context_remembered_guard); lu_session_tags_default &= ~tags; - key_set_version++; - spin_unlock(&lu_keys_guard); + atomic_inc(&key_set_version); + spin_unlock(&lu_context_remembered_guard); } EXPORT_SYMBOL(lu_session_tags_clear); @@ -1841,6 +1984,119 @@ int lu_env_refill_by_tags(struct lu_env *env, __u32 ctags, } EXPORT_SYMBOL(lu_env_refill_by_tags); + +struct lu_env_item { + struct task_struct *lei_task; /* rhashtable key */ + struct rhash_head lei_linkage; + struct lu_env *lei_env; + struct rcu_head lei_rcu_head; +}; + +static const struct rhashtable_params lu_env_rhash_params = { + .key_len = sizeof(struct task_struct *), + .key_offset = offsetof(struct lu_env_item, lei_task), + .head_offset = offsetof(struct lu_env_item, lei_linkage), + }; + +struct rhashtable lu_env_rhash; + +struct lu_env_percpu { + struct task_struct *lep_task; + struct lu_env *lep_env ____cacheline_aligned_in_smp; +}; + +static struct lu_env_percpu lu_env_percpu[NR_CPUS]; + +int lu_env_add_task(struct lu_env *env, struct task_struct *task) +{ + struct lu_env_item *lei, *old; + + LASSERT(env); + + OBD_ALLOC_PTR(lei); + if (!lei) + return -ENOMEM; + + lei->lei_task = task; + lei->lei_env = env; + + old = rhashtable_lookup_get_insert_fast(&lu_env_rhash, + &lei->lei_linkage, + lu_env_rhash_params); + LASSERT(!old); + + return 0; +} +EXPORT_SYMBOL(lu_env_add_task); + +int lu_env_add(struct lu_env *env) +{ + return lu_env_add_task(env, current); +} +EXPORT_SYMBOL(lu_env_add); + +static void lu_env_item_free(struct rcu_head *head) +{ + struct lu_env_item *lei; + + lei = container_of(head, struct lu_env_item, lei_rcu_head); + OBD_FREE_PTR(lei); +} + +void lu_env_remove(struct lu_env *env) +{ + struct lu_env_item *lei; + const void *task = current; + int i; + + for_each_possible_cpu(i) { + if (lu_env_percpu[i].lep_env == env) { + LASSERT(lu_env_percpu[i].lep_task == task); + lu_env_percpu[i].lep_task = NULL; + lu_env_percpu[i].lep_env = NULL; + } + } + + /* The rcu_lock is not taking in this case since the key + * used is the actual task_struct. This implies that each + * object is only removed by the owning thread, so there + * can never be a race on a particular object. + */ + lei = rhashtable_lookup_fast(&lu_env_rhash, &task, + lu_env_rhash_params); + if (lei && rhashtable_remove_fast(&lu_env_rhash, &lei->lei_linkage, + lu_env_rhash_params) == 0) + call_rcu(&lei->lei_rcu_head, lu_env_item_free); +} +EXPORT_SYMBOL(lu_env_remove); + +struct lu_env *lu_env_find(void) +{ + struct lu_env *env = NULL; + struct lu_env_item *lei; + const void *task = current; + int i = get_cpu(); + + if (lu_env_percpu[i].lep_task == current) { + env = lu_env_percpu[i].lep_env; + put_cpu(); + LASSERT(env); + return env; + } + + lei = rhashtable_lookup_fast(&lu_env_rhash, &task, + lu_env_rhash_params); + if (lei) { + env = lei->lei_env; + lu_env_percpu[i].lep_task = current; + lu_env_percpu[i].lep_env = env; + } + put_cpu(); + + return env; +} +EXPORT_SYMBOL(lu_env_find); + static struct shrinker *lu_site_shrinker; typedef struct lu_site_stats{ @@ -1850,37 +2106,102 @@ typedef struct lu_site_stats{ unsigned lss_busy; } lu_site_stats_t; -static void lu_site_stats_get(cfs_hash_t *hs, - lu_site_stats_t *stats, int populated) +static void lu_site_stats_get(const struct lu_site *s, + lu_site_stats_t *stats) { - cfs_hash_bd_t bd; - int i; - - cfs_hash_for_each_bucket(hs, &bd, i) { - struct lu_site_bkt_data *bkt = cfs_hash_bd_extra_get(hs, &bd); - cfs_hlist_head_t *hhead; + int cnt = atomic_read(&s->ls_obj_hash.nelems); + /* + * percpu_counter_sum_positive() won't accept a const pointer + * as it does modify the struct by taking a spinlock + */ + struct lu_site *s2 = (struct lu_site *)s; - cfs_hash_bd_lock(hs, &bd, 1); - stats->lss_busy += bkt->lsb_busy; - stats->lss_total += cfs_hash_bd_count_get(&bd); - stats->lss_max_search = max((int)stats->lss_max_search, - cfs_hash_bd_depmax_get(&bd)); - if (!populated) { - cfs_hash_bd_unlock(hs, &bd, 1); - continue; - } + stats->lss_busy += cnt - + percpu_counter_sum_positive(&s2->ls_lru_len_counter); - cfs_hash_bd_for_each_hlist(hs, &bd, hhead) { - if (!cfs_hlist_empty(hhead)) - stats->lss_populated++; - } - cfs_hash_bd_unlock(hs, &bd, 1); - } + stats->lss_total += cnt; + stats->lss_max_search = 0; + stats->lss_populated = 0; } -#ifdef __KERNEL__ /* + * lu_cache_shrink_count() returns an approximate number of cached objects + * that can be freed by shrink_slab(). A counter, which tracks the + * number of items in the site's lru, is maintained in a percpu_counter + * for each site. The percpu values are incremented and decremented as + * objects are added or removed from the lru. The percpu values are summed + * and saved whenever a percpu value exceeds a threshold. Thus the saved, + * summed value at any given time may not accurately reflect the current + * lru length. But this value is sufficiently accurate for the needs of + * a shrinker. + * + * Using a per cpu counter is a compromise solution to concurrent access: + * lu_object_put() can update the counter without locking the site and + * lu_cache_shrink_count can sum the counters without locking each + * ls_obj_hash bucket. + */ +static unsigned long lu_cache_shrink_count(struct shrinker *sk, + struct shrink_control *sc) +{ + struct lu_site *s; + struct lu_site *tmp; + unsigned long cached = 0; + + if (!(sc->gfp_mask & __GFP_FS)) + return 0; + + down_read(&lu_sites_guard); + list_for_each_entry_safe(s, tmp, &lu_sites, ls_linkage) + cached += percpu_counter_read_positive(&s->ls_lru_len_counter); + up_read(&lu_sites_guard); + + cached = (cached / 100) * sysctl_vfs_cache_pressure; + CDEBUG(D_INODE, "%ld objects cached, cache pressure %d\n", + cached, sysctl_vfs_cache_pressure); + + return cached; +} + +static unsigned long lu_cache_shrink_scan(struct shrinker *sk, + struct shrink_control *sc) +{ + struct lu_site *s; + struct lu_site *tmp; + unsigned long remain = sc->nr_to_scan; + LIST_HEAD(splice); + + if (!(sc->gfp_mask & __GFP_FS)) + /* We must not take the lu_sites_guard lock when + * __GFP_FS is *not* set because of the deadlock + * possibility detailed above. Additionally, + * since we cannot determine the number of + * objects in the cache without taking this + * lock, we're in a particularly tough spot. As + * a result, we'll just lie and say our cache is + * empty. This _should_ be ok, as we can't + * reclaim objects when __GFP_FS is *not* set + * anyways. + */ + return SHRINK_STOP; + + down_write(&lu_sites_guard); + list_for_each_entry_safe(s, tmp, &lu_sites, ls_linkage) { + remain = lu_site_purge(&lu_shrink_env, s, remain); + /* + * Move just shrunk site to the tail of site list to + * assure shrinking fairness. + */ + list_move_tail(&s->ls_linkage, &splice); + } + list_splice(&splice, lu_sites.prev); + up_write(&lu_sites_guard); + + return sc->nr_to_scan - remain; +} + +#ifndef HAVE_SHRINKER_COUNT +/* * There exists a potential lock inversion deadlock scenario when using * Lustre on top of ZFS. This occurs between one of ZFS's * buf_hash_table.ht_lock's, and Lustre's lu_sites_guard lock. Essentially, @@ -1895,64 +2216,29 @@ static void lu_site_stats_get(cfs_hash_t *hs, * is safe to take the lu_sites_guard lock. * * Ideally we should accurately return the remaining number of cached - * objects without taking the lu_sites_guard lock, but this is not + * objects without taking the lu_sites_guard lock, but this is not * possible in the current implementation. */ static int lu_cache_shrink(SHRINKER_ARGS(sc, nr_to_scan, gfp_mask)) { - lu_site_stats_t stats; - struct lu_site *s; - struct lu_site *tmp; int cached = 0; - int remain = shrink_param(sc, nr_to_scan); - CFS_LIST_HEAD(splice); + struct shrink_control scv = { + .nr_to_scan = shrink_param(sc, nr_to_scan), + .gfp_mask = shrink_param(sc, gfp_mask) + }; - if (!(shrink_param(sc, gfp_mask) & __GFP_FS)) { - if (remain != 0) - return -1; - else - /* We must not take the lu_sites_guard lock when - * __GFP_FS is *not* set because of the deadlock - * possibility detailed above. Additionally, - * since we cannot determine the number of - * objects in the cache without taking this - * lock, we're in a particularly tough spot. As - * a result, we'll just lie and say our cache is - * empty. This _should_ be ok, as we can't - * reclaim objects when __GFP_FS is *not* set - * anyways. - */ - return 0; - } - - CDEBUG(D_INODE, "Shrink %d objects\n", remain); - - mutex_lock(&lu_sites_guard); - cfs_list_for_each_entry_safe(s, tmp, &lu_sites, ls_linkage) { - if (shrink_param(sc, nr_to_scan) != 0) { - remain = lu_site_purge(&lu_shrink_env, s, remain); - /* - * Move just shrunk site to the tail of site list to - * assure shrinking fairness. - */ - cfs_list_move_tail(&s->ls_linkage, &splice); - } + CDEBUG(D_INODE, "Shrink %lu objects\n", scv.nr_to_scan); - memset(&stats, 0, sizeof(stats)); - lu_site_stats_get(s->ls_obj_hash, &stats, 0); - cached += stats.lss_total - stats.lss_busy; - if (shrink_param(sc, nr_to_scan) && remain <= 0) - break; - } - cfs_list_splice(&splice, lu_sites.prev); - mutex_unlock(&lu_sites_guard); + if (scv.nr_to_scan != 0) + lu_cache_shrink_scan(shrinker, &scv); - cached = (cached / 100) * sysctl_vfs_cache_pressure; - if (shrink_param(sc, nr_to_scan) == 0) - CDEBUG(D_INODE, "%d objects cached\n", cached); - return cached; + cached = lu_cache_shrink_count(shrinker, &scv); + return cached; } +#endif /* HAVE_SHRINKER_COUNT */ + + /* * Debugging stuff. */ @@ -1960,13 +2246,13 @@ static int lu_cache_shrink(SHRINKER_ARGS(sc, nr_to_scan, gfp_mask)) /** * Environment to be used in debugger, contains all tags. */ -struct lu_env lu_debugging_env; +static struct lu_env lu_debugging_env; /** * Debugging printer function using printk(). */ int lu_printk_printer(const struct lu_env *env, - void *unused, const char *format, ...) + void *unused, const char *format, ...) { va_list args; @@ -1983,7 +2269,7 @@ int lu_debugging_setup(void) void lu_context_keys_dump(void) { - int i; + unsigned int i; for (i = 0; i < ARRAY_SIZE(lu_keys); ++i) { struct lu_context_key *key; @@ -1993,27 +2279,22 @@ void lu_context_keys_dump(void) CERROR("[%d]: %p %x (%p,%p,%p) %d %d \"%s\"@%p\n", i, key, key->lct_tags, key->lct_init, key->lct_fini, key->lct_exit, - key->lct_index, cfs_atomic_read(&key->lct_used), + key->lct_index, atomic_read(&key->lct_used), key->lct_owner ? key->lct_owner->name : "", key->lct_owner); lu_ref_print(&key->lct_reference); } } } -EXPORT_SYMBOL(lu_context_keys_dump); -#else /* !__KERNEL__ */ -static int lu_cache_shrink(int nr, unsigned int gfp_mask) -{ - return 0; -} -#endif /* __KERNEL__ */ /** * Initialization of global lu_* data. */ int lu_global_init(void) { - int result; + int result; + DEF_SHRINKER_VAR(shvar, lu_cache_shrink, + lu_cache_shrink_count, lu_cache_shrink_scan); CDEBUG(D_INFO, "Lustre LU module (%p).\n", &lu_keys); @@ -2031,9 +2312,9 @@ int lu_global_init(void) * conservatively. This should not be too bad, because this * environment is global. */ - mutex_lock(&lu_sites_guard); + down_write(&lu_sites_guard); result = lu_env_init(&lu_shrink_env, LCT_SHRINKER); - mutex_unlock(&lu_sites_guard); + up_write(&lu_sites_guard); if (result != 0) return result; @@ -2042,10 +2323,12 @@ int lu_global_init(void) * inode, one for ea. Unfortunately setting this high value results in * lu_object/inode cache consuming all the memory. */ - lu_site_shrinker = set_shrinker(DEFAULT_SEEKS, lu_cache_shrink); + lu_site_shrinker = set_shrinker(DEFAULT_SEEKS, &shvar); if (lu_site_shrinker == NULL) return -ENOMEM; + result = rhashtable_init(&lu_env_rhash, &lu_env_rhash_params); + return result; } @@ -2065,22 +2348,24 @@ void lu_global_fini(void) * Tear shrinker environment down _after_ de-registering * lu_global_key, because the latter has a value in the former. */ - mutex_lock(&lu_sites_guard); + down_write(&lu_sites_guard); lu_env_fini(&lu_shrink_env); - mutex_unlock(&lu_sites_guard); + up_write(&lu_sites_guard); + + rhashtable_destroy(&lu_env_rhash); lu_ref_global_fini(); } static __u32 ls_stats_read(struct lprocfs_stats *stats, int idx) { -#ifdef LPROCFS - struct lprocfs_counter ret; +#ifdef CONFIG_PROC_FS + struct lprocfs_counter ret; - lprocfs_stats_collect(stats, idx, &ret); - return (__u32)ret.lc_count; + lprocfs_stats_collect(stats, idx, &ret); + return (__u32)ret.lc_count; #else - return 0; + return 0; #endif } @@ -2088,27 +2373,35 @@ static __u32 ls_stats_read(struct lprocfs_stats *stats, int idx) * Output site statistical counters into a buffer. Suitable for * lprocfs_rd_*()-style functions. */ -int lu_site_stats_print(const struct lu_site *s, char *page, int count) -{ - lu_site_stats_t stats; - - memset(&stats, 0, sizeof(stats)); - lu_site_stats_get(s->ls_obj_hash, &stats, 1); - - return snprintf(page, count, "%d/%d %d/%d %d %d %d %d %d %d %d\n", - stats.lss_busy, - stats.lss_total, - stats.lss_populated, - CFS_HASH_NHLIST(s->ls_obj_hash), - stats.lss_max_search, - ls_stats_read(s->ls_stats, LU_SS_CREATED), - ls_stats_read(s->ls_stats, LU_SS_CACHE_HIT), - ls_stats_read(s->ls_stats, LU_SS_CACHE_MISS), - ls_stats_read(s->ls_stats, LU_SS_CACHE_RACE), - ls_stats_read(s->ls_stats, LU_SS_CACHE_DEATH_RACE), - ls_stats_read(s->ls_stats, LU_SS_LRU_PURGED)); +int lu_site_stats_seq_print(const struct lu_site *s, struct seq_file *m) +{ + const struct bucket_table *tbl; + lu_site_stats_t stats; + unsigned int chains; + + memset(&stats, 0, sizeof(stats)); + lu_site_stats_get(s, &stats); + + rcu_read_lock(); + tbl = rht_dereference_rcu(s->ls_obj_hash.tbl, + &((struct lu_site *)s)->ls_obj_hash); + chains = tbl->size; + rcu_read_unlock(); + seq_printf(m, "%d/%d %d/%u %d %d %d %d %d %d %d\n", + stats.lss_busy, + stats.lss_total, + stats.lss_populated, + chains, + stats.lss_max_search, + ls_stats_read(s->ls_stats, LU_SS_CREATED), + ls_stats_read(s->ls_stats, LU_SS_CACHE_HIT), + ls_stats_read(s->ls_stats, LU_SS_CACHE_MISS), + ls_stats_read(s->ls_stats, LU_SS_CACHE_RACE), + ls_stats_read(s->ls_stats, LU_SS_CACHE_DEATH_RACE), + ls_stats_read(s->ls_stats, LU_SS_LRU_PURGED)); + return 0; } -EXPORT_SYMBOL(lu_site_stats_print); +EXPORT_SYMBOL(lu_site_stats_seq_print); /** * Helper function to initialize a number of kmem slab caches at once. @@ -2157,25 +2450,27 @@ void lu_object_assign_fid(const struct lu_env *env, struct lu_object *o, { struct lu_site *s = o->lo_dev->ld_site; struct lu_fid *old = &o->lo_header->loh_fid; - struct lu_site_bkt_data *bkt; - struct lu_object *shadow; - wait_queue_t waiter; - cfs_hash_t *hs; - cfs_hash_bd_t bd; - __u64 version = 0; + int rc; LASSERT(fid_is_zero(old)); - - hs = s->ls_obj_hash; - cfs_hash_bd_get_and_lock(hs, (void *)fid, &bd, 1); - shadow = htable_lookup(s, &bd, fid, &waiter, &version); - /* supposed to be unique */ - LASSERT(IS_ERR(shadow) && PTR_ERR(shadow) == -ENOENT); *old = *fid; - bkt = cfs_hash_bd_extra_get(hs, &bd); - cfs_hash_bd_add_locked(hs, &bd, &o->lo_header->loh_hash); - bkt->lsb_busy++; - cfs_hash_bd_unlock(hs, &bd, 1); +try_again: + rc = rhashtable_lookup_insert_fast(&s->ls_obj_hash, + &o->lo_header->loh_hash, + obj_hash_params); + /* supposed to be unique */ + LASSERT(rc != -EEXIST); + /* handle hash table resizing */ + if (rc == -ENOMEM) { + msleep(20); + goto try_again; + } + /* trim the hash if its growing to big */ + lu_object_limit(env, o->lo_dev); + if (rc == -E2BIG) + goto try_again; + + LASSERTF(rc == 0, "failed hashtable insertion: rc = %d\n", rc); } EXPORT_SYMBOL(lu_object_assign_fid); @@ -2188,11 +2483,19 @@ struct lu_object *lu_object_anon(const struct lu_env *env, struct lu_device *dev, const struct lu_object_conf *conf) { - struct lu_fid fid; + struct lu_fid fid; struct lu_object *o; + int rc; fid_zero(&fid); - o = lu_object_alloc(env, dev, &fid, conf); + o = lu_object_alloc(env, dev, &fid); + if (!IS_ERR(o)) { + rc = lu_object_start(env, dev, o, conf); + if (rc) { + lu_object_free(env, o); + return ERR_PTR(rc); + } + } return o; } @@ -2216,7 +2519,7 @@ void lu_buf_free(struct lu_buf *buf) } EXPORT_SYMBOL(lu_buf_free); -void lu_buf_alloc(struct lu_buf *buf, int size) +void lu_buf_alloc(struct lu_buf *buf, size_t size) { LASSERT(buf); LASSERT(buf->lb_buf == NULL); @@ -2227,14 +2530,14 @@ void lu_buf_alloc(struct lu_buf *buf, int size) } EXPORT_SYMBOL(lu_buf_alloc); -void lu_buf_realloc(struct lu_buf *buf, int size) +void lu_buf_realloc(struct lu_buf *buf, size_t size) { lu_buf_free(buf); lu_buf_alloc(buf, size); } EXPORT_SYMBOL(lu_buf_realloc); -struct lu_buf *lu_buf_check_and_alloc(struct lu_buf *buf, int len) +struct lu_buf *lu_buf_check_and_alloc(struct lu_buf *buf, size_t len) { if (buf->lb_buf == NULL && buf->lb_len == 0) lu_buf_alloc(buf, len); @@ -2252,7 +2555,7 @@ EXPORT_SYMBOL(lu_buf_check_and_alloc); * old buffer remains unchanged on error * \retval 0 or -ENOMEM */ -int lu_buf_check_and_grow(struct lu_buf *buf, int len) +int lu_buf_check_and_grow(struct lu_buf *buf, size_t len) { char *ptr; @@ -2274,4 +2577,3 @@ int lu_buf_check_and_grow(struct lu_buf *buf, int len) return 0; } EXPORT_SYMBOL(lu_buf_check_and_grow); -