From 7aa969cf00075426d30cbce164ccaeb8a1b0a2aa Mon Sep 17 00:00:00 2001 From: trz Date: Fri, 6 May 2005 19:16:07 +0000 Subject: [PATCH] initial revision --- runtime/transport/Makefile | 14 +++ runtime/transport/control.c | 228 ++++++++++++++++++++++++++++++++++ runtime/transport/control.h | 32 +++++ runtime/transport/netlink.c | 165 ++++++++++++++++++++++++ runtime/transport/netlink.h | 16 +++ runtime/transport/relayfs.c | 113 +++++++++++++++++ runtime/transport/relayfs.h | 16 +++ runtime/transport/transport.c | 166 +++++++++++++++++++++++++ runtime/transport/transport.h | 75 +++++++++++ 9 files changed, 825 insertions(+) create mode 100644 runtime/transport/Makefile create mode 100644 runtime/transport/control.c create mode 100644 runtime/transport/control.h create mode 100644 runtime/transport/netlink.c create mode 100644 runtime/transport/netlink.h create mode 100644 runtime/transport/relayfs.c create mode 100644 runtime/transport/relayfs.h create mode 100644 runtime/transport/transport.c create mode 100644 runtime/transport/transport.h diff --git a/runtime/transport/Makefile b/runtime/transport/Makefile new file mode 100644 index 000000000..37268a2a7 --- /dev/null +++ b/runtime/transport/Makefile @@ -0,0 +1,14 @@ +# Makes stp-control.ko + +PWD := $(shell pwd) +KDIR := /usr/local/src/linux-$(shell uname -r) + +stp-control-objs := control.o netlink.o + +obj-m := stp-control.o + +default: + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules + +clean: + /bin/rm -rf *.o *.ko *~ *.mod.c .*.cmd .tmp_versions diff --git a/runtime/transport/control.c b/runtime/transport/control.c new file mode 100644 index 000000000..1699f54d8 --- /dev/null +++ b/runtime/transport/control.c @@ -0,0 +1,228 @@ +/* + * stp-control - stp control channel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2005 + * Copyright (C) Redhat Inc, 2005 + * + */ + +/** @file control.c + * @brief Systemtap control channel functions + */ + +/** @addtogroup transport Transport Functions + * @{ + */ + +#include +#include +#include "control.h" +#include "netlink.h" + +/* the control channel */ +struct sock *stp_control; + +/* the command handlers hash table */ +static struct hlist_head *handlers; + +/* protection for the handlers table */ +static DEFINE_SPINLOCK(handlers_lock); + +/** + * _stp_lookup_handler - look up the command handler in the handlers table + * @pid: the pid to find the corresponding handler of + * + * Returns the pointer to the cmd_handler struct, NULL if not + * found. + * + * NOTE: the handlers_lock must be held when calling this function + */ +static struct cmd_handler *_stp_lookup_handler(int pid) +{ + struct hlist_node *node; + struct cmd_handler *handler; + unsigned long key = hash_long((unsigned long)pid, HANDLER_SHIFT); + struct hlist_head *head = &handlers[key]; + + hlist_for_each(node, head) { + handler = hlist_entry(node, struct cmd_handler, hlist); + if (handler->pid == pid) { + return handler; + break; + } + } + + return NULL; +} + +/** + * _stp_handler_find - find the command handler for a given pid + * @pid: the pid to find the corresponding handler of + * + * Returns the pointer to the command handler callback, NULL if + * not found. + */ +static int (*_stp_handler_find(int pid))(int, int, void *) +{ + struct cmd_handler *cmd_handler; + + spin_lock(&handlers_lock); + cmd_handler = _stp_lookup_handler(pid); + spin_unlock(&handlers_lock); + + if (cmd_handler) + return cmd_handler->handler; + + return NULL; +} + +/** + * _stp_ctrl_register - register a command handler for a pid + * @pid: the pid to unregister + * @cmd_handler: the callback function to be called to handle commands + * + * Adds a pid's command handler to the handler table. The + * command handler will be called to handle commands from the + * daemon with the given pid. Should be called at probe module + * initialization before any commands are sent by the daemon. + */ +int _stp_ctrl_register(int pid, + int (*cmd_handler) (int pid, int cmd, void *data)) +{ + unsigned long key = hash_long((unsigned long)pid, HANDLER_SHIFT); + struct hlist_head *head = &handlers[key]; + struct cmd_handler *handler; + + spin_lock(&handlers_lock); + handler = _stp_lookup_handler(pid); + spin_unlock(&handlers_lock); + + if (handler) + return -EBUSY; + + handler = kmalloc(sizeof(struct cmd_handler), GFP_KERNEL); + if (!handler) + return -ENOMEM; + handler->pid = pid; + handler->handler = cmd_handler; + INIT_HLIST_NODE(&handler->hlist); + + spin_lock(&handlers_lock); + hlist_add_head(&handler->hlist, head); + spin_unlock(&handlers_lock); + + return 0; +} + +/** + * _stp_ctrl_unregister - unregister a pid's command handler + * @pid: the pid to unregister + * + * Removes the pid's handler from the handler table. Should be + * called when the daemon is no longer sending commands. + */ +void _stp_ctrl_unregister(int pid) +{ + struct cmd_handler *handler; + + spin_lock(&handlers_lock); + handler = _stp_lookup_handler(pid); + if (handler) { + hlist_del(&handler->hlist); + kfree(handler); + } + spin_unlock(&handlers_lock); +} + +/** + * _stp_ctrl_send - send data over the control channel + * @type: the type of data being sent + * @data: the data + * @len: the data length + * @pid: the pid to send the data to + * + * Returns the result of the transport's send function. + */ +int _stp_ctrl_send(int type, void *data, int len, int pid) +{ + return _stp_netlink_send(type, data, len, pid); +} + +/** + * _stp_ctrl_handler - control channel command dispatcher + * @pid: the pid the command is from + * @cmd: the command + * @data: command-specific data + * + * Returns the result from the pid's command handler, 0 if the + * command was handled, non-zero otherwise. + */ +static int _stp_ctrl_handler(int pid, int cmd, void *data) +{ + int (*cmd_handler) (int, int, void *); + + cmd_handler = _stp_handler_find(pid); + if (!cmd_handler) + return -EINVAL; + + return cmd_handler(pid, cmd, data); +} + +/** + * _stp_ctrl_init - module init function + */ +static int __init _stp_ctrl_init(void) +{ + int i; + + handlers = kmalloc(sizeof(struct hlist_head) * HANDLER_SLOTS, GFP_KERNEL); + if (!handlers) + return -ENOMEM; + + for (i = 0; i < HANDLER_SLOTS; i++) + INIT_HLIST_HEAD(&handlers[i]); + + stp_control = _stp_netlink_open(NETLINK_USERSOCK, _stp_ctrl_handler); + if (!stp_control) { + printk ("stp_ctrl: couldn't open netlink socket\n"); + kfree(handlers); + return -ENOMEM; + } + + return 0; +} + +/** + * _stp_ctrl_exit - module exit function + */ +static void _stp_ctrl_exit(void) +{ + _stp_netlink_close(stp_control); + kfree(handlers); +} + +module_init(_stp_ctrl_init); +module_exit(_stp_ctrl_exit); + +EXPORT_SYMBOL_GPL(_stp_ctrl_register); +EXPORT_SYMBOL_GPL(_stp_ctrl_unregister); +EXPORT_SYMBOL_GPL(_stp_ctrl_send); + +MODULE_DESCRIPTION("SystemTap control channel"); +MODULE_AUTHOR("Tom Zanussi "); +MODULE_LICENSE("GPL"); + diff --git a/runtime/transport/control.h b/runtime/transport/control.h new file mode 100644 index 000000000..a614ecb70 --- /dev/null +++ b/runtime/transport/control.h @@ -0,0 +1,32 @@ +#ifndef _TRANSPORT_CONTROL_H_ /* -*- linux-c -*- */ +#define _TRANSPORT_CONTROL_H_ + +/** @file control.h + * @brief Header file for transport control channel + */ + +/* command handlers hash table entry struct */ +struct cmd_handler +{ + struct hlist_node hlist; + int pid; + int (*handler) (int pid, int cmd, void *data); +}; + +#define HANDLER_SHIFT 5 +#define HANDLER_SLOTS (1 << HANDLER_SHIFT) + +/* stp control channel command values */ +enum +{ + STP_BUF_INFO = 1, + STP_SUBBUFS_CONSUMED, + STP_REALTIME_DATA, + STP_EXIT, +}; + +extern int _stp_ctrl_register(int pid, int (*cmd_handler) (int pid, int cmd, void *data)); +extern void _stp_ctrl_unregister(int pid); +extern int _stp_ctrl_send(int type, void *reply, int len, int pid); + +#endif /* _TRANSPORT_CONTROL_H_ */ diff --git a/runtime/transport/netlink.c b/runtime/transport/netlink.c new file mode 100644 index 000000000..bfb9d22ab --- /dev/null +++ b/runtime/transport/netlink.c @@ -0,0 +1,165 @@ +/* + * netlink.c - stp relayfs-related transport functions + * + * Copyright (C) IBM Corporation, 2005 + * Copyright (C) Redhat Inc, 2005 + * + * This file is released under the GPL. + */ + +/** @file netlink.c + * @brief Systemtap netlink-related transport functions + */ + +/** @addtogroup transport Transport Functions + * @{ + */ + +#include "netlink.h" + +/* the control socket */ +extern struct sock *stp_control; + +/* queued packets logged from irq context */ +static struct sk_buff_head delayed_pkts; + +/* for netlink sequence numbers */ +static int seq; + +/** + * _stp_msg_rcv_skb - dispatch netlink control channel requests + */ +static void _stp_msg_rcv_skb(struct sk_buff *skb, + int (*cmd_handler) (int pid, int cmd, void *data)) +{ + struct nlmsghdr *nlh = NULL; + int pid, flags; + int nlmsglen, skblen; + void *data; + + skblen = skb->len; + + if (skblen < sizeof (*nlh)) + return; + + nlh = (struct nlmsghdr *)skb->data; + nlmsglen = nlh->nlmsg_len; + + if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) + return; + + pid = nlh->nlmsg_pid; + flags = nlh->nlmsg_flags; + + if (pid <= 0 || !(flags & NLM_F_REQUEST)) { + netlink_ack(skb, nlh, -EINVAL); + return; + } + + if (flags & MSG_TRUNC) { + netlink_ack(skb, nlh, -ECOMM); + return; + } + + data = NLMSG_DATA(nlh); + + if (cmd_handler(pid, nlh->nlmsg_type, data)) + netlink_ack(skb, nlh, -EINVAL); + + if (flags & NLM_F_ACK) + netlink_ack(skb, nlh, 0); +} + +/** + * _stp_msg_rcv - handle netlink control channel requests + */ +static void _stp_msg_rcv(struct sock *sk, int len) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&sk->sk_receive_queue))) { + _stp_msg_rcv_skb(skb, sk->sk_user_data); + kfree_skb(skb); + } +} + +/** + * _stp_send_delayed_packets - send delayed netlink packets + */ +static void _stp_send_delayed_pkts(unsigned long ignored) +{ + struct sk_buff *skb; + while ((skb = skb_dequeue(&delayed_pkts)) != NULL) { + struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; + int pid = nlh->nlmsg_pid; + netlink_unicast(stp_control, skb, pid, MSG_DONTWAIT); + } +} +static DECLARE_TASKLET(delayed_pkts_tasklet, _stp_send_delayed_pkts, 0); + +/** + * _stp_netlink_send - send data over netlink channel + * @type: message type + * @data: data to send + * @len: length of data + * @pid: pid to send data to + */ +int _stp_netlink_send(int type, void *data, int len, int pid) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + void *d; + int size; + int err = 0; + + size = NLMSG_SPACE(len); + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + nlh = NLMSG_PUT(skb, pid, seq++, type, size - sizeof(*nlh)); + nlh->nlmsg_flags = 0; + d = NLMSG_DATA(nlh); + memcpy(d, data, len); + + if (in_irq()) { + skb_queue_tail(&delayed_pkts, skb); + tasklet_schedule(&delayed_pkts_tasklet); + } else + err = netlink_unicast(stp_control, skb, pid, MSG_DONTWAIT); + + return err; + +nlmsg_failure: + if (skb) + kfree_skb(skb); + + return -1; +} + +/** + * _stp_netlink_open - create netlink socket + * @unit: the netlink 'unit' to create + * @handler: handler function for stp 'commands' + */ +struct sock *_stp_netlink_open(int unit, + int (*handler) (int pid, int cmd, void *data)) +{ + struct sock *nl = netlink_kernel_create(unit, _stp_msg_rcv); + if (!nl) { + printk("stp-control: couldn't create netlink transport\n"); + return NULL; + } + nl->sk_user_data = handler; + + return nl; +} + +/** + * _stp_netlink_close - close netlink socket + */ +void _stp_netlink_close (struct sock *nl) +{ + BUG_ON(!nl); + sock_release(nl->sk_socket); +} diff --git a/runtime/transport/netlink.h b/runtime/transport/netlink.h new file mode 100644 index 000000000..5f9514f10 --- /dev/null +++ b/runtime/transport/netlink.h @@ -0,0 +1,16 @@ +#ifndef _TRANSPORT_NETLINK_H_ /* -*- linux-c -*- */ +#define _TRANSPORT_NETLINK_H_ + +/** @file netlink.h + * @brief Header file for netlink transport + */ + +#include +#include +#include + +extern struct sock *_stp_netlink_open(int unit, int (*handler) (int pid, int cmd, void *data)); +extern void _stp_netlink_close(struct sock *nl); +extern int _stp_netlink_send(int type, void *reply, int len, int pid); + +#endif /* _TRANSPORT_NETLINK_H_ */ diff --git a/runtime/transport/relayfs.c b/runtime/transport/relayfs.c new file mode 100644 index 000000000..181a130e7 --- /dev/null +++ b/runtime/transport/relayfs.c @@ -0,0 +1,113 @@ +#ifndef _TRANSPORT_RELAYFS_C_ /* -*- linux-c -*- */ +#define _TRANSPORT_RELAYFS_C_ + +/* + * relayfs.c - stp relayfs-related transport functions + * + * Copyright (C) IBM Corporation, 2005 + * Copyright (C) Redhat Inc, 2005 + * + * This file is released under the GPL. + */ + +/** @file relayfs.c + * @brief Systemtap relayfs-related transport functions + */ + +/** @addtogroup transport Transport Functions + * @{ + */ + +#include "relayfs.h" + +/** + * _stp_subbuf_start - subbuf_start() relayfs callback implementation + */ +static int _stp_subbuf_start(struct rchan_buf *buf, + void *subbuf, + unsigned prev_subbuf_idx, + void *prev_subbuf) +{ + unsigned padding = buf->padding[prev_subbuf_idx]; + if (prev_subbuf) + *((unsigned *)prev_subbuf) = padding; + + return sizeof(padding); /* reserve space for padding */ +} + +/** + * _stp_buf_full - buf_full() relayfs callback implementation + */ +static void _stp_buf_full(struct rchan_buf *buf, + unsigned subbuf_idx, + void *subbuf) +{ + unsigned padding = buf->padding[subbuf_idx]; + *((unsigned *)subbuf) = padding; +} + +/* relayfs callback functions */ +static struct rchan_callbacks stp_rchan_callbacks = +{ + .subbuf_start = _stp_subbuf_start, + .buf_full = _stp_buf_full, +}; + +/** + * _stp_relayfs_close - destroys relayfs channel + * @chan: the relayfs channel + * @dir: the directory containing the relayfs files + */ +void _stp_relayfs_close(struct rchan *chan, struct dentry *dir) +{ + if (!chan) + return; + + relay_close(chan); + if (dir) + relayfs_remove_dir(dir); +} + +/** + * _stp_relayfs_open - create relayfs channel + * @n_subbufs: number of relayfs sub-buffers + * @subbuf_size: size of relayfs sub-buffers + * @pid: daemon pid + * @outdir: receives directory dentry + * + * Returns relay channel, NULL on failure + * + * Creates relayfs files as /systemtap/pid/cpuX in relayfs root + */ +struct rchan *_stp_relayfs_open(unsigned n_subbufs, + unsigned subbuf_size, + int pid, + struct dentry **outdir) +{ + char dirname[16]; + struct rchan *chan; + struct dentry* dir = NULL; + + sprintf(dirname, "%d", pid); + + /* TODO: need to create systemtap dir */ + dir = relayfs_create_dir(dirname, NULL); + if (!dir) { + printk("STP: couldn't create relayfs dir %s.\n", dirname); + return NULL; + } + + chan = relay_open("cpu", dir, subbuf_size, + n_subbufs, 0, &stp_rchan_callbacks); + if (!chan) { + printk("STP: couldn't create relayfs channel.\n"); + if (dir) + relayfs_remove_dir(dir); + } + + *outdir = dir; + return chan; +} + +#endif /* _TRANSPORT_RELAYFS_C_ */ + diff --git a/runtime/transport/relayfs.h b/runtime/transport/relayfs.h new file mode 100644 index 000000000..2f30138c7 --- /dev/null +++ b/runtime/transport/relayfs.h @@ -0,0 +1,16 @@ +#ifndef _TRANSPORT_RELAYFS_H_ /* -*- linux-c -*- */ +#define _TRANSPORT_RELAYFS_H_ + +/** @file relayfs.h + * @brief Header file for relayfs transport + */ + +#include + +struct rchan *_stp_relayfs_open(unsigned n_subbufs, + unsigned subbuf_size, + int pid, + struct dentry **outdir); +void _stp_relayfs_close(struct rchan *chan, struct dentry *dir); + +#endif /* _TRANSPORT_RELAYFS_H_ */ diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c new file mode 100644 index 000000000..080469a21 --- /dev/null +++ b/runtime/transport/transport.c @@ -0,0 +1,166 @@ +#ifndef _TRANSPORT_TRANSPORT_C_ /* -*- linux-c -*- */ +#define _TRANSPORT_TRANSPORT_C_ + +/* + * transport.c - stp transport functions + * + * Copyright (C) IBM Corporation, 2005 + * Copyright (C) Redhat Inc, 2005 + * + * This file is released under the GPL. + */ + +/** @file transport.c + * @brief Systemtap transport functions + */ + +/** @addtogroup transport Transport Functions + * @{ + */ + +#include +#include "transport.h" +#include "control.h" +#include "relayfs.c" + +/** @file transport.c + * @brief transport functions + */ +/** @addtogroup io transport + * transport functions + * @{ + */ + +/* transport-related data for this probe */ +struct stp_transport *t; + +/* forward declaration of probe-defined exit function */ +static void probe_exit(void); + +/** + * _stp_handle_buf_info - handle relayfs buffer info command + */ +static void _stp_handle_buf_info(int pid, struct buf_info *in) +{ + struct buf_info out; + BUG_ON(!(t && t->chan)); + + out.cpu = in->cpu; + out.produced = atomic_read(&t->chan->buf[in->cpu]->subbufs_produced); + out.consumed = atomic_read(&t->chan->buf[in->cpu]->subbufs_consumed); + + _stp_ctrl_send(STP_BUF_INFO, &out, sizeof(out), pid); +} + +/** + * _stp_handle_subbufs_consumed - handle relayfs subbufs consumed command + */ +static void _stp_handle_subbufs_consumed(int pid, struct consumed_info *info) +{ + BUG_ON(!(t && t->chan)); + relay_subbufs_consumed(t->chan, info->cpu, info->consumed); +} + +/** + * _stp_handle_exit - handle exit command + */ +static void _stp_handle_exit(int pid) +{ + BUG_ON(!t); + probe_exit(); + _stp_transport_flush(); + _stp_ctrl_send(STP_EXIT, __this_module.name, + strlen(__this_module.name) + 1, pid); +} + +/** + * _stp_cmd_handler - control channel command handler callback + * @pid: the pid of the daemon the command was sent from + * @cmd: the command id + * @data: command-specific data + * + * This function must return 0 if the command was handled, nonzero + * otherwise. + */ +static int _stp_cmd_handler(int pid, int cmd, void *data) +{ + int err = 0; + + switch (cmd) { + case STP_BUF_INFO: + _stp_handle_buf_info(pid, data); + break; + case STP_SUBBUFS_CONSUMED: + _stp_handle_subbufs_consumed(pid, data); + break; + case STP_EXIT: + _stp_handle_exit(pid); + break; + default: + err = -1; + break; + } + + return err; +} + +/** + * _stp_transport_close - close netlink and relayfs channels + * + * This must be called after all I/O is done, probably at the end + * of module cleanup. + */ +void _stp_transport_close() +{ + if (!t) + return; + + _stp_ctrl_unregister(t->pid); + if (!_stp_streaming()) + _stp_relayfs_close(t->chan, t->dir); + + /* in case the module has been manually removed */ + _stp_ctrl_send(STP_EXIT, __this_module.name, + strlen(__this_module.name) + 1, t->pid); + kfree(t); +} + +/** + * _stp_transport_open - open netlink and relayfs channels + * @n_subbufs: number of relayfs sub-buffers + * @subbuf_size: size of relayfs sub-buffers + * @pid: daemon pid + * + * Returns negative on failure, 0 otherwise. + * + * This function registers the probe with the control channel, + * and if the probe output will not be 'streaming', creates a + * relayfs channel for it. This must be called before any I/O is + * done, probably at the start of module initialization. + */ +int _stp_transport_open(unsigned n_subbufs, unsigned subbuf_size, int pid) +{ + BUG_ON(!(n_subbufs && subbuf_size)); + + t = kcalloc(1, sizeof(struct stp_transport), GFP_KERNEL); + if (!t) + return -ENOMEM; + + t->pid = pid; + _stp_ctrl_register(t->pid, _stp_cmd_handler); + + if (_stp_streaming()) + return 0; + + t->chan = _stp_relayfs_open(n_subbufs, subbuf_size, t->pid, &t->dir); + if (!t->chan) { + _stp_ctrl_unregister(t->pid); + kfree(t); + return -ENOMEM; + } + + return 0; +} + +/** @} */ +#endif /* _TRANSPORT_C_ */ diff --git a/runtime/transport/transport.h b/runtime/transport/transport.h new file mode 100644 index 000000000..6e0550b3a --- /dev/null +++ b/runtime/transport/transport.h @@ -0,0 +1,75 @@ +#ifndef _TRANSPORT_TRANSPORT_H_ /* -*- linux-c -*- */ +#define _TRANSPORT_TRANSPORT_H_ + +/** @file transport.h + * @brief Header file for stp transport + */ + +#include "control.h" +#include "netlink.h" +#include "relayfs.h" + +/* transport data structure */ +struct stp_transport +{ + struct rchan *chan; + struct dentry *dir; + int pid; +}; + +/* control channel command structs */ +struct buf_info +{ + int cpu; + unsigned produced; + unsigned consumed; +}; + +struct consumed_info +{ + int cpu; + unsigned consumed; +}; + +/** + * _stp_transport_write - write data to the transport + * @t: the transport struct + * @data: the data to send + * @len: length of the data to send + */ +#ifdef STP_NETLINK_ONLY +#define _stp_transport_write(t, data, len) \ + _stp_ctrl_send(STP_REALTIME_DATA, data, len, t->pid) +#else +#define _stp_transport_write(t, data, len) \ + relay_write(t->chan, data, len) +#endif + +/** + * _stp_streaming - boolean, are we using 'streaming' output? + */ +#ifdef STP_NETLINK_ONLY +#define _stp_streaming() (1) +#else +#define _stp_streaming() (0) +#endif + +/** + * _stp_transport_flush - flush the transport, if applicable + */ +static inline void _stp_transport_flush(void) +{ +#ifndef STP_NETLINK_ONLY + extern struct stp_transport *t; + + BUG_ON(!t->chan); + relay_flush(t->chan); + ssleep(1); /* FIXME: time for data to be flushed */ +#endif +} + +extern int _stp_transport_open(unsigned n_subbufs, unsigned subbuf_size, + int pid); +extern void _stp_transport_close(void); + +#endif /* _TRANSPORT_TRANSPORT_H_ */ -- 2.43.5