diff options
| author | Jeff Garzik <jgarzik@pobox.com> | 2005-11-10 04:12:10 -0500 | 
|---|---|---|
| committer | Jeff Garzik <jgarzik@pobox.com> | 2005-11-10 04:12:10 -0500 | 
| commit | 2f67bdb23d74a6c6fd4f98f64239c5c34d1833cc (patch) | |
| tree | fe533abe3e7c400848647b95e4806f5125c654c3 /kernel/sysctl.c | |
| parent | d40d9d29c020f8466c96f8e3ad4b7c014ff1085d (diff) | |
| parent | 3b44f137b9a846c5452d9e6e1271b79b1dbcc942 (diff) | |
Merge branch 'master'
Diffstat (limited to 'kernel/sysctl.c')
| -rw-r--r-- | kernel/sysctl.c | 141 | 
1 files changed, 110 insertions, 31 deletions
| diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8e56e2495542..9990e10192e8 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -169,7 +169,7 @@ struct file_operations proc_sys_file_operations = { | |||
| 169 | 169 | ||
| 170 | extern struct proc_dir_entry *proc_sys_root; | 170 | extern struct proc_dir_entry *proc_sys_root; | 
| 171 | 171 | ||
| 172 | static void register_proc_table(ctl_table *, struct proc_dir_entry *); | 172 | static void register_proc_table(ctl_table *, struct proc_dir_entry *, void *); | 
| 173 | static void unregister_proc_table(ctl_table *, struct proc_dir_entry *); | 173 | static void unregister_proc_table(ctl_table *, struct proc_dir_entry *); | 
| 174 | #endif | 174 | #endif | 
| 175 | 175 | ||
| @@ -952,7 +952,7 @@ static ctl_table fs_table[] = { | |||
| 952 | .data = &aio_nr, | 952 | .data = &aio_nr, | 
| 953 | .maxlen = sizeof(aio_nr), | 953 | .maxlen = sizeof(aio_nr), | 
| 954 | .mode = 0444, | 954 | .mode = 0444, | 
| 955 | .proc_handler = &proc_dointvec, | 955 | .proc_handler = &proc_doulongvec_minmax, | 
| 956 | }, | 956 | }, | 
| 957 | { | 957 | { | 
| 958 | .ctl_name = FS_AIO_MAX_NR, | 958 | .ctl_name = FS_AIO_MAX_NR, | 
| @@ -960,7 +960,7 @@ static ctl_table fs_table[] = { | |||
| 960 | .data = &aio_max_nr, | 960 | .data = &aio_max_nr, | 
| 961 | .maxlen = sizeof(aio_max_nr), | 961 | .maxlen = sizeof(aio_max_nr), | 
| 962 | .mode = 0644, | 962 | .mode = 0644, | 
| 963 | .proc_handler = &proc_dointvec, | 963 | .proc_handler = &proc_doulongvec_minmax, | 
| 964 | }, | 964 | }, | 
| 965 | #ifdef CONFIG_INOTIFY | 965 | #ifdef CONFIG_INOTIFY | 
| 966 | { | 966 | { | 
| @@ -992,10 +992,51 @@ static ctl_table dev_table[] = { | |||
| 992 | 992 | ||
| 993 | extern void init_irq_proc (void); | 993 | extern void init_irq_proc (void); | 
| 994 | 994 | ||
| 995 | static DEFINE_SPINLOCK(sysctl_lock); | ||
| 996 | |||
| 997 | /* called under sysctl_lock */ | ||
| 998 | static int use_table(struct ctl_table_header *p) | ||
| 999 | { | ||
| 1000 | if (unlikely(p->unregistering)) | ||
| 1001 | return 0; | ||
| 1002 | p->used++; | ||
| 1003 | return 1; | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | /* called under sysctl_lock */ | ||
| 1007 | static void unuse_table(struct ctl_table_header *p) | ||
| 1008 | { | ||
| 1009 | if (!--p->used) | ||
| 1010 | if (unlikely(p->unregistering)) | ||
| 1011 | complete(p->unregistering); | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | /* called under sysctl_lock, will reacquire if has to wait */ | ||
| 1015 | static void start_unregistering(struct ctl_table_header *p) | ||
| 1016 | { | ||
| 1017 | /* | ||
| 1018 | * if p->used is 0, nobody will ever touch that entry again; | ||
| 1019 | * we'll eliminate all paths to it before dropping sysctl_lock | ||
| 1020 | */ | ||
| 1021 | if (unlikely(p->used)) { | ||
| 1022 | struct completion wait; | ||
| 1023 | init_completion(&wait); | ||
| 1024 | p->unregistering = &wait; | ||
| 1025 | spin_unlock(&sysctl_lock); | ||
| 1026 | wait_for_completion(&wait); | ||
| 1027 | spin_lock(&sysctl_lock); | ||
| 1028 | } | ||
| 1029 | /* | ||
| 1030 | * do not remove from the list until nobody holds it; walking the | ||
| 1031 | * list in do_sysctl() relies on that. | ||
| 1032 | */ | ||
| 1033 | list_del_init(&p->ctl_entry); | ||
| 1034 | } | ||
| 1035 | |||
| 995 | void __init sysctl_init(void) | 1036 | void __init sysctl_init(void) | 
| 996 | { | 1037 | { | 
| 997 | #ifdef CONFIG_PROC_FS | 1038 | #ifdef CONFIG_PROC_FS | 
| 998 | register_proc_table(root_table, proc_sys_root); | 1039 | register_proc_table(root_table, proc_sys_root, &root_table_header); | 
| 999 | init_irq_proc(); | 1040 | init_irq_proc(); | 
| 1000 | #endif | 1041 | #endif | 
| 1001 | } | 1042 | } | 
| @@ -1004,6 +1045,7 @@ int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *ol | |||
| 1004 | void __user *newval, size_t newlen) | 1045 | void __user *newval, size_t newlen) | 
| 1005 | { | 1046 | { | 
| 1006 | struct list_head *tmp; | 1047 | struct list_head *tmp; | 
| 1048 | int error = -ENOTDIR; | ||
| 1007 | 1049 | ||
| 1008 | if (nlen <= 0 || nlen >= CTL_MAXNAME) | 1050 | if (nlen <= 0 || nlen >= CTL_MAXNAME) | 
| 1009 | return -ENOTDIR; | 1051 | return -ENOTDIR; | 
| @@ -1012,20 +1054,30 @@ int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *ol | |||
| 1012 | if (!oldlenp || get_user(old_len, oldlenp)) | 1054 | if (!oldlenp || get_user(old_len, oldlenp)) | 
| 1013 | return -EFAULT; | 1055 | return -EFAULT; | 
| 1014 | } | 1056 | } | 
| 1057 | spin_lock(&sysctl_lock); | ||
| 1015 | tmp = &root_table_header.ctl_entry; | 1058 | tmp = &root_table_header.ctl_entry; | 
| 1016 | do { | 1059 | do { | 
| 1017 | struct ctl_table_header *head = | 1060 | struct ctl_table_header *head = | 
| 1018 | list_entry(tmp, struct ctl_table_header, ctl_entry); | 1061 | list_entry(tmp, struct ctl_table_header, ctl_entry); | 
| 1019 | void *context = NULL; | 1062 | void *context = NULL; | 
| 1020 | int error = parse_table(name, nlen, oldval, oldlenp, | 1063 | |
| 1064 | if (!use_table(head)) | ||
| 1065 | continue; | ||
| 1066 | |||
| 1067 | spin_unlock(&sysctl_lock); | ||
| 1068 | |||
| 1069 | error = parse_table(name, nlen, oldval, oldlenp, | ||
| 1021 | newval, newlen, head->ctl_table, | 1070 | newval, newlen, head->ctl_table, | 
| 1022 | &context); | 1071 | &context); | 
| 1023 | kfree(context); | 1072 | kfree(context); | 
| 1073 | |||
| 1074 | spin_lock(&sysctl_lock); | ||
| 1075 | unuse_table(head); | ||
| 1024 | if (error != -ENOTDIR) | 1076 | if (error != -ENOTDIR) | 
| 1025 | return error; | 1077 | break; | 
| 1026 | tmp = tmp->next; | 1078 | } while ((tmp = tmp->next) != &root_table_header.ctl_entry); | 
| 1027 | } while (tmp != &root_table_header.ctl_entry); | 1079 | spin_unlock(&sysctl_lock); | 
| 1028 | return -ENOTDIR; | 1080 | return error; | 
| 1029 | } | 1081 | } | 
| 1030 | 1082 | ||
| 1031 | asmlinkage long sys_sysctl(struct __sysctl_args __user *args) | 1083 | asmlinkage long sys_sysctl(struct __sysctl_args __user *args) | 
| @@ -1236,12 +1288,16 @@ struct ctl_table_header *register_sysctl_table(ctl_table * table, | |||
| 1236 | return NULL; | 1288 | return NULL; | 
| 1237 | tmp->ctl_table = table; | 1289 | tmp->ctl_table = table; | 
| 1238 | INIT_LIST_HEAD(&tmp->ctl_entry); | 1290 | INIT_LIST_HEAD(&tmp->ctl_entry); | 
| 1291 | tmp->used = 0; | ||
| 1292 | tmp->unregistering = NULL; | ||
| 1293 | spin_lock(&sysctl_lock); | ||
| 1239 | if (insert_at_head) | 1294 | if (insert_at_head) | 
| 1240 | list_add(&tmp->ctl_entry, &root_table_header.ctl_entry); | 1295 | list_add(&tmp->ctl_entry, &root_table_header.ctl_entry); | 
| 1241 | else | 1296 | else | 
| 1242 | list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry); | 1297 | list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry); | 
| 1298 | spin_unlock(&sysctl_lock); | ||
| 1243 | #ifdef CONFIG_PROC_FS | 1299 | #ifdef CONFIG_PROC_FS | 
| 1244 | register_proc_table(table, proc_sys_root); | 1300 | register_proc_table(table, proc_sys_root, tmp); | 
| 1245 | #endif | 1301 | #endif | 
| 1246 | return tmp; | 1302 | return tmp; | 
| 1247 | } | 1303 | } | 
| @@ -1255,10 +1311,13 @@ struct ctl_table_header *register_sysctl_table(ctl_table * table, | |||
| 1255 | */ | 1311 | */ | 
| 1256 | void unregister_sysctl_table(struct ctl_table_header * header) | 1312 | void unregister_sysctl_table(struct ctl_table_header * header) | 
| 1257 | { | 1313 | { | 
| 1258 | list_del(&header->ctl_entry); | 1314 | might_sleep(); | 
| 1315 | spin_lock(&sysctl_lock); | ||
| 1316 | start_unregistering(header); | ||
| 1259 | #ifdef CONFIG_PROC_FS | 1317 | #ifdef CONFIG_PROC_FS | 
| 1260 | unregister_proc_table(header->ctl_table, proc_sys_root); | 1318 | unregister_proc_table(header->ctl_table, proc_sys_root); | 
| 1261 | #endif | 1319 | #endif | 
| 1320 | spin_unlock(&sysctl_lock); | ||
| 1262 | kfree(header); | 1321 | kfree(header); | 
| 1263 | } | 1322 | } | 
| 1264 | 1323 | ||
| @@ -1269,7 +1328,7 @@ void unregister_sysctl_table(struct ctl_table_header * header) | |||
| 1269 | #ifdef CONFIG_PROC_FS | 1328 | #ifdef CONFIG_PROC_FS | 
| 1270 | 1329 | ||
| 1271 | /* Scan the sysctl entries in table and add them all into /proc */ | 1330 | /* Scan the sysctl entries in table and add them all into /proc */ | 
| 1272 | static void register_proc_table(ctl_table * table, struct proc_dir_entry *root) | 1331 | static void register_proc_table(ctl_table * table, struct proc_dir_entry *root, void *set) | 
| 1273 | { | 1332 | { | 
| 1274 | struct proc_dir_entry *de; | 1333 | struct proc_dir_entry *de; | 
| 1275 | int len; | 1334 | int len; | 
| @@ -1305,13 +1364,14 @@ static void register_proc_table(ctl_table * table, struct proc_dir_entry *root) | |||
| 1305 | de = create_proc_entry(table->procname, mode, root); | 1364 | de = create_proc_entry(table->procname, mode, root); | 
| 1306 | if (!de) | 1365 | if (!de) | 
| 1307 | continue; | 1366 | continue; | 
| 1367 | de->set = set; | ||
| 1308 | de->data = (void *) table; | 1368 | de->data = (void *) table; | 
| 1309 | if (table->proc_handler) | 1369 | if (table->proc_handler) | 
| 1310 | de->proc_fops = &proc_sys_file_operations; | 1370 | de->proc_fops = &proc_sys_file_operations; | 
| 1311 | } | 1371 | } | 
| 1312 | table->de = de; | 1372 | table->de = de; | 
| 1313 | if (de->mode & S_IFDIR) | 1373 | if (de->mode & S_IFDIR) | 
| 1314 | register_proc_table(table->child, de); | 1374 | register_proc_table(table->child, de, set); | 
| 1315 | } | 1375 | } | 
| 1316 | } | 1376 | } | 
| 1317 | 1377 | ||
| @@ -1336,6 +1396,13 @@ static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root | |||
| 1336 | continue; | 1396 | continue; | 
| 1337 | } | 1397 | } | 
| 1338 | 1398 | ||
| 1399 | /* | ||
| 1400 | * In any case, mark the entry as goner; we'll keep it | ||
| 1401 | * around if it's busy, but we'll know to do nothing with | ||
| 1402 | * its fields. We are under sysctl_lock here. | ||
| 1403 | */ | ||
| 1404 | de->data = NULL; | ||
| 1405 | |||
| 1339 | /* Don't unregister proc entries that are still being used.. */ | 1406 | /* Don't unregister proc entries that are still being used.. */ | 
| 1340 | if (atomic_read(&de->count)) | 1407 | if (atomic_read(&de->count)) | 
| 1341 | continue; | 1408 | continue; | 
| @@ -1349,27 +1416,38 @@ static ssize_t do_rw_proc(int write, struct file * file, char __user * buf, | |||
| 1349 | size_t count, loff_t *ppos) | 1416 | size_t count, loff_t *ppos) | 
| 1350 | { | 1417 | { | 
| 1351 | int op; | 1418 | int op; | 
| 1352 | struct proc_dir_entry *de; | 1419 | struct proc_dir_entry *de = PDE(file->f_dentry->d_inode); | 
| 1353 | struct ctl_table *table; | 1420 | struct ctl_table *table; | 
| 1354 | size_t res; | 1421 | size_t res; | 
| 1355 | ssize_t error; | 1422 | ssize_t error = -ENOTDIR; | 
| 1356 | |||
| 1357 | de = PDE(file->f_dentry->d_inode); | ||
| 1358 | if (!de || !de->data) | ||
| 1359 | return -ENOTDIR; | ||
| 1360 | table = (struct ctl_table *) de->data; | ||
| 1361 | if (!table || !table->proc_handler) | ||
| 1362 | return -ENOTDIR; | ||
| 1363 | op = (write ? 002 : 004); | ||
| 1364 | if (ctl_perm(table, op)) | ||
| 1365 | return -EPERM; | ||
| 1366 | 1423 | ||
| 1367 | res = count; | 1424 | spin_lock(&sysctl_lock); | 
| 1368 | 1425 | if (de && de->data && use_table(de->set)) { | |
| 1369 | error = (*table->proc_handler) (table, write, file, buf, &res, ppos); | 1426 | /* | 
| 1370 | if (error) | 1427 | * at that point we know that sysctl was not unregistered | 
| 1371 | return error; | 1428 | * and won't be until we finish | 
| 1372 | return res; | 1429 | */ | 
| 1430 | spin_unlock(&sysctl_lock); | ||
| 1431 | table = (struct ctl_table *) de->data; | ||
| 1432 | if (!table || !table->proc_handler) | ||
| 1433 | goto out; | ||
| 1434 | error = -EPERM; | ||
| 1435 | op = (write ? 002 : 004); | ||
| 1436 | if (ctl_perm(table, op)) | ||
| 1437 | goto out; | ||
| 1438 | |||
| 1439 | /* careful: calling conventions are nasty here */ | ||
| 1440 | res = count; | ||
| 1441 | error = (*table->proc_handler)(table, write, file, | ||
| 1442 | buf, &res, ppos); | ||
| 1443 | if (!error) | ||
| 1444 | error = res; | ||
| 1445 | out: | ||
| 1446 | spin_lock(&sysctl_lock); | ||
| 1447 | unuse_table(de->set); | ||
| 1448 | } | ||
| 1449 | spin_unlock(&sysctl_lock); | ||
| 1450 | return error; | ||
| 1373 | } | 1451 | } | 
| 1374 | 1452 | ||
| 1375 | static int proc_opensys(struct inode *inode, struct file *file) | 1453 | static int proc_opensys(struct inode *inode, struct file *file) | 
| @@ -1997,6 +2075,7 @@ int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, | |||
| 1997 | * @filp: the file structure | 2075 | * @filp: the file structure | 
| 1998 | * @buffer: the user buffer | 2076 | * @buffer: the user buffer | 
| 1999 | * @lenp: the size of the user buffer | 2077 | * @lenp: the size of the user buffer | 
| 2078 | * @ppos: pointer to the file position | ||
| 2000 | * | 2079 | * | 
| 2001 | * Reads/writes up to table->maxlen/sizeof(unsigned int) integer | 2080 | * Reads/writes up to table->maxlen/sizeof(unsigned int) integer | 
| 2002 | * values from/to the user buffer, treated as an ASCII string. | 2081 | * values from/to the user buffer, treated as an ASCII string. | 
