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