params.c 22.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* 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
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/module.h>
22
#include <linux/moduleparam.h>
Linus Torvalds's avatar
Linus Torvalds committed
23 24
#include <linux/device.h>
#include <linux/err.h>
Tim Schmielau's avatar
Tim Schmielau committed
25
#include <linux/slab.h>
26
#include <linux/ctype.h>
Linus Torvalds's avatar
Linus Torvalds committed
27

28 29 30
/* Protects all parameters, and incidentally kmalloced_param list. */
static DEFINE_MUTEX(param_lock);

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/* This just allows us to keep track of which parameters are kmalloced. */
struct kmalloced_param {
	struct list_head list;
	char val[];
};
static LIST_HEAD(kmalloced_params);

static void *kmalloc_parameter(unsigned int size)
{
	struct kmalloced_param *p;

	p = kmalloc(sizeof(*p) + size, GFP_KERNEL);
	if (!p)
		return NULL;

	list_add(&p->list, &kmalloced_params);
	return p->val;
}

/* Does nothing if parameter wasn't kmalloced above. */
static void maybe_kfree_parameter(void *param)
{
	struct kmalloced_param *p;

	list_for_each_entry(p, &kmalloced_params, list) {
		if (p->val == param) {
			list_del(&p->list);
			kfree(p);
			break;
		}
	}
}

64
static char dash2underscore(char c)
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68 69 70
{
	if (c == '-')
		return '_';
	return c;
}

71
bool parameqn(const char *a, const char *b, size_t n)
Linus Torvalds's avatar
Linus Torvalds committed
72
{
73 74 75 76 77 78 79 80 81 82 83 84
	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);
Linus Torvalds's avatar
Linus Torvalds committed
85 86
}

87 88 89 90 91 92 93 94 95
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\n",
			kp->name);
		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
96 97
static int parse_one(char *param,
		     char *val,
98
		     const char *doing,
99
		     const struct kernel_param *params,
Linus Torvalds's avatar
Linus Torvalds committed
100
		     unsigned num_params,
101 102
		     s16 min_level,
		     s16 max_level,
103 104
		     int (*handle_unknown)(char *param, char *val,
				     const char *doing))
Linus Torvalds's avatar
Linus Torvalds committed
105 106
{
	unsigned int i;
107
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
108 109 110 111

	/* Find parameter */
	for (i = 0; i < num_params; i++) {
		if (parameq(param, params[i].name)) {
112 113 114
			if (params[i].level < min_level
			    || params[i].level > max_level)
				return 0;
Lucas De Marchi's avatar
Lucas De Marchi committed
115
			/* No one handled NULL, so do it here. */
116
			if (!val &&
117
			    !(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG))
118
				return -EINVAL;
119 120
			pr_debug("handling %s with %p\n", param,
				params[i].ops->set);
121
			mutex_lock(&param_lock);
122
			param_check_unsafe(&params[i]);
123 124 125
			err = params[i].ops->set(val, &params[i]);
			mutex_unlock(&param_lock);
			return err;
Linus Torvalds's avatar
Linus Torvalds committed
126 127 128 129
		}
	}

	if (handle_unknown) {
130 131
		pr_debug("doing %s: %s='%s'\n", doing, param, val);
		return handle_unknown(param, val, doing);
Linus Torvalds's avatar
Linus Torvalds committed
132 133
	}

134
	pr_debug("Unknown argument '%s'\n", param);
Linus Torvalds's avatar
Linus Torvalds committed
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
	return -ENOENT;
}

/* You can use " around spaces, but can't escape ". */
/* Hyphens and underscores equivalent in parameter names. */
static char *next_arg(char *args, char **param, char **val)
{
	unsigned int i, equals = 0;
	int in_quote = 0, quoted = 0;
	char *next;

	if (*args == '"') {
		args++;
		in_quote = 1;
		quoted = 1;
	}

	for (i = 0; args[i]; i++) {
153
		if (isspace(args[i]) && !in_quote)
Linus Torvalds's avatar
Linus Torvalds committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
			break;
		if (equals == 0) {
			if (args[i] == '=')
				equals = i;
		}
		if (args[i] == '"')
			in_quote = !in_quote;
	}

	*param = args;
	if (!equals)
		*val = NULL;
	else {
		args[equals] = '\0';
		*val = args + equals + 1;

		/* Don't include quotes in value. */
		if (**val == '"') {
			(*val)++;
			if (args[i-1] == '"')
				args[i-1] = '\0';
		}
		if (quoted && args[i-1] == '"')
			args[i-1] = '\0';
	}

	if (args[i]) {
		args[i] = '\0';
		next = args + i + 1;
	} else
		next = args + i;
185 186

	/* Chew up trailing spaces. */
187
	return skip_spaces(next);
Linus Torvalds's avatar
Linus Torvalds committed
188 189 190
}

/* Args looks like "foo=bar,bar2 baz=fuz wiz". */
191 192 193 194 195 196 197
char *parse_args(const char *doing,
		 char *args,
		 const struct kernel_param *params,
		 unsigned num,
		 s16 min_level,
		 s16 max_level,
		 int (*unknown)(char *param, char *val, const char *doing))
Linus Torvalds's avatar
Linus Torvalds committed
198 199 200
{
	char *param, *val;

201
	/* Chew leading spaces */
202
	args = skip_spaces(args);
203

204
	if (*args)
205 206
		pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args);

Linus Torvalds's avatar
Linus Torvalds committed
207 208
	while (*args) {
		int ret;
209
		int irq_was_disabled;
Linus Torvalds's avatar
Linus Torvalds committed
210 211

		args = next_arg(args, &param, &val);
212 213 214
		/* Stop at -- */
		if (!val && strcmp(param, "--") == 0)
			return args;
215
		irq_was_disabled = irqs_disabled();
216
		ret = parse_one(param, val, doing, params, num,
217
				min_level, max_level, unknown);
218 219 220 221
		if (irq_was_disabled && !irqs_disabled())
			pr_warn("%s: option '%s' enabled irq's!\n",
				doing, param);

Linus Torvalds's avatar
Linus Torvalds committed
222 223
		switch (ret) {
		case -ENOENT:
224
			pr_err("%s: Unknown parameter `%s'\n", doing, param);
225
			return ERR_PTR(ret);
Linus Torvalds's avatar
Linus Torvalds committed
226
		case -ENOSPC:
227
			pr_err("%s: `%s' too large for parameter `%s'\n",
228
			       doing, val ?: "", param);
229
			return ERR_PTR(ret);
Linus Torvalds's avatar
Linus Torvalds committed
230 231 232
		case 0:
			break;
		default:
233
			pr_err("%s: `%s' invalid for parameter `%s'\n",
234
			       doing, val ?: "", param);
235
			return ERR_PTR(ret);
Linus Torvalds's avatar
Linus Torvalds committed
236 237 238 239
		}
	}

	/* All parsed OK. */
240
	return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
241 242 243
}

/* Lazy bastard, eh? */
244
#define STANDARD_PARAM_DEF(name, type, format, strtolfn)      		\
245
	int param_set_##name(const char *val, const struct kernel_param *kp) \
Linus Torvalds's avatar
Linus Torvalds committed
246
	{								\
247
		return strtolfn(val, 0, (type *)kp->arg);		\
Linus Torvalds's avatar
Linus Torvalds committed
248
	}								\
249
	int param_get_##name(char *buffer, const struct kernel_param *kp) \
Linus Torvalds's avatar
Linus Torvalds committed
250
	{								\
251 252
		return scnprintf(buffer, PAGE_SIZE, format,		\
				*((type *)kp->arg));			\
253
	}								\
254 255 256 257
	struct kernel_param_ops param_ops_##name = {			\
		.set = param_set_##name,				\
		.get = param_get_##name,				\
	};								\
258
	EXPORT_SYMBOL(param_set_##name);				\
259 260 261
	EXPORT_SYMBOL(param_get_##name);				\
	EXPORT_SYMBOL(param_ops_##name)

Linus Torvalds's avatar
Linus Torvalds committed
262

263 264 265 266 267 268 269
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);
270
STANDARD_PARAM_DEF(ullong, unsigned long long, "%llu", kstrtoull);
Linus Torvalds's avatar
Linus Torvalds committed
271

272
int param_set_charp(const char *val, const struct kernel_param *kp)
Linus Torvalds's avatar
Linus Torvalds committed
273 274
{
	if (strlen(val) > 1024) {
275
		pr_err("%s: string parameter too long\n", kp->name);
Linus Torvalds's avatar
Linus Torvalds committed
276 277 278
		return -ENOSPC;
	}

279 280 281
	maybe_kfree_parameter(*(char **)kp->arg);

	/* This is a hack.  We can't kmalloc in early boot, and we
282 283
	 * don't need to; this mangled commandline is preserved. */
	if (slab_is_available()) {
284
		*(char **)kp->arg = kmalloc_parameter(strlen(val)+1);
285
		if (!*(char **)kp->arg)
286
			return -ENOMEM;
287
		strcpy(*(char **)kp->arg, val);
288 289 290
	} else
		*(const char **)kp->arg = val;

Linus Torvalds's avatar
Linus Torvalds committed
291 292
	return 0;
}
293
EXPORT_SYMBOL(param_set_charp);
Linus Torvalds's avatar
Linus Torvalds committed
294

295
int param_get_charp(char *buffer, const struct kernel_param *kp)
Linus Torvalds's avatar
Linus Torvalds committed
296
{
297
	return scnprintf(buffer, PAGE_SIZE, "%s", *((char **)kp->arg));
Linus Torvalds's avatar
Linus Torvalds committed
298
}
299
EXPORT_SYMBOL(param_get_charp);
Linus Torvalds's avatar
Linus Torvalds committed
300

301 302 303 304 305
static void param_free_charp(void *arg)
{
	maybe_kfree_parameter(*((char **)arg));
}

306 307 308
struct kernel_param_ops param_ops_charp = {
	.set = param_set_charp,
	.get = param_get_charp,
309
	.free = param_free_charp,
310 311 312
};
EXPORT_SYMBOL(param_ops_charp);

313
/* Actually could be a bool or an int, for historical reasons. */
314
int param_set_bool(const char *val, const struct kernel_param *kp)
Linus Torvalds's avatar
Linus Torvalds committed
315 316 317 318 319
{
	/* No equals means "set"... */
	if (!val) val = "1";

	/* One of =[yYnN01] */
320
	return strtobool(val, kp->arg);
Linus Torvalds's avatar
Linus Torvalds committed
321
}
322
EXPORT_SYMBOL(param_set_bool);
Linus Torvalds's avatar
Linus Torvalds committed
323

324
int param_get_bool(char *buffer, const struct kernel_param *kp)
Linus Torvalds's avatar
Linus Torvalds committed
325 326
{
	/* Y and N chosen as being relatively non-coder friendly */
327
	return sprintf(buffer, "%c", *(bool *)kp->arg ? 'Y' : 'N');
Linus Torvalds's avatar
Linus Torvalds committed
328
}
329
EXPORT_SYMBOL(param_get_bool);
Linus Torvalds's avatar
Linus Torvalds committed
330

331
struct kernel_param_ops param_ops_bool = {
332
	.flags = KERNEL_PARAM_OPS_FL_NOARG,
333 334 335 336 337
	.set = param_set_bool,
	.get = param_get_bool,
};
EXPORT_SYMBOL(param_ops_bool);

338
/* This one must be bool. */
339
int param_set_invbool(const char *val, const struct kernel_param *kp)
Linus Torvalds's avatar
Linus Torvalds committed
340
{
341 342
	int ret;
	bool boolval;
343
	struct kernel_param dummy;
Linus Torvalds's avatar
Linus Torvalds committed
344

345
	dummy.arg = &boolval;
Linus Torvalds's avatar
Linus Torvalds committed
346 347
	ret = param_set_bool(val, &dummy);
	if (ret == 0)
348
		*(bool *)kp->arg = !boolval;
Linus Torvalds's avatar
Linus Torvalds committed
349 350
	return ret;
}
351
EXPORT_SYMBOL(param_set_invbool);
Linus Torvalds's avatar
Linus Torvalds committed
352

353
int param_get_invbool(char *buffer, const struct kernel_param *kp)
Linus Torvalds's avatar
Linus Torvalds committed
354
{
355
	return sprintf(buffer, "%c", (*(bool *)kp->arg) ? 'N' : 'Y');
Linus Torvalds's avatar
Linus Torvalds committed
356
}
357
EXPORT_SYMBOL(param_get_invbool);
Linus Torvalds's avatar
Linus Torvalds committed
358

359 360 361 362 363 364
struct kernel_param_ops param_ops_invbool = {
	.set = param_set_invbool,
	.get = param_get_invbool,
};
EXPORT_SYMBOL(param_ops_invbool);

365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
int param_set_bint(const char *val, const struct kernel_param *kp)
{
	struct kernel_param boolkp;
	bool v;
	int ret;

	/* Match bool exactly, by re-using it. */
	boolkp = *kp;
	boolkp.arg = &v;

	ret = param_set_bool(val, &boolkp);
	if (ret == 0)
		*(int *)kp->arg = v;
	return ret;
}
EXPORT_SYMBOL(param_set_bint);

struct kernel_param_ops param_ops_bint = {
383
	.flags = KERNEL_PARAM_OPS_FL_NOARG,
384 385 386 387 388
	.set = param_set_bint,
	.get = param_get_int,
};
EXPORT_SYMBOL(param_ops_bint);

389
/* We break the rule and mangle the string. */
390 391 392 393
static int param_array(const char *name,
		       const char *val,
		       unsigned int min, unsigned int max,
		       void *elem, int elemsize,
394
		       int (*set)(const char *, const struct kernel_param *kp),
395
		       s16 level,
396
		       unsigned int *num)
Linus Torvalds's avatar
Linus Torvalds committed
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;
405
	kp.level = level;
Linus Torvalds's avatar
Linus Torvalds committed
406 407 408 409 410 411 412

	*num = 0;
	/* We expect a comma-separated list of values. */
	do {
		int len;

		if (*num == max) {
413
			pr_err("%s: can only take %i arguments\n", name, max);
Linus Torvalds's avatar
Linus Torvalds committed
414 415 416 417 418 419 420
			return -EINVAL;
		}
		len = strcspn(val, ",");

		/* nul-terminate and parse */
		save = val[len];
		((char *)val)[len] = '\0';
421
		BUG_ON(!mutex_is_locked(&param_lock));
Linus Torvalds's avatar
Linus Torvalds committed
422 423 424 425 426 427 428 429 430 431
		ret = set(val, &kp);

		if (ret != 0)
			return ret;
		kp.arg += elemsize;
		val += len+1;
		(*num)++;
	} while (save == ',');

	if (*num < min) {
432
		pr_err("%s: needs at least %i arguments\n", name, min);
Linus Torvalds's avatar
Linus Torvalds committed
433 434 435 436 437
		return -EINVAL;
	}
	return 0;
}

438
static int param_array_set(const char *val, const struct kernel_param *kp)
Linus Torvalds's avatar
Linus Torvalds committed
439
{
440
	const struct kparam_array *arr = kp->arr;
441
	unsigned int temp_num;
Linus Torvalds's avatar
Linus Torvalds committed
442 443

	return param_array(kp->name, val, 1, arr->max, arr->elem,
444
			   arr->elemsize, arr->ops->set, kp->level,
445
			   arr->num ?: &temp_num);
Linus Torvalds's avatar
Linus Torvalds committed
446 447
}

448
static int param_array_get(char *buffer, const struct kernel_param *kp)
Linus Torvalds's avatar
Linus Torvalds committed
449 450
{
	int i, off, ret;
451
	const struct kparam_array *arr = kp->arr;
Linus Torvalds's avatar
Linus Torvalds committed
452 453 454 455 456 457 458
	struct kernel_param p;

	p = *kp;
	for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
		if (i)
			buffer[off++] = ',';
		p.arg = arr->elem + arr->elemsize * i;
459
		BUG_ON(!mutex_is_locked(&param_lock));
460
		ret = arr->ops->get(buffer + off, &p);
Linus Torvalds's avatar
Linus Torvalds committed
461 462 463 464 465 466 467 468
		if (ret < 0)
			return ret;
		off += ret;
	}
	buffer[off] = '\0';
	return off;
}

469 470 471 472 473 474 475 476 477 478
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);
}

479 480 481
struct kernel_param_ops param_array_ops = {
	.set = param_array_set,
	.get = param_array_get,
482
	.free = param_array_free,
483 484 485 486
};
EXPORT_SYMBOL(param_array_ops);

int param_set_copystring(const char *val, const struct kernel_param *kp)
Linus Torvalds's avatar
Linus Torvalds committed
487
{
488
	const struct kparam_string *kps = kp->str;
Linus Torvalds's avatar
Linus Torvalds committed
489 490

	if (strlen(val)+1 > kps->maxlen) {
491
		pr_err("%s: string doesn't fit in %u chars.\n",
Linus Torvalds's avatar
Linus Torvalds committed
492 493 494 495 496 497
		       kp->name, kps->maxlen-1);
		return -ENOSPC;
	}
	strcpy(kps->string, val);
	return 0;
}
498
EXPORT_SYMBOL(param_set_copystring);
Linus Torvalds's avatar
Linus Torvalds committed
499

500
int param_get_string(char *buffer, const struct kernel_param *kp)
Linus Torvalds's avatar
Linus Torvalds committed
501
{
502
	const struct kparam_string *kps = kp->str;
Linus Torvalds's avatar
Linus Torvalds committed
503 504
	return strlcpy(buffer, kps->string, kps->maxlen);
}
505
EXPORT_SYMBOL(param_get_string);
Linus Torvalds's avatar
Linus Torvalds committed
506

507 508 509 510 511 512
struct kernel_param_ops param_ops_string = {
	.set = param_set_copystring,
	.get = param_get_string,
};
EXPORT_SYMBOL(param_ops_string);

Linus Torvalds's avatar
Linus Torvalds committed
513
/* sysfs output in /sys/modules/XYZ/parameters/ */
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)
Linus Torvalds's avatar
Linus Torvalds committed
516 517 518 519

struct param_attribute
{
	struct module_attribute mattr;
520
	const struct kernel_param *param;
Linus Torvalds's avatar
Linus Torvalds committed
521 522 523 524
};

struct module_param_attrs
{
525
	unsigned int num;
Linus Torvalds's avatar
Linus Torvalds committed
526 527 528 529
	struct attribute_group grp;
	struct param_attribute attrs[0];
};

530
#ifdef CONFIG_SYSFS
531
#define to_param_attr(n) container_of(n, struct param_attribute, mattr)
Linus Torvalds's avatar
Linus Torvalds committed
532 533

static ssize_t param_attr_show(struct module_attribute *mattr,
534
			       struct module_kobject *mk, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
535 536 537 538
{
	int count;
	struct param_attribute *attribute = to_param_attr(mattr);

539
	if (!attribute->param->ops->get)
Linus Torvalds's avatar
Linus Torvalds committed
540 541
		return -EPERM;

542
	mutex_lock(&param_lock);
543
	count = attribute->param->ops->get(buf, attribute->param);
544
	mutex_unlock(&param_lock);
Linus Torvalds's avatar
Linus Torvalds committed
545 546 547 548 549 550 551 552 553
	if (count > 0) {
		strcat(buf, "\n");
		++count;
	}
	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,
554
				struct module_kobject *km,
Linus Torvalds's avatar
Linus Torvalds committed
555 556 557 558 559
				const char *buf, size_t len)
{
 	int err;
	struct param_attribute *attribute = to_param_attr(mattr);

560
	if (!attribute->param->ops->set)
Linus Torvalds's avatar
Linus Torvalds committed
561 562
		return -EPERM;

563
	mutex_lock(&param_lock);
564
	param_check_unsafe(attribute->param);
565
	err = attribute->param->ops->set(buf, attribute->param);
566
	mutex_unlock(&param_lock);
Linus Torvalds's avatar
Linus Torvalds committed
567 568 569 570
	if (!err)
		return len;
	return err;
}
571
#endif
Linus Torvalds's avatar
Linus Torvalds committed
572 573 574 575 576 577 578

#ifdef CONFIG_MODULES
#define __modinit
#else
#define __modinit __init
#endif

579
#ifdef CONFIG_SYSFS
580 581 582 583 584 585 586 587 588 589 590 591
void __kernel_param_lock(void)
{
	mutex_lock(&param_lock);
}
EXPORT_SYMBOL(__kernel_param_lock);

void __kernel_param_unlock(void)
{
	mutex_unlock(&param_lock);
}
EXPORT_SYMBOL(__kernel_param_unlock);

Linus Torvalds's avatar
Linus Torvalds committed
592
/*
593 594 595 596
 * add_sysfs_param - add a parameter to sysfs
 * @mk: struct module_kobject
 * @kparam: the actual parameter definition to add to sysfs
 * @name: name of parameter
Linus Torvalds's avatar
Linus Torvalds committed
597
 *
598 599 600
 * 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.
Linus Torvalds's avatar
Linus Torvalds committed
601
 */
602
static __modinit int add_sysfs_param(struct module_kobject *mk,
603
				     const struct kernel_param *kp,
604
				     const char *name)
Linus Torvalds's avatar
Linus Torvalds committed
605
{
606 607 608
	struct module_param_attrs *new_mp;
	struct attribute **new_attrs;
	unsigned int i;
609 610 611 612 613

	/* We don't bother calling this with invisible parameters. */
	BUG_ON(!kp->perm);

	if (!mk->mp) {
614 615 616 617 618 619 620 621 622 623 624
		/* 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;
625
	}
Linus Torvalds's avatar
Linus Torvalds committed
626

627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
	/* 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;
643 644

	/* Tack new one on the end. */
645
	memset(&mk->mp->attrs[mk->mp->num], 0, sizeof(mk->mp->attrs[0]));
646 647 648
	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;
649 650 651
	/* 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;
652 653
	else
		mk->mp->attrs[mk->mp->num].mattr.store = NULL;
654 655 656
	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++;
657 658

	/* Fix up all the pointers, since krealloc can move us */
659 660 661
	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;
662 663
	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
664

665
#ifdef CONFIG_MODULES
666 667
static void free_module_param_attrs(struct module_kobject *mk)
{
668 669
	if (mk->mp)
		kfree(mk->mp->grp.attrs);
670 671
	kfree(mk->mp);
	mk->mp = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
672 673 674 675 676 677 678 679
}

/*
 * module_param_sysfs_setup - setup sysfs support for one module
 * @mod: module
 * @kparam: module parameters (array)
 * @num_params: number of module parameters
 *
680 681
 * Adds sysfs entries for module parameters under
 * /sys/module/[mod->name]/parameters/
Linus Torvalds's avatar
Linus Torvalds committed
682 683
 */
int module_param_sysfs_setup(struct module *mod,
684
			     const struct kernel_param *kparam,
Linus Torvalds's avatar
Linus Torvalds committed
685 686
			     unsigned int num_params)
{
687 688 689 690 691 692 693
	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);
694 695
		if (err) {
			free_module_param_attrs(&mod->mkobj);
696
			return err;
697
		}
698 699
		params = true;
	}
Linus Torvalds's avatar
Linus Torvalds committed
700

701 702
	if (!params)
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
703

704 705 706 707 708
	/* 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;
Linus Torvalds's avatar
Linus Torvalds committed
709 710 711 712 713 714 715 716 717 718 719
}

/*
 * 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)
{
720 721
	if (mod->mkobj.mp) {
		sysfs_remove_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp);
Linus Torvalds's avatar
Linus Torvalds committed
722 723
		/* We are positive that no one is using any param
		 * attrs at this point.  Deallocate immediately. */
724
		free_module_param_attrs(&mod->mkobj);
Linus Torvalds's avatar
Linus Torvalds committed
725 726 727 728
	}
}
#endif

729 730
void destroy_params(const struct kernel_param *params, unsigned num)
{
731 732 733 734 735
	unsigned int i;

	for (i = 0; i < num; i++)
		if (params[i].ops->free)
			params[i].ops->free(params[i].arg);
736 737
}

738
static struct module_kobject * __init locate_module_kobject(const char *name)
Linus Torvalds's avatar
Linus Torvalds committed
739 740
{
	struct module_kobject *mk;
741 742
	struct kobject *kobj;
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
743

744 745 746 747 748 749 750 751 752 753 754
	kobj = kset_find_obj(module_kset, name);
	if (kobj) {
		mk = to_module_kobject(kobj);
	} 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);
755 756 757 758
#ifdef CONFIG_MODULES
		if (!err)
			err = sysfs_create_file(&mk->kobj, &module_uevent.attr);
#endif
759 760
		if (err) {
			kobject_put(&mk->kobj);
761
			pr_crit("Adding module '%s' to sysfs failed (%d), the system may be unstable.\n",
762 763
				name, err);
			return NULL;
764
		}
765 766

		/* So that we hold reference in both cases. */
767
		kobject_get(&mk->kobj);
768
	}
769

770 771 772 773
	return mk;
}

static void __init kernel_add_sysfs_param(const char *name,
774
					  const struct kernel_param *kparam,
775 776 777 778 779 780 781 782 783 784 785 786 787
					  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);

788 789 790 791 792
	/* 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);
793
	kobject_uevent(&mk->kobj, KOBJ_ADD);
794
	kobject_put(&mk->kobj);
Linus Torvalds's avatar
Linus Torvalds committed
795 796 797
}

/*
Jean Delvare's avatar
Jean Delvare committed
798
 * param_sysfs_builtin - add sysfs parameters for built-in modules
Linus Torvalds's avatar
Linus Torvalds committed
799 800 801 802 803 804
 *
 * 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,
805
 * and for all who have the same, call kernel_add_sysfs_param.
Linus Torvalds's avatar
Linus Torvalds committed
806 807 808
 */
static void __init param_sysfs_builtin(void)
{
809
	const struct kernel_param *kp;
810 811
	unsigned int name_len;
	char modname[MODULE_NAME_LEN];
Linus Torvalds's avatar
Linus Torvalds committed
812

813
	for (kp = __start___param; kp < __stop___param; kp++) {
Linus Torvalds's avatar
Linus Torvalds committed
814 815
		char *dot;

816 817
		if (kp->perm == 0)
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
818

819
		dot = strchr(kp->name, '.');
Linus Torvalds's avatar
Linus Torvalds committed
820
		if (!dot) {
821 822 823 824 825 826
			/* This happens for core_param() */
			strcpy(modname, "kernel");
			name_len = 0;
		} else {
			name_len = dot - kp->name + 1;
			strlcpy(modname, kp->name, name_len);
Linus Torvalds's avatar
Linus Torvalds committed
827
		}
828
		kernel_add_sysfs_param(modname, kp, name_len);
Linus Torvalds's avatar
Linus Torvalds committed
829 830 831
	}
}

832
ssize_t __modver_version_show(struct module_attribute *mattr,
833
			      struct module_kobject *mk, char *buf)
834 835 836 837
{
	struct module_version_attribute *vattr =
		container_of(mattr, struct module_version_attribute, mattr);

838
	return scnprintf(buf, PAGE_SIZE, "%s\n", vattr->version);
839 840
}

841 842
extern const struct module_version_attribute *__start___modver[];
extern const struct module_version_attribute *__stop___modver[];
843 844 845

static void __init version_sysfs_builtin(void)
{
846
	const struct module_version_attribute **p;
847 848 849
	struct module_kobject *mk;
	int err;

850 851 852
	for (p = __start___modver; p < __stop___modver; p++) {
		const struct module_version_attribute *vattr = *p;

853 854 855 856 857 858 859 860
		mk = locate_module_kobject(vattr->module_name);
		if (mk) {
			err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr);
			kobject_uevent(&mk->kobj, KOBJ_ADD);
			kobject_put(&mk->kobj);
		}
	}
}
Linus Torvalds's avatar
Linus Torvalds committed
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875

/* module-related sysfs stuff */

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)
876
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
877

878
	ret = attribute->show(attribute, mk, buf);
Linus Torvalds's avatar
Linus Torvalds committed
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894

	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)
895
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
896

897
	ret = attribute->store(attribute, mk, buf, len);
Linus Torvalds's avatar
Linus Torvalds committed
898 899 900 901

	return ret;
}

902
static const struct sysfs_ops module_sysfs_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
903 904 905 906
	.show = module_attr_show,
	.store = module_attr_store,
};

Kay Sievers's avatar
Kay Sievers committed
907 908 909 910 911 912 913 914 915
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;
}

916
static const struct kset_uevent_ops module_uevent_ops = {
Kay Sievers's avatar
Kay Sievers committed
917 918 919
	.filter = uevent_filter,
};

920
struct kset *module_kset;
921
int module_sysfs_initialized;
Linus Torvalds's avatar
Linus Torvalds committed
922

923 924 925 926 927 928
static void module_kobj_release(struct kobject *kobj)
{
	struct module_kobject *mk = to_module_kobject(kobj);
	complete(mk->kobj_completion);
}

929
struct kobj_type module_ktype = {
930
	.release   =	module_kobj_release,
Linus Torvalds's avatar
Linus Torvalds committed
931 932 933 934 935 936 937 938
	.sysfs_ops =	&module_sysfs_ops,
};

/*
 * param_sysfs_init - wrapper for built-in params support
 */
static int __init param_sysfs_init(void)
{
939 940 941 942 943
	module_kset = kset_create_and_add("module", &module_uevent_ops, NULL);
	if (!module_kset) {
		printk(KERN_WARNING "%s (%d): error creating kset\n",
			__FILE__, __LINE__);
		return -ENOMEM;
944
	}
945
	module_sysfs_initialized = 1;
Linus Torvalds's avatar
Linus Torvalds committed
946

947
	version_sysfs_builtin();
Linus Torvalds's avatar
Linus Torvalds committed
948 949 950 951
	param_sysfs_builtin();

	return 0;
}
952
subsys_initcall(param_sysfs_init);
Linus Torvalds's avatar
Linus Torvalds committed
953

954
#endif /* CONFIG_SYSFS */