Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sched/rr #17

Merged
merged 14 commits into from
Sep 26, 2014
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions tempesta.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
SSOCKET=sync_socket
TDB=tempesta_db
TFW=tempesta_fw
SCHED=tfw_sched_dummy
TFW_ROOT=`pwd`/$TFW
TFW_CACHE_SIZE=`expr 256 \* 1024`
TFW_CACHE_PATH=$TFW_ROOT/cache
Expand Down Expand Up @@ -44,6 +45,9 @@ start()
cache_path="$TFW_CACHE_PATH"
[ $? -ne 0 ] && error "cannot load tempesta module"

insmod $TFW_ROOT/sched/$SCHED.ko
[ $? -ne 0 ] && error "cannot load scheduler module"

sysctl --load=tempesta.sysctl.conf
[ $? -ne 0 ] && error "cannot apply configuration via sysctl"

Expand Down
2 changes: 1 addition & 1 deletion tempesta_fw/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ obj-m = tempesta_fw.o
tempesta_fw-objs = cache.o classifier.o client.o connection.o filter.o gfsm.o \
http.o http_msg.o http_parser.o if.o lib.o main.o pool.o \
sched.o server.o session.o sock_backend.o sock_frontend.o \
stress.o
stress.o debugfs.o

obj-m += log/ classifier/ stress/ sched/ filter/
369 changes: 369 additions & 0 deletions tempesta_fw/debugfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,369 @@
/**
* Tempesta FW
*
* Copyright (C) 2012-2014 NatSys Lab. (info@natsys-lab.com).
*
* 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.
*/

#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/string.h>

#include "debugfs.h"
#include "log.h"
#include "tempesta.h"


#ifdef DEBUG

/* The root directory for the Tempesta debugfs module.
* All paths are referenced relative to this root. */
struct dentry *tfw_debugfs_root;

/* Name of the directory which is created in the debugfs upon initialization. */
#define TFW_DEBUGFS_ROOT_NAME "tempesta"

/**
* Initialize the debugfs wrapper by creating a directory in the debugfs
* where all files owned by the Tempesta FW are hosted.
*/
int
tfw_debugfs_init(void)
{
int ret = 0;

tfw_debugfs_root = debugfs_create_dir(TFW_DEBUGFS_ROOT_NAME, NULL);

if (IS_ERR_OR_NULL(tfw_debugfs_root)) {
ret = (int)PTR_ERR(tfw_debugfs_root);
TFW_WARN("Can't create debugfs directory (%d)\n", ret);
}

return ret;
}

/**
* Delete the Tempesta's debugfs directory with all files recursively.
*/
void
tfw_debugfs_exit(void)
{
if (tfw_debugfs_root) {
debugfs_remove_recursive(tfw_debugfs_root);
tfw_debugfs_root = NULL;
}
}

/**
* Look up for a file (or directory) in VFS.
*/
static struct dentry *
lookup_file(const char *path, struct dentry *parent)
{
int ret;
struct path p;

BUG_ON(!parent);
ret = vfs_path_lookup(parent, NULL, path, 0, &p);

return (ret ? NULL : p.dentry);
}

/**
* Create a file in the debugfs, also create parent directories if needed and
* remove the old file if it exists.
*
* @param path Path to a file to be created.
* The path is always treated relative to the Tempesta root
* directory in the debugfs (see tfw_debugfs_root).
* @param data A pointer to some data which is saved in 'file' and 'inode'
* structures. It may be retrieved by any function in @fops as
* file->private_data or file->f_inode->i_private (it is copied
* into both places).
* @param fops A set of functions that handle system calls on the created file.
*
*
* The function creates a file in the debugfs, but does it in a robust way:
* - the file is replaced if it already exists
* - all parent directories are created if they don't exist
*
* Returns: An ERR_PTR if the file is not created.
*/
static struct dentry *
create_with_parents(const char *path, void *data,
const struct file_operations *fops)
{
size_t name_size;
char *buf, *pos, *component;
struct dentry *parent, *child;

/* Copy the path to a temporary buffer where it can be modified. */
name_size = strlen(path) + 1;
buf = kmalloc(name_size, GFP_KERNEL);
BUG_ON(ZERO_OR_NULL_PTR(buf));
strlcpy(buf, path, name_size);

/* Eat the leading slash to allow specify either /foo/bar or foo/bar */
pos = buf;
if (*pos == '/')
++pos;

/* Walk over the path and create non-existing directories. */
parent = tfw_debugfs_root;
component = pos;
do {
if (*pos != '/')
continue;

*pos = '\0';
child = lookup_file(component, parent);
if (!child) {
child = debugfs_create_dir(component, parent);
BUG_ON(!parent);
}
parent = child;
component = pos + 1;
} while (*(++pos));

/* Remove the file if it already exists. */
child = lookup_file(component, parent);
if (child) {
TFW_DBG("Removing already existing debugfs file: %s\n", path);
debugfs_remove(child);
}

/* Create the actual file. */
child = debugfs_create_file(component, S_IRWXU, parent, data, fops);
if (IS_ERR_OR_NULL(child)) {
int err = PTR_ERR(child);
TFW_WARN("Can't create debugfs file: %s (%d)\n", path, err);
} else {
TFW_DBG("Created debugfs file: %s\n", path);
}

kfree(buf);

return child;
}

/**
* A state maintained between open() and release() calls.
*
* Usage of this structure depens on the data direction:
*
* 1. If a file is open()'ed for reading (@is_input is false):
* - open() allocates the @buf and invokes a callback that writes data to it.
* - read() just copies data from the @buf to the user-space.
* - write() returns an error.
* - close() releases all the allocated memory.
*
* 2. If a file is open()'ed for writing (@is_input is true):
* - open() allocates the @buf
* - read() returns an error.
* - write() copies data from user-space to the @buf.
* - close() invokes a callback passing the @buf to it and releases the memory.
*
* The file may be open()'ed only for either reading or writing but not both.
*/
typedef struct {
bool is_input;
int len;
int buf_size;
char *buf;
} TfwDebugfsIoState;

static int
fop_open(struct inode *inode, struct file *file)
{
fmode_t mode = file->f_mode;
tfw_debugfs_handler_t fn = file->f_inode->i_private;
TfwDebugfsIoState *state;

if ((mode & FMODE_READ) && (mode & FMODE_WRITE)) {
TFW_ERR("This debugfs file can't be opened in read-write mode");
return -EPERM;
}

state = kzalloc(sizeof(*state), GFP_KERNEL);
BUG_ON(!state);

/* Simply allocate a fixed-size buffer. The buffer doesn't expand, the
* data is cropped if it doesn't fit to it. Later on we may change it */
state->buf_size = PAGE_SIZE;
state->buf = kmalloc(state->buf_size, GFP_KERNEL);
BUG_ON(!state->buf);

if (mode & FMODE_WRITE)
state->is_input = true;
else
state->len = fn(state->is_input, state->buf, state->buf_size);

file->private_data = state;

return 0;
}

static ssize_t
fop_read(struct file *file, char __user *user_buf, size_t count,
loff_t *ppos)
{
TfwDebugfsIoState *state = file->private_data;

if (state->is_input) {
TFW_ERR("Can't read this debugfs file: "
"it was open()'ed only for writing\n");
return -EPERM;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not be braces aroung one-line IF-body

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frankly, I tend to put braces around a single-line body as well. The reasoning is as follows:

  • Every so often I need to put some debug info there, so I end up adding the braces anyway.
  • I find that it's safer exactly because of that chance that the block will be expanded in future.
  • It makes the block clearly visible, makes it stand out. That makes it easier to read and distinguish these from other parts of the code.
  • If there's no block, then the next line is often left empty. Putting a closing bracket there doesn't hurt the code density (and I like dense code), but again, makes the code clearly visible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this particular case there was a debug message before the return.
I deleted the message, but forgot about the braces.
I tend to put such debug messages inside such branches, and the braces always hinder a little bit.
Also I endorse all other arguments: for me the code looks cleaner and more readable.

I'm just saying, I'll fix it according to the convention anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alkesey, linux kernel Coding Style says

    Do not unnecessarily use braces where a single statement will do.

    if (condition)
            action();

The same is in our coding style, so it's better to follow the style so the whole code looks the same.


if (state->len < 0)
return state->len;

return simple_read_from_buffer(user_buf, count, ppos,
state->buf,
state->len);
}

static ssize_t
fop_write(struct file *file, const char __user *user_buf,
size_t count,
loff_t *ppos)
{
int len;
TfwDebugfsIoState *state = file->private_data;

if (!state->is_input) {
TFW_ERR("Can't write to this debugfs file: "
"it was open()'ed only for reading\n");
return -EPERM;
}

/* copy data from user-space */
len = simple_write_to_buffer(state->buf, (state->buf_size - 1),
ppos,
user_buf, count);
if (len > 0) {
state->len += len;
state->buf[state->len] = '\0';
}

return len;
}

static int
fop_release(struct inode *inode, struct file *file)
{
int ret = 0;
tfw_debugfs_handler_t fn = file->f_inode->i_private;
TfwDebugfsIoState *state = file->private_data;

if (state->is_input)
ret = fn(state->is_input, state->buf, state->len);

kfree(state->buf);
kfree(state);

return ret;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra space again

/**
* Create a file in debugfs and bind a function with it.
*
* @path Path to of a file to be created.
* The path is always treated as relative to tempesta root directory
* in the debugfs. The file may exist, in this case it will be replaced.
* Parent directories may not exist (they will be created automatically).
* @fn A function that handles I/O (that is either receives or sends data to
* user-space).
*
* This function allows to bind a file (that may be read/written in user-space)
* with a function (that works in kernel-space).
*
* Consider the following example:
*
* int my_io_handler(bool is_input, char *buf, size_t size)
* {
* if (is_input) {
* printk("got input data from user-space: %.*s", size, buf);
* return 0;
* } else {
* return snprintf("hello from kernel-space!\n");
* }
* }
*
* tfw_debugfs_bind("/foo/bar", my_io_handler);
*
* The code binds the file /foo/bar with the function my_io_handler() and allows
* to interact with the kernel function from a user-space shell like this:
* $ cat /sys/kernel/debug/tempesta/foo/bar
* hello from kernel-space!
* $ echo "hi from user-space" > /sys/kernel/debug/tempesta/foo/bar
* $ dmesg | head -n1
* got input data from user-space: hi from user-space
*
* When the file is open()'ed in read mode, the my_io_handler() is called with
* is_input=false. In this case the function is requested to put some data to
* the buffer. The buffer then is saved and returned to user-space during
* subsequent read() calls until the file is close()'d.
*
* When the file is open()'ed in write mode, the data is accumulated in the
* buffer during write() calls. When the file is close()'d, the my_io_handler()
* is called to take the data received from user-space.
*
* Therefore, the handler function doesn't need to care about streaming, it
* receives or sends the data "atomically" with a single big chunk.
* This approach simplifies your code (no need to preserve state between calls),
* but it is not very effective, so don't use it for large amounts of data.
*
* @return A status code: 0 if the file is created, -1 otherwise.
*/
int
tfw_debugfs_bind(const char *path, tfw_debugfs_handler_t fn)
{
static const struct file_operations fops = {
.llseek = default_llseek,
.open = fop_open,
.read = fop_read,
.write = fop_write,
.release = fop_release,
};
struct dentry *d;

BUG_ON(!path || !fn);
d = create_with_parents(path, fn, &fops);

return IS_ERR_OR_NULL(d) ? -1 : 0;
}
EXPORT_SYMBOL(tfw_debugfs_bind);

#else /* ifdef DEBUG */

int tfw_debugfs_bind(const char *path, tfw_debugfs_handler_t handler_fn)
{
return 0;
}
EXPORT_SYMBOL(tfw_debugfs_bind);

int tfw_debugfs_init(void)
{
return 0;
}

void tfw_debugfs_exit(void)
{
}

#endif /* ifndef DEBUG */
Loading