Whamcloud - gitweb
Branch HEAD
[fs/lustre-release.git] / libcfs / libcfs / lwt.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright  2008 Sun Microsystems, Inc. All rights reserved
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * libcfs/libcfs/lwt.c
37  *
38  * Author: Eric Barton <eeb@clusterfs.com>
39  */
40
41 #ifndef EXPORT_SYMTAB
42 # define EXPORT_SYMTAB
43 #endif
44
45 #ifndef AUTOCONF_INCLUDED
46 #include <linux/config.h>
47 #endif
48 #include <linux/module.h>
49 #include <linux/kmod.h>
50 #include <linux/kernel.h>
51 #include <linux/kernel.h>
52 #include <linux/mm.h>
53 #include <linux/string.h>
54 #include <linux/stat.h>
55 #include <linux/errno.h>
56 #include <linux/smp_lock.h>
57 #include <linux/unistd.h>
58 #include <linux/interrupt.h>
59 #include <asm/system.h>
60 #include <asm/uaccess.h>
61
62 #define DEBUG_SUBSYSTEM S_LNET
63
64 #include <libcfs/libcfs.h>
65
66 #if LWT_SUPPORT
67
68 #if !KLWT_SUPPORT
69 int         lwt_enabled;
70 lwt_cpu_t   lwt_cpus[NR_CPUS];
71 #endif
72
73 int         lwt_pages_per_cpu;
74
75 /* NB only root is allowed to retrieve LWT info; it's an open door into the
76  * kernel... */
77
78 int
79 lwt_lookup_string (int *size, char *knl_ptr,
80                    char *user_ptr, int user_size)
81 {
82         int   maxsize = 128;
83         
84         /* knl_ptr was retrieved from an LWT snapshot and the caller wants to
85          * turn it into a string.  NB we can crash with an access violation
86          * trying to determine the string length, so we're trusting our
87          * caller... */
88
89         if (!cfs_capable(CFS_CAP_SYS_ADMIN))
90                 return (-EPERM);
91
92         if (user_size > 0 && 
93             maxsize > user_size)
94                 maxsize = user_size;
95
96         *size = strnlen (knl_ptr, maxsize - 1) + 1;
97         
98         if (user_ptr != NULL) {
99                 if (user_size < 4)
100                         return (-EINVAL);
101                 
102                 if (copy_to_user (user_ptr, knl_ptr, *size))
103                         return (-EFAULT);
104
105                 /* Did I truncate the string?  */
106                 if (knl_ptr[*size - 1] != 0)
107                         copy_to_user (user_ptr + *size - 4, "...", 4);
108         }
109
110         return (0);
111 }
112
113 int
114 lwt_control (int enable, int clear)
115 {
116         lwt_page_t  *p;
117         int          i;
118         int          j;
119
120         if (!cfs_capable(CFS_CAP_SYS_ADMIN))
121                 return (-EPERM);
122
123         if (!enable) {
124                 LWT_EVENT(0,0,0,0);
125                 lwt_enabled = 0;
126                 mb();
127                 /* give people some time to stop adding traces */
128                 schedule_timeout(10);
129         }
130
131         for (i = 0; i < num_online_cpus(); i++) {
132                 p = lwt_cpus[i].lwtc_current_page;
133
134                 if (p == NULL)
135                         return (-ENODATA);
136
137                 if (!clear)
138                         continue;
139
140                 for (j = 0; j < lwt_pages_per_cpu; j++) {
141                         memset (p->lwtp_events, 0, CFS_PAGE_SIZE);
142
143                         p = list_entry (p->lwtp_list.next,
144                                         lwt_page_t, lwtp_list);
145                 }
146         }
147
148         if (enable) {
149                 lwt_enabled = 1;
150                 mb();
151                 LWT_EVENT(0,0,0,0);
152         }
153
154         return (0);
155 }
156
157 int
158 lwt_snapshot (cycles_t *now, int *ncpu, int *total_size, 
159               void *user_ptr, int user_size)
160 {
161         const int    events_per_page = CFS_PAGE_SIZE / sizeof(lwt_event_t);
162         const int    bytes_per_page = events_per_page * sizeof(lwt_event_t);
163         lwt_page_t  *p;
164         int          i;
165         int          j;
166
167         if (!cfs_capable(CFS_CAP_SYS_ADMIN))
168                 return (-EPERM);
169
170         *ncpu = num_online_cpus();
171         *total_size = num_online_cpus() * lwt_pages_per_cpu * bytes_per_page;
172         *now = get_cycles();
173         
174         if (user_ptr == NULL)
175                 return (0);
176
177         for (i = 0; i < num_online_cpus(); i++) {
178                 p = lwt_cpus[i].lwtc_current_page;
179
180                 if (p == NULL)
181                         return (-ENODATA);
182                 
183                 for (j = 0; j < lwt_pages_per_cpu; j++) {
184                         if (copy_to_user(user_ptr, p->lwtp_events,
185                                          bytes_per_page))
186                                 return (-EFAULT);
187
188                         user_ptr = ((char *)user_ptr) + bytes_per_page;
189                         p = list_entry(p->lwtp_list.next,
190                                        lwt_page_t, lwtp_list);
191                         
192                 }
193         }
194
195         return (0);
196 }
197
198 int
199 lwt_init () 
200 {
201         int     i;
202         int     j;
203
204         for (i = 0; i < num_online_cpus(); i++)
205                 if (lwt_cpus[i].lwtc_current_page != NULL)
206                         return (-EALREADY);
207         
208         LASSERT (!lwt_enabled);
209
210         /* NULL pointers, zero scalars */
211         memset (lwt_cpus, 0, sizeof (lwt_cpus));
212         lwt_pages_per_cpu = LWT_MEMORY / (num_online_cpus() * CFS_PAGE_SIZE);
213
214         for (i = 0; i < num_online_cpus(); i++)
215                 for (j = 0; j < lwt_pages_per_cpu; j++) {
216                         struct page *page = alloc_page (GFP_KERNEL);
217                         lwt_page_t  *lwtp;
218
219                         if (page == NULL) {
220                                 CERROR ("Can't allocate page\n");
221                                 lwt_fini ();
222                                 return (-ENOMEM);
223                         }
224
225                         LIBCFS_ALLOC(lwtp, sizeof (*lwtp));
226                         if (lwtp == NULL) {
227                                 CERROR ("Can't allocate lwtp\n");
228                                 __free_page(page);
229                                 lwt_fini ();
230                                 return (-ENOMEM);
231                         }
232
233                         lwtp->lwtp_page = page;
234                         lwtp->lwtp_events = page_address(page);
235                         memset (lwtp->lwtp_events, 0, CFS_PAGE_SIZE);
236
237                         if (j == 0) {
238                                 INIT_LIST_HEAD (&lwtp->lwtp_list);
239                                 lwt_cpus[i].lwtc_current_page = lwtp;
240                         } else {
241                                 list_add (&lwtp->lwtp_list,
242                                     &lwt_cpus[i].lwtc_current_page->lwtp_list);
243                         }
244                 }
245
246         lwt_enabled = 1;
247         mb();
248
249         LWT_EVENT(0,0,0,0);
250
251         return (0);
252 }
253
254 void
255 lwt_fini () 
256 {
257         int    i;
258
259         lwt_control(0, 0);
260         
261         for (i = 0; i < num_online_cpus(); i++)
262                 while (lwt_cpus[i].lwtc_current_page != NULL) {
263                         lwt_page_t *lwtp = lwt_cpus[i].lwtc_current_page;
264                         
265                         if (list_empty (&lwtp->lwtp_list)) {
266                                 lwt_cpus[i].lwtc_current_page = NULL;
267                         } else {
268                                 lwt_cpus[i].lwtc_current_page =
269                                         list_entry (lwtp->lwtp_list.next,
270                                                     lwt_page_t, lwtp_list);
271
272                                 list_del (&lwtp->lwtp_list);
273                         }
274                         
275                         __free_page (lwtp->lwtp_page);
276                         LIBCFS_FREE (lwtp, sizeof (*lwtp));
277                 }
278 }
279
280 EXPORT_SYMBOL(lwt_enabled);
281 EXPORT_SYMBOL(lwt_cpus);
282
283 EXPORT_SYMBOL(lwt_init);
284 EXPORT_SYMBOL(lwt_fini);
285 EXPORT_SYMBOL(lwt_lookup_string);
286 EXPORT_SYMBOL(lwt_control);
287 EXPORT_SYMBOL(lwt_snapshot);
288 #endif