| /* |
| * 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) 2008, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| /* |
| * This file is part of Lustre, http://www.lustre.org/ |
| * Lustre is a trademark of Sun Microsystems, Inc. |
| * |
| * libcfs/libcfs/lwt.c |
| * |
| * Author: Eric Barton <eeb@clusterfs.com> |
| */ |
| |
| #define DEBUG_SUBSYSTEM S_LNET |
| |
| #include <linux/libcfs/libcfs.h> |
| |
| #if LWT_SUPPORT |
| |
| #if !KLWT_SUPPORT |
| int lwt_enabled; |
| lwt_cpu_t lwt_cpus[NR_CPUS]; |
| #endif |
| |
| int lwt_pages_per_cpu; |
| |
| /* NB only root is allowed to retrieve LWT info; it's an open door into the |
| * kernel... */ |
| |
| int |
| lwt_lookup_string (int *size, char *knl_ptr, |
| char *user_ptr, int user_size) |
| { |
| int maxsize = 128; |
| |
| /* knl_ptr was retrieved from an LWT snapshot and the caller wants to |
| * turn it into a string. NB we can crash with an access violation |
| * trying to determine the string length, so we're trusting our |
| * caller... */ |
| |
| if (!cfs_capable(CFS_CAP_SYS_ADMIN)) |
| return (-EPERM); |
| |
| if (user_size > 0 && |
| maxsize > user_size) |
| maxsize = user_size; |
| |
| *size = strnlen (knl_ptr, maxsize - 1) + 1; |
| |
| if (user_ptr != NULL) { |
| if (user_size < 4) |
| return (-EINVAL); |
| |
| if (copy_to_user (user_ptr, knl_ptr, *size)) |
| return (-EFAULT); |
| |
| /* Did I truncate the string? */ |
| if (knl_ptr[*size - 1] != 0) |
| copy_to_user (user_ptr + *size - 4, "...", 4); |
| } |
| |
| return (0); |
| } |
| |
| int |
| lwt_control (int enable, int clear) |
| { |
| lwt_page_t *p; |
| int i; |
| int j; |
| |
| if (!cfs_capable(CFS_CAP_SYS_ADMIN)) |
| return (-EPERM); |
| |
| if (!enable) { |
| LWT_EVENT(0,0,0,0); |
| lwt_enabled = 0; |
| mb(); |
| /* give people some time to stop adding traces */ |
| schedule_timeout(10); |
| } |
| |
| for (i = 0; i < num_online_cpus(); i++) { |
| p = lwt_cpus[i].lwtc_current_page; |
| |
| if (p == NULL) |
| return (-ENODATA); |
| |
| if (!clear) |
| continue; |
| |
| for (j = 0; j < lwt_pages_per_cpu; j++) { |
| memset (p->lwtp_events, 0, PAGE_CACHE_SIZE); |
| |
| p = list_entry (p->lwtp_list.next, |
| lwt_page_t, lwtp_list); |
| } |
| } |
| |
| if (enable) { |
| lwt_enabled = 1; |
| mb(); |
| LWT_EVENT(0,0,0,0); |
| } |
| |
| return (0); |
| } |
| |
| int |
| lwt_snapshot (cfs_cycles_t *now, int *ncpu, int *total_size, |
| void *user_ptr, int user_size) |
| { |
| const int events_per_page = PAGE_CACHE_SIZE / sizeof(lwt_event_t); |
| const int bytes_per_page = events_per_page * sizeof(lwt_event_t); |
| lwt_page_t *p; |
| int i; |
| int j; |
| |
| if (!cfs_capable(CFS_CAP_SYS_ADMIN)) |
| return (-EPERM); |
| |
| *ncpu = num_online_cpus(); |
| *total_size = num_online_cpus() * lwt_pages_per_cpu * |
| bytes_per_page; |
| *now = get_cycles(); |
| |
| if (user_ptr == NULL) |
| return (0); |
| |
| for (i = 0; i < num_online_cpus(); i++) { |
| p = lwt_cpus[i].lwtc_current_page; |
| |
| if (p == NULL) |
| return (-ENODATA); |
| |
| for (j = 0; j < lwt_pages_per_cpu; j++) { |
| if (copy_to_user(user_ptr, p->lwtp_events, |
| bytes_per_page)) |
| return (-EFAULT); |
| |
| user_ptr = ((char *)user_ptr) + bytes_per_page; |
| p = list_entry(p->lwtp_list.next, |
| lwt_page_t, lwtp_list); |
| } |
| } |
| |
| return (0); |
| } |
| |
| int |
| lwt_init () |
| { |
| int i; |
| int j; |
| |
| for (i = 0; i < num_online_cpus(); i++) |
| if (lwt_cpus[i].lwtc_current_page != NULL) |
| return (-EALREADY); |
| |
| LASSERT (!lwt_enabled); |
| |
| /* NULL pointers, zero scalars */ |
| memset (lwt_cpus, 0, sizeof (lwt_cpus)); |
| lwt_pages_per_cpu = |
| LWT_MEMORY / (num_online_cpus() * PAGE_CACHE_SIZE); |
| |
| for (i = 0; i < num_online_cpus(); i++) |
| for (j = 0; j < lwt_pages_per_cpu; j++) { |
| struct page *page = alloc_page (GFP_KERNEL); |
| lwt_page_t *lwtp; |
| |
| if (page == NULL) { |
| CERROR ("Can't allocate page\n"); |
| lwt_fini (); |
| return (-ENOMEM); |
| } |
| |
| LIBCFS_ALLOC(lwtp, sizeof (*lwtp)); |
| if (lwtp == NULL) { |
| CERROR ("Can't allocate lwtp\n"); |
| __free_page(page); |
| lwt_fini (); |
| return (-ENOMEM); |
| } |
| |
| lwtp->lwtp_page = page; |
| lwtp->lwtp_events = page_address(page); |
| memset (lwtp->lwtp_events, 0, PAGE_CACHE_SIZE); |
| |
| if (j == 0) { |
| INIT_LIST_HEAD (&lwtp->lwtp_list); |
| lwt_cpus[i].lwtc_current_page = lwtp; |
| } else { |
| list_add (&lwtp->lwtp_list, |
| &lwt_cpus[i].lwtc_current_page->lwtp_list); |
| } |
| } |
| |
| lwt_enabled = 1; |
| mb(); |
| |
| LWT_EVENT(0,0,0,0); |
| |
| return (0); |
| } |
| |
| void |
| lwt_fini () |
| { |
| int i; |
| |
| lwt_control(0, 0); |
| |
| for (i = 0; i < num_online_cpus(); i++) |
| while (lwt_cpus[i].lwtc_current_page != NULL) { |
| lwt_page_t *lwtp = lwt_cpus[i].lwtc_current_page; |
| |
| if (list_empty (&lwtp->lwtp_list)) { |
| lwt_cpus[i].lwtc_current_page = NULL; |
| } else { |
| lwt_cpus[i].lwtc_current_page = |
| list_entry (lwtp->lwtp_list.next, |
| lwt_page_t, lwtp_list); |
| |
| list_del (&lwtp->lwtp_list); |
| } |
| |
| __free_page (lwtp->lwtp_page); |
| LIBCFS_FREE (lwtp, sizeof (*lwtp)); |
| } |
| } |
| |
| EXPORT_SYMBOL(lwt_enabled); |
| EXPORT_SYMBOL(lwt_cpus); |
| |
| EXPORT_SYMBOL(lwt_init); |
| EXPORT_SYMBOL(lwt_fini); |
| EXPORT_SYMBOL(lwt_lookup_string); |
| EXPORT_SYMBOL(lwt_control); |
| EXPORT_SYMBOL(lwt_snapshot); |
| #endif |