Commit 4500e917 authored by Hideaki Yoshifuji's avatar Hideaki Yoshifuji Committed by David S. Miller

[NET]: Add proc_dointvec_userhz_jiffies, use it for proper handling of neighbour sysctls.

parent 33e9809b
...@@ -738,6 +738,8 @@ extern int proc_dointvec_minmax(ctl_table *, int, struct file *, ...@@ -738,6 +738,8 @@ extern int proc_dointvec_minmax(ctl_table *, int, struct file *,
void __user *, size_t *); void __user *, size_t *);
extern int proc_dointvec_jiffies(ctl_table *, int, struct file *, extern int proc_dointvec_jiffies(ctl_table *, int, struct file *,
void __user *, size_t *); void __user *, size_t *);
extern int proc_dointvec_userhz_jiffies(ctl_table *, int, struct file *,
void __user *, size_t *);
extern int proc_doulongvec_minmax(ctl_table *, int, struct file *, extern int proc_doulongvec_minmax(ctl_table *, int, struct file *,
void __user *, size_t *); void __user *, size_t *);
extern int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int, extern int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int,
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/hugetlb.h> #include <linux/hugetlb.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/initrd.h> #include <linux/initrd.h>
#include <linux/times.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#ifdef CONFIG_ROOT_NFS #ifdef CONFIG_ROOT_NFS
...@@ -1070,8 +1071,8 @@ int do_sysctl_strategy (ctl_table *table, ...@@ -1070,8 +1071,8 @@ int do_sysctl_strategy (ctl_table *table,
* cover common cases - * cover common cases -
* *
* proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(), * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(),
* proc_dointvec_minmax(), proc_doulongvec_ms_jiffies_minmax(), * proc_dointvec_userhz_jiffies(), proc_dointvec_minmax(),
* proc_doulongvec_minmax() * proc_doulongvec_ms_jiffies_minmax(), proc_doulongvec_minmax()
* *
* It is the handler's job to read the input buffer from user memory * It is the handler's job to read the input buffer from user memory
* and process it. The handler should return 0 on success. * and process it. The handler should return 0 on success.
...@@ -1342,19 +1343,36 @@ static int proc_doutsstring(ctl_table *table, int write, struct file *filp, ...@@ -1342,19 +1343,36 @@ static int proc_doutsstring(ctl_table *table, int write, struct file *filp,
return r; return r;
} }
#define OP_SET 0 static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp,
#define OP_AND 1 int *valp,
#define OP_OR 2 int write, void *data)
#define OP_MAX 3 {
#define OP_MIN 4 if (write) {
*valp = *negp ? -*lvalp : *lvalp;
} else {
int val = *valp;
if (val < 0) {
*negp = -1;
*lvalp = (unsigned long)-val;
} else {
*negp = 0;
*lvalp = (unsigned long)val;
}
}
return 0;
}
static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp, int conv, int op) void __user *buffer, size_t *lenp,
int (*conv)(int *negp, unsigned long *lvalp, int *valp,
int write, void *data),
void *data)
{ {
#define TMPBUFLEN 20
int *i, vleft, first=1, neg, val; int *i, vleft, first=1, neg, val;
unsigned long lval;
size_t left, len; size_t left, len;
#define TMPBUFLEN 20
char buf[TMPBUFLEN], *p; char buf[TMPBUFLEN], *p;
if (!table->data || !table->maxlen || !*lenp || if (!table->data || !table->maxlen || !*lenp ||
...@@ -1364,9 +1382,12 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, ...@@ -1364,9 +1382,12 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
} }
i = (int *) table->data; i = (int *) table->data;
vleft = table->maxlen / sizeof(int); vleft = table->maxlen / sizeof(*i);
left = *lenp; left = *lenp;
if (!conv)
conv = do_proc_dointvec_conv;
for (; left && vleft--; i++, first=0) { for (; left && vleft--; i++, first=0) {
if (write) { if (write) {
while (left) { while (left) {
...@@ -1382,8 +1403,8 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, ...@@ -1382,8 +1403,8 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
break; break;
neg = 0; neg = 0;
len = left; len = left;
if (len > TMPBUFLEN-1) if (len > sizeof(buf) - 1)
len = TMPBUFLEN-1; len = sizeof(buf) - 1;
if(copy_from_user(buf, buffer, len)) if(copy_from_user(buf, buffer, len))
return -EFAULT; return -EFAULT;
buf[len] = 0; buf[len] = 0;
...@@ -1394,7 +1415,9 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, ...@@ -1394,7 +1415,9 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
} }
if (*p < '0' || *p > '9') if (*p < '0' || *p > '9')
break; break;
val = simple_strtoul(p, &p, 0) * conv;
lval = simple_strtoul(p, &p, 0);
len = p-buf; len = p-buf;
if ((len < left) && *p && !isspace(*p)) if ((len < left) && *p && !isspace(*p))
break; break;
...@@ -1402,22 +1425,18 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, ...@@ -1402,22 +1425,18 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
val = -val; val = -val;
buffer += len; buffer += len;
left -= len; left -= len;
switch(op) {
case OP_SET: *i = val; break; if (conv(&neg, &lval, i, 1, data))
case OP_AND: *i &= val; break; break;
case OP_OR: *i |= val; break;
case OP_MAX: if(*i < val)
*i = val;
break;
case OP_MIN: if(*i > val)
*i = val;
break;
}
} else { } else {
p = buf; p = buf;
if (!first) if (!first)
*p++ = '\t'; *p++ = '\t';
sprintf(p, "%d", (*i) / conv);
if (conv(&neg, &lval, i, 0, data))
break;
sprintf(p, "%s%lu", neg ? "-" : "", lval);
len = strlen(buf); len = strlen(buf);
if (len > left) if (len > left)
len = left; len = left;
...@@ -1449,6 +1468,7 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, ...@@ -1449,6 +1468,7 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
*lenp -= left; *lenp -= left;
filp->f_pos += *lenp; filp->f_pos += *lenp;
return 0; return 0;
#undef TMPBUFLEN
} }
/** /**
...@@ -1467,7 +1487,45 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, ...@@ -1467,7 +1487,45 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
int proc_dointvec(ctl_table *table, int write, struct file *filp, int proc_dointvec(ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp) void __user *buffer, size_t *lenp)
{ {
return do_proc_dointvec(table,write,filp,buffer,lenp,1,OP_SET); return do_proc_dointvec(table,write,filp,buffer,lenp,
NULL,NULL);
}
#define OP_SET 0
#define OP_AND 1
#define OP_OR 2
#define OP_MAX 3
#define OP_MIN 4
static int do_proc_dointvec_bset_conv(int *negp, unsigned long *lvalp,
int *valp,
int write, void *data)
{
int op = *(int *)data;
if (write) {
int val = *negp ? -*lvalp : *lvalp;
switch(op) {
case OP_SET: *valp = val; break;
case OP_AND: *valp &= val; break;
case OP_OR: *valp |= val; break;
case OP_MAX: if(*valp < val)
*valp = val;
break;
case OP_MIN: if(*valp > val)
*valp = val;
break;
}
} else {
int val = *valp;
if (val < 0) {
*negp = -1;
*lvalp = (unsigned long)-val;
} else {
*negp = 0;
*lvalp = (unsigned long)val;
}
}
return 0;
} }
/* /*
...@@ -1477,11 +1535,44 @@ int proc_dointvec(ctl_table *table, int write, struct file *filp, ...@@ -1477,11 +1535,44 @@ int proc_dointvec(ctl_table *table, int write, struct file *filp,
int proc_dointvec_bset(ctl_table *table, int write, struct file *filp, int proc_dointvec_bset(ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp) void __user *buffer, size_t *lenp)
{ {
int op;
if (!capable(CAP_SYS_MODULE)) { if (!capable(CAP_SYS_MODULE)) {
return -EPERM; return -EPERM;
} }
return do_proc_dointvec(table,write,filp,buffer,lenp,1,
(current->pid == 1) ? OP_SET : OP_AND); op = (current->pid == 1) ? OP_SET : OP_AND;
return do_proc_dointvec(table,write,filp,buffer,lenp,
do_proc_dointvec_bset_conv,&op);
}
struct do_proc_dointvec_minmax_conv_param {
int *min;
int *max;
};
static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp,
int *valp,
int write, void *data)
{
struct do_proc_dointvec_minmax_conv_param *param = data;
if (write) {
int val = *negp ? -*lvalp : *lvalp;
if ((param->min && *param->min > val) ||
(param->max && *param->max < val))
return -EINVAL;
*valp = val;
} else {
int val = *valp;
if (val < 0) {
*negp = -1;
*lvalp = (unsigned long)-val;
} else {
*negp = 0;
*lvalp = (unsigned long)val;
}
}
return 0;
} }
/** /**
...@@ -1503,98 +1594,12 @@ int proc_dointvec_bset(ctl_table *table, int write, struct file *filp, ...@@ -1503,98 +1594,12 @@ int proc_dointvec_bset(ctl_table *table, int write, struct file *filp,
int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp, int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp) void __user *buffer, size_t *lenp)
{ {
int *i, *min, *max, vleft, first=1, neg, val; struct do_proc_dointvec_minmax_conv_param param = {
size_t len, left; .min = (int *) table->extra1,
#define TMPBUFLEN 20 .max = (int *) table->extra2,
char buf[TMPBUFLEN], *p; };
return do_proc_dointvec(table, write, filp, buffer, lenp,
if (!table->data || !table->maxlen || !*lenp || do_proc_dointvec_minmax_conv, &param);
(filp->f_pos && !write)) {
*lenp = 0;
return 0;
}
i = (int *) table->data;
min = (int *) table->extra1;
max = (int *) table->extra2;
vleft = table->maxlen / sizeof(int);
left = *lenp;
for (; left && vleft--; i++, min++, max++, first=0) {
if (write) {
while (left) {
char c;
if(get_user(c, (char *) buffer))
return -EFAULT;
if (!isspace(c))
break;
left--;
buffer++;
}
if (!left)
break;
neg = 0;
len = left;
if (len > TMPBUFLEN-1)
len = TMPBUFLEN-1;
if(copy_from_user(buf, buffer, len))
return -EFAULT;
buf[len] = 0;
p = buf;
if (*p == '-' && left > 1) {
neg = 1;
left--, p++;
}
if (*p < '0' || *p > '9')
break;
val = simple_strtoul(p, &p, 0);
len = p-buf;
if ((len < left) && *p && !isspace(*p))
break;
if (neg)
val = -val;
buffer += len;
left -= len;
if ((min && val < *min) || (max && val > *max))
continue;
*i = val;
} else {
p = buf;
if (!first)
*p++ = '\t';
sprintf(p, "%d", *i);
len = strlen(buf);
if (len > left)
len = left;
if(copy_to_user(buffer, buf, len))
return -EFAULT;
left -= len;
buffer += len;
}
}
if (!write && !first && left) {
if(put_user('\n', (char *) buffer))
return -EFAULT;
left--, buffer++;
}
if (write) {
p = (char *) buffer;
while (left) {
char c;
if(get_user(c, p++))
return -EFAULT;
if (!isspace(c))
break;
left--;
}
}
if (write && first)
return -EINVAL;
*lenp -= left;
filp->f_pos += *lenp;
return 0;
} }
static int do_proc_doulongvec_minmax(ctl_table *table, int write, static int do_proc_doulongvec_minmax(ctl_table *table, int write,
...@@ -1749,6 +1754,48 @@ int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write, ...@@ -1749,6 +1754,48 @@ int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write,
} }
static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp,
int *valp,
int write, void *data)
{
if (write) {
*valp = *negp ? -(*lvalp*HZ) : (*lvalp*HZ);
} else {
int val = *valp;
unsigned long lval;
if (val < 0) {
*negp = -1;
lval = (unsigned long)-val;
} else {
*negp = 0;
lval = (unsigned long)val;
}
*lvalp = lval / HZ;
}
return 0;
}
static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp,
int *valp,
int write, void *data)
{
if (write) {
*valp = clock_t_to_jiffies(*negp ? -*lvalp : *lvalp);
} else {
int val = *valp;
unsigned long lval;
if (val < 0) {
*negp = -1;
lval = (unsigned long)-val;
} else {
*negp = 0;
lval = (unsigned long)val;
}
*lvalp = jiffies_to_clock_t(lval);
}
return 0;
}
/** /**
* proc_dointvec_jiffies - read a vector of integers as seconds * proc_dointvec_jiffies - read a vector of integers as seconds
* @table: the sysctl table * @table: the sysctl table
...@@ -1767,7 +1814,30 @@ int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write, ...@@ -1767,7 +1814,30 @@ int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write,
int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp) void __user *buffer, size_t *lenp)
{ {
return do_proc_dointvec(table,write,filp,buffer,lenp,HZ,OP_SET); return do_proc_dointvec(table,write,filp,buffer,lenp,
do_proc_dointvec_jiffies_conv,NULL);
}
/**
* proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds
* @table: the sysctl table
* @write: %TRUE if this is a write to the sysctl file
* @filp: the file structure
* @buffer: the user buffer
* @lenp: the size of the user buffer
*
* Reads/writes up to table->maxlen/sizeof(unsigned int) integer
* values from/to the user buffer, treated as an ASCII string.
* The values read are assumed to be in 1/USER_HZ seconds, and
* are converted into jiffies.
*
* Returns 0 on success.
*/
int proc_dointvec_userhz_jiffies(ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp)
{
return do_proc_dointvec(table,write,filp,buffer,lenp,
do_proc_dointvec_userhz_jiffies_conv,NULL);
} }
#else /* CONFIG_PROC_FS */ #else /* CONFIG_PROC_FS */
...@@ -1808,6 +1878,12 @@ int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, ...@@ -1808,6 +1878,12 @@ int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
return -ENOSYS; return -ENOSYS;
} }
int proc_dointvec_userhz_jiffies(ctl_table *table, int write, struct file *filp,
void *buffer, size_t *lenp)
{
return -ENOSYS;
}
int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp, int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp,
void *buffer, size_t *lenp) void *buffer, size_t *lenp)
{ {
...@@ -1995,6 +2071,12 @@ int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, ...@@ -1995,6 +2071,12 @@ int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
return -ENOSYS; return -ENOSYS;
} }
int proc_dointvec_userhz_jiffies(ctl_table *table, int write, struct file *filp,
void *buffer, size_t *lenp)
{
return -ENOSYS;
}
int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp, int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp) void __user *buffer, size_t *lenp)
{ {
...@@ -2027,6 +2109,7 @@ void unregister_sysctl_table(struct ctl_table_header * table) ...@@ -2027,6 +2109,7 @@ void unregister_sysctl_table(struct ctl_table_header * table)
EXPORT_SYMBOL(proc_dointvec); EXPORT_SYMBOL(proc_dointvec);
EXPORT_SYMBOL(proc_dointvec_jiffies); EXPORT_SYMBOL(proc_dointvec_jiffies);
EXPORT_SYMBOL(proc_dointvec_minmax); EXPORT_SYMBOL(proc_dointvec_minmax);
EXPORT_SYMBOL(proc_dointvec_userhz_jiffies);
EXPORT_SYMBOL(proc_dostring); EXPORT_SYMBOL(proc_dostring);
EXPORT_SYMBOL(proc_doulongvec_minmax); EXPORT_SYMBOL(proc_doulongvec_minmax);
EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax); EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax);
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
#include <linux/sysctl.h> #include <linux/sysctl.h>
#endif #endif
#include <linux/times.h>
#include <net/neighbour.h> #include <net/neighbour.h>
#include <net/dst.h> #include <net/dst.h>
#include <net/sock.h> #include <net/sock.h>
...@@ -1510,7 +1511,7 @@ struct neigh_sysctl_table { ...@@ -1510,7 +1511,7 @@ struct neigh_sysctl_table {
.procname = "retrans_time", .procname = "retrans_time",
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec, .proc_handler = &proc_dointvec_userhz_jiffies,
}, },
{ {
.ctl_name = NET_NEIGH_REACHABLE_TIME, .ctl_name = NET_NEIGH_REACHABLE_TIME,
...@@ -1555,21 +1556,21 @@ struct neigh_sysctl_table { ...@@ -1555,21 +1556,21 @@ struct neigh_sysctl_table {
.procname = "anycast_delay", .procname = "anycast_delay",
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec, .proc_handler = &proc_dointvec_userhz_jiffies,
}, },
{ {
.ctl_name = NET_NEIGH_PROXY_DELAY, .ctl_name = NET_NEIGH_PROXY_DELAY,
.procname = "proxy_delay", .procname = "proxy_delay",
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec, .proc_handler = &proc_dointvec_userhz_jiffies,
}, },
{ {
.ctl_name = NET_NEIGH_LOCKTIME, .ctl_name = NET_NEIGH_LOCKTIME,
.procname = "locktime", .procname = "locktime",
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec, .proc_handler = &proc_dointvec_userhz_jiffies,
}, },
{ {
.ctl_name = NET_NEIGH_GC_INTERVAL, .ctl_name = NET_NEIGH_GC_INTERVAL,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment