/*
* 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.gnu.org/licenses/gpl-2.0.html
*
* GPL HEADER END
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 2012, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
* Lustre is a trademark of Sun Microsystems, Inc.
*
* libcfs/libcfs/tracefile.c
*
* Author: Zach Brown <zab@clusterfs.com>
* Author: Phil Schwan <phil@clusterfs.com>
*/
#define DEBUG_SUBSYSTEM S_LNET
#define LUSTRE_TRACEFILE_PRIVATE
#define pr_fmt(fmt) "Lustre: " fmt
#include "tracefile.h"
#include "../../include/linux/libcfs/libcfs.h"
/* XXX move things up to the top, comment */
union cfs_trace_data_union (*cfs_trace_data[TCD_MAX_TYPES])[NR_CPUS] __cacheline_aligned;
char cfs_tracefile[TRACEFILE_NAME_SIZE];
long long cfs_tracefile_size = CFS_TRACEFILE_SIZE;
static struct tracefiled_ctl trace_tctl;
static DEFINE_MUTEX(cfs_trace_thread_mutex);
static int thread_running;
static atomic_t cfs_tage_allocated = ATOMIC_INIT(0);
struct page_collection {
struct list_head pc_pages;
/*
* if this flag is set, collect_pages() will spill both
* ->tcd_daemon_pages and ->tcd_pages to the ->pc_pages. Otherwise,
* only ->tcd_pages are spilled.
*/
int pc_want_daemon_pages;
};
struct tracefiled_ctl {
struct completion tctl_start;
struct completion tctl_stop;
wait_queue_head_t tctl_waitq;
pid_t tctl_pid;
atomic_t tctl_shutdown;
};
/*
* small data-structure for each page owned by tracefiled.
*/
struct cfs_trace_page {
/*
* page itself
*/
struct page *page;
/*
* linkage into one of the lists in trace_data_union or
* page_collection
*/
struct list_head linkage;
/*
* number of bytes used within this page
*/
unsigned int used;
/*
* cpu that owns this page
*/
unsigned short cpu;
/*
* type(context) of this page
*/
unsigned short type;
};
static void put_pages_on_tcd_daemon_list(struct page_collection *pc,
struct cfs_trace_cpu_data *tcd);
static inline struct cfs_trace_page *
cfs_tage_from_list(struct list_head *list)
{
return list_entry(list, struct cfs_trace_page, linkage);
}
static struct cfs_trace_page *cfs_tage_alloc(gfp_t gfp)
{
struct page *page;
struct cfs_trace_page *tage;
/* My caller is trying to free memory */
if (!in_interrupt() && memory_pressure_get())
return NULL;
/*
* Don't spam console with allocation failures: they will be reported
* by upper layer anyway.
*/
gfp |= __GFP_NOWARN;
page = alloc_page(gfp);
if (!page)
return NULL;
tage = kmalloc(sizeof(*tage), gfp);
if (!tage) {
__free_page(page);
return NULL;
}
tage->page = page;
atomic_inc(&cfs_tage_allocated);
return tage;
}
static void cfs_tage_free(struct cfs_trace_page *tage)
{
__free_page(tage->page);
kfree(tage);
atomic_dec(&cfs_tage_allocated);
}
static void cfs_tage_to_tail(struct cfs_trace_page *tage,
struct list_head *queue)
{
list_move_tail(&tage