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 | |
18 | * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf | |
19 | * | |
20 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, | |
21 | * CA 95054 USA or visit www.sun.com if you need additional information or | |
22 | * have any questions. | |
23 | * | |
24 | * GPL HEADER END | |
25 | */ | |
26 | /* | |
27 | * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. | |
28 | * Use is subject to license terms. | |
29 | * | |
30 | * Copyright (c) 2011, 2012, Intel Corporation. | |
31 | */ | |
32 | /* | |
33 | * This file is part of Lustre, http://www.lustre.org/ | |
34 | * Lustre is a trademark of Sun Microsystems, Inc. | |
35 | * | |
36 | * lustre/ptlrpc/sec_gc.c | |
37 | * | |
38 | * Author: Eric Mei <ericm@clusterfs.com> | |
39 | */ | |
40 | ||
41 | #define DEBUG_SUBSYSTEM S_SEC | |
42 | ||
9fdaf8c0 | 43 | #include "../../include/linux/libcfs/libcfs.h" |
d7e09d03 PT |
44 | |
45 | #include <obd_support.h> | |
46 | #include <obd_class.h> | |
47 | #include <lustre_net.h> | |
48 | #include <lustre_sec.h> | |
49 | ||
50 | #define SEC_GC_INTERVAL (30 * 60) | |
51 | ||
52 | ||
53 | static struct mutex sec_gc_mutex; | |
54 | static LIST_HEAD(sec_gc_list); | |
55 | static spinlock_t sec_gc_list_lock; | |
56 | ||
57 | static LIST_HEAD(sec_gc_ctx_list); | |
58 | static spinlock_t sec_gc_ctx_list_lock; | |
59 | ||
60 | static struct ptlrpc_thread sec_gc_thread; | |
61 | static atomic_t sec_gc_wait_del = ATOMIC_INIT(0); | |
62 | ||
63 | ||
64 | void sptlrpc_gc_add_sec(struct ptlrpc_sec *sec) | |
65 | { | |
66 | LASSERT(sec->ps_policy->sp_cops->gc_ctx); | |
67 | LASSERT(sec->ps_gc_interval > 0); | |
68 | LASSERT(list_empty(&sec->ps_gc_list)); | |
69 | ||
70 | sec->ps_gc_next = cfs_time_current_sec() + sec->ps_gc_interval; | |
71 | ||
72 | spin_lock(&sec_gc_list_lock); | |
73 | list_add_tail(&sec_gc_list, &sec->ps_gc_list); | |
74 | spin_unlock(&sec_gc_list_lock); | |
75 | ||
76 | CDEBUG(D_SEC, "added sec %p(%s)\n", sec, sec->ps_policy->sp_name); | |
77 | } | |
78 | EXPORT_SYMBOL(sptlrpc_gc_add_sec); | |
79 | ||
80 | void sptlrpc_gc_del_sec(struct ptlrpc_sec *sec) | |
81 | { | |
82 | if (list_empty(&sec->ps_gc_list)) | |
83 | return; | |
84 | ||
85 | might_sleep(); | |
86 | ||
87 | /* signal before list_del to make iteration in gc thread safe */ | |
88 | atomic_inc(&sec_gc_wait_del); | |
89 | ||
90 | spin_lock(&sec_gc_list_lock); | |
91 | list_del_init(&sec->ps_gc_list); | |
92 | spin_unlock(&sec_gc_list_lock); | |
93 | ||
94 | /* barrier */ | |
95 | mutex_lock(&sec_gc_mutex); | |
96 | mutex_unlock(&sec_gc_mutex); | |
97 | ||
98 | atomic_dec(&sec_gc_wait_del); | |
99 | ||
100 | CDEBUG(D_SEC, "del sec %p(%s)\n", sec, sec->ps_policy->sp_name); | |
101 | } | |
102 | EXPORT_SYMBOL(sptlrpc_gc_del_sec); | |
103 | ||
104 | void sptlrpc_gc_add_ctx(struct ptlrpc_cli_ctx *ctx) | |
105 | { | |
106 | LASSERT(list_empty(&ctx->cc_gc_chain)); | |
107 | ||
108 | CDEBUG(D_SEC, "hand over ctx %p(%u->%s)\n", | |
109 | ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec)); | |
110 | spin_lock(&sec_gc_ctx_list_lock); | |
111 | list_add(&ctx->cc_gc_chain, &sec_gc_ctx_list); | |
112 | spin_unlock(&sec_gc_ctx_list_lock); | |
113 | ||
114 | thread_add_flags(&sec_gc_thread, SVC_SIGNAL); | |
115 | wake_up(&sec_gc_thread.t_ctl_waitq); | |
116 | } | |
117 | EXPORT_SYMBOL(sptlrpc_gc_add_ctx); | |
118 | ||
119 | static void sec_process_ctx_list(void) | |
120 | { | |
121 | struct ptlrpc_cli_ctx *ctx; | |
122 | ||
123 | spin_lock(&sec_gc_ctx_list_lock); | |
124 | ||
125 | while (!list_empty(&sec_gc_ctx_list)) { | |
126 | ctx = list_entry(sec_gc_ctx_list.next, | |
127 | struct ptlrpc_cli_ctx, cc_gc_chain); | |
128 | list_del_init(&ctx->cc_gc_chain); | |
129 | spin_unlock(&sec_gc_ctx_list_lock); | |
130 | ||
131 | LASSERT(ctx->cc_sec); | |
132 | LASSERT(atomic_read(&ctx->cc_refcount) == 1); | |
133 | CDEBUG(D_SEC, "gc pick up ctx %p(%u->%s)\n", | |
134 | ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec)); | |
135 | sptlrpc_cli_ctx_put(ctx, 1); | |
136 | ||
137 | spin_lock(&sec_gc_ctx_list_lock); | |
138 | } | |
139 | ||
140 | spin_unlock(&sec_gc_ctx_list_lock); | |
141 | } | |
142 | ||
143 | static void sec_do_gc(struct ptlrpc_sec *sec) | |
144 | { | |
145 | LASSERT(sec->ps_policy->sp_cops->gc_ctx); | |
146 | ||
147 | if (unlikely(sec->ps_gc_next == 0)) { | |
148 | CDEBUG(D_SEC, "sec %p(%s) has 0 gc time\n", | |
149 | sec, sec->ps_policy->sp_name); | |
150 | return; | |
151 | } | |
152 | ||
153 | CDEBUG(D_SEC, "check on sec %p(%s)\n", sec, sec->ps_policy->sp_name); | |
154 | ||
155 | if (cfs_time_after(sec->ps_gc_next, cfs_time_current_sec())) | |
156 | return; | |
157 | ||
158 | sec->ps_policy->sp_cops->gc_ctx(sec); | |
159 | sec->ps_gc_next = cfs_time_current_sec() + sec->ps_gc_interval; | |
160 | } | |
161 | ||
162 | static int sec_gc_main(void *arg) | |
163 | { | |
164 | struct ptlrpc_thread *thread = (struct ptlrpc_thread *) arg; | |
165 | struct l_wait_info lwi; | |
166 | ||
167 | unshare_fs_struct(); | |
168 | ||
169 | /* Record that the thread is running */ | |
170 | thread_set_flags(thread, SVC_RUNNING); | |
171 | wake_up(&thread->t_ctl_waitq); | |
172 | ||
173 | while (1) { | |
174 | struct ptlrpc_sec *sec; | |
175 | ||
176 | thread_clear_flags(thread, SVC_SIGNAL); | |
177 | sec_process_ctx_list(); | |
178 | again: | |
179 | /* go through sec list do gc. | |
180 | * FIXME here we iterate through the whole list each time which | |
181 | * is not optimal. we perhaps want to use balanced binary tree | |
182 | * to trace each sec as order of expiry time. | |
183 | * another issue here is we wakeup as fixed interval instead of | |
184 | * according to each sec's expiry time */ | |
185 | mutex_lock(&sec_gc_mutex); | |
186 | list_for_each_entry(sec, &sec_gc_list, ps_gc_list) { | |
187 | /* if someone is waiting to be deleted, let it | |
188 | * proceed as soon as possible. */ | |
189 | if (atomic_read(&sec_gc_wait_del)) { | |
190 | CDEBUG(D_SEC, "deletion pending, start over\n"); | |
191 | mutex_unlock(&sec_gc_mutex); | |
192 | goto again; | |
193 | } | |
194 | ||
195 | sec_do_gc(sec); | |
196 | } | |
197 | mutex_unlock(&sec_gc_mutex); | |
198 | ||
199 | /* check ctx list again before sleep */ | |
200 | sec_process_ctx_list(); | |
201 | ||
202 | lwi = LWI_TIMEOUT(SEC_GC_INTERVAL * HZ, NULL, NULL); | |
203 | l_wait_event(thread->t_ctl_waitq, | |
204 | thread_is_stopping(thread) || | |
205 | thread_is_signal(thread), | |
206 | &lwi); | |
207 | ||
208 | if (thread_test_and_clear_flags(thread, SVC_STOPPING)) | |
209 | break; | |
210 | } | |
211 | ||
212 | thread_set_flags(thread, SVC_STOPPED); | |
213 | wake_up(&thread->t_ctl_waitq); | |
214 | return 0; | |
215 | } | |
216 | ||
217 | int sptlrpc_gc_init(void) | |
218 | { | |
219 | struct l_wait_info lwi = { 0 }; | |
68b636b6 | 220 | struct task_struct *task; |
d7e09d03 PT |
221 | |
222 | mutex_init(&sec_gc_mutex); | |
223 | spin_lock_init(&sec_gc_list_lock); | |
224 | spin_lock_init(&sec_gc_ctx_list_lock); | |
225 | ||
226 | /* initialize thread control */ | |
227 | memset(&sec_gc_thread, 0, sizeof(sec_gc_thread)); | |
228 | init_waitqueue_head(&sec_gc_thread.t_ctl_waitq); | |
229 | ||
230 | task = kthread_run(sec_gc_main, &sec_gc_thread, "sptlrpc_gc"); | |
231 | if (IS_ERR(task)) { | |
232 | CERROR("can't start gc thread: %ld\n", PTR_ERR(task)); | |
233 | return PTR_ERR(task); | |
234 | } | |
235 | ||
236 | l_wait_event(sec_gc_thread.t_ctl_waitq, | |
237 | thread_is_running(&sec_gc_thread), &lwi); | |
238 | return 0; | |
239 | } | |
240 | ||
241 | void sptlrpc_gc_fini(void) | |
242 | { | |
243 | struct l_wait_info lwi = { 0 }; | |
244 | ||
245 | thread_set_flags(&sec_gc_thread, SVC_STOPPING); | |
246 | wake_up(&sec_gc_thread.t_ctl_waitq); | |
247 | ||
248 | l_wait_event(sec_gc_thread.t_ctl_waitq, | |
249 | thread_is_stopped(&sec_gc_thread), &lwi); | |
250 | } |