runtime/transport/control.c - systemtap
Global variables defined
Functions defined
Macros defined
Source code
#include "control.h"
#include "../mempool.c"
#include "symbols.c"
#include <linux/delay.h>
#include <linux/poll.h>
#include "../uidgid_compatibility.h"
static _stp_mempool_t *_stp_pool_q;
static struct list_head _stp_ctl_ready_q;
static DEFINE_SPINLOCK(_stp_ctl_ready_lock);
static DEFINE_SPINLOCK(_stp_ctl_special_msg_lock);
static void _stp_cleanup_and_exit(int send_exit);
static void _stp_handle_tzinfo (struct _stp_msg_tzinfo* tzi);
static void _stp_handle_privilege_credentials (struct _stp_msg_privilege_credentials* pc);
static void _stp_handle_remote_id (struct _stp_msg_remote_id* rem);
static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
static DEFINE_MUTEX(cmd_mutex);
u32 type;
int rc = 0;
#ifdef STAPCONF_TASK_UID
uid_t euid = current->euid;
#else
#if defined(CONFIG_USER_NS) || (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
uid_t euid = from_kuid_munged(current_user_ns(), current_euid());
#else
uid_t euid = current_euid();
#endif
#endif
if (count < sizeof(u32))
return 0;
if (get_user(type, (u32 __user *)buf))
return -EFAULT;
count -= sizeof(u32);
buf += sizeof(u32);
#if defined(DEBUG_TRANS) && (DEBUG_TRANS >= 2)
if (type < STP_MAX_CMD)
dbug_trans2("Got %s. len=%d\n", _stp_command_name[type],
(int)count);
#endif
mutex_lock (& cmd_mutex);
switch (type) {
case STP_START:
{
static struct _stp_msg_start st;
if (count < sizeof(st)) {
rc = 0; goto out;
}
if (copy_from_user(&st, buf, sizeof(st))) {
rc = -EFAULT;
goto out;
}
_stp_handle_start(&st);
}
break;
case STP_EXIT:
_stp_cleanup_and_exit(1);
break;
case STP_BULK:
#ifdef STP_BULKMODE
break;
#else
rc = -EINVAL;
goto out;
#endif
case STP_RELOCATION:
if (euid != 0) {
rc = -EPERM;
goto out;
}
XXX _stp_do_relocation (buf, count);
break;
case STP_TZINFO:
{
static struct _stp_msg_tzinfo tzi;
if (euid != 0) {
rc = -EPERM;
goto out;
}
if (count < sizeof(tzi)) {
rc = 0;
goto out;
}
if (copy_from_user(&tzi, buf, sizeof(tzi))) {
rc = -EFAULT;
goto out;
}
_stp_handle_tzinfo(&tzi);
}
break;
case STP_PRIVILEGE_CREDENTIALS:
{
static struct _stp_msg_privilege_credentials pc;
if (euid != 0) {
rc = -EPERM;
goto out;
}
if (count < sizeof(pc)) {
rc = 0;
goto out;
}
if (copy_from_user(&pc, buf, sizeof(pc))) {
rc = -EFAULT;
goto out;
}
_stp_handle_privilege_credentials(&pc);
}
break;
case STP_REMOTE_ID:
{
static struct _stp_msg_remote_id rem;
if (euid != 0) {
rc = -EPERM;
goto out;
}
if (count < sizeof(rem)) {
rc = 0;
goto out;
}
if (copy_from_user(&rem, buf, sizeof(rem))) {
rc = -EFAULT;
goto out;
}
_stp_handle_remote_id(&rem);
}
break;
case STP_READY:
break;
default:
#ifdef DEBUG_TRANS
dbug_trans2("invalid command type %d\n", type);
#endif
rc = -EINVAL;
goto out;
}
rc = count + sizeof(u32);
out:
mutex_unlock (& cmd_mutex);
#if defined(DEBUG_TRANS) && (DEBUG_TRANS >= 2)
if (type < STP_MAX_CMD)
dbug_trans2("Completed %s (rc=%d)\n", _stp_command_name[type], rc);
#endif
return rc;
}
static DECLARE_WAIT_QUEUE_HEAD(_stp_ctl_wq);
#ifdef DEBUG_TRANS
static void _stp_ctl_write_dbug(int type, void *data, int len)
{
char buf[64];
switch (type) {
case STP_START:
dbug_trans2("sending STP_START\n");
break;
case STP_EXIT:
dbug_trans2("sending STP_EXIT\n");
break;
case STP_OOB_DATA:
snprintf(buf, sizeof(buf), "%s", (char *)data);
dbug_trans2("sending %d bytes of STP_OOB_DATA: %s\n", len,
buf);
break;
case STP_SYSTEM:
snprintf(buf, sizeof(buf), "%s", (char *)data);
dbug_trans2("sending STP_SYSTEM: %s\n", buf);
break;
case STP_TRANSPORT:
dbug_trans2("sending STP_TRANSPORT\n");
break;
case STP_CONNECT:
dbug_trans2("sending STP_CONNECT\n");
break;
case STP_DISCONNECT:
dbug_trans2("sending STP_DISCONNECT\n");
break;
case STP_BULK:
dbug_trans2("sending STP_BULK\n");
break;
case STP_READY:
case STP_RELOCATION:
case STP_BUF_INFO:
case STP_SUBBUFS_CONSUMED:
dbug_trans2("sending old message\n");
break;
case STP_REALTIME_DATA:
dbug_trans2("sending %d bytes of STP_REALTIME_DATA\n", len);
break;
case STP_REQUEST_EXIT:
dbug_trans2("sending STP_REQUEST_EXIT\n");
break;
default:
dbug_trans2("ERROR: unknown message type: %d\n", type);
break;
}
}
#endif
#define _STP_CTL_MSG_UNUSED STP_MAX_CMD
static struct _stp_buffer *_stp_ctl_start_msg;
static struct _stp_buffer *_stp_ctl_exit_msg;
static struct _stp_buffer *_stp_ctl_transport_msg;
static struct _stp_buffer *_stp_ctl_request_exit_msg;
static struct _stp_buffer *_stp_ctl_oob_warn;
static struct _stp_buffer *_stp_ctl_oob_err;
static struct _stp_buffer *_stp_ctl_system_warn;
static struct _stp_buffer *_stp_ctl_realtime_err;
static int _stp_ctl_alloc_special_buffers(void)
{
size_t len;
const char *msg;
_stp_ctl_start_msg = _stp_mempool_alloc(_stp_pool_q);
if (_stp_ctl_start_msg == NULL)
return -1;
_stp_ctl_start_msg->type = _STP_CTL_MSG_UNUSED;
_stp_ctl_exit_msg = _stp_mempool_alloc(_stp_pool_q);
if (_stp_ctl_exit_msg == NULL)
return -1;
_stp_ctl_exit_msg->type = _STP_CTL_MSG_UNUSED;
_stp_ctl_transport_msg = _stp_mempool_alloc(_stp_pool_q);
if (_stp_ctl_transport_msg == NULL)
return -1;
_stp_ctl_transport_msg->type = _STP_CTL_MSG_UNUSED;
_stp_ctl_request_exit_msg = _stp_mempool_alloc(_stp_pool_q);
if (_stp_ctl_request_exit_msg == NULL)
return -1;
_stp_ctl_request_exit_msg->type = _STP_CTL_MSG_UNUSED;
_stp_ctl_oob_warn = _stp_mempool_alloc(_stp_pool_q);
if (_stp_ctl_oob_warn == NULL)
return -1;
_stp_ctl_oob_warn->type = _STP_CTL_MSG_UNUSED;
msg = "WARNING: too many pending (warning) messages\n";
len = strlen(msg) + 1;
_stp_ctl_oob_warn->len = len;
memcpy(&_stp_ctl_oob_warn->buf, msg, len);
_stp_ctl_oob_err = _stp_mempool_alloc(_stp_pool_q);
if (_stp_ctl_oob_err == NULL)
return -1;
_stp_ctl_oob_err->type = _STP_CTL_MSG_UNUSED;
msg = "ERROR: too many pending (error) messages\n";
len = strlen(msg) + 1;
_stp_ctl_oob_err->len = len;
memcpy(&_stp_ctl_oob_err->buf, msg, len);
_stp_ctl_system_warn = _stp_mempool_alloc(_stp_pool_q);
if (_stp_ctl_system_warn == NULL)
return -1;
_stp_ctl_system_warn->type = _STP_CTL_MSG_UNUSED;
msg = "WARNING: too many pending (system) messages\n";
len = strlen(msg) + 1;
_stp_ctl_system_warn->len = len;
memcpy(&_stp_ctl_system_warn->buf, msg, len);
_stp_ctl_realtime_err = _stp_mempool_alloc(_stp_pool_q);
if (_stp_ctl_realtime_err == NULL)
return -1;
_stp_ctl_realtime_err->type = _STP_CTL_MSG_UNUSED;
msg = "ERROR: too many pending (realtime) messages\n";
len = strlen(msg) + 1;
_stp_ctl_realtime_err->len = len;
memcpy(&_stp_ctl_realtime_err->buf, msg, len);
return 0;
}
static void _stp_ctl_free_special_buffers(void)
{
if (_stp_ctl_start_msg != NULL) {
_stp_mempool_free(_stp_ctl_start_msg);
_stp_ctl_start_msg = NULL;
}
if (_stp_ctl_exit_msg != NULL) {
_stp_mempool_free(_stp_ctl_exit_msg);
_stp_ctl_exit_msg = NULL;
}
if (_stp_ctl_transport_msg != NULL) {
_stp_mempool_free(_stp_ctl_transport_msg);
_stp_ctl_transport_msg = NULL;
}
if (_stp_ctl_request_exit_msg != NULL) {
_stp_mempool_free(_stp_ctl_request_exit_msg);
_stp_ctl_request_exit_msg = NULL;
}
if (_stp_ctl_oob_warn != NULL) {
_stp_mempool_free(_stp_ctl_oob_warn);
_stp_ctl_oob_warn = NULL;
}
if (_stp_ctl_oob_err != NULL) {
_stp_mempool_free(_stp_ctl_oob_err);
_stp_ctl_oob_err = NULL;
}
if (_stp_ctl_system_warn != NULL) {
_stp_mempool_free(_stp_ctl_system_warn);
_stp_ctl_system_warn = NULL;
}
if (_stp_ctl_realtime_err != NULL) {
_stp_mempool_free(_stp_ctl_realtime_err);
_stp_ctl_realtime_err = NULL;
}
}
static struct _stp_buffer *_stp_ctl_get_buffer(int type, void *data,
unsigned len)
{
unsigned long flags;
struct _stp_buffer *bptr = NULL;
if (type == STP_OOB_DATA
|| type == STP_SYSTEM
|| type == STP_REALTIME_DATA)
bptr = _stp_mempool_alloc(_stp_pool_q);
if (bptr != NULL) {
bptr->type = type;
memcpy(bptr->buf, data, len);
bptr->len = len;
} else {
switch (type) {
case STP_START:
bptr = _stp_ctl_start_msg;
break;
case STP_EXIT:
bptr = _stp_ctl_exit_msg;
break;
case STP_TRANSPORT:
bptr = _stp_ctl_transport_msg;
break;
case STP_REQUEST_EXIT:
bptr = _stp_ctl_request_exit_msg;
break;
case STP_OOB_DATA:
if (data && len >= 9
&& strncmp(data, "WARNING: ", 9) == 0)
bptr = _stp_ctl_oob_warn;
else if (data && len >= 7
&& strncmp(data, "ERROR: ", 7) == 0)
bptr = _stp_ctl_oob_err;
else
printk(KERN_WARNING "_stp_ctl_get_buffer unexpected STP_OOB_DATA\n");
break;
case STP_SYSTEM:
bptr = _stp_ctl_system_warn;
type = STP_OOB_DATA; break;
case STP_REALTIME_DATA:
bptr = _stp_ctl_realtime_err;
type = STP_OOB_DATA; break;
default:
printk(KERN_WARNING "_stp_ctl_get_buffer unknown type: %d\n", type);
bptr = NULL;
break;
}
if (bptr != NULL) {
spin_lock_irqsave(&_stp_ctl_special_msg_lock, flags);
if (bptr->type == _STP_CTL_MSG_UNUSED)
bptr->type = type;
else
bptr = NULL;
spin_unlock_irqrestore(&_stp_ctl_special_msg_lock, flags);
}
if (bptr != NULL
&& bptr != _stp_ctl_oob_warn
&& bptr != _stp_ctl_oob_err
&& bptr != _stp_ctl_system_warn
&& bptr != _stp_ctl_realtime_err) {
memcpy(bptr->buf, data, len);
bptr->len = len;
}
}
return bptr;
}
static void _stp_ctl_free_buffer(struct _stp_buffer *bptr)
{
unsigned long flags;
if (bptr == _stp_ctl_start_msg
|| bptr == _stp_ctl_exit_msg
|| bptr == _stp_ctl_transport_msg
|| bptr == _stp_ctl_request_exit_msg
|| bptr == _stp_ctl_oob_warn
|| bptr == _stp_ctl_oob_err
|| bptr == _stp_ctl_system_warn
|| bptr == _stp_ctl_realtime_err) {
spin_lock_irqsave(&_stp_ctl_special_msg_lock, flags);
bptr->type = _STP_CTL_MSG_UNUSED;
spin_unlock_irqrestore(&_stp_ctl_special_msg_lock, flags);
} else {
_stp_mempool_free(bptr);
}
}
static int _stp_ctl_send(int type, void *data, unsigned len)
{
struct context* __restrict__ c = NULL;
struct _stp_buffer *bptr;
unsigned long flags;
unsigned hlen;
#ifdef DEBUG_TRANS
_stp_ctl_write_dbug(type, data, len);
#endif
hlen = _stp_ctl_write_fs(type, data, len);
if (hlen > 0)
return hlen;
if (unlikely(len > STP_CTL_BUFFER_SIZE)) {
printk(KERN_ERR "ctl_write_msg type=%d len=%d too large\n",
type, len);
return 0;
}
c = _stp_runtime_entryfn_get_context();
bptr = _stp_ctl_get_buffer(type, data, len);
if (unlikely(bptr == NULL)) {
_stp_runtime_entryfn_put_context(c);
return -ENOMEM;
}
spin_lock_irqsave(&_stp_ctl_ready_lock, flags);
list_add_tail(&bptr->list, &_stp_ctl_ready_q);
spin_unlock_irqrestore(&_stp_ctl_ready_lock, flags);
_stp_runtime_entryfn_put_context(c);
return len + sizeof(bptr->type);
}
static int _stp_ctl_send_notify(int type, void *data, unsigned len)
{
int ret;
dbug_trans(1, "_stp_ctl_send_notify: type=%d len=%d\n", type, len);
ret = _stp_ctl_send(type, data, len);
if (ret > 0)
wake_up_interruptible(&_stp_ctl_wq);
return ret;
}
static ssize_t _stp_ctl_read_cmd(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct context* __restrict__ c = NULL;
struct _stp_buffer *bptr;
int len;
unsigned long flags;
c = _stp_runtime_entryfn_get_context();
spin_lock_irqsave(&_stp_ctl_ready_lock, flags);
while (list_empty(&_stp_ctl_ready_q)) {
spin_unlock_irqrestore(&_stp_ctl_ready_lock, flags);
_stp_runtime_entryfn_put_context(c);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
if (wait_event_interruptible(_stp_ctl_wq, !list_empty(&_stp_ctl_ready_q)))
return -ERESTARTSYS;
c = _stp_runtime_entryfn_get_context();
spin_lock_irqsave(&_stp_ctl_ready_lock, flags);
}
bptr = (struct _stp_buffer *)_stp_ctl_ready_q.next;
list_del_init(&bptr->list);
spin_unlock_irqrestore(&_stp_ctl_ready_lock, flags);
_stp_runtime_entryfn_put_context(c);
len = bptr->len + 4;
if (len > count || copy_to_user(buf, &bptr->type, len)) {
FIXME errk("Supplied buffer too small. count:%d len:%d\n", (int)count, len);
return -EFAULT;
}
c = _stp_runtime_entryfn_get_context();
_stp_ctl_free_buffer(bptr);
_stp_runtime_entryfn_put_context(c);
return len;
}
static int _stp_ctl_open_cmd(struct inode *inode, struct file *file)
{
if (atomic_inc_return (&_stp_ctl_attached) > 1) {
atomic_dec (&_stp_ctl_attached);
return -EBUSY;
}
_stp_attach();
return 0;
}
static int _stp_ctl_close_cmd(struct inode *inode, struct file *file)
{
if (atomic_dec_return (&_stp_ctl_attached) > 0) {
BUG();
return -EINVAL;
}
_stp_detach();
return 0;
}
static unsigned _stp_ctl_poll_cmd(struct file *file, poll_table *wait)
{
unsigned res = POLLPRI | POLLOUT | POLLWRNORM;
unsigned long flags;
poll_wait(file, &_stp_ctl_wq, wait);
spin_lock_irqsave(&_stp_ctl_ready_lock, flags);
if (! list_empty(&_stp_ctl_ready_q))
res |= POLLIN | POLLRDNORM;
spin_unlock_irqrestore(&_stp_ctl_ready_lock, flags);
return res;
}
static struct file_operations _stp_ctl_fops_cmd = {
.owner = THIS_MODULE,
.read = _stp_ctl_read_cmd,
.write = _stp_ctl_write_cmd,
.open = _stp_ctl_open_cmd,
.release = _stp_ctl_close_cmd,
.poll = _stp_ctl_poll_cmd
};
static int _stp_register_ctl_channel(void)
{
INIT_LIST_HEAD(&_stp_ctl_ready_q);
_stp_pool_q = _stp_mempool_init(sizeof(struct _stp_buffer),
STP_DEFAULT_BUFFERS);
if (unlikely(_stp_pool_q == NULL))
goto err0;
_stp_allocated_net_memory += sizeof(struct _stp_buffer) * STP_DEFAULT_BUFFERS;
if (unlikely(_stp_ctl_alloc_special_buffers() != 0))
goto err0;
if (_stp_register_ctl_channel_fs() != 0)
goto err0;
return 0;
err0:
_stp_mempool_destroy(_stp_pool_q);
errk("Error creating systemtap control channel.\n");
return -1;
}
static void _stp_unregister_ctl_channel(void)
{
struct _stp_buffer *bptr, *tmp;
_stp_unregister_ctl_channel_fs();
list_for_each_entry_safe(bptr, tmp, &_stp_ctl_ready_q, list) {
list_del(&bptr->list);
_stp_ctl_free_buffer(bptr);
}
_stp_ctl_free_special_buffers();
_stp_mempool_destroy(_stp_pool_q);
}