From: "Christopher S. Aker" <caker@theshore.net>
To: uml-devel <user-mode-linux-devel@lists.sourceforge.net>
Subject: [uml-devel] [PATCH] I/O-Request Token Bucket Limiter
Date: Sun, 28 Mar 2004 03:06:22 -0600 [thread overview]
Message-ID: <004901c414a3$f4d28a80$0201a8c0@hawk> (raw)
[-- Attachment #1: Type: text/plain, Size: 2035 bytes --]
Patch is also available here:
http://www.theshore.net/~caker/patches/
Apply to a tree already patched with 2.4-um or 2.6-um.
-Chris
---token-limiter-v1.README---
This patch implements a simple token bucket filter/limiter on requests
processed through the async io_thread. It will work with or without COW,
but not on ubd devices in sync mode.
* Two new command line arguments for setting at boot-time:
token_max=60000
This sets the maximum tokens in the token-bucket I/O request limiter.
Each io_thread request subtracts one token from the bucket. The bucket
is supplied with <token_refill> tokens every second. When the bucket
becomes empty, I/O requests are throttled to the <token_refill> rate.
token_refill=2000
This sets the bucket's refill rate, adding <token_refill> tokens to the
bucket every second. This becomes the I/O request rate when the bucket
becomes empty.
* Three new mconsole commands to get status information and change the two
variables above:
(mconsole) help
io_status - Return current I/O status and settings
io_token_max <num> - sets the bucket size
io_token_refill <num> - number of tokens to add each second
* Example io_status output while:
idle:
io_count=1845202 io_rate=0 io_tokens=50000 token_refill=1024 \
token_max=50000
bursting:
io_count=1861594 io_rate=6806 io_tokens=35754 token_refill=1024 \
token_max=50000
throttled:
io_count=1994811 io_rate=1087 io_tokens=-153 token_refill=1024 \
token_max=50000
io_count=2052289 io_rate=1057 io_tokens=-286 token_refill=1024 \
token_max=50000
io_count=2067794 io_rate=1015 io_tokens=-132 token_refill=1024 \
token_max=50000
Our sample shows io_tokens being negative, but - while waiting for the bucket
to be re-supplied, requests will still dribble through no faster than
token_refill req/second. Or, perhaps this is due to inaccuracies of nanosleep
or a bug in my logic.
* TODO
Stop using an alarm in the io_thread, and instead use gettimeofday to
calculate the token credits due and io_rate.
[-- Attachment #2: token-limiter-v1.patch --]
[-- Type: application/octet-stream, Size: 7985 bytes --]
diff -aur -X diff-exclude linode23/arch/um/drivers/mconsole_kern.c linode23-limiter/arch/um/drivers/mconsole_kern.c
--- linode23/arch/um/drivers/mconsole_kern.c 2004-02-18 20:04:28.000000000 -0500
+++ linode23-limiter/arch/um/drivers/mconsole_kern.c 2004-03-28 00:04:44.546996347 -0500
@@ -30,6 +30,7 @@
#include "os.h"
#include "umid.h"
#include "irq_kern.h"
+#include "ubd_user.h"
static int do_unlink_socket(struct notifier_block *notifier,
unsigned long what, void *data)
@@ -219,6 +220,9 @@
go - continue the UML after a 'stop' \n\
log <string> - make UML enter <string> into the kernel log\n\
proc <file> - returns the contents of the UML's /proc/<file>\n\
+ io_status - Return current I/O status and settings\n\
+ io_token_max <num> - sets the bucket size\n\
+ io_token_refill <num> - number of tokens to add each second\n\
"
void mconsole_help(struct mc_request *req)
@@ -548,6 +552,51 @@
EXPORT_SYMBOL(mconsole_notify_socket);
+void mconsole_io_status(struct mc_request *req)
+{
+ char str[256];
+
+ sprintf(str, "io_count=%d io_rate=%d io_tokens=%d token_refill=%d token_max=%d",
+ get_io_count(),
+ get_io_rate(),
+ get_io_tokens(),
+ get_token_refill(),
+ get_token_max()
+ );
+
+ mconsole_reply(req, str, 0, 0);
+}
+
+void mconsole_io_token_refill(struct mc_request *req)
+{
+ char *end;
+ int n;
+
+ char *ptr = req->request.data;
+ ptr += strlen("io_token_refill ");
+ n = simple_strtoul(ptr, &end, 0);
+
+ if (n>0)
+ set_token_refill(n);
+
+ mconsole_io_status(req);
+}
+
+void mconsole_io_token_max(struct mc_request *req)
+{
+ char *end;
+ int n;
+
+ char *ptr = req->request.data;
+ ptr += strlen("io_token_max ");
+ n = simple_strtoul(ptr, &end, 0);
+
+ if (n>0)
+ set_token_max(n);
+
+ mconsole_io_status(req);
+}
+
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
diff -aur -X diff-exclude linode23/arch/um/drivers/mconsole_user.c linode23-limiter/arch/um/drivers/mconsole_user.c
--- linode23/arch/um/drivers/mconsole_user.c 2004-02-18 20:04:28.000000000 -0500
+++ linode23-limiter/arch/um/drivers/mconsole_user.c 2004-03-26 20:32:12.129731275 -0500
@@ -30,6 +30,9 @@
{ "go", mconsole_go, MCONSOLE_INTR },
{ "log", mconsole_log, MCONSOLE_INTR },
{ "proc", mconsole_proc, MCONSOLE_PROC },
+ { "io_status", mconsole_io_status, MCONSOLE_INTR },
+ { "io_token_refill", mconsole_io_token_refill, MCONSOLE_INTR },
+ { "io_token_max", mconsole_io_token_max, MCONSOLE_INTR },
};
/* Initialized in mconsole_init, which is an initcall */
diff -aur -X diff-exclude linode23/arch/um/drivers/ubd_user.c linode23-limiter/arch/um/drivers/ubd_user.c
--- linode23/arch/um/drivers/ubd_user.c 2004-02-18 20:04:28.000000000 -0500
+++ linode23-limiter/arch/um/drivers/ubd_user.c 2004-03-28 01:03:07.265680876 -0500
@@ -26,6 +26,10 @@
#include <endian.h>
#include <byteswap.h>
+#include <stdlib.h>
+#include <time.h>
+#include "init.h"
+
static int same_backing_files(char *from_cmdline, char *from_cow, char *cow)
{
struct uml_stat buf1, buf2;
@@ -307,12 +311,23 @@
/* Only changed by the io thread */
int io_count = 0;
+/* Are set by the kernel and read from the io_thread or vice-versa */
+int token_refill = 2000;// add this many tokens to the bucket every second
+int token_delay = 1000000000 / 2000;
+int token_max = 60000; // max tokens in bucket
+int io_tokens = 60000; // token bucket
+int io_count_last = 0; // snapshot of io_count last time in io_alarm()
+int io_rate = 0; // calculated io ops per second
+
int io_thread(void *arg)
{
struct io_thread_req req;
int n;
signal(SIGWINCH, SIG_IGN);
+ signal(SIGALRM, io_alarm);
+ io_alarm(14);
+
while(1){
n = os_read_file(kernel_fd, &req, sizeof(req));
if(n != sizeof(req)){
@@ -327,6 +342,7 @@
}
io_count++;
do_io(&req);
+ io_delay();
n = os_write_file(kernel_fd, &req, sizeof(req));
if(n != sizeof(req))
printk("io_thread - write failed, fd = %d, err = %d\n",
@@ -365,6 +381,103 @@
return(err);
}
+void io_alarm(int sig)
+{
+ io_rate = io_count - io_count_last;
+ io_count_last = io_count;
+
+ io_tokens += token_refill;
+ if (io_tokens > token_max){
+ io_tokens = token_max;
+ }
+
+ /* printk("io_alarm - current speed: %d, io_tokens : %d \n",
+ io_rate, io_tokens); */
+
+ alarm(1);
+}
+
+void io_delay()
+{
+ io_tokens--;
+ if (io_tokens <= 0) {
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = token_delay;
+ nanosleep(&ts, NULL);
+ }
+}
+
+int set_token_refill(int tokens)
+{
+ token_refill = tokens;
+
+ token_delay = 1000;
+ if (token_refill)
+ token_delay = 1000000000 / token_refill;
+
+ return 0;
+}
+
+int get_token_refill()
+{
+ return token_refill;
+}
+
+int get_io_count()
+{
+ return io_count;
+}
+
+int set_token_max(int tokens)
+{
+ token_max = tokens;
+ return 0;
+}
+
+int get_token_max()
+{
+ return token_max;
+}
+
+int get_io_rate()
+{
+ return io_rate;
+}
+
+int get_io_tokens()
+{
+ return io_tokens;
+}
+
+static int cmdline_token_max(char *name, int *add)
+{
+ set_token_max(atoi(name));
+ return(0);
+}
+
+__uml_setup("token_max=", cmdline_token_max,
+"token_max=60000\n" \
+" This sets the maximum tokens in the token-bucket I/O request limiter.\n" \
+" Each io_thread request subtracts one token from the bucket. The bucket\n" \
+" is supplied with <token_refill> tokens every second. When the bucket\n" \
+" becomes empty, I/O requests are throttled to the <token_refill> rate.\n\n"
+);
+
+
+static int cmdline_token_refill(char *name, int *add)
+{
+ set_token_refill(atoi(name));
+ return(0);
+}
+
+__uml_setup("token_refill=", cmdline_token_refill,
+"token_refill=2000\n" \
+" This sets the bucket's refill rate, adding <token_refill> tokens to the\n" \
+" bucket every second. This becomes the I/O request rate when the bucket\n" \
+" becomes empty.\n\n"
+);
+
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
diff -aur -X diff-exclude linode23/arch/um/include/mconsole.h linode23-limiter/arch/um/include/mconsole.h
--- linode23/arch/um/include/mconsole.h 2004-02-18 20:04:28.000000000 -0500
+++ linode23-limiter/arch/um/include/mconsole.h 2004-03-26 20:31:32.333430818 -0500
@@ -81,6 +81,9 @@
extern void mconsole_go(struct mc_request *req);
extern void mconsole_log(struct mc_request *req);
extern void mconsole_proc(struct mc_request *req);
+extern void mconsole_io_status(struct mc_request *req);
+extern void mconsole_io_token_refill(struct mc_request *req);
+extern void mconsole_io_token_max(struct mc_request *req);
extern int mconsole_get_request(int fd, struct mc_request *req);
extern int mconsole_notify(char *sock_name, int type, const void *data,
diff -aur -X diff-exclude linode23/arch/um/include/ubd_user.h linode23-limiter/arch/um/include/ubd_user.h
--- linode23/arch/um/include/ubd_user.h 2004-02-18 20:04:28.000000000 -0500
+++ linode23-limiter/arch/um/include/ubd_user.h 2004-03-27 22:23:18.413800952 -0500
@@ -42,6 +42,18 @@
extern int start_io_thread(unsigned long sp, int *fds_out);
extern void do_io(struct io_thread_req *req);
+static int cmdline_token_max(char *name, int *add);
+static int cmdline_token_refill(char *name, int *add);
+extern void io_delay(void);
+extern void io_alarm(int sig);
+extern int set_token_refill(int tokens);
+extern int get_token_refill(void);
+extern int set_token_max(int tokens);
+extern int get_token_max(void);
+extern int get_io_count(void);
+extern int get_io_tokens(void);
+extern int get_io_rate(void);
+
static inline int ubd_test_bit(__u64 bit, unsigned char *data)
{
__u64 n;
next reply other threads:[~2004-03-28 9:03 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-03-28 9:06 Christopher S. Aker [this message]
2004-03-28 9:52 ` [uml-devel] [PATCH] I/O-Request Token Bucket Limiter Christopher S. Aker
2004-03-28 12:28 ` roland
2004-03-28 13:41 ` Christopher S. Aker
2004-03-28 16:18 ` roland
2004-03-28 18:26 ` BlaisorBlade
2004-05-28 11:13 ` roland
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='004901c414a3$f4d28a80$0201a8c0@hawk' \
--to=caker@theshore.net \
--cc=user-mode-linux-devel@lists.sourceforge.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox