Blame view
kernel/params.c
23.2 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* Helpers for initial module or kernel cmdline parsing Copyright (C) 2001 Rusty Russell. 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 */ |
1da177e4c
|
18 19 20 21 |
#include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/module.h> |
63a12d9d0
|
22 |
#include <linux/moduleparam.h> |
1da177e4c
|
23 24 |
#include <linux/device.h> #include <linux/err.h> |
4e57b6817
|
25 |
#include <linux/slab.h> |
26d052bfc
|
26 |
#include <linux/ctype.h> |
1da177e4c
|
27 |
|
cf2fde7b3
|
28 |
#ifdef CONFIG_SYSFS |
b51d23e4e
|
29 |
/* Protects all built-in parameters, modules use their own param_lock */ |
907b29eb4
|
30 |
static DEFINE_MUTEX(param_lock); |
b51d23e4e
|
31 |
/* Use the module's mutex, or if built-in use the built-in mutex */ |
20bdc2cfd
|
32 |
#ifdef CONFIG_MODULES |
b51d23e4e
|
33 |
#define KPARAM_MUTEX(mod) ((mod) ? &(mod)->param_lock : ¶m_lock) |
20bdc2cfd
|
34 35 36 |
#else #define KPARAM_MUTEX(mod) (¶m_lock) #endif |
cf2fde7b3
|
37 38 39 40 41 42 43 44 45 46 |
static inline void check_kparam_locked(struct module *mod) { BUG_ON(!mutex_is_locked(KPARAM_MUTEX(mod))); } #else static inline void check_kparam_locked(struct module *mod) { } #endif /* !CONFIG_SYSFS */ |
b51d23e4e
|
47 |
|
a1054322a
|
48 49 50 51 52 |
/* This just allows us to keep track of which parameters are kmalloced. */ struct kmalloced_param { struct list_head list; char val[]; }; |
a1054322a
|
53 |
static LIST_HEAD(kmalloced_params); |
b51d23e4e
|
54 |
static DEFINE_SPINLOCK(kmalloced_params_lock); |
a1054322a
|
55 56 57 58 59 60 61 62 |
static void *kmalloc_parameter(unsigned int size) { struct kmalloced_param *p; p = kmalloc(sizeof(*p) + size, GFP_KERNEL); if (!p) return NULL; |
b51d23e4e
|
63 |
spin_lock(&kmalloced_params_lock); |
a1054322a
|
64 |
list_add(&p->list, &kmalloced_params); |
b51d23e4e
|
65 |
spin_unlock(&kmalloced_params_lock); |
a1054322a
|
66 67 68 69 70 71 72 |
return p->val; } /* Does nothing if parameter wasn't kmalloced above. */ static void maybe_kfree_parameter(void *param) { struct kmalloced_param *p; |
b51d23e4e
|
73 |
spin_lock(&kmalloced_params_lock); |
a1054322a
|
74 75 76 77 78 79 80 |
list_for_each_entry(p, &kmalloced_params, list) { if (p->val == param) { list_del(&p->list); kfree(p); break; } } |
b51d23e4e
|
81 |
spin_unlock(&kmalloced_params_lock); |
a1054322a
|
82 |
} |
b1e4d20cb
|
83 |
static char dash2underscore(char c) |
1da177e4c
|
84 85 86 87 88 |
{ if (c == '-') return '_'; return c; } |
b1e4d20cb
|
89 |
bool parameqn(const char *a, const char *b, size_t n) |
1da177e4c
|
90 |
{ |
b1e4d20cb
|
91 92 93 94 95 96 97 98 99 100 101 102 |
size_t i; for (i = 0; i < n; i++) { if (dash2underscore(a[i]) != dash2underscore(b[i])) return false; } return true; } bool parameq(const char *a, const char *b) { return parameqn(a, b, strlen(a)+1); |
1da177e4c
|
103 |
} |
7a486d378
|
104 105 106 107 108 109 110 111 112 |
static void param_check_unsafe(const struct kernel_param *kp) { if (kp->flags & KERNEL_PARAM_FL_UNSAFE) { pr_warn("Setting dangerous option %s - tainting kernel ", kp->name); add_taint(TAINT_USER, LOCKDEP_STILL_OK); } } |
1da177e4c
|
113 114 |
static int parse_one(char *param, char *val, |
9fb48c744
|
115 |
const char *doing, |
914dcaa84
|
116 |
const struct kernel_param *params, |
1da177e4c
|
117 |
unsigned num_params, |
026cee008
|
118 119 |
s16 min_level, s16 max_level, |
ecc861705
|
120 |
void *arg, |
9fb48c744
|
121 |
int (*handle_unknown)(char *param, char *val, |
ecc861705
|
122 |
const char *doing, void *arg)) |
1da177e4c
|
123 124 |
{ unsigned int i; |
907b29eb4
|
125 |
int err; |
1da177e4c
|
126 127 128 129 |
/* Find parameter */ for (i = 0; i < num_params; i++) { if (parameq(param, params[i].name)) { |
026cee008
|
130 131 132 |
if (params[i].level < min_level || params[i].level > max_level) return 0; |
25985edce
|
133 |
/* No one handled NULL, so do it here. */ |
ab013c5f6
|
134 |
if (!val && |
6a4c26431
|
135 |
!(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG)) |
2e9fb9953
|
136 |
return -EINVAL; |
9fb48c744
|
137 138 139 |
pr_debug("handling %s with %p ", param, params[i].ops->set); |
b51d23e4e
|
140 |
kernel_param_lock(params[i].mod); |
7a486d378
|
141 |
param_check_unsafe(¶ms[i]); |
907b29eb4
|
142 |
err = params[i].ops->set(val, ¶ms[i]); |
b51d23e4e
|
143 |
kernel_param_unlock(params[i].mod); |
907b29eb4
|
144 |
return err; |
1da177e4c
|
145 146 147 148 |
} } if (handle_unknown) { |
9fb48c744
|
149 150 |
pr_debug("doing %s: %s='%s' ", doing, param, val); |
ecc861705
|
151 |
return handle_unknown(param, val, doing, arg); |
1da177e4c
|
152 |
} |
9fb48c744
|
153 154 |
pr_debug("Unknown argument '%s' ", param); |
1da177e4c
|
155 156 |
return -ENOENT; } |
1da177e4c
|
157 |
/* Args looks like "foo=bar,bar2 baz=fuz wiz". */ |
51e158c12
|
158 159 160 161 162 163 |
char *parse_args(const char *doing, char *args, const struct kernel_param *params, unsigned num, s16 min_level, s16 max_level, |
ecc861705
|
164 165 166 |
void *arg, int (*unknown)(char *param, char *val, const char *doing, void *arg)) |
1da177e4c
|
167 |
{ |
74b22c465
|
168 |
char *param, *val, *err = NULL; |
1da177e4c
|
169 |
|
f36462f07
|
170 |
/* Chew leading spaces */ |
e7d2860b6
|
171 |
args = skip_spaces(args); |
f36462f07
|
172 |
|
1ef9eaf2b
|
173 |
if (*args) |
9fb48c744
|
174 175 |
pr_debug("doing %s, parsing ARGS: '%s' ", doing, args); |
1da177e4c
|
176 177 |
while (*args) { int ret; |
a416aba63
|
178 |
int irq_was_disabled; |
1da177e4c
|
179 180 |
args = next_arg(args, ¶m, &val); |
51e158c12
|
181 182 |
/* Stop at -- */ if (!val && strcmp(param, "--") == 0) |
74b22c465
|
183 |
return err ?: args; |
a416aba63
|
184 |
irq_was_disabled = irqs_disabled(); |
9fb48c744
|
185 |
ret = parse_one(param, val, doing, params, num, |
ecc861705
|
186 |
min_level, max_level, arg, unknown); |
b5f3abf95
|
187 188 189 190 |
if (irq_was_disabled && !irqs_disabled()) pr_warn("%s: option '%s' enabled irq's! ", doing, param); |
1da177e4c
|
191 |
switch (ret) { |
74b22c465
|
192 193 |
case 0: continue; |
1da177e4c
|
194 |
case -ENOENT: |
b5f3abf95
|
195 196 |
pr_err("%s: Unknown parameter `%s' ", doing, param); |
74b22c465
|
197 |
break; |
1da177e4c
|
198 |
case -ENOSPC: |
b5f3abf95
|
199 200 |
pr_err("%s: `%s' too large for parameter `%s' ", |
9fb48c744
|
201 |
doing, val ?: "", param); |
1da177e4c
|
202 203 |
break; default: |
b5f3abf95
|
204 205 |
pr_err("%s: `%s' invalid for parameter `%s' ", |
9fb48c744
|
206 |
doing, val ?: "", param); |
74b22c465
|
207 |
break; |
1da177e4c
|
208 |
} |
74b22c465
|
209 210 |
err = ERR_PTR(ret); |
1da177e4c
|
211 |
} |
74b22c465
|
212 |
return err; |
1da177e4c
|
213 214 215 |
} /* Lazy bastard, eh? */ |
88a88b320
|
216 |
#define STANDARD_PARAM_DEF(name, type, format, strtolfn) \ |
9bbb9e5a3
|
217 |
int param_set_##name(const char *val, const struct kernel_param *kp) \ |
1da177e4c
|
218 |
{ \ |
88a88b320
|
219 |
return strtolfn(val, 0, (type *)kp->arg); \ |
1da177e4c
|
220 |
} \ |
9bbb9e5a3
|
221 |
int param_get_##name(char *buffer, const struct kernel_param *kp) \ |
1da177e4c
|
222 |
{ \ |
96802e6b1
|
223 224 |
return scnprintf(buffer, PAGE_SIZE, format " ", \ |
f4940ab7c
|
225 |
*((type *)kp->arg)); \ |
a14fe249a
|
226 |
} \ |
9c27847dd
|
227 |
const struct kernel_param_ops param_ops_##name = { \ |
9bbb9e5a3
|
228 229 230 |
.set = param_set_##name, \ .get = param_get_##name, \ }; \ |
a14fe249a
|
231 |
EXPORT_SYMBOL(param_set_##name); \ |
9bbb9e5a3
|
232 233 |
EXPORT_SYMBOL(param_get_##name); \ EXPORT_SYMBOL(param_ops_##name) |
1da177e4c
|
234 |
|
e0596c80f
|
235 236 237 238 239 240 241 242 |
STANDARD_PARAM_DEF(byte, unsigned char, "%hhu", kstrtou8); STANDARD_PARAM_DEF(short, short, "%hi", kstrtos16); STANDARD_PARAM_DEF(ushort, unsigned short, "%hu", kstrtou16); STANDARD_PARAM_DEF(int, int, "%i", kstrtoint); STANDARD_PARAM_DEF(uint, unsigned int, "%u", kstrtouint); STANDARD_PARAM_DEF(long, long, "%li", kstrtol); STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", kstrtoul); STANDARD_PARAM_DEF(ullong, unsigned long long, "%llu", kstrtoull); |
1da177e4c
|
243 |
|
9bbb9e5a3
|
244 |
int param_set_charp(const char *val, const struct kernel_param *kp) |
1da177e4c
|
245 |
{ |
1da177e4c
|
246 |
if (strlen(val) > 1024) { |
b5f3abf95
|
247 248 |
pr_err("%s: string parameter too long ", kp->name); |
1da177e4c
|
249 250 |
return -ENOSPC; } |
a1054322a
|
251 252 253 |
maybe_kfree_parameter(*(char **)kp->arg); /* This is a hack. We can't kmalloc in early boot, and we |
e180a6b77
|
254 255 |
* don't need to; this mangled commandline is preserved. */ if (slab_is_available()) { |
a1054322a
|
256 |
*(char **)kp->arg = kmalloc_parameter(strlen(val)+1); |
d553ad864
|
257 |
if (!*(char **)kp->arg) |
e180a6b77
|
258 |
return -ENOMEM; |
a1054322a
|
259 |
strcpy(*(char **)kp->arg, val); |
e180a6b77
|
260 261 |
} else *(const char **)kp->arg = val; |
1da177e4c
|
262 263 |
return 0; } |
a14fe249a
|
264 |
EXPORT_SYMBOL(param_set_charp); |
1da177e4c
|
265 |
|
9bbb9e5a3
|
266 |
int param_get_charp(char *buffer, const struct kernel_param *kp) |
1da177e4c
|
267 |
{ |
96802e6b1
|
268 269 |
return scnprintf(buffer, PAGE_SIZE, "%s ", *((char **)kp->arg)); |
1da177e4c
|
270 |
} |
a14fe249a
|
271 |
EXPORT_SYMBOL(param_get_charp); |
1da177e4c
|
272 |
|
3d9c637f4
|
273 |
void param_free_charp(void *arg) |
a1054322a
|
274 275 276 |
{ maybe_kfree_parameter(*((char **)arg)); } |
3d9c637f4
|
277 |
EXPORT_SYMBOL(param_free_charp); |
a1054322a
|
278 |
|
9c27847dd
|
279 |
const struct kernel_param_ops param_ops_charp = { |
9bbb9e5a3
|
280 281 |
.set = param_set_charp, .get = param_get_charp, |
a1054322a
|
282 |
.free = param_free_charp, |
9bbb9e5a3
|
283 284 |
}; EXPORT_SYMBOL(param_ops_charp); |
fddd52012
|
285 |
/* Actually could be a bool or an int, for historical reasons. */ |
9bbb9e5a3
|
286 |
int param_set_bool(const char *val, const struct kernel_param *kp) |
1da177e4c
|
287 288 289 290 291 |
{ /* No equals means "set"... */ if (!val) val = "1"; /* One of =[yYnN01] */ |
8b8252813
|
292 |
return strtobool(val, kp->arg); |
1da177e4c
|
293 |
} |
a14fe249a
|
294 |
EXPORT_SYMBOL(param_set_bool); |
1da177e4c
|
295 |
|
9bbb9e5a3
|
296 |
int param_get_bool(char *buffer, const struct kernel_param *kp) |
1da177e4c
|
297 298 |
{ /* Y and N chosen as being relatively non-coder friendly */ |
96802e6b1
|
299 300 |
return sprintf(buffer, "%c ", *(bool *)kp->arg ? 'Y' : 'N'); |
1da177e4c
|
301 |
} |
a14fe249a
|
302 |
EXPORT_SYMBOL(param_get_bool); |
1da177e4c
|
303 |
|
9c27847dd
|
304 |
const struct kernel_param_ops param_ops_bool = { |
6a4c26431
|
305 |
.flags = KERNEL_PARAM_OPS_FL_NOARG, |
9bbb9e5a3
|
306 307 308 309 |
.set = param_set_bool, .get = param_get_bool, }; EXPORT_SYMBOL(param_ops_bool); |
d19f05d8a
|
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
int param_set_bool_enable_only(const char *val, const struct kernel_param *kp) { int err = 0; bool new_value; bool orig_value = *(bool *)kp->arg; struct kernel_param dummy_kp = *kp; dummy_kp.arg = &new_value; err = param_set_bool(val, &dummy_kp); if (err) return err; /* Don't let them unset it once it's set! */ if (!new_value && orig_value) return -EROFS; if (new_value) err = param_set_bool(val, kp); return err; } EXPORT_SYMBOL_GPL(param_set_bool_enable_only); const struct kernel_param_ops param_ops_bool_enable_only = { .flags = KERNEL_PARAM_OPS_FL_NOARG, .set = param_set_bool_enable_only, .get = param_get_bool, }; |
154be21c5
|
339 |
EXPORT_SYMBOL_GPL(param_ops_bool_enable_only); |
d19f05d8a
|
340 |
|
fddd52012
|
341 |
/* This one must be bool. */ |
9bbb9e5a3
|
342 |
int param_set_invbool(const char *val, const struct kernel_param *kp) |
1da177e4c
|
343 |
{ |
fddd52012
|
344 345 |
int ret; bool boolval; |
22e48eaf5
|
346 |
struct kernel_param dummy; |
1da177e4c
|
347 |
|
22e48eaf5
|
348 |
dummy.arg = &boolval; |
1da177e4c
|
349 350 |
ret = param_set_bool(val, &dummy); if (ret == 0) |
9a71af2c3
|
351 |
*(bool *)kp->arg = !boolval; |
1da177e4c
|
352 353 |
return ret; } |
a14fe249a
|
354 |
EXPORT_SYMBOL(param_set_invbool); |
1da177e4c
|
355 |
|
9bbb9e5a3
|
356 |
int param_get_invbool(char *buffer, const struct kernel_param *kp) |
1da177e4c
|
357 |
{ |
96802e6b1
|
358 359 |
return sprintf(buffer, "%c ", (*(bool *)kp->arg) ? 'N' : 'Y'); |
1da177e4c
|
360 |
} |
a14fe249a
|
361 |
EXPORT_SYMBOL(param_get_invbool); |
1da177e4c
|
362 |
|
9c27847dd
|
363 |
const struct kernel_param_ops param_ops_invbool = { |
9bbb9e5a3
|
364 365 366 367 |
.set = param_set_invbool, .get = param_get_invbool, }; EXPORT_SYMBOL(param_ops_invbool); |
69116f279
|
368 369 |
int param_set_bint(const char *val, const struct kernel_param *kp) { |
5104b7d76
|
370 371 |
/* Match bool exactly, by re-using it. */ struct kernel_param boolkp = *kp; |
69116f279
|
372 373 |
bool v; int ret; |
69116f279
|
374 |
boolkp.arg = &v; |
69116f279
|
375 376 377 378 379 380 381 |
ret = param_set_bool(val, &boolkp); if (ret == 0) *(int *)kp->arg = v; return ret; } EXPORT_SYMBOL(param_set_bint); |
9c27847dd
|
382 |
const struct kernel_param_ops param_ops_bint = { |
6a4c26431
|
383 |
.flags = KERNEL_PARAM_OPS_FL_NOARG, |
69116f279
|
384 385 386 387 |
.set = param_set_bint, .get = param_get_int, }; EXPORT_SYMBOL(param_ops_bint); |
9730b5b06
|
388 |
/* We break the rule and mangle the string. */ |
b51d23e4e
|
389 390 |
static int param_array(struct module *mod, const char *name, |
9871728b7
|
391 392 393 |
const char *val, unsigned int min, unsigned int max, void *elem, int elemsize, |
9bbb9e5a3
|
394 |
int (*set)(const char *, const struct kernel_param *kp), |
026cee008
|
395 |
s16 level, |
eb38a996e
|
396 |
unsigned int *num) |
1da177e4c
|
397 398 399 400 401 402 403 404 |
{ int ret; struct kernel_param kp; char save; /* Get the name right for errors. */ kp.name = name; kp.arg = elem; |
026cee008
|
405 |
kp.level = level; |
1da177e4c
|
406 |
|
1da177e4c
|
407 408 409 410 411 412 |
*num = 0; /* We expect a comma-separated list of values. */ do { int len; if (*num == max) { |
b5f3abf95
|
413 414 |
pr_err("%s: can only take %i arguments ", name, max); |
1da177e4c
|
415 416 417 418 419 420 421 |
return -EINVAL; } len = strcspn(val, ","); /* nul-terminate and parse */ save = val[len]; ((char *)val)[len] = '\0'; |
cf2fde7b3
|
422 |
check_kparam_locked(mod); |
1da177e4c
|
423 424 425 426 427 428 429 430 431 432 |
ret = set(val, &kp); if (ret != 0) return ret; kp.arg += elemsize; val += len+1; (*num)++; } while (save == ','); if (*num < min) { |
b5f3abf95
|
433 434 |
pr_err("%s: needs at least %i arguments ", name, min); |
1da177e4c
|
435 436 437 438 |
return -EINVAL; } return 0; } |
9bbb9e5a3
|
439 |
static int param_array_set(const char *val, const struct kernel_param *kp) |
1da177e4c
|
440 |
{ |
22e48eaf5
|
441 |
const struct kparam_array *arr = kp->arr; |
31143a120
|
442 |
unsigned int temp_num; |
1da177e4c
|
443 |
|
b51d23e4e
|
444 |
return param_array(kp->mod, kp->name, val, 1, arr->max, arr->elem, |
026cee008
|
445 |
arr->elemsize, arr->ops->set, kp->level, |
3c7d76e37
|
446 |
arr->num ?: &temp_num); |
1da177e4c
|
447 |
} |
9bbb9e5a3
|
448 |
static int param_array_get(char *buffer, const struct kernel_param *kp) |
1da177e4c
|
449 450 |
{ int i, off, ret; |
22e48eaf5
|
451 |
const struct kparam_array *arr = kp->arr; |
5104b7d76
|
452 |
struct kernel_param p = *kp; |
1da177e4c
|
453 |
|
1da177e4c
|
454 |
for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) { |
96802e6b1
|
455 456 |
/* Replace with comma */ |
1da177e4c
|
457 |
if (i) |
96802e6b1
|
458 |
buffer[off - 1] = ','; |
1da177e4c
|
459 |
p.arg = arr->elem + arr->elemsize * i; |
cf2fde7b3
|
460 |
check_kparam_locked(p.mod); |
9bbb9e5a3
|
461 |
ret = arr->ops->get(buffer + off, &p); |
1da177e4c
|
462 463 464 465 466 467 468 |
if (ret < 0) return ret; off += ret; } buffer[off] = '\0'; return off; } |
e6df34a44
|
469 470 471 472 473 474 475 476 477 |
static void param_array_free(void *arg) { unsigned int i; const struct kparam_array *arr = arg; if (arr->ops->free) for (i = 0; i < (arr->num ? *arr->num : arr->max); i++) arr->ops->free(arr->elem + arr->elemsize * i); } |
9c27847dd
|
478 |
const struct kernel_param_ops param_array_ops = { |
9bbb9e5a3
|
479 480 |
.set = param_array_set, .get = param_array_get, |
e6df34a44
|
481 |
.free = param_array_free, |
9bbb9e5a3
|
482 483 484 485 |
}; EXPORT_SYMBOL(param_array_ops); int param_set_copystring(const char *val, const struct kernel_param *kp) |
1da177e4c
|
486 |
{ |
22e48eaf5
|
487 |
const struct kparam_string *kps = kp->str; |
1da177e4c
|
488 489 |
if (strlen(val)+1 > kps->maxlen) { |
b5f3abf95
|
490 491 |
pr_err("%s: string doesn't fit in %u chars. ", |
1da177e4c
|
492 493 494 495 496 497 |
kp->name, kps->maxlen-1); return -ENOSPC; } strcpy(kps->string, val); return 0; } |
a14fe249a
|
498 |
EXPORT_SYMBOL(param_set_copystring); |
1da177e4c
|
499 |
|
9bbb9e5a3
|
500 |
int param_get_string(char *buffer, const struct kernel_param *kp) |
1da177e4c
|
501 |
{ |
22e48eaf5
|
502 |
const struct kparam_string *kps = kp->str; |
96802e6b1
|
503 504 |
return scnprintf(buffer, PAGE_SIZE, "%s ", kps->string); |
1da177e4c
|
505 |
} |
a14fe249a
|
506 |
EXPORT_SYMBOL(param_get_string); |
1da177e4c
|
507 |
|
9c27847dd
|
508 |
const struct kernel_param_ops param_ops_string = { |
9bbb9e5a3
|
509 510 511 512 |
.set = param_set_copystring, .get = param_get_string, }; EXPORT_SYMBOL(param_ops_string); |
1da177e4c
|
513 |
/* sysfs output in /sys/modules/XYZ/parameters/ */ |
350f82586
|
514 515 |
#define to_module_attr(n) container_of(n, struct module_attribute, attr) #define to_module_kobject(n) container_of(n, struct module_kobject, kobj) |
1da177e4c
|
516 |
|
1da177e4c
|
517 518 519 |
struct param_attribute { struct module_attribute mattr; |
9bbb9e5a3
|
520 |
const struct kernel_param *param; |
1da177e4c
|
521 522 523 524 |
}; struct module_param_attrs { |
9b473de87
|
525 |
unsigned int num; |
1da177e4c
|
526 527 528 |
struct attribute_group grp; struct param_attribute attrs[0]; }; |
ef665c1a0
|
529 |
#ifdef CONFIG_SYSFS |
350f82586
|
530 |
#define to_param_attr(n) container_of(n, struct param_attribute, mattr) |
1da177e4c
|
531 532 |
static ssize_t param_attr_show(struct module_attribute *mattr, |
4befb026c
|
533 |
struct module_kobject *mk, char *buf) |
1da177e4c
|
534 535 536 |
{ int count; struct param_attribute *attribute = to_param_attr(mattr); |
9bbb9e5a3
|
537 |
if (!attribute->param->ops->get) |
1da177e4c
|
538 |
return -EPERM; |
b51d23e4e
|
539 |
kernel_param_lock(mk->mod); |
9bbb9e5a3
|
540 |
count = attribute->param->ops->get(buf, attribute->param); |
b51d23e4e
|
541 |
kernel_param_unlock(mk->mod); |
1da177e4c
|
542 543 544 545 546 |
return count; } /* sysfs always hands a nul-terminated string in buf. We rely on that. */ static ssize_t param_attr_store(struct module_attribute *mattr, |
b51d23e4e
|
547 |
struct module_kobject *mk, |
1da177e4c
|
548 549 550 551 |
const char *buf, size_t len) { int err; struct param_attribute *attribute = to_param_attr(mattr); |
9bbb9e5a3
|
552 |
if (!attribute->param->ops->set) |
1da177e4c
|
553 |
return -EPERM; |
b51d23e4e
|
554 |
kernel_param_lock(mk->mod); |
7a486d378
|
555 |
param_check_unsafe(attribute->param); |
9bbb9e5a3
|
556 |
err = attribute->param->ops->set(buf, attribute->param); |
b51d23e4e
|
557 |
kernel_param_unlock(mk->mod); |
1da177e4c
|
558 559 560 561 |
if (!err) return len; return err; } |
ef665c1a0
|
562 |
#endif |
1da177e4c
|
563 564 565 566 567 568 |
#ifdef CONFIG_MODULES #define __modinit #else #define __modinit __init #endif |
ef665c1a0
|
569 |
#ifdef CONFIG_SYSFS |
b51d23e4e
|
570 |
void kernel_param_lock(struct module *mod) |
907b29eb4
|
571 |
{ |
b51d23e4e
|
572 |
mutex_lock(KPARAM_MUTEX(mod)); |
907b29eb4
|
573 |
} |
907b29eb4
|
574 |
|
b51d23e4e
|
575 |
void kernel_param_unlock(struct module *mod) |
907b29eb4
|
576 |
{ |
b51d23e4e
|
577 |
mutex_unlock(KPARAM_MUTEX(mod)); |
907b29eb4
|
578 |
} |
b51d23e4e
|
579 |
|
b51d23e4e
|
580 581 |
EXPORT_SYMBOL(kernel_param_lock); EXPORT_SYMBOL(kernel_param_unlock); |
907b29eb4
|
582 |
|
1da177e4c
|
583 |
/* |
9b473de87
|
584 585 |
* add_sysfs_param - add a parameter to sysfs * @mk: struct module_kobject |
630cc2b30
|
586 |
* @kp: the actual parameter definition to add to sysfs |
9b473de87
|
587 |
* @name: name of parameter |
1da177e4c
|
588 |
* |
9b473de87
|
589 590 591 |
* Create a kobject if for a (per-module) parameter if mp NULL, and * create file in sysfs. Returns an error on out of memory. Always cleans up * if there's an error. |
1da177e4c
|
592 |
*/ |
9b473de87
|
593 |
static __modinit int add_sysfs_param(struct module_kobject *mk, |
9bbb9e5a3
|
594 |
const struct kernel_param *kp, |
9b473de87
|
595 |
const char *name) |
1da177e4c
|
596 |
{ |
18eb74fa9
|
597 598 599 |
struct module_param_attrs *new_mp; struct attribute **new_attrs; unsigned int i; |
9b473de87
|
600 601 602 603 604 |
/* We don't bother calling this with invisible parameters. */ BUG_ON(!kp->perm); if (!mk->mp) { |
18eb74fa9
|
605 606 607 608 609 610 611 612 613 614 615 |
/* First allocation. */ mk->mp = kzalloc(sizeof(*mk->mp), GFP_KERNEL); if (!mk->mp) return -ENOMEM; mk->mp->grp.name = "parameters"; /* NULL-terminated attribute array. */ mk->mp->grp.attrs = kzalloc(sizeof(mk->mp->grp.attrs[0]), GFP_KERNEL); /* Caller will cleanup via free_module_param_attrs */ if (!mk->mp->grp.attrs) return -ENOMEM; |
9b473de87
|
616 |
} |
1da177e4c
|
617 |
|
18eb74fa9
|
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 |
/* Enlarge allocations. */ new_mp = krealloc(mk->mp, sizeof(*mk->mp) + sizeof(mk->mp->attrs[0]) * (mk->mp->num + 1), GFP_KERNEL); if (!new_mp) return -ENOMEM; mk->mp = new_mp; /* Extra pointer for NULL terminator */ new_attrs = krealloc(mk->mp->grp.attrs, sizeof(mk->mp->grp.attrs[0]) * (mk->mp->num + 2), GFP_KERNEL); if (!new_attrs) return -ENOMEM; mk->mp->grp.attrs = new_attrs; |
9b473de87
|
634 635 |
/* Tack new one on the end. */ |
c772be523
|
636 |
memset(&mk->mp->attrs[mk->mp->num], 0, sizeof(mk->mp->attrs[0])); |
18eb74fa9
|
637 638 639 |
sysfs_attr_init(&mk->mp->attrs[mk->mp->num].mattr.attr); mk->mp->attrs[mk->mp->num].param = kp; mk->mp->attrs[mk->mp->num].mattr.show = param_attr_show; |
b0a65b0cc
|
640 641 642 |
/* Do not allow runtime DAC changes to make param writable. */ if ((kp->perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) mk->mp->attrs[mk->mp->num].mattr.store = param_attr_store; |
574732c73
|
643 644 |
else mk->mp->attrs[mk->mp->num].mattr.store = NULL; |
18eb74fa9
|
645 646 647 |
mk->mp->attrs[mk->mp->num].mattr.attr.name = (char *)name; mk->mp->attrs[mk->mp->num].mattr.attr.mode = kp->perm; mk->mp->num++; |
9b473de87
|
648 649 |
/* Fix up all the pointers, since krealloc can move us */ |
18eb74fa9
|
650 651 652 |
for (i = 0; i < mk->mp->num; i++) mk->mp->grp.attrs[i] = &mk->mp->attrs[i].mattr.attr; mk->mp->grp.attrs[mk->mp->num] = NULL; |
9b473de87
|
653 |
return 0; |
9b473de87
|
654 |
} |
1da177e4c
|
655 |
|
d2441183d
|
656 |
#ifdef CONFIG_MODULES |
9b473de87
|
657 658 |
static void free_module_param_attrs(struct module_kobject *mk) { |
18eb74fa9
|
659 660 |
if (mk->mp) kfree(mk->mp->grp.attrs); |
9b473de87
|
661 662 |
kfree(mk->mp); mk->mp = NULL; |
1da177e4c
|
663 |
} |
1da177e4c
|
664 665 666 667 668 669 |
/* * module_param_sysfs_setup - setup sysfs support for one module * @mod: module * @kparam: module parameters (array) * @num_params: number of module parameters * |
9b473de87
|
670 671 |
* Adds sysfs entries for module parameters under * /sys/module/[mod->name]/parameters/ |
1da177e4c
|
672 673 |
*/ int module_param_sysfs_setup(struct module *mod, |
9bbb9e5a3
|
674 |
const struct kernel_param *kparam, |
1da177e4c
|
675 676 |
unsigned int num_params) { |
9b473de87
|
677 678 679 680 681 682 683 |
int i, err; bool params = false; for (i = 0; i < num_params; i++) { if (kparam[i].perm == 0) continue; err = add_sysfs_param(&mod->mkobj, &kparam[i], kparam[i].name); |
18eb74fa9
|
684 685 |
if (err) { free_module_param_attrs(&mod->mkobj); |
9b473de87
|
686 |
return err; |
18eb74fa9
|
687 |
} |
9b473de87
|
688 689 |
params = true; } |
1da177e4c
|
690 |
|
9b473de87
|
691 692 |
if (!params) return 0; |
1da177e4c
|
693 |
|
9b473de87
|
694 695 696 697 698 |
/* Create the param group. */ err = sysfs_create_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp); if (err) free_module_param_attrs(&mod->mkobj); return err; |
1da177e4c
|
699 700 701 702 703 704 705 706 707 708 709 |
} /* * module_param_sysfs_remove - remove sysfs support for one module * @mod: module * * Remove sysfs entries for module parameters and the corresponding * kobject. */ void module_param_sysfs_remove(struct module *mod) { |
9b473de87
|
710 711 |
if (mod->mkobj.mp) { sysfs_remove_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp); |
1da177e4c
|
712 713 |
/* We are positive that no one is using any param * attrs at this point. Deallocate immediately. */ |
9b473de87
|
714 |
free_module_param_attrs(&mod->mkobj); |
1da177e4c
|
715 716 717 |
} } #endif |
e180a6b77
|
718 719 |
void destroy_params(const struct kernel_param *params, unsigned num) { |
e6df34a44
|
720 721 722 723 724 |
unsigned int i; for (i = 0; i < num; i++) if (params[i].ops->free) params[i].ops->free(params[i].arg); |
e180a6b77
|
725 |
} |
e94965ed5
|
726 |
static struct module_kobject * __init locate_module_kobject(const char *name) |
1da177e4c
|
727 728 |
{ struct module_kobject *mk; |
9b473de87
|
729 730 |
struct kobject *kobj; int err; |
1da177e4c
|
731 |
|
9b473de87
|
732 733 |
kobj = kset_find_obj(module_kset, name); if (kobj) { |
9b473de87
|
734 |
mk = to_module_kobject(kobj); |
9b473de87
|
735 736 737 738 739 740 741 742 |
} else { mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL); BUG_ON(!mk); mk->mod = THIS_MODULE; mk->kobj.kset = module_kset; err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL, "%s", name); |
88bfa3247
|
743 744 745 746 |
#ifdef CONFIG_MODULES if (!err) err = sysfs_create_file(&mk->kobj, &module_uevent.attr); #endif |
9b473de87
|
747 748 |
if (err) { kobject_put(&mk->kobj); |
b5f3abf95
|
749 750 |
pr_crit("Adding module '%s' to sysfs failed (%d), the system may be unstable. ", |
e94965ed5
|
751 |
name, err); |
e94965ed5
|
752 |
return NULL; |
9b473de87
|
753 |
} |
e94965ed5
|
754 755 |
/* So that we hold reference in both cases. */ |
9b473de87
|
756 |
kobject_get(&mk->kobj); |
74c5b597e
|
757 |
} |
9b473de87
|
758 |
|
e94965ed5
|
759 760 761 762 |
return mk; } static void __init kernel_add_sysfs_param(const char *name, |
63a12d9d0
|
763 |
const struct kernel_param *kparam, |
e94965ed5
|
764 765 766 767 768 769 770 771 772 773 774 775 |
unsigned int name_skip) { struct module_kobject *mk; int err; mk = locate_module_kobject(name); if (!mk) return; /* We need to remove old parameters before adding more. */ if (mk->mp) sysfs_remove_group(&mk->kobj, &mk->mp->grp); |
9b473de87
|
776 777 778 779 780 |
/* These should not fail at boot. */ err = add_sysfs_param(mk, kparam, kparam->name + name_skip); BUG_ON(err); err = sysfs_create_group(&mk->kobj, &mk->mp->grp); BUG_ON(err); |
f30c53a87
|
781 |
kobject_uevent(&mk->kobj, KOBJ_ADD); |
9b473de87
|
782 |
kobject_put(&mk->kobj); |
1da177e4c
|
783 784 785 |
} /* |
b634d130e
|
786 |
* param_sysfs_builtin - add sysfs parameters for built-in modules |
1da177e4c
|
787 788 789 790 791 792 |
* * Add module_parameters to sysfs for "modules" built into the kernel. * * The "module" name (KBUILD_MODNAME) is stored before a dot, the * "parameter" name is stored behind a dot in kernel_param->name. So, * extract the "module" name for all built-in kernel_param-eters, |
9b473de87
|
793 |
* and for all who have the same, call kernel_add_sysfs_param. |
1da177e4c
|
794 795 796 |
*/ static void __init param_sysfs_builtin(void) { |
63a12d9d0
|
797 |
const struct kernel_param *kp; |
9b473de87
|
798 799 |
unsigned int name_len; char modname[MODULE_NAME_LEN]; |
1da177e4c
|
800 |
|
9b473de87
|
801 |
for (kp = __start___param; kp < __stop___param; kp++) { |
1da177e4c
|
802 |
char *dot; |
9b473de87
|
803 804 |
if (kp->perm == 0) continue; |
1da177e4c
|
805 |
|
730b69d22
|
806 |
dot = strchr(kp->name, '.'); |
1da177e4c
|
807 |
if (!dot) { |
67e67ceaa
|
808 809 810 811 812 813 |
/* This happens for core_param() */ strcpy(modname, "kernel"); name_len = 0; } else { name_len = dot - kp->name + 1; strlcpy(modname, kp->name, name_len); |
1da177e4c
|
814 |
} |
67e67ceaa
|
815 |
kernel_add_sysfs_param(modname, kp, name_len); |
1da177e4c
|
816 |
} |
1da177e4c
|
817 |
} |
e94965ed5
|
818 |
ssize_t __modver_version_show(struct module_attribute *mattr, |
4befb026c
|
819 |
struct module_kobject *mk, char *buf) |
e94965ed5
|
820 821 822 |
{ struct module_version_attribute *vattr = container_of(mattr, struct module_version_attribute, mattr); |
f4940ab7c
|
823 824 |
return scnprintf(buf, PAGE_SIZE, "%s ", vattr->version); |
e94965ed5
|
825 |
} |
b4bc84280
|
826 827 |
extern const struct module_version_attribute *__start___modver[]; extern const struct module_version_attribute *__stop___modver[]; |
e94965ed5
|
828 829 830 |
static void __init version_sysfs_builtin(void) { |
b4bc84280
|
831 |
const struct module_version_attribute **p; |
e94965ed5
|
832 833 |
struct module_kobject *mk; int err; |
b4bc84280
|
834 835 |
for (p = __start___modver; p < __stop___modver; p++) { const struct module_version_attribute *vattr = *p; |
e94965ed5
|
836 837 838 |
mk = locate_module_kobject(vattr->module_name); if (mk) { err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr); |
74c3dea35
|
839 |
WARN_ON_ONCE(err); |
e94965ed5
|
840 841 842 843 844 |
kobject_uevent(&mk->kobj, KOBJ_ADD); kobject_put(&mk->kobj); } } } |
1da177e4c
|
845 846 |
/* module-related sysfs stuff */ |
1da177e4c
|
847 |
|
1da177e4c
|
848 849 850 851 852 853 854 855 856 857 858 859 |
static ssize_t module_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct module_attribute *attribute; struct module_kobject *mk; int ret; attribute = to_module_attr(attr); mk = to_module_kobject(kobj); if (!attribute->show) |
70f2817a4
|
860 |
return -EIO; |
1da177e4c
|
861 |
|
4befb026c
|
862 |
ret = attribute->show(attribute, mk, buf); |
1da177e4c
|
863 |
|
1da177e4c
|
864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 |
return ret; } static ssize_t module_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { struct module_attribute *attribute; struct module_kobject *mk; int ret; attribute = to_module_attr(attr); mk = to_module_kobject(kobj); if (!attribute->store) |
70f2817a4
|
879 |
return -EIO; |
1da177e4c
|
880 |
|
4befb026c
|
881 |
ret = attribute->store(attribute, mk, buf, len); |
1da177e4c
|
882 |
|
1da177e4c
|
883 884 |
return ret; } |
52cf25d0a
|
885 |
static const struct sysfs_ops module_sysfs_ops = { |
1da177e4c
|
886 887 888 |
.show = module_attr_show, .store = module_attr_store, }; |
270a6c4ca
|
889 890 891 892 893 894 895 896 |
static int uevent_filter(struct kset *kset, struct kobject *kobj) { struct kobj_type *ktype = get_ktype(kobj); if (ktype == &module_ktype) return 1; return 0; } |
9cd43611c
|
897 |
static const struct kset_uevent_ops module_uevent_ops = { |
270a6c4ca
|
898 899 |
.filter = uevent_filter, }; |
7405c1e15
|
900 |
struct kset *module_kset; |
823bccfc4
|
901 |
int module_sysfs_initialized; |
1da177e4c
|
902 |
|
942e44312
|
903 904 905 906 907 |
static void module_kobj_release(struct kobject *kobj) { struct module_kobject *mk = to_module_kobject(kobj); complete(mk->kobj_completion); } |
7405c1e15
|
908 |
struct kobj_type module_ktype = { |
942e44312
|
909 |
.release = module_kobj_release, |
1da177e4c
|
910 911 |
.sysfs_ops = &module_sysfs_ops, }; |
1da177e4c
|
912 913 914 915 916 |
/* * param_sysfs_init - wrapper for built-in params support */ static int __init param_sysfs_init(void) { |
7405c1e15
|
917 918 919 920 921 922 |
module_kset = kset_create_and_add("module", &module_uevent_ops, NULL); if (!module_kset) { printk(KERN_WARNING "%s (%d): error creating kset ", __FILE__, __LINE__); return -ENOMEM; |
d8c7649e9
|
923 |
} |
823bccfc4
|
924 |
module_sysfs_initialized = 1; |
1da177e4c
|
925 |
|
e94965ed5
|
926 |
version_sysfs_builtin(); |
1da177e4c
|
927 928 929 930 |
param_sysfs_builtin(); return 0; } |
d10be6d1b
|
931 |
subsys_initcall(param_sysfs_init); |
1da177e4c
|
932 |
|
7405c1e15
|
933 |
#endif /* CONFIG_SYSFS */ |