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 48 MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@selenic.com>"); 49 MODULE_DESCRIPTION("Console driver for network interfaces"); 50 MODULE_LICENSE("GPL"); 51 52 #define MAX_PARAM_LENGTH 256 53 #define MAX_PRINT_CHUNK 1000 54 55 static char config[MAX_PARAM_LENGTH]; 56 module_param_string(netconsole, config, MAX_PARAM_LENGTH, 0); 57 MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]"); 58 59 static bool oops_only = false; 60 module_param(oops_only, bool, 0600); 61 MODULE_PARM_DESC(oops_only, "Only log oops messages"); 62 63 #ifndef MODULE 64 static 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 */ 73 static LIST_HEAD(target_list); 74 75 /* This needs to be a spinlock because write_msg() cannot sleep */ 76 static 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 */ 98 struct 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 109 static struct configfs_subsystem netconsole_subsys; 110 111 static 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 118 static 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 */ 128 static void netconsole_target_get(struct netconsole_target *nt) 129 { 130 if (config_item_name(&nt->item)) 131 config_item_get(&nt->item); 132 } 133 134 static 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 142 static int __init dynamic_netconsole_init(void) 143 { 144 return 0; 145 } 146 147 static 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 */ 155 static void netconsole_target_get(struct netconsole_target *nt) 156 { 157 } 158 159 static 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 */ 166 static 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 198 fail: 199 kfree(nt); 200 return ERR_PTR(err); 201 } 202 203 /* Cleanup netpoll for given target (from boot/module param) and free it */ 204 static 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 230 struct 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 239 static 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 250 static ssize_t show_enabled(struct netconsole_target *nt, char *buf) 251 { 252 return snprintf(buf, PAGE_SIZE, "%d\n", nt->enabled); 253 } 254 255 static 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 260 static 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 265 static 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 270 static 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 275 static 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 280 static 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 288 static 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 */ 300 static 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 341 static 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 364 static 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 383 static 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 402 static 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 418 static 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 434 static 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) \ 461 static struct netconsole_target_attr netconsole_target_##_name = \ 462 __CONFIGFS_ATTR(_name, S_IRUGO, show_##_name, NULL) 463 464 #define NETCONSOLE_TARGET_ATTR_RW(_name) \ 465 static struct netconsole_target_attr netconsole_target_##_name = \ 466 __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, show_##_name, store_##_name) 467 468 NETCONSOLE_TARGET_ATTR_RW(enabled); 469 NETCONSOLE_TARGET_ATTR_RW(dev_name); 470 NETCONSOLE_TARGET_ATTR_RW(local_port); 471 NETCONSOLE_TARGET_ATTR_RW(remote_port); 472 NETCONSOLE_TARGET_ATTR_RW(local_ip); 473 NETCONSOLE_TARGET_ATTR_RW(remote_ip); 474 NETCONSOLE_TARGET_ATTR_RO(local_mac); 475 NETCONSOLE_TARGET_ATTR_RW(remote_mac); 476 477 static 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 493 static void netconsole_target_release(struct config_item *item) 494 { 495 kfree(to_target(item)); 496 } 497 498 static 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 513 static 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 529 static 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 535 static 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 545 static 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 576 static 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 596 static struct configfs_group_operations netconsole_subsys_group_ops = { 597 .make_item = make_netconsole_target, 598 .drop_item = drop_netconsole_target, 599 }; 600 601 static 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 */ 607 static 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 */ 619 static 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 675 done: 676 return NOTIFY_DONE; 677 } 678 679 static struct notifier_block netconsole_netdev_notifier = { 680 .notifier_call = netconsole_netdev_event, 681 }; 682 683 static 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 719 static struct console netconsole = { 720 .name = "netcon", 721 .flags = CON_ENABLED, 722 .write = write_msg, 723 }; 724 725 static 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 762 undonotifier: 763 unregister_netdevice_notifier(&netconsole_netdev_notifier); 764 765 fail: 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 781 static 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 */ 809 late_initcall(init_netconsole); 810 module_exit(cleanup_netconsole); 811