at v3.8 21 kB view raw
1/* 2 * linux/drivers/net/netconsole.c 3 * 4 * Copyright (C) 2001 Ingo Molnar <mingo@redhat.com> 5 * 6 * This file contains the implementation of an IRQ-safe, crash-safe 7 * kernel console implementation that outputs kernel messages to the 8 * network. 9 * 10 * Modification history: 11 * 12 * 2001-09-17 started by Ingo Molnar. 13 * 2003-08-11 2.6 port by Matt Mackall 14 * simplified options 15 * generic card hooks 16 * works non-modular 17 * 2003-09-07 rewritten with netpoll api 18 */ 19 20/**************************************************************** 21 * This program is free software; you can redistribute it and/or modify 22 * it under the terms of the GNU General Public License as published by 23 * the Free Software Foundation; either version 2, or (at your option) 24 * any later version. 25 * 26 * This program is distributed in the hope that it will be useful, 27 * but WITHOUT ANY WARRANTY; without even the implied warranty of 28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 * GNU General Public License for more details. 30 * 31 * You should have received a copy of the GNU General Public License 32 * along with this program; if not, write to the Free Software 33 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 34 * 35 ****************************************************************/ 36 37#include <linux/mm.h> 38#include <linux/init.h> 39#include <linux/module.h> 40#include <linux/slab.h> 41#include <linux/console.h> 42#include <linux/moduleparam.h> 43#include <linux/string.h> 44#include <linux/netpoll.h> 45#include <linux/inet.h> 46#include <linux/configfs.h> 47 48MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@selenic.com>"); 49MODULE_DESCRIPTION("Console driver for network interfaces"); 50MODULE_LICENSE("GPL"); 51 52#define MAX_PARAM_LENGTH 256 53#define MAX_PRINT_CHUNK 1000 54 55static char config[MAX_PARAM_LENGTH]; 56module_param_string(netconsole, config, MAX_PARAM_LENGTH, 0); 57MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]"); 58 59static bool oops_only = false; 60module_param(oops_only, bool, 0600); 61MODULE_PARM_DESC(oops_only, "Only log oops messages"); 62 63#ifndef MODULE 64static int __init option_setup(char *opt) 65{ 66 strlcpy(config, opt, MAX_PARAM_LENGTH); 67 return 1; 68} 69__setup("netconsole=", option_setup); 70#endif /* MODULE */ 71 72/* Linked list of all configured targets */ 73static LIST_HEAD(target_list); 74 75/* This needs to be a spinlock because write_msg() cannot sleep */ 76static DEFINE_SPINLOCK(target_list_lock); 77 78/** 79 * struct netconsole_target - Represents a configured netconsole target. 80 * @list: Links this target into the target_list. 81 * @item: Links us into the configfs subsystem hierarchy. 82 * @enabled: On / off knob to enable / disable target. 83 * Visible from userspace (read-write). 84 * We maintain a strict 1:1 correspondence between this and 85 * whether the corresponding netpoll is active or inactive. 86 * Also, other parameters of a target may be modified at 87 * runtime only when it is disabled (enabled == 0). 88 * @np: The netpoll structure for this target. 89 * Contains the other userspace visible parameters: 90 * dev_name (read-write) 91 * local_port (read-write) 92 * remote_port (read-write) 93 * local_ip (read-write) 94 * remote_ip (read-write) 95 * local_mac (read-only) 96 * remote_mac (read-write) 97 */ 98struct netconsole_target { 99 struct list_head list; 100#ifdef CONFIG_NETCONSOLE_DYNAMIC 101 struct config_item item; 102#endif 103 int enabled; 104 struct netpoll np; 105}; 106 107#ifdef CONFIG_NETCONSOLE_DYNAMIC 108 109static struct configfs_subsystem netconsole_subsys; 110 111static int __init dynamic_netconsole_init(void) 112{ 113 config_group_init(&netconsole_subsys.su_group); 114 mutex_init(&netconsole_subsys.su_mutex); 115 return configfs_register_subsystem(&netconsole_subsys); 116} 117 118static void __exit dynamic_netconsole_exit(void) 119{ 120 configfs_unregister_subsystem(&netconsole_subsys); 121} 122 123/* 124 * Targets that were created by parsing the boot/module option string 125 * do not exist in the configfs hierarchy (and have NULL names) and will 126 * never go away, so make these a no-op for them. 127 */ 128static void netconsole_target_get(struct netconsole_target *nt) 129{ 130 if (config_item_name(&nt->item)) 131 config_item_get(&nt->item); 132} 133 134static void netconsole_target_put(struct netconsole_target *nt) 135{ 136 if (config_item_name(&nt->item)) 137 config_item_put(&nt->item); 138} 139 140#else /* !CONFIG_NETCONSOLE_DYNAMIC */ 141 142static int __init dynamic_netconsole_init(void) 143{ 144 return 0; 145} 146 147static void __exit dynamic_netconsole_exit(void) 148{ 149} 150 151/* 152 * No danger of targets going away from under us when dynamic 153 * reconfigurability is off. 154 */ 155static void netconsole_target_get(struct netconsole_target *nt) 156{ 157} 158 159static void netconsole_target_put(struct netconsole_target *nt) 160{ 161} 162 163#endif /* CONFIG_NETCONSOLE_DYNAMIC */ 164 165/* Allocate new target (from boot/module param) and setup netpoll for it */ 166static struct netconsole_target *alloc_param_target(char *target_config) 167{ 168 int err = -ENOMEM; 169 struct netconsole_target *nt; 170 171 /* 172 * Allocate and initialize with defaults. 173 * Note that these targets get their config_item fields zeroed-out. 174 */ 175 nt = kzalloc(sizeof(*nt), GFP_KERNEL); 176 if (!nt) 177 goto fail; 178 179 nt->np.name = "netconsole"; 180 strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ); 181 nt->np.local_port = 6665; 182 nt->np.remote_port = 6666; 183 memset(nt->np.remote_mac, 0xff, ETH_ALEN); 184 185 /* Parse parameters and setup netpoll */ 186 err = netpoll_parse_options(&nt->np, target_config); 187 if (err) 188 goto fail; 189 190 err = netpoll_setup(&nt->np); 191 if (err) 192 goto fail; 193 194 nt->enabled = 1; 195 196 return nt; 197 198fail: 199 kfree(nt); 200 return ERR_PTR(err); 201} 202 203/* Cleanup netpoll for given target (from boot/module param) and free it */ 204static void free_param_target(struct netconsole_target *nt) 205{ 206 netpoll_cleanup(&nt->np); 207 kfree(nt); 208} 209 210#ifdef CONFIG_NETCONSOLE_DYNAMIC 211 212/* 213 * Our subsystem hierarchy is: 214 * 215 * /sys/kernel/config/netconsole/ 216 * | 217 * <target>/ 218 * | enabled 219 * | dev_name 220 * | local_port 221 * | remote_port 222 * | local_ip 223 * | remote_ip 224 * | local_mac 225 * | remote_mac 226 * | 227 * <target>/... 228 */ 229 230struct netconsole_target_attr { 231 struct configfs_attribute attr; 232 ssize_t (*show)(struct netconsole_target *nt, 233 char *buf); 234 ssize_t (*store)(struct netconsole_target *nt, 235 const char *buf, 236 size_t count); 237}; 238 239static struct netconsole_target *to_target(struct config_item *item) 240{ 241 return item ? 242 container_of(item, struct netconsole_target, item) : 243 NULL; 244} 245 246/* 247 * Attribute operations for netconsole_target. 248 */ 249 250static ssize_t show_enabled(struct netconsole_target *nt, char *buf) 251{ 252 return snprintf(buf, PAGE_SIZE, "%d\n", nt->enabled); 253} 254 255static ssize_t show_dev_name(struct netconsole_target *nt, char *buf) 256{ 257 return snprintf(buf, PAGE_SIZE, "%s\n", nt->np.dev_name); 258} 259 260static ssize_t show_local_port(struct netconsole_target *nt, char *buf) 261{ 262 return snprintf(buf, PAGE_SIZE, "%d\n", nt->np.local_port); 263} 264 265static ssize_t show_remote_port(struct netconsole_target *nt, char *buf) 266{ 267 return snprintf(buf, PAGE_SIZE, "%d\n", nt->np.remote_port); 268} 269 270static ssize_t show_local_ip(struct netconsole_target *nt, char *buf) 271{ 272 return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip); 273} 274 275static ssize_t show_remote_ip(struct netconsole_target *nt, char *buf) 276{ 277 return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip); 278} 279 280static ssize_t show_local_mac(struct netconsole_target *nt, char *buf) 281{ 282 struct net_device *dev = nt->np.dev; 283 static const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 284 285 return snprintf(buf, PAGE_SIZE, "%pM\n", dev ? dev->dev_addr : bcast); 286} 287 288static ssize_t show_remote_mac(struct netconsole_target *nt, char *buf) 289{ 290 return snprintf(buf, PAGE_SIZE, "%pM\n", nt->np.remote_mac); 291} 292 293/* 294 * This one is special -- targets created through the configfs interface 295 * are not enabled (and the corresponding netpoll activated) by default. 296 * The user is expected to set the desired parameters first (which 297 * would enable him to dynamically add new netpoll targets for new 298 * network interfaces as and when they come up). 299 */ 300static ssize_t store_enabled(struct netconsole_target *nt, 301 const char *buf, 302 size_t count) 303{ 304 int enabled; 305 int err; 306 307 err = kstrtoint(buf, 10, &enabled); 308 if (err < 0) 309 return err; 310 if (enabled < 0 || enabled > 1) 311 return -EINVAL; 312 if (enabled == nt->enabled) { 313 printk(KERN_INFO "netconsole: network logging has already %s\n", 314 nt->enabled ? "started" : "stopped"); 315 return -EINVAL; 316 } 317 318 if (enabled) { /* 1 */ 319 320 /* 321 * Skip netpoll_parse_options() -- all the attributes are 322 * already configured via configfs. Just print them out. 323 */ 324 netpoll_print_options(&nt->np); 325 326 err = netpoll_setup(&nt->np); 327 if (err) 328 return err; 329 330 printk(KERN_INFO "netconsole: network logging started\n"); 331 332 } else { /* 0 */ 333 netpoll_cleanup(&nt->np); 334 } 335 336 nt->enabled = enabled; 337 338 return strnlen(buf, count); 339} 340 341static ssize_t store_dev_name(struct netconsole_target *nt, 342 const char *buf, 343 size_t count) 344{ 345 size_t len; 346 347 if (nt->enabled) { 348 printk(KERN_ERR "netconsole: target (%s) is enabled, " 349 "disable to update parameters\n", 350 config_item_name(&nt->item)); 351 return -EINVAL; 352 } 353 354 strlcpy(nt->np.dev_name, buf, IFNAMSIZ); 355 356 /* Get rid of possible trailing newline from echo(1) */ 357 len = strnlen(nt->np.dev_name, IFNAMSIZ); 358 if (nt->np.dev_name[len - 1] == '\n') 359 nt->np.dev_name[len - 1] = '\0'; 360 361 return strnlen(buf, count); 362} 363 364static ssize_t store_local_port(struct netconsole_target *nt, 365 const char *buf, 366 size_t count) 367{ 368 int rv; 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 rv = kstrtou16(buf, 10, &nt->np.local_port); 378 if (rv < 0) 379 return rv; 380 return strnlen(buf, count); 381} 382 383static ssize_t store_remote_port(struct netconsole_target *nt, 384 const char *buf, 385 size_t count) 386{ 387 int rv; 388 389 if (nt->enabled) { 390 printk(KERN_ERR "netconsole: target (%s) is enabled, " 391 "disable to update parameters\n", 392 config_item_name(&nt->item)); 393 return -EINVAL; 394 } 395 396 rv = kstrtou16(buf, 10, &nt->np.remote_port); 397 if (rv < 0) 398 return rv; 399 return strnlen(buf, count); 400} 401 402static ssize_t store_local_ip(struct netconsole_target *nt, 403 const char *buf, 404 size_t count) 405{ 406 if (nt->enabled) { 407 printk(KERN_ERR "netconsole: target (%s) is enabled, " 408 "disable to update parameters\n", 409 config_item_name(&nt->item)); 410 return -EINVAL; 411 } 412 413 nt->np.local_ip = in_aton(buf); 414 415 return strnlen(buf, count); 416} 417 418static ssize_t store_remote_ip(struct netconsole_target *nt, 419 const char *buf, 420 size_t count) 421{ 422 if (nt->enabled) { 423 printk(KERN_ERR "netconsole: target (%s) is enabled, " 424 "disable to update parameters\n", 425 config_item_name(&nt->item)); 426 return -EINVAL; 427 } 428 429 nt->np.remote_ip = in_aton(buf); 430 431 return strnlen(buf, count); 432} 433 434static ssize_t store_remote_mac(struct netconsole_target *nt, 435 const char *buf, 436 size_t count) 437{ 438 u8 remote_mac[ETH_ALEN]; 439 440 if (nt->enabled) { 441 printk(KERN_ERR "netconsole: target (%s) is enabled, " 442 "disable to update parameters\n", 443 config_item_name(&nt->item)); 444 return -EINVAL; 445 } 446 447 if (!mac_pton(buf, remote_mac)) 448 return -EINVAL; 449 if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n') 450 return -EINVAL; 451 memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN); 452 453 return strnlen(buf, count); 454} 455 456/* 457 * Attribute definitions for netconsole_target. 458 */ 459 460#define NETCONSOLE_TARGET_ATTR_RO(_name) \ 461static struct netconsole_target_attr netconsole_target_##_name = \ 462 __CONFIGFS_ATTR(_name, S_IRUGO, show_##_name, NULL) 463 464#define NETCONSOLE_TARGET_ATTR_RW(_name) \ 465static struct netconsole_target_attr netconsole_target_##_name = \ 466 __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, show_##_name, store_##_name) 467 468NETCONSOLE_TARGET_ATTR_RW(enabled); 469NETCONSOLE_TARGET_ATTR_RW(dev_name); 470NETCONSOLE_TARGET_ATTR_RW(local_port); 471NETCONSOLE_TARGET_ATTR_RW(remote_port); 472NETCONSOLE_TARGET_ATTR_RW(local_ip); 473NETCONSOLE_TARGET_ATTR_RW(remote_ip); 474NETCONSOLE_TARGET_ATTR_RO(local_mac); 475NETCONSOLE_TARGET_ATTR_RW(remote_mac); 476 477static struct configfs_attribute *netconsole_target_attrs[] = { 478 &netconsole_target_enabled.attr, 479 &netconsole_target_dev_name.attr, 480 &netconsole_target_local_port.attr, 481 &netconsole_target_remote_port.attr, 482 &netconsole_target_local_ip.attr, 483 &netconsole_target_remote_ip.attr, 484 &netconsole_target_local_mac.attr, 485 &netconsole_target_remote_mac.attr, 486 NULL, 487}; 488 489/* 490 * Item operations and type for netconsole_target. 491 */ 492 493static void netconsole_target_release(struct config_item *item) 494{ 495 kfree(to_target(item)); 496} 497 498static ssize_t netconsole_target_attr_show(struct config_item *item, 499 struct configfs_attribute *attr, 500 char *buf) 501{ 502 ssize_t ret = -EINVAL; 503 struct netconsole_target *nt = to_target(item); 504 struct netconsole_target_attr *na = 505 container_of(attr, struct netconsole_target_attr, attr); 506 507 if (na->show) 508 ret = na->show(nt, buf); 509 510 return ret; 511} 512 513static ssize_t netconsole_target_attr_store(struct config_item *item, 514 struct configfs_attribute *attr, 515 const char *buf, 516 size_t count) 517{ 518 ssize_t ret = -EINVAL; 519 struct netconsole_target *nt = to_target(item); 520 struct netconsole_target_attr *na = 521 container_of(attr, struct netconsole_target_attr, attr); 522 523 if (na->store) 524 ret = na->store(nt, buf, count); 525 526 return ret; 527} 528 529static struct configfs_item_operations netconsole_target_item_ops = { 530 .release = netconsole_target_release, 531 .show_attribute = netconsole_target_attr_show, 532 .store_attribute = netconsole_target_attr_store, 533}; 534 535static struct config_item_type netconsole_target_type = { 536 .ct_attrs = netconsole_target_attrs, 537 .ct_item_ops = &netconsole_target_item_ops, 538 .ct_owner = THIS_MODULE, 539}; 540 541/* 542 * Group operations and type for netconsole_subsys. 543 */ 544 545static struct config_item *make_netconsole_target(struct config_group *group, 546 const char *name) 547{ 548 unsigned long flags; 549 struct netconsole_target *nt; 550 551 /* 552 * Allocate and initialize with defaults. 553 * Target is disabled at creation (enabled == 0). 554 */ 555 nt = kzalloc(sizeof(*nt), GFP_KERNEL); 556 if (!nt) 557 return ERR_PTR(-ENOMEM); 558 559 nt->np.name = "netconsole"; 560 strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ); 561 nt->np.local_port = 6665; 562 nt->np.remote_port = 6666; 563 memset(nt->np.remote_mac, 0xff, ETH_ALEN); 564 565 /* Initialize the config_item member */ 566 config_item_init_type_name(&nt->item, name, &netconsole_target_type); 567 568 /* Adding, but it is disabled */ 569 spin_lock_irqsave(&target_list_lock, flags); 570 list_add(&nt->list, &target_list); 571 spin_unlock_irqrestore(&target_list_lock, flags); 572 573 return &nt->item; 574} 575 576static void drop_netconsole_target(struct config_group *group, 577 struct config_item *item) 578{ 579 unsigned long flags; 580 struct netconsole_target *nt = to_target(item); 581 582 spin_lock_irqsave(&target_list_lock, flags); 583 list_del(&nt->list); 584 spin_unlock_irqrestore(&target_list_lock, flags); 585 586 /* 587 * The target may have never been enabled, or was manually disabled 588 * before being removed so netpoll may have already been cleaned up. 589 */ 590 if (nt->enabled) 591 netpoll_cleanup(&nt->np); 592 593 config_item_put(&nt->item); 594} 595 596static struct configfs_group_operations netconsole_subsys_group_ops = { 597 .make_item = make_netconsole_target, 598 .drop_item = drop_netconsole_target, 599}; 600 601static struct config_item_type netconsole_subsys_type = { 602 .ct_group_ops = &netconsole_subsys_group_ops, 603 .ct_owner = THIS_MODULE, 604}; 605 606/* The netconsole configfs subsystem */ 607static struct configfs_subsystem netconsole_subsys = { 608 .su_group = { 609 .cg_item = { 610 .ci_namebuf = "netconsole", 611 .ci_type = &netconsole_subsys_type, 612 }, 613 }, 614}; 615 616#endif /* CONFIG_NETCONSOLE_DYNAMIC */ 617 618/* Handle network interface device notifications */ 619static int netconsole_netdev_event(struct notifier_block *this, 620 unsigned long event, 621 void *ptr) 622{ 623 unsigned long flags; 624 struct netconsole_target *nt; 625 struct net_device *dev = ptr; 626 bool stopped = false; 627 628 if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER || 629 event == NETDEV_RELEASE || event == NETDEV_JOIN)) 630 goto done; 631 632 spin_lock_irqsave(&target_list_lock, flags); 633 list_for_each_entry(nt, &target_list, list) { 634 netconsole_target_get(nt); 635 if (nt->np.dev == dev) { 636 switch (event) { 637 case NETDEV_CHANGENAME: 638 strlcpy(nt->np.dev_name, dev->name, IFNAMSIZ); 639 break; 640 case NETDEV_RELEASE: 641 case NETDEV_JOIN: 642 case NETDEV_UNREGISTER: 643 /* 644 * rtnl_lock already held 645 */ 646 if (nt->np.dev) { 647 __netpoll_cleanup(&nt->np); 648 dev_put(nt->np.dev); 649 nt->np.dev = NULL; 650 } 651 nt->enabled = 0; 652 stopped = true; 653 break; 654 } 655 } 656 netconsole_target_put(nt); 657 } 658 spin_unlock_irqrestore(&target_list_lock, flags); 659 if (stopped) { 660 printk(KERN_INFO "netconsole: network logging stopped on " 661 "interface %s as it ", dev->name); 662 switch (event) { 663 case NETDEV_UNREGISTER: 664 printk(KERN_CONT "unregistered\n"); 665 break; 666 case NETDEV_RELEASE: 667 printk(KERN_CONT "released slaves\n"); 668 break; 669 case NETDEV_JOIN: 670 printk(KERN_CONT "is joining a master device\n"); 671 break; 672 } 673 } 674 675done: 676 return NOTIFY_DONE; 677} 678 679static struct notifier_block netconsole_netdev_notifier = { 680 .notifier_call = netconsole_netdev_event, 681}; 682 683static void write_msg(struct console *con, const char *msg, unsigned int len) 684{ 685 int frag, left; 686 unsigned long flags; 687 struct netconsole_target *nt; 688 const char *tmp; 689 690 if (oops_only && !oops_in_progress) 691 return; 692 /* Avoid taking lock and disabling interrupts unnecessarily */ 693 if (list_empty(&target_list)) 694 return; 695 696 spin_lock_irqsave(&target_list_lock, flags); 697 list_for_each_entry(nt, &target_list, list) { 698 netconsole_target_get(nt); 699 if (nt->enabled && netif_running(nt->np.dev)) { 700 /* 701 * We nest this inside the for-each-target loop above 702 * so that we're able to get as much logging out to 703 * at least one target if we die inside here, instead 704 * of unnecessarily keeping all targets in lock-step. 705 */ 706 tmp = msg; 707 for (left = len; left;) { 708 frag = min(left, MAX_PRINT_CHUNK); 709 netpoll_send_udp(&nt->np, tmp, frag); 710 tmp += frag; 711 left -= frag; 712 } 713 } 714 netconsole_target_put(nt); 715 } 716 spin_unlock_irqrestore(&target_list_lock, flags); 717} 718 719static struct console netconsole = { 720 .name = "netcon", 721 .flags = CON_ENABLED, 722 .write = write_msg, 723}; 724 725static int __init init_netconsole(void) 726{ 727 int err; 728 struct netconsole_target *nt, *tmp; 729 unsigned long flags; 730 char *target_config; 731 char *input = config; 732 733 if (strnlen(input, MAX_PARAM_LENGTH)) { 734 while ((target_config = strsep(&input, ";"))) { 735 nt = alloc_param_target(target_config); 736 if (IS_ERR(nt)) { 737 err = PTR_ERR(nt); 738 goto fail; 739 } 740 /* Dump existing printks when we register */ 741 netconsole.flags |= CON_PRINTBUFFER; 742 743 spin_lock_irqsave(&target_list_lock, flags); 744 list_add(&nt->list, &target_list); 745 spin_unlock_irqrestore(&target_list_lock, flags); 746 } 747 } 748 749 err = register_netdevice_notifier(&netconsole_netdev_notifier); 750 if (err) 751 goto fail; 752 753 err = dynamic_netconsole_init(); 754 if (err) 755 goto undonotifier; 756 757 register_console(&netconsole); 758 printk(KERN_INFO "netconsole: network logging started\n"); 759 760 return err; 761 762undonotifier: 763 unregister_netdevice_notifier(&netconsole_netdev_notifier); 764 765fail: 766 printk(KERN_ERR "netconsole: cleaning up\n"); 767 768 /* 769 * Remove all targets and destroy them (only targets created 770 * from the boot/module option exist here). Skipping the list 771 * lock is safe here, and netpoll_cleanup() will sleep. 772 */ 773 list_for_each_entry_safe(nt, tmp, &target_list, list) { 774 list_del(&nt->list); 775 free_param_target(nt); 776 } 777 778 return err; 779} 780 781static void __exit cleanup_netconsole(void) 782{ 783 struct netconsole_target *nt, *tmp; 784 785 unregister_console(&netconsole); 786 dynamic_netconsole_exit(); 787 unregister_netdevice_notifier(&netconsole_netdev_notifier); 788 789 /* 790 * Targets created via configfs pin references on our module 791 * and would first be rmdir(2)'ed from userspace. We reach 792 * here only when they are already destroyed, and only those 793 * created from the boot/module option are left, so remove and 794 * destroy them. Skipping the list lock is safe here, and 795 * netpoll_cleanup() will sleep. 796 */ 797 list_for_each_entry_safe(nt, tmp, &target_list, list) { 798 list_del(&nt->list); 799 free_param_target(nt); 800 } 801} 802 803/* 804 * Use late_initcall to ensure netconsole is 805 * initialized after network device driver if built-in. 806 * 807 * late_initcall() and module_init() are identical if built as module. 808 */ 809late_initcall(init_netconsole); 810module_exit(cleanup_netconsole);