Commit | Line | Data |
---|---|---|
d7e09d03 PT |
1 | /* |
2 | * GPL HEADER START | |
3 | * | |
4 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 only, | |
8 | * as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License version 2 for more details (a copy is included | |
14 | * in the LICENSE file that accompanied this code). | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * version 2 along with this program; If not, see | |
6a5b99a4 | 18 | * http://www.gnu.org/licenses/gpl-2.0.html |
d7e09d03 | 19 | * |
d7e09d03 PT |
20 | * GPL HEADER END |
21 | */ | |
22 | /* | |
23 | * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. | |
24 | * Use is subject to license terms. | |
25 | * | |
26 | * Copyright (c) 2011, 2012, Intel Corporation. | |
27 | */ | |
28 | /* | |
29 | * This file is part of Lustre, http://www.lustre.org/ | |
30 | * Lustre is a trademark of Sun Microsystems, Inc. | |
31 | * | |
32 | * lustre/obdclass/lustre_handles.c | |
33 | * | |
34 | * Author: Phil Schwan <phil@clusterfs.com> | |
35 | */ | |
36 | ||
37 | #define DEBUG_SUBSYSTEM S_CLASS | |
38 | ||
610f7377 GKH |
39 | #include "../include/obd_support.h" |
40 | #include "../include/lustre_handles.h" | |
41 | #include "../include/lustre_lib.h" | |
d7e09d03 | 42 | |
d7e09d03 PT |
43 | static __u64 handle_base; |
44 | #define HANDLE_INCR 7 | |
45 | static spinlock_t handle_base_lock; | |
46 | ||
47 | static struct handle_bucket { | |
48 | spinlock_t lock; | |
49 | struct list_head head; | |
50 | } *handle_hash; | |
51 | ||
52 | #define HANDLE_HASH_SIZE (1 << 16) | |
53 | #define HANDLE_HASH_MASK (HANDLE_HASH_SIZE - 1) | |
54 | ||
55 | /* | |
56 | * Generate a unique 64bit cookie (hash) for a handle and insert it into | |
57 | * global (per-node) hash-table. | |
58 | */ | |
59 | void class_handle_hash(struct portals_handle *h, | |
60 | struct portals_handle_ops *ops) | |
61 | { | |
62 | struct handle_bucket *bucket; | |
d7e09d03 | 63 | |
cce3c2da | 64 | LASSERT(h); |
d7e09d03 PT |
65 | LASSERT(list_empty(&h->h_link)); |
66 | ||
67 | /* | |
68 | * This is fast, but simplistic cookie generation algorithm, it will | |
69 | * need a re-do at some point in the future for security. | |
70 | */ | |
71 | spin_lock(&handle_base_lock); | |
72 | handle_base += HANDLE_INCR; | |
73 | ||
74 | if (unlikely(handle_base == 0)) { | |
75 | /* | |
76 | * Cookie of zero is "dangerous", because in many places it's | |
77 | * assumed that 0 means "unassigned" handle, not bound to any | |
78 | * object. | |
79 | */ | |
80 | CWARN("The universe has been exhausted: cookie wrap-around.\n"); | |
81 | handle_base += HANDLE_INCR; | |
82 | } | |
83 | h->h_cookie = handle_base; | |
84 | spin_unlock(&handle_base_lock); | |
85 | ||
86 | h->h_ops = ops; | |
87 | spin_lock_init(&h->h_lock); | |
88 | ||
89 | bucket = &handle_hash[h->h_cookie & HANDLE_HASH_MASK]; | |
90 | spin_lock(&bucket->lock); | |
91 | list_add_rcu(&h->h_link, &bucket->head); | |
92 | h->h_in = 1; | |
93 | spin_unlock(&bucket->lock); | |
94 | ||
55f5a824 | 95 | CDEBUG(D_INFO, "added object %p with handle %#llx to hash\n", |
d7e09d03 | 96 | h, h->h_cookie); |
d7e09d03 PT |
97 | } |
98 | EXPORT_SYMBOL(class_handle_hash); | |
99 | ||
100 | static void class_handle_unhash_nolock(struct portals_handle *h) | |
101 | { | |
102 | if (list_empty(&h->h_link)) { | |
55f5a824 | 103 | CERROR("removing an already-removed handle (%#llx)\n", |
d7e09d03 PT |
104 | h->h_cookie); |
105 | return; | |
106 | } | |
107 | ||
55f5a824 | 108 | CDEBUG(D_INFO, "removing object %p with handle %#llx from hash\n", |
d7e09d03 PT |
109 | h, h->h_cookie); |
110 | ||
111 | spin_lock(&h->h_lock); | |
112 | if (h->h_in == 0) { | |
113 | spin_unlock(&h->h_lock); | |
114 | return; | |
115 | } | |
116 | h->h_in = 0; | |
117 | spin_unlock(&h->h_lock); | |
118 | list_del_rcu(&h->h_link); | |
119 | } | |
120 | ||
121 | void class_handle_unhash(struct portals_handle *h) | |
122 | { | |
123 | struct handle_bucket *bucket; | |
50ffcb7e | 124 | |
d7e09d03 PT |
125 | bucket = handle_hash + (h->h_cookie & HANDLE_HASH_MASK); |
126 | ||
127 | spin_lock(&bucket->lock); | |
128 | class_handle_unhash_nolock(h); | |
129 | spin_unlock(&bucket->lock); | |
130 | } | |
131 | EXPORT_SYMBOL(class_handle_unhash); | |
132 | ||
8015e180 | 133 | void *class_handle2object(__u64 cookie, const void *owner) |
d7e09d03 PT |
134 | { |
135 | struct handle_bucket *bucket; | |
136 | struct portals_handle *h; | |
137 | void *retval = NULL; | |
d7e09d03 | 138 | |
cce3c2da | 139 | LASSERT(handle_hash); |
d7e09d03 PT |
140 | |
141 | /* Be careful when you want to change this code. See the | |
6ba59179 OD |
142 | * rcu_read_lock() definition on top this file. - jxiong |
143 | */ | |
d7e09d03 PT |
144 | bucket = handle_hash + (cookie & HANDLE_HASH_MASK); |
145 | ||
146 | rcu_read_lock(); | |
147 | list_for_each_entry_rcu(h, &bucket->head, h_link) { | |
8015e180 | 148 | if (h->h_cookie != cookie || h->h_owner != owner) |
d7e09d03 PT |
149 | continue; |
150 | ||
151 | spin_lock(&h->h_lock); | |
152 | if (likely(h->h_in != 0)) { | |
153 | h->h_ops->hop_addref(h); | |
154 | retval = h; | |
155 | } | |
156 | spin_unlock(&h->h_lock); | |
157 | break; | |
158 | } | |
159 | rcu_read_unlock(); | |
160 | ||
0a3bdb00 | 161 | return retval; |
d7e09d03 PT |
162 | } |
163 | EXPORT_SYMBOL(class_handle2object); | |
164 | ||
8504a9e5 | 165 | void class_handle_free_cb(struct rcu_head *rcu) |
d7e09d03 PT |
166 | { |
167 | struct portals_handle *h = RCU2HANDLE(rcu); | |
168 | void *ptr = (void *)(unsigned long)h->h_cookie; | |
169 | ||
cce3c2da | 170 | if (h->h_ops->hop_free) |
d7e09d03 PT |
171 | h->h_ops->hop_free(ptr, h->h_size); |
172 | else | |
d7279044 | 173 | kfree(ptr); |
d7e09d03 PT |
174 | } |
175 | EXPORT_SYMBOL(class_handle_free_cb); | |
176 | ||
177 | int class_handle_init(void) | |
178 | { | |
179 | struct handle_bucket *bucket; | |
1f4fc343 | 180 | struct timespec64 ts; |
d7e09d03 PT |
181 | int seed[2]; |
182 | ||
cce3c2da | 183 | LASSERT(!handle_hash); |
d7e09d03 | 184 | |
fea2e68e JL |
185 | handle_hash = libcfs_kvzalloc(sizeof(*bucket) * HANDLE_HASH_SIZE, |
186 | GFP_NOFS); | |
cce3c2da | 187 | if (!handle_hash) |
d7e09d03 PT |
188 | return -ENOMEM; |
189 | ||
190 | spin_lock_init(&handle_base_lock); | |
191 | for (bucket = handle_hash + HANDLE_HASH_SIZE - 1; bucket >= handle_hash; | |
192 | bucket--) { | |
193 | INIT_LIST_HEAD(&bucket->head); | |
194 | spin_lock_init(&bucket->lock); | |
195 | } | |
196 | ||
197 | /** bug 21430: add randomness to the initial base */ | |
198 | cfs_get_random_bytes(seed, sizeof(seed)); | |
1f4fc343 AB |
199 | ktime_get_ts64(&ts); |
200 | cfs_srand(ts.tv_sec ^ seed[0], ts.tv_nsec ^ seed[1]); | |
d7e09d03 PT |
201 | |
202 | cfs_get_random_bytes(&handle_base, sizeof(handle_base)); | |
203 | LASSERT(handle_base != 0ULL); | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | static int cleanup_all_handles(void) | |
209 | { | |
210 | int rc; | |
211 | int i; | |
212 | ||
213 | for (rc = i = 0; i < HANDLE_HASH_SIZE; i++) { | |
214 | struct portals_handle *h; | |
215 | ||
216 | spin_lock(&handle_hash[i].lock); | |
49880263 | 217 | list_for_each_entry_rcu(h, &handle_hash[i].head, h_link) { |
55f5a824 | 218 | CERROR("force clean handle %#llx addr %p ops %p\n", |
d7e09d03 PT |
219 | h->h_cookie, h, h->h_ops); |
220 | ||
221 | class_handle_unhash_nolock(h); | |
222 | rc++; | |
223 | } | |
224 | spin_unlock(&handle_hash[i].lock); | |
225 | } | |
226 | ||
227 | return rc; | |
228 | } | |
229 | ||
230 | void class_handle_cleanup(void) | |
231 | { | |
232 | int count; | |
50ffcb7e | 233 | |
cce3c2da | 234 | LASSERT(handle_hash); |
d7e09d03 PT |
235 | |
236 | count = cleanup_all_handles(); | |
237 | ||
fea2e68e | 238 | kvfree(handle_hash); |
d7e09d03 PT |
239 | handle_hash = NULL; |
240 | ||
241 | if (count != 0) | |
242 | CERROR("handle_count at cleanup: %d\n", count); | |
243 | } |