| /* |
| * GPL HEADER START |
| * |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 only, |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License version 2 for more details (a copy is included |
| * in the LICENSE file that accompanied this code). |
| * |
| * 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. |
| * |
| * GPL HEADER END |
| */ |
| /* |
| * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Use is subject to license terms. |
| * |
| * Copyright (c) 2011, 2012, Intel Corporation. |
| */ |
| /* |
| * This file is part of Lustre, http://www.lustre.org/ |
| * Lustre is a trademark of Sun Microsystems, Inc. |
| * |
| * lustre/obdclass/lustre_handles.c |
| * |
| * Author: Phil Schwan <phil@clusterfs.com> |
| */ |
| |
| #define DEBUG_SUBSYSTEM S_CLASS |
| |
| #include "../include/obd_support.h" |
| #include "../include/lustre_handles.h" |
| #include "../include/lustre_lib.h" |
| |
| static __u64 handle_base; |
| #define HANDLE_INCR 7 |
| static spinlock_t handle_base_lock; |
| |
| static struct handle_bucket { |
| spinlock_t lock; |
| struct list_head head; |
| } *handle_hash; |
| |
| #define HANDLE_HASH_SIZE (1 << 16) |
| #define HANDLE_HASH_MASK (HANDLE_HASH_SIZE - 1) |
| |
| /* |
| * Generate a unique 64bit cookie (hash) for a handle and insert it into |
| * global (per-node) hash-table. |
| */ |
| void class_handle_hash(struct portals_handle *h, |
| struct portals_handle_ops *ops) |
| { |
| struct handle_bucket *bucket; |
| |
| LASSERT(h); |
| LASSERT(list_empty(&h->h_link)); |
| |
| /* |
| * This is fast, but simplistic cookie generation algorithm, it will |
| * need a re-do at some point in the future for security. |
| */ |
| spin_lock(&handle_base_lock); |
| handle_base += HANDLE_INCR; |
| |
| if (unlikely(handle_base == 0)) { |
| /* |
| * Cookie of zero is "dangerous", because in many places it's |
| * assumed that 0 means "unassigned" handle, not bound to any |
| * object. |
| */ |
| CWARN("The universe has been exhausted: cookie wrap-around.\n"); |
| handle_base += HANDLE_INCR; |
| } |
| h->h_cookie = handle_base; |
| spin_unlock(&handle_base_lock); |
| |
| h->h_ops = ops; |
| spin_lock_init(&h->h_lock); |
| |
| bucket = &handle_hash[h->h_cookie & HANDLE_HASH_MASK]; |
| spin_lock(&bucket->lock); |
| list_add_rcu(&h->h_link, &bucket->head); |
| h->h_in = 1; |
| spin_unlock(&bucket->lock); |
| |
| CDEBUG(D_INFO, "added object %p with handle %#llx to hash\n", |
| h, h->h_cookie); |
| } |
| EXPORT_SYMBOL(class_handle_hash); |
| |
| static void class_handle_unhash_nolock(struct portals_handle *h) |
| { |
| if (list_empty(&h->h_link)) { |
| CERROR("removing an already-removed handle (%#llx)\n", |
| h->h_cookie); |
| return; |
| } |
| |
| CDEBUG(D_INFO, "removing object %p with handle %#llx from hash\n", |
| h, h->h_cookie); |
| |
| spin_lock(&h->h_lock); |
| if (h->h_in == 0) { |
| spin_unlock(&h->h_lock); |
| return; |
| } |
| h->h_in = 0; |
| spin_unlock(&h->h_lock); |
| list_del_rcu(&h->h_link); |
| } |
| |
| void class_handle_unhash(struct portals_handle *h) |
| { |
| struct handle_bucket *bucket; |
| |
| bucket = handle_hash + (h->h_cookie & HANDLE_HASH_MASK); |
| |
| spin_lock(&bucket->lock); |
| class_handle_unhash_nolock(h); |
| spin_unlock(&bucket->lock); |
| } |
| EXPORT_SYMBOL(class_handle_unhash); |
| |
| void *class_handle2object(__u64 cookie) |
| { |
| struct handle_bucket *bucket; |
| struct portals_handle *h; |
| void *retval = NULL; |
| |
| LASSERT(handle_hash); |
| |
| /* Be careful when you want to change this code. See the |
| * rcu_read_lock() definition on top this file. - jxiong |
| */ |
| bucket = handle_hash + (cookie & HANDLE_HASH_MASK); |
| |
| rcu_read_lock(); |
| list_for_each_entry_rcu(h, &bucket->head, h_link) { |
| if (h->h_cookie != cookie) |
| continue; |
| |
| spin_lock(&h->h_lock); |
| if (likely(h->h_in != 0)) { |
| h->h_ops->hop_addref(h); |
| retval = h; |
| } |
| spin_unlock(&h->h_lock); |
| break; |
| } |
| rcu_read_unlock(); |
| |
| return retval; |
| } |
| EXPORT_SYMBOL(class_handle2object); |
| |
| void class_handle_free_cb(struct rcu_head *rcu) |
| { |
| struct portals_handle *h = RCU2HANDLE(rcu); |
| void *ptr = (void *)(unsigned long)h->h_cookie; |
| |
| if (h->h_ops->hop_free) |
| h->h_ops->hop_free(ptr, h->h_size); |
| else |
| kfree(ptr); |
| } |
| EXPORT_SYMBOL(class_handle_free_cb); |
| |
| int class_handle_init(void) |
| { |
| struct handle_bucket *bucket; |
| struct timespec64 ts; |
| int seed[2]; |
| |
| LASSERT(!handle_hash); |
| |
| handle_hash = libcfs_kvzalloc(sizeof(*bucket) * HANDLE_HASH_SIZE, |
| GFP_NOFS); |
| if (!handle_hash) |
| return -ENOMEM; |
| |
| spin_lock_init(&handle_base_lock); |
| for (bucket = handle_hash + HANDLE_HASH_SIZE - 1; bucket >= handle_hash; |
| bucket--) { |
| INIT_LIST_HEAD(&bucket->head); |
| spin_lock_init(&bucket->lock); |
| } |
| |
| /** bug 21430: add randomness to the initial base */ |
| cfs_get_random_bytes(seed, sizeof(seed)); |
| ktime_get_ts64(&ts); |
| cfs_srand(ts.tv_sec ^ seed[0], ts.tv_nsec ^ seed[1]); |
| |
| cfs_get_random_bytes(&handle_base, sizeof(handle_base)); |
| LASSERT(handle_base != 0ULL); |
| |
| return 0; |
| } |
| |
| static int cleanup_all_handles(void) |
| { |
| int rc; |
| int i; |
| |
| for (rc = i = 0; i < HANDLE_HASH_SIZE; i++) { |
| struct portals_handle *h; |
| |
| spin_lock(&handle_hash[i].lock); |
| list_for_each_entry_rcu(h, &(handle_hash[i].head), h_link) { |
| CERROR("force clean handle %#llx addr %p ops %p\n", |
| h->h_cookie, h, h->h_ops); |
| |
| class_handle_unhash_nolock(h); |
| rc++; |
| } |
| spin_unlock(&handle_hash[i].lock); |
| } |
| |
| return rc; |
| } |
| |
| void class_handle_cleanup(void) |
| { |
| int count; |
| |
| LASSERT(handle_hash); |
| |
| count = cleanup_all_handles(); |
| |
| kvfree(handle_hash); |
| handle_hash = NULL; |
| |
| if (count != 0) |
| CERROR("handle_count at cleanup: %d\n", count); |
| } |