aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/netconsole.c
diff options
context:
space:
mode:
authorSatyam Sharma <satyam@infradead.org>2007-08-10 18:35:05 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-10-10 19:48:06 -0400
commit0bcc1816188e570bde1d56a208996660f2633ae0 (patch)
tree8104c0b0c54a93a510b4b9b50a45cbaabad245f4 /drivers/net/netconsole.c
parentb5427c27173e128dda1541bd9d3b05df79af5882 (diff)
[NET] netconsole: Support dynamic reconfiguration using configfs
Based upon initial work by Keiichi Kii <k-keiichi@bx.jp.nec.com>. This patch introduces support for dynamic reconfiguration (adding, removing and/or modifying parameters of netconsole targets at runtime) using a userspace interface exported via configfs. Documentation is also updated accordingly. Issues and brief design overview: (1) Kernel-initiated creation / destruction of kernel objects is not possible with configfs -- the lifetimes of the "config items" is managed exclusively from userspace. But netconsole must support boot/module params too, and these are parsed in kernel and hence netpolls must be setup from the kernel. Joel Becker suggested to separately manage the lifetimes of the two kinds of netconsole_target objects -- those created via configfs mkdir(2) from userspace and those specified from the boot/module option string. This adds complexity and some redundancy here and also means that boot/module param-created targets are not exposed through the configfs namespace (and hence cannot be updated / destroyed dynamically). However, this saves us from locking / refcounting complexities that would need to be introduced in configfs to support kernel-initiated item creation / destroy there. (2) In configfs, item creation takes place in the call chain of the mkdir(2) syscall in the driver subsystem. If we used an ioctl(2) to create / destroy objects from userspace, the special userspace program is able to fill out the structure to be passed into the ioctl and hence specify attributes such as local interface that are required at the time we set up the netpoll. For configfs, this information is not available at the time of mkdir(2). So, we keep all newly-created targets (via configfs) disabled by default. The user is expected to set various attributes appropriately (including the local network interface if required) and then write(2) "1" to the "enabled" attribute. Thus, netpoll_setup() is then called on the set parameters in the context of _this_ write(2) on the "enabled" attribute itself. This design enables the user to reconfigure existing netconsole targets at runtime to be attached to newly-come-up interfaces that may not have existed when netconsole was loaded or when the targets were actually created. All this effectively enables us to get rid of custom ioctls. (3) Ultra-paranoid configfs attribute show() and store() operations, with sanity and input range checking, using only safe string primitives, and compliant with the recommendations in Documentation/filesystems/sysfs.txt. (4) A new function netpoll_print_options() is created in the netpoll API, that just prints out the configured parameters for a netpoll structure. netpoll_parse_options() is modified to use that and it is also exported to be used from netconsole. Signed-off-by: Satyam Sharma <satyam@infradead.org> Acked-by: Keiichi Kii <k-keiichi@bx.jp.nec.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/netconsole.c')
-rw-r--r--drivers/net/netconsole.c605
1 files changed, 579 insertions, 26 deletions
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 458c4d674a9e..69ef1eb03bea 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -41,6 +41,8 @@
41#include <linux/moduleparam.h> 41#include <linux/moduleparam.h>
42#include <linux/string.h> 42#include <linux/string.h>
43#include <linux/netpoll.h> 43#include <linux/netpoll.h>
44#include <linux/inet.h>
45#include <linux/configfs.h>
44 46
45MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@selenic.com>"); 47MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@selenic.com>");
46MODULE_DESCRIPTION("Console driver for network interfaces"); 48MODULE_DESCRIPTION("Console driver for network interfaces");
@@ -71,20 +73,100 @@ static DEFINE_SPINLOCK(target_list_lock);
71/** 73/**
72 * struct netconsole_target - Represents a configured netconsole target. 74 * struct netconsole_target - Represents a configured netconsole target.
73 * @list: Links this target into the target_list. 75 * @list: Links this target into the target_list.
76 * @item: Links us into the configfs subsystem hierarchy.
77 * @enabled: On / off knob to enable / disable target.
78 * Visible from userspace (read-write).
79 * We maintain a strict 1:1 correspondence between this and
80 * whether the corresponding netpoll is active or inactive.
81 * Also, other parameters of a target may be modified at
82 * runtime only when it is disabled (enabled == 0).
74 * @np: The netpoll structure for this target. 83 * @np: The netpoll structure for this target.
84 * Contains the other userspace visible parameters:
85 * dev_name (read-write)
86 * local_port (read-write)
87 * remote_port (read-write)
88 * local_ip (read-write)
89 * remote_ip (read-write)
90 * local_mac (read-only)
91 * remote_mac (read-write)
75 */ 92 */
76struct netconsole_target { 93struct netconsole_target {
77 struct list_head list; 94 struct list_head list;
95#ifdef CONFIG_NETCONSOLE_DYNAMIC
96 struct config_item item;
97#endif
98 int enabled;
78 struct netpoll np; 99 struct netpoll np;
79}; 100};
80 101
81/* Allocate new target and setup netpoll for it */ 102#ifdef CONFIG_NETCONSOLE_DYNAMIC
82static struct netconsole_target *alloc_target(char *target_config) 103
104static struct configfs_subsystem netconsole_subsys;
105
106static int __init dynamic_netconsole_init(void)
107{
108 config_group_init(&netconsole_subsys.su_group);
109 mutex_init(&netconsole_subsys.su_mutex);
110 return configfs_register_subsystem(&netconsole_subsys);
111}
112
113static void __exit dynamic_netconsole_exit(void)
114{
115 configfs_unregister_subsystem(&netconsole_subsys);
116}
117
118/*
119 * Targets that were created by parsing the boot/module option string
120 * do not exist in the configfs hierarchy (and have NULL names) and will
121 * never go away, so make these a no-op for them.
122 */
123static void netconsole_target_get(struct netconsole_target *nt)
124{
125 if (config_item_name(&nt->item))
126 config_item_get(&nt->item);
127}
128
129static void netconsole_target_put(struct netconsole_target *nt)
130{
131 if (config_item_name(&nt->item))
132 config_item_put(&nt->item);
133}
134
135#else /* !CONFIG_NETCONSOLE_DYNAMIC */
136
137static int __init dynamic_netconsole_init(void)
138{
139 return 0;
140}
141
142static void __exit dynamic_netconsole_exit(void)
143{
144}
145
146/*
147 * No danger of targets going away from under us when dynamic
148 * reconfigurability is off.
149 */
150static void netconsole_target_get(struct netconsole_target *nt)
151{
152}
153
154static void netconsole_target_put(struct netconsole_target *nt)
155{
156}
157
158#endif /* CONFIG_NETCONSOLE_DYNAMIC */
159
160/* Allocate new target (from boot/module param) and setup netpoll for it */
161static struct netconsole_target *alloc_param_target(char *target_config)
83{ 162{
84 int err = -ENOMEM; 163 int err = -ENOMEM;
85 struct netconsole_target *nt; 164 struct netconsole_target *nt;
86 165
87 /* Allocate and initialize with defaults */ 166 /*
167 * Allocate and initialize with defaults.
168 * Note that these targets get their config_item fields zeroed-out.
169 */
88 nt = kzalloc(sizeof(*nt), GFP_KERNEL); 170 nt = kzalloc(sizeof(*nt), GFP_KERNEL);
89 if (!nt) { 171 if (!nt) {
90 printk(KERN_ERR "netconsole: failed to allocate memory\n"); 172 printk(KERN_ERR "netconsole: failed to allocate memory\n");
@@ -106,6 +188,8 @@ static struct netconsole_target *alloc_target(char *target_config)
106 if (err) 188 if (err)
107 goto fail; 189 goto fail;
108 190
191 nt->enabled = 1;
192
109 return nt; 193 return nt;
110 194
111fail: 195fail:
@@ -113,13 +197,469 @@ fail:
113 return ERR_PTR(err); 197 return ERR_PTR(err);
114} 198}
115 199
116/* Cleanup netpoll for given target and free it */ 200/* Cleanup netpoll for given target (from boot/module param) and free it */
117static void free_target(struct netconsole_target *nt) 201static void free_param_target(struct netconsole_target *nt)
118{ 202{
119 netpoll_cleanup(&nt->np); 203 netpoll_cleanup(&nt->np);
120 kfree(nt); 204 kfree(nt);
121} 205}
122 206
207#ifdef CONFIG_NETCONSOLE_DYNAMIC
208
209/*
210 * Our subsystem hierarchy is:
211 *
212 * /sys/kernel/config/netconsole/
213 * |
214 * <target>/
215 * | enabled
216 * | dev_name
217 * | local_port
218 * | remote_port
219 * | local_ip
220 * | remote_ip
221 * | local_mac
222 * | remote_mac
223 * |
224 * <target>/...
225 */
226
227struct netconsole_target_attr {
228 struct configfs_attribute attr;
229 ssize_t (*show)(struct netconsole_target *nt,
230 char *buf);
231 ssize_t (*store)(struct netconsole_target *nt,
232 const char *buf,
233 size_t count);
234};
235
236static struct netconsole_target *to_target(struct config_item *item)
237{
238 return item ?
239 container_of(item, struct netconsole_target, item) :
240 NULL;
241}
242
243/*
244 * Wrapper over simple_strtol (base 10) with sanity and range checking.
245 * We return (signed) long only because we may want to return errors.
246 * Do not use this to convert numbers that are allowed to be negative.
247 */
248static long strtol10_check_range(const char *cp, long min, long max)
249{
250 long ret;
251 char *p = (char *) cp;
252
253 WARN_ON(min < 0);
254 WARN_ON(max < min);
255
256 ret = simple_strtol(p, &p, 10);
257
258 if (*p && (*p != '\n')) {
259 printk(KERN_ERR "netconsole: invalid input\n");
260 return -EINVAL;
261 }
262 if ((ret < min) || (ret > max)) {
263 printk(KERN_ERR "netconsole: input %ld must be between "
264 "%ld and %ld\n", ret, min, max);
265 return -EINVAL;
266 }
267
268 return ret;
269}
270
271/*
272 * Attribute operations for netconsole_target.
273 */
274
275static ssize_t show_enabled(struct netconsole_target *nt, char *buf)
276{
277 return snprintf(buf, PAGE_SIZE, "%d\n", nt->enabled);
278}
279
280static ssize_t show_dev_name(struct netconsole_target *nt, char *buf)
281{
282 return snprintf(buf, PAGE_SIZE, "%s\n", nt->np.dev_name);
283}
284
285static ssize_t show_local_port(struct netconsole_target *nt, char *buf)
286{
287 return snprintf(buf, PAGE_SIZE, "%d\n", nt->np.local_port);
288}
289
290static ssize_t show_remote_port(struct netconsole_target *nt, char *buf)
291{
292 return snprintf(buf, PAGE_SIZE, "%d\n", nt->np.remote_port);
293}
294
295static ssize_t show_local_ip(struct netconsole_target *nt, char *buf)
296{
297 return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n",
298 HIPQUAD(nt->np.local_ip));
299}
300
301static ssize_t show_remote_ip(struct netconsole_target *nt, char *buf)
302{
303 return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n",
304 HIPQUAD(nt->np.remote_ip));
305}
306
307static ssize_t show_local_mac(struct netconsole_target *nt, char *buf)
308{
309 return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
310 nt->np.local_mac[0], nt->np.local_mac[1],
311 nt->np.local_mac[2], nt->np.local_mac[3],
312 nt->np.local_mac[4], nt->np.local_mac[5]);
313}
314
315static ssize_t show_remote_mac(struct netconsole_target *nt, char *buf)
316{
317 return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
318 nt->np.remote_mac[0], nt->np.remote_mac[1],
319 nt->np.remote_mac[2], nt->np.remote_mac[3],
320 nt->np.remote_mac[4], nt->np.remote_mac[5]);
321}
322
323/*
324 * This one is special -- targets created through the configfs interface
325 * are not enabled (and the corresponding netpoll activated) by default.
326 * The user is expected to set the desired parameters first (which
327 * would enable him to dynamically add new netpoll targets for new
328 * network interfaces as and when they come up).
329 */
330static ssize_t store_enabled(struct netconsole_target *nt,
331 const char *buf,
332 size_t count)
333{
334 int err;
335 long enabled;
336
337 enabled = strtol10_check_range(buf, 0, 1);
338 if (enabled < 0)
339 return enabled;
340
341 if (enabled) { /* 1 */
342
343 /*
344 * Skip netpoll_parse_options() -- all the attributes are
345 * already configured via configfs. Just print them out.
346 */
347 netpoll_print_options(&nt->np);
348
349 err = netpoll_setup(&nt->np);
350 if (err)
351 return err;
352
353 printk(KERN_INFO "netconsole: network logging started\n");
354
355 } else { /* 0 */
356 netpoll_cleanup(&nt->np);
357 }
358
359 nt->enabled = enabled;
360
361 return strnlen(buf, count);
362}
363
364static ssize_t store_dev_name(struct netconsole_target *nt,
365 const char *buf,
366 size_t count)
367{
368 size_t len;
369
370 if (nt->enabled) {
371 printk(KERN_ERR "netconsole: target (%s) is enabled, "
372 "disable to update parameters\n",
373 config_item_name(&nt->item));
374 return -EINVAL;
375 }
376
377 strlcpy(nt->np.dev_name, buf, IFNAMSIZ);
378
379 /* Get rid of possible trailing newline from echo(1) */
380 len = strnlen(nt->np.dev_name, IFNAMSIZ);
381 if (nt->np.dev_name[len - 1] == '\n')
382 nt->np.dev_name[len - 1] = '\0';
383
384 return strnlen(buf, count);
385}
386
387static ssize_t store_local_port(struct netconsole_target *nt,
388 const char *buf,
389 size_t count)
390{
391 long local_port;
392#define __U16_MAX ((__u16) ~0U)
393
394 if (nt->enabled) {
395 printk(KERN_ERR "netconsole: target (%s) is enabled, "
396 "disable to update parameters\n",
397 config_item_name(&nt->item));
398 return -EINVAL;
399 }
400
401 local_port = strtol10_check_range(buf, 0, __U16_MAX);
402 if (local_port < 0)
403 return local_port;
404
405 nt->np.local_port = local_port;
406
407 return strnlen(buf, count);
408}
409
410static ssize_t store_remote_port(struct netconsole_target *nt,
411 const char *buf,
412 size_t count)
413{
414 long remote_port;
415#define __U16_MAX ((__u16) ~0U)
416
417 if (nt->enabled) {
418 printk(KERN_ERR "netconsole: target (%s) is enabled, "
419 "disable to update parameters\n",
420 config_item_name(&nt->item));
421 return -EINVAL;
422 }
423
424 remote_port = strtol10_check_range(buf, 0, __U16_MAX);
425 if (remote_port < 0)
426 return remote_port;
427
428 nt->np.remote_port = remote_port;
429
430 return strnlen(buf, count);
431}
432
433static ssize_t store_local_ip(struct netconsole_target *nt,
434 const char *buf,
435 size_t count)
436{
437 if (nt->enabled) {
438 printk(KERN_ERR "netconsole: target (%s) is enabled, "
439 "disable to update parameters\n",
440 config_item_name(&nt->item));
441 return -EINVAL;
442 }
443
444 nt->np.local_ip = ntohl(in_aton(buf));
445
446 return strnlen(buf, count);
447}
448
449static ssize_t store_remote_ip(struct netconsole_target *nt,
450 const char *buf,
451 size_t count)
452{
453 if (nt->enabled) {
454 printk(KERN_ERR "netconsole: target (%s) is enabled, "
455 "disable to update parameters\n",
456 config_item_name(&nt->item));
457 return -EINVAL;
458 }
459
460 nt->np.remote_ip = ntohl(in_aton(buf));
461
462 return strnlen(buf, count);
463}
464
465static ssize_t store_remote_mac(struct netconsole_target *nt,
466 const char *buf,
467 size_t count)
468{
469 u8 remote_mac[ETH_ALEN];
470 char *p = (char *) buf;
471 int i;
472
473 if (nt->enabled) {
474 printk(KERN_ERR "netconsole: target (%s) is enabled, "
475 "disable to update parameters\n",
476 config_item_name(&nt->item));
477 return -EINVAL;
478 }
479
480 for (i = 0; i < ETH_ALEN - 1; i++) {
481 remote_mac[i] = simple_strtoul(p, &p, 16);
482 if (*p != ':')
483 goto invalid;
484 p++;
485 }
486 remote_mac[ETH_ALEN - 1] = simple_strtoul(p, &p, 16);
487 if (*p && (*p != '\n'))
488 goto invalid;
489
490 memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN);
491
492 return strnlen(buf, count);
493
494invalid:
495 printk(KERN_ERR "netconsole: invalid input\n");
496 return -EINVAL;
497}
498
499/*
500 * Attribute definitions for netconsole_target.
501 */
502
503#define NETCONSOLE_TARGET_ATTR_RO(_name) \
504static struct netconsole_target_attr netconsole_target_##_name = \
505 __CONFIGFS_ATTR(_name, S_IRUGO, show_##_name, NULL)
506
507#define NETCONSOLE_TARGET_ATTR_RW(_name) \
508static struct netconsole_target_attr netconsole_target_##_name = \
509 __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, show_##_name, store_##_name)
510
511NETCONSOLE_TARGET_ATTR_RW(enabled);
512NETCONSOLE_TARGET_ATTR_RW(dev_name);
513NETCONSOLE_TARGET_ATTR_RW(local_port);
514NETCONSOLE_TARGET_ATTR_RW(remote_port);
515NETCONSOLE_TARGET_ATTR_RW(local_ip);
516NETCONSOLE_TARGET_ATTR_RW(remote_ip);
517NETCONSOLE_TARGET_ATTR_RO(local_mac);
518NETCONSOLE_TARGET_ATTR_RW(remote_mac);
519
520static struct configfs_attribute *netconsole_target_attrs[] = {
521 &netconsole_target_enabled.attr,
522 &netconsole_target_dev_name.attr,
523 &netconsole_target_local_port.attr,
524 &netconsole_target_remote_port.attr,
525 &netconsole_target_local_ip.attr,
526 &netconsole_target_remote_ip.attr,
527 &netconsole_target_local_mac.attr,
528 &netconsole_target_remote_mac.attr,
529 NULL,
530};
531
532/*
533 * Item operations and type for netconsole_target.
534 */
535
536static void netconsole_target_release(struct config_item *item)
537{
538 kfree(to_target(item));
539}
540
541static ssize_t netconsole_target_attr_show(struct config_item *item,
542 struct configfs_attribute *attr,
543 char *buf)
544{
545 ssize_t ret = -EINVAL;
546 struct netconsole_target *nt = to_target(item);
547 struct netconsole_target_attr *na =
548 container_of(attr, struct netconsole_target_attr, attr);
549
550 if (na->show)
551 ret = na->show(nt, buf);
552
553 return ret;
554}
555
556static ssize_t netconsole_target_attr_store(struct config_item *item,
557 struct configfs_attribute *attr,
558 const char *buf,
559 size_t count)
560{
561 ssize_t ret = -EINVAL;
562 struct netconsole_target *nt = to_target(item);
563 struct netconsole_target_attr *na =
564 container_of(attr, struct netconsole_target_attr, attr);
565
566 if (na->store)
567 ret = na->store(nt, buf, count);
568
569 return ret;
570}
571
572static struct configfs_item_operations netconsole_target_item_ops = {
573 .release = netconsole_target_release,
574 .show_attribute = netconsole_target_attr_show,
575 .store_attribute = netconsole_target_attr_store,
576};
577
578static struct config_item_type netconsole_target_type = {
579 .ct_attrs = netconsole_target_attrs,
580 .ct_item_ops = &netconsole_target_item_ops,
581 .ct_owner = THIS_MODULE,
582};
583
584/*
585 * Group operations and type for netconsole_subsys.
586 */
587
588static struct config_item *make_netconsole_target(struct config_group *group,
589 const char *name)
590{
591 unsigned long flags;
592 struct netconsole_target *nt;
593
594 /*
595 * Allocate and initialize with defaults.
596 * Target is disabled at creation (enabled == 0).
597 */
598 nt = kzalloc(sizeof(*nt), GFP_KERNEL);
599 if (!nt) {
600 printk(KERN_ERR "netconsole: failed to allocate memory\n");
601 return NULL;
602 }
603
604 nt->np.name = "netconsole";
605 strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
606 nt->np.local_port = 6665;
607 nt->np.remote_port = 6666;
608 memset(nt->np.remote_mac, 0xff, ETH_ALEN);
609
610 /* Initialize the config_item member */
611 config_item_init_type_name(&nt->item, name, &netconsole_target_type);
612
613 /* Adding, but it is disabled */
614 spin_lock_irqsave(&target_list_lock, flags);
615 list_add(&nt->list, &target_list);
616 spin_unlock_irqrestore(&target_list_lock, flags);
617
618 return &nt->item;
619}
620
621static void drop_netconsole_target(struct config_group *group,
622 struct config_item *item)
623{
624 unsigned long flags;
625 struct netconsole_target *nt = to_target(item);
626
627 spin_lock_irqsave(&target_list_lock, flags);
628 list_del(&nt->list);
629 spin_unlock_irqrestore(&target_list_lock, flags);
630
631 /*
632 * The target may have never been enabled, or was manually disabled
633 * before being removed so netpoll may have already been cleaned up.
634 */
635 if (nt->enabled)
636 netpoll_cleanup(&nt->np);
637
638 config_item_put(&nt->item);
639}
640
641static struct configfs_group_operations netconsole_subsys_group_ops = {
642 .make_item = make_netconsole_target,
643 .drop_item = drop_netconsole_target,
644};
645
646static struct config_item_type netconsole_subsys_type = {
647 .ct_group_ops = &netconsole_subsys_group_ops,
648 .ct_owner = THIS_MODULE,
649};
650
651/* The netconsole configfs subsystem */
652static struct configfs_subsystem netconsole_subsys = {
653 .su_group = {
654 .cg_item = {
655 .ci_namebuf = "netconsole",
656 .ci_type = &netconsole_subsys_type,
657 },
658 },
659};
660
661#endif /* CONFIG_NETCONSOLE_DYNAMIC */
662
123/* Handle network interface device notifications */ 663/* Handle network interface device notifications */
124static int netconsole_netdev_event(struct notifier_block *this, 664static int netconsole_netdev_event(struct notifier_block *this,
125 unsigned long event, 665 unsigned long event,
@@ -134,6 +674,7 @@ static int netconsole_netdev_event(struct notifier_block *this,
134 674
135 spin_lock_irqsave(&target_list_lock, flags); 675 spin_lock_irqsave(&target_list_lock, flags);
136 list_for_each_entry(nt, &target_list, list) { 676 list_for_each_entry(nt, &target_list, list) {
677 netconsole_target_get(nt);
137 if (nt->np.dev == dev) { 678 if (nt->np.dev == dev) {
138 switch (event) { 679 switch (event) {
139 case NETDEV_CHANGEADDR: 680 case NETDEV_CHANGEADDR:
@@ -145,6 +686,7 @@ static int netconsole_netdev_event(struct notifier_block *this,
145 break; 686 break;
146 } 687 }
147 } 688 }
689 netconsole_target_put(nt);
148 } 690 }
149 spin_unlock_irqrestore(&target_list_lock, flags); 691 spin_unlock_irqrestore(&target_list_lock, flags);
150 692
@@ -169,7 +711,8 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
169 711
170 spin_lock_irqsave(&target_list_lock, flags); 712 spin_lock_irqsave(&target_list_lock, flags);
171 list_for_each_entry(nt, &target_list, list) { 713 list_for_each_entry(nt, &target_list, list) {
172 if (netif_running(nt->np.dev)) { 714 netconsole_target_get(nt);
715 if (nt->enabled && netif_running(nt->np.dev)) {
173 /* 716 /*
174 * We nest this inside the for-each-target loop above 717 * We nest this inside the for-each-target loop above
175 * so that we're able to get as much logging out to 718 * so that we're able to get as much logging out to
@@ -184,6 +727,7 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
184 left -= frag; 727 left -= frag;
185 } 728 }
186 } 729 }
730 netconsole_target_put(nt);
187 } 731 }
188 spin_unlock_irqrestore(&target_list_lock, flags); 732 spin_unlock_irqrestore(&target_list_lock, flags);
189} 733}
@@ -196,48 +740,52 @@ static struct console netconsole = {
196 740
197static int __init init_netconsole(void) 741static int __init init_netconsole(void)
198{ 742{
199 int err = 0; 743 int err;
200 struct netconsole_target *nt, *tmp; 744 struct netconsole_target *nt, *tmp;
201 unsigned long flags; 745 unsigned long flags;
202 char *target_config; 746 char *target_config;
203 char *input = config; 747 char *input = config;
204 748
205 if (!strnlen(input, MAX_PARAM_LENGTH)) { 749 if (strnlen(input, MAX_PARAM_LENGTH)) {
206 printk(KERN_INFO "netconsole: not configured, aborting\n"); 750 while ((target_config = strsep(&input, ";"))) {
207 goto out; 751 nt = alloc_param_target(target_config);
208 } 752 if (IS_ERR(nt)) {
209 753 err = PTR_ERR(nt);
210 while ((target_config = strsep(&input, ";"))) { 754 goto fail;
211 nt = alloc_target(target_config); 755 }
212 if (IS_ERR(nt)) { 756 spin_lock_irqsave(&target_list_lock, flags);
213 err = PTR_ERR(nt); 757 list_add(&nt->list, &target_list);
214 goto fail; 758 spin_unlock_irqrestore(&target_list_lock, flags);
215 } 759 }
216 spin_lock_irqsave(&target_list_lock, flags);
217 list_add(&nt->list, &target_list);
218 spin_unlock_irqrestore(&target_list_lock, flags);
219 } 760 }
220 761
221 err = register_netdevice_notifier(&netconsole_netdev_notifier); 762 err = register_netdevice_notifier(&netconsole_netdev_notifier);
222 if (err) 763 if (err)
223 goto fail; 764 goto fail;
224 765
766 err = dynamic_netconsole_init();
767 if (err)
768 goto undonotifier;
769
225 register_console(&netconsole); 770 register_console(&netconsole);
226 printk(KERN_INFO "netconsole: network logging started\n"); 771 printk(KERN_INFO "netconsole: network logging started\n");
227 772
228out:
229 return err; 773 return err;
230 774
775undonotifier:
776 unregister_netdevice_notifier(&netconsole_netdev_notifier);
777
231fail: 778fail:
232 printk(KERN_ERR "netconsole: cleaning up\n"); 779 printk(KERN_ERR "netconsole: cleaning up\n");
233 780
234 /* 781 /*
235 * Remove all targets and destroy them. Skipping the list 782 * Remove all targets and destroy them (only targets created
783 * from the boot/module option exist here). Skipping the list
236 * lock is safe here, and netpoll_cleanup() will sleep. 784 * lock is safe here, and netpoll_cleanup() will sleep.
237 */ 785 */
238 list_for_each_entry_safe(nt, tmp, &target_list, list) { 786 list_for_each_entry_safe(nt, tmp, &target_list, list) {
239 list_del(&nt->list); 787 list_del(&nt->list);
240 free_target(nt); 788 free_param_target(nt);
241 } 789 }
242 790
243 return err; 791 return err;
@@ -248,15 +796,20 @@ static void __exit cleanup_netconsole(void)
248 struct netconsole_target *nt, *tmp; 796 struct netconsole_target *nt, *tmp;
249 797
250 unregister_console(&netconsole); 798 unregister_console(&netconsole);
799 dynamic_netconsole_exit();
251 unregister_netdevice_notifier(&netconsole_netdev_notifier); 800 unregister_netdevice_notifier(&netconsole_netdev_notifier);
252 801
253 /* 802 /*
254 * Remove all targets and destroy them. Skipping the list 803 * Targets created via configfs pin references on our module
255 * lock is safe here, and netpoll_cleanup() will sleep. 804 * and would first be rmdir(2)'ed from userspace. We reach
805 * here only when they are already destroyed, and only those
806 * created from the boot/module option are left, so remove and
807 * destroy them. Skipping the list lock is safe here, and
808 * netpoll_cleanup() will sleep.
256 */ 809 */
257 list_for_each_entry_safe(nt, tmp, &target_list, list) { 810 list_for_each_entry_safe(nt, tmp, &target_list, list) {
258 list_del(&nt->list); 811 list_del(&nt->list);
259 free_target(nt); 812 free_param_target(nt);
260 } 813 }
261} 814}
262 815