]> sourceware.org Git - systemtap.git/blame - runtime/transport/relay_v2.c
PR13319: fix _stp_bufsize-related start-time net/ memory report printk
[systemtap.git] / runtime / transport / relay_v2.c
CommitLineData
b47be87f
DS
1/* -*- linux-c -*-
2 *
42b8c995
DS
3 * This transport version uses relayfs on top of a debugfs file. This
4 * code started as a proposed relayfs interface called 'utt'. It has
5 * been modified and simplified for systemtap.
b47be87f 6 *
f2b610b6 7 * Changes Copyright (C) 2009-2010 Red Hat Inc.
b47be87f 8 *
42b8c995
DS
9 * Original utt code by:
10 * Copyright (C) 2006 Jens Axboe <axboe@suse.de>
11 * Moved to utt.c by Tom Zanussi, 2006
b47be87f
DS
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
e8daaf60 23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
b47be87f
DS
24 *
25 */
4a8c28f9 26#include <linux/kernel.h>
b47be87f
DS
27#include <linux/module.h>
28#include <linux/percpu.h>
29#include <linux/init.h>
4a8c28f9 30#include <linux/debugfs.h>
b47be87f
DS
31#include <linux/mm.h>
32#include <linux/relay.h>
33#include <linux/timer.h>
4a8c28f9 34
b47be87f
DS
35#ifndef STP_RELAY_TIMER_INTERVAL
36/* Wakeup timer interval in jiffies (default 10 ms) */
37#define STP_RELAY_TIMER_INTERVAL ((HZ + 99) / 100)
4a8c28f9 38#endif
4a8c28f9 39
43041178
DS
40/* Note: if struct _stp_relay_data_type changes, staplog.c might need
41 * to be changed. */
b47be87f 42struct _stp_relay_data_type {
b47be87f 43 struct rchan *rchan;
f2b610b6 44 atomic_t /* enum _stp_transport_state */ transport_state;
b47be87f
DS
45 struct dentry *dropped_file;
46 atomic_t dropped;
b47be87f
DS
47 atomic_t wakeup;
48 struct timer_list timer;
49 int overwrite_flag;
50};
51struct _stp_relay_data_type _stp_relay_data;
4a8c28f9 52
8af0ed12
PP
53/* relay_file_operations is const, so .owner is obviously not set there.
54 * Below struct, filled in _stp_transport_data_fs_init(), fixes it. */
55static struct file_operations relay_file_operations_w_owner;
56
b47be87f
DS
57/*
58 * __stp_relay_switch_subbuf - switch to a new sub-buffer
59 *
60 * Most of this function is deadcopy of relay_switch_subbuf.
61 */
62static size_t __stp_relay_switch_subbuf(struct rchan_buf *buf, size_t length)
4a8c28f9 63{
b47be87f
DS
64 char *old, *new;
65 size_t old_subbuf, new_subbuf;
4a8c28f9 66
b47be87f
DS
67 if (unlikely(buf == NULL))
68 return 0;
4a8c28f9 69
b47be87f
DS
70 if (unlikely(length > buf->chan->subbuf_size))
71 goto toobig;
72
73 if (buf->offset != buf->chan->subbuf_size + 1) {
74 buf->prev_padding = buf->chan->subbuf_size - buf->offset;
75 old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs;
76 buf->padding[old_subbuf] = buf->prev_padding;
77 buf->subbufs_produced++;
78 buf->dentry->d_inode->i_size += buf->chan->subbuf_size -
79 buf->padding[old_subbuf];
80 smp_mb();
81 if (waitqueue_active(&buf->read_wait))
82 /*
83 * Calling wake_up_interruptible() and __mod_timer()
84 * from here will deadlock if we happen to be logging
85 * from the scheduler and timer (trying to re-grab
86 * rq->lock/timer->base->lock), so just set a flag.
87 */
88 atomic_set(&_stp_relay_data.wakeup, 1);
89 }
4a8c28f9 90
b47be87f
DS
91 old = buf->data;
92 new_subbuf = buf->subbufs_produced % buf->chan->n_subbufs;
93 new = (char*)buf->start + new_subbuf * buf->chan->subbuf_size;
94 buf->offset = 0;
95 if (!buf->chan->cb->subbuf_start(buf, new, old, buf->prev_padding)) {
96 buf->offset = buf->chan->subbuf_size + 1;
97 return 0;
4a8c28f9 98 }
b47be87f
DS
99 buf->data = new;
100 buf->padding[new_subbuf] = 0;
101
102 if (unlikely(length + buf->offset > buf->chan->subbuf_size))
103 goto toobig;
104
105 return length;
4a8c28f9 106
b47be87f
DS
107toobig:
108 buf->chan->last_toobig = length;
109 return 0;
4a8c28f9
DS
110}
111
b47be87f 112static void __stp_relay_wakeup_readers(struct rchan_buf *buf)
4a8c28f9 113{
b47be87f
DS
114 if (buf && waitqueue_active(&buf->read_wait) &&
115 buf->subbufs_produced != buf->subbufs_consumed)
116 wake_up_interruptible(&buf->read_wait);
117}
4a8c28f9 118
b47be87f
DS
119static void __stp_relay_wakeup_timer(unsigned long val)
120{
4a8c28f9 121#ifdef STP_BULKMODE
b47be87f 122 int i;
4a8c28f9 123#endif
4a8c28f9 124
b47be87f
DS
125 if (atomic_read(&_stp_relay_data.wakeup)) {
126 atomic_set(&_stp_relay_data.wakeup, 0);
4a8c28f9 127#ifdef STP_BULKMODE
b47be87f
DS
128 for_each_possible_cpu(i)
129 __stp_relay_wakeup_readers(_stp_relay_data.rchan->buf[i]);
4a8c28f9 130#else
b47be87f 131 __stp_relay_wakeup_readers(_stp_relay_data.rchan->buf[0]);
4a8c28f9 132#endif
b47be87f 133 }
4a8c28f9 134
f2b610b6
FCE
135 if (atomic_read(&_stp_relay_data.transport_state) == STP_TRANSPORT_RUNNING)
136 mod_timer(&_stp_relay_data.timer, jiffies + STP_RELAY_TIMER_INTERVAL);
137 else
138 dbug_trans(0, "relay_v2 wakeup timer expiry\n");
4a8c28f9
DS
139}
140
b47be87f 141static void __stp_relay_timer_init(void)
4a8c28f9 142{
b47be87f
DS
143 atomic_set(&_stp_relay_data.wakeup, 0);
144 init_timer(&_stp_relay_data.timer);
145 _stp_relay_data.timer.expires = jiffies + STP_RELAY_TIMER_INTERVAL;
146 _stp_relay_data.timer.function = __stp_relay_wakeup_timer;
147 _stp_relay_data.timer.data = 0;
148 add_timer(&_stp_relay_data.timer);
149 smp_mb();
4a8c28f9
DS
150}
151
7b1be319
DS
152static enum _stp_transport_state _stp_transport_get_state(void)
153{
f2b610b6 154 return atomic_read (&_stp_relay_data.transport_state);
7b1be319
DS
155}
156
24a5e9a6 157static void _stp_transport_data_fs_overwrite(int overwrite)
4a8c28f9 158{
b47be87f
DS
159 _stp_relay_data.overwrite_flag = overwrite;
160}
4a8c28f9 161
b47be87f 162static int __stp_relay_dropped_open(struct inode *inode, struct file *filp)
4a8c28f9 163{
4a8c28f9
DS
164 return 0;
165}
166
b47be87f
DS
167static ssize_t __stp_relay_dropped_read(struct file *filp, char __user *buffer,
168 size_t count, loff_t *ppos)
169{
170 char buf[16];
4a8c28f9 171
b47be87f
DS
172 snprintf(buf, sizeof(buf), "%u\n",
173 atomic_read(&_stp_relay_data.dropped));
174
175 return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
176}
177
178static struct file_operations __stp_relay_dropped_fops = {
179 .owner = THIS_MODULE,
180 .open = __stp_relay_dropped_open,
181 .read = __stp_relay_dropped_read,
182};
4a8c28f9
DS
183
184/*
b47be87f
DS
185 * Keep track of how many times we encountered a full subbuffer, to aid
186 * the user space app in telling how many lost events there were.
4a8c28f9 187 */
b47be87f
DS
188static int __stp_relay_subbuf_start_callback(struct rchan_buf *buf,
189 void *subbuf, void *prev_subbuf,
190 size_t prev_padding)
4a8c28f9 191{
b47be87f
DS
192 if (_stp_relay_data.overwrite_flag || !relay_buf_full(buf))
193 return 1;
4a8c28f9 194
b47be87f
DS
195 atomic_inc(&_stp_relay_data.dropped);
196 return 0;
4a8c28f9 197}
4a8c28f9 198
b47be87f 199static int __stp_relay_remove_buf_file_callback(struct dentry *dentry)
4a8c28f9 200{
b47be87f
DS
201 debugfs_remove(dentry);
202 return 0;
203}
4a8c28f9 204
b47be87f
DS
205static struct dentry *
206__stp_relay_create_buf_file_callback(const char *filename,
207 struct dentry *parent,
208 int mode,
209 struct rchan_buf *buf,
210 int *is_global)
211{
212 struct dentry *file = debugfs_create_file(filename, mode, parent, buf,
8af0ed12 213 &relay_file_operations_w_owner);
d05b7a1c
DS
214 /*
215 * Here's what 'is_global' does (from linux/relay.h):
216 *
217 * Setting the is_global outparam to a non-zero value will
218 * cause relay_open() to create a single global buffer rather
219 * than the default set of per-cpu buffers.
220 */
221 if (is_global) {
222#ifdef STP_BULKMODE
223 *is_global = 0;
224#else
225 *is_global = 1;
226#endif
227 }
228
e57421f4
DS
229 if (IS_ERR(file)) {
230 file = NULL;
231 }
232 else if (file) {
b47be87f
DS
233 file->d_inode->i_uid = _stp_uid;
234 file->d_inode->i_gid = _stp_gid;
4a8c28f9 235 }
b47be87f 236 return file;
4a8c28f9
DS
237}
238
b47be87f
DS
239static struct rchan_callbacks __stp_relay_callbacks = {
240 .subbuf_start = __stp_relay_subbuf_start_callback,
241 .create_buf_file = __stp_relay_create_buf_file_callback,
242 .remove_buf_file = __stp_relay_remove_buf_file_callback,
243};
4a8c28f9 244
01adca5b
DS
245static void _stp_transport_data_fs_start(void)
246{
f2b610b6
FCE
247 if (atomic_read (&_stp_relay_data.transport_state) == STP_TRANSPORT_INITIALIZED) {
248 atomic_set (&_stp_relay_data.transport_state, STP_TRANSPORT_RUNNING);
01adca5b
DS
249 /* We're initialized. Now start the timer. */
250 __stp_relay_timer_init();
01adca5b
DS
251 }
252}
253
254static void _stp_transport_data_fs_stop(void)
4a8c28f9 255{
f2b610b6
FCE
256 if (atomic_read (&_stp_relay_data.transport_state) == STP_TRANSPORT_RUNNING) {
257 atomic_set (&_stp_relay_data.transport_state, STP_TRANSPORT_STOPPED);
b47be87f 258 del_timer_sync(&_stp_relay_data.timer);
01adca5b 259 dbug_trans(0, "flushing...\n");
01adca5b
DS
260 if (_stp_relay_data.rchan)
261 relay_flush(_stp_relay_data.rchan);
262 }
263}
b47be87f 264
01adca5b
DS
265static void _stp_transport_data_fs_close(void)
266{
267 _stp_transport_data_fs_stop();
b47be87f
DS
268 if (_stp_relay_data.dropped_file)
269 debugfs_remove(_stp_relay_data.dropped_file);
270 if (_stp_relay_data.rchan) {
b47be87f 271 relay_close(_stp_relay_data.rchan);
01adca5b 272 _stp_relay_data.rchan = NULL;
b47be87f 273 }
4a8c28f9
DS
274}
275
4a8c28f9
DS
276static int _stp_transport_data_fs_init(void)
277{
278 int rc;
279 u64 npages;
280 struct sysinfo si;
281
f2b610b6 282 atomic_set(&_stp_relay_data.transport_state, STP_TRANSPORT_STOPPED);
b47be87f
DS
283 _stp_relay_data.overwrite_flag = 0;
284 atomic_set(&_stp_relay_data.dropped, 0);
285 _stp_relay_data.dropped_file = NULL;
286 _stp_relay_data.rchan = NULL;
287
288 /* Create "dropped" file. */
289 _stp_relay_data.dropped_file
c714669f 290 = debugfs_create_file("dropped", 0400, _stp_get_module_dir(),
b47be87f
DS
291 NULL, &__stp_relay_dropped_fops);
292 if (!_stp_relay_data.dropped_file) {
293 rc = -EIO;
294 goto err;
295 }
e57421f4
DS
296 else if (IS_ERR(_stp_relay_data.dropped_file)) {
297 rc = PTR_ERR(_stp_relay_data.dropped_file);
298 _stp_relay_data.dropped_file = NULL;
299 goto err;
300 }
301
945563a7
DS
302 _stp_relay_data.dropped_file->d_inode->i_uid = _stp_uid;
303 _stp_relay_data.dropped_file->d_inode->i_gid = _stp_gid;
4a8c28f9 304
b47be87f 305 /* Create "trace" file. */
4a8c28f9
DS
306 npages = _stp_subbuf_size * _stp_nsubbufs;
307#ifdef STP_BULKMODE
66abcb3c 308 npages *= num_online_cpus();
4a8c28f9
DS
309#endif
310 npages >>= PAGE_SHIFT;
311 si_meminfo(&si);
312#define MB(i) (unsigned long)((i) >> (20 - PAGE_SHIFT))
313 if (npages > (si.freeram + si.bufferram)) {
314 errk("Not enough free+buffered memory(%luMB) for log buffer(%luMB)\n",
315 MB(si.freeram + si.bufferram),
316 MB(npages));
317 rc = -ENOMEM;
318 goto err;
319 }
320 else if (npages > si.freeram) {
321 /* exceeds freeram, but below freeram+bufferram */
322 printk(KERN_WARNING
323 "log buffer size exceeds free memory(%luMB)\n",
324 MB(si.freeram));
325 }
8af0ed12
PP
326 relay_file_operations_w_owner = relay_file_operations;
327 relay_file_operations_w_owner.owner = THIS_MODULE;
4a8c28f9 328#if (RELAYFS_CHANNEL_VERSION >= 7)
b47be87f
DS
329 _stp_relay_data.rchan = relay_open("trace", _stp_get_module_dir(),
330 _stp_subbuf_size, _stp_nsubbufs,
331 &__stp_relay_callbacks, NULL);
4a8c28f9 332#else /* (RELAYFS_CHANNEL_VERSION < 7) */
b47be87f
DS
333 _stp_relay_data.rchan = relay_open("trace", _stp_get_module_dir(),
334 _stp_subbuf_size, _stp_nsubbufs,
335 &__stp_relay_callbacks);
4a8c28f9 336#endif /* (RELAYFS_CHANNEL_VERSION < 7) */
b47be87f 337 if (!_stp_relay_data.rchan) {
4a8c28f9
DS
338 rc = -ENOENT;
339 goto err;
340 }
e04e95a4
FCE
341 /* Increment _stp_allocated_memory and _stp_allocated_net_memory to account for buffers
342 allocated by relay_open. */
343 {
344 u64 relay_mem;
345 relay_mem = _stp_subbuf_size * _stp_nsubbufs;
346#ifdef STP_BULKMODE
347 relay_mem *= num_online_cpus();
348#endif
349 _stp_allocated_net_memory += relay_mem;
350 _stp_allocated_memory += relay_mem;
351 }
352
4a8c28f9 353 dbug_trans(1, "returning 0...\n");
f2b610b6 354 atomic_set (&_stp_relay_data.transport_state, STP_TRANSPORT_INITIALIZED);
b47be87f 355
4a8c28f9
DS
356 return 0;
357
358err:
b47be87f 359 _stp_transport_data_fs_close();
4a8c28f9
DS
360 return rc;
361}
362
b47be87f
DS
363
364/**
42b8c995
DS
365 * _stp_data_write_reserve - try to reserve size_request bytes
366 * @size_request: number of bytes to attempt to reserve
367 * @entry: entry is returned here
b47be87f 368 *
42b8c995
DS
369 * Returns number of bytes reserved, 0 if full. On return, entry
370 * will point to allocated opaque pointer. Use
371 * _stp_data_entry_data() to get pointer to copy data into.
b47be87f 372 *
42b8c995
DS
373 * (For this code's purposes, entry is filled in with the actual
374 * data pointer, but the caller doesn't know that.)
b47be87f
DS
375 */
376static size_t
377_stp_data_write_reserve(size_t size_request, void **entry)
378{
379 struct rchan_buf *buf;
380
381 if (entry == NULL)
382 return -EINVAL;
383
384 buf = _stp_relay_data.rchan->buf[smp_processor_id()];
385 if (unlikely(buf->offset + size_request > buf->chan->subbuf_size)) {
386 size_request = __stp_relay_switch_subbuf(buf, size_request);
387 if (!size_request)
388 return 0;
389 }
390 *entry = (char*)buf->data + buf->offset;
391 buf->offset += size_request;
392
393 return size_request;
394}
395
396static unsigned char *_stp_data_entry_data(void *entry)
4a8c28f9 397{
b47be87f
DS
398 /* Nothing to do here. */
399 return entry;
4a8c28f9
DS
400}
401
b47be87f
DS
402static int _stp_data_write_commit(void *entry)
403{
404 /* Nothing to do here. */
405 return 0;
406}
This page took 0.111829 seconds and 5 git commands to generate.