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