-
Notifications
You must be signed in to change notification settings - Fork 103
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
Sched/rr #17
Changes from 13 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
566afbe
basic round-robin scheduler implementation
vdmit11 d08314b
basic debugfs wrapper for calling functions from userspace
vdmit11 7c9da9b
Add support for custom read/write handlers in the debugfs wrapper.
vdmit11 7cc4143
stylistic refactoring of the round-robin scheduler
vdmit11 485a8bf
extract dummy scheduler module to a separate file
vdmit11 074c442
fix incorrect spinlock usage in the round-robin scheduler
vdmit11 a2243af
load tfw_sched_rr module from tempesta.sh
vdmit11 58e78f4
extract dummy scheduler to a separate kernel module
vdmit11 4750b29
a faster round-robin scheduler implementation
vdmit11 00d41cb
fix stylistic issues
vdmit11 809ae2a
fix an infinite loop on attempt to get a server from the empty list
vdmit11 0deb5db
add tfw_server_snprint() required by tfw_sched_rr
vdmit11 6c5397f
add generic struct sockaddr to TfwAddr required by tfw_sched_rr
vdmit11 a5bf0d2
Fix small code review issues
vdmit11 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
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; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 */ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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:
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
The same is in our coding style, so it's better to follow the style so the whole code looks the same.