diff options
Diffstat (limited to 'drivers/net/netconsole.c')
-rw-r--r-- | drivers/net/netconsole.c | 605 |
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 | ||
45 | MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@selenic.com>"); | 47 | MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@selenic.com>"); |
46 | MODULE_DESCRIPTION("Console driver for network interfaces"); | 48 | MODULE_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 | */ |
76 | struct netconsole_target { | 93 | struct 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 |
82 | static struct netconsole_target *alloc_target(char *target_config) | 103 | |
104 | static struct configfs_subsystem netconsole_subsys; | ||
105 | |||
106 | static 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 | |||
113 | static 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 | */ | ||
123 | static void netconsole_target_get(struct netconsole_target *nt) | ||
124 | { | ||
125 | if (config_item_name(&nt->item)) | ||
126 | config_item_get(&nt->item); | ||
127 | } | ||
128 | |||
129 | static 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 | |||
137 | static int __init dynamic_netconsole_init(void) | ||
138 | { | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static 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 | */ | ||
150 | static void netconsole_target_get(struct netconsole_target *nt) | ||
151 | { | ||
152 | } | ||
153 | |||
154 | static 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 */ | ||
161 | static 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 | ||
111 | fail: | 195 | fail: |
@@ -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 */ |
117 | static void free_target(struct netconsole_target *nt) | 201 | static 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 | |||
227 | struct 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 | |||
236 | static 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 | */ | ||
248 | static 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 | |||
275 | static ssize_t show_enabled(struct netconsole_target *nt, char *buf) | ||
276 | { | ||
277 | return snprintf(buf, PAGE_SIZE, "%d\n", nt->enabled); | ||
278 | } | ||
279 | |||
280 | static 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 | |||
285 | static 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 | |||
290 | static 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 | |||
295 | static 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 | |||
301 | static 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 | |||
307 | static 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 | |||
315 | static 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 | */ | ||
330 | static 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 | |||
364 | static 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 | |||
387 | static 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 | |||
410 | static 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 | |||
433 | static 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 | |||
449 | static 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 | |||
465 | static 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 | |||
494 | invalid: | ||
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) \ | ||
504 | static struct netconsole_target_attr netconsole_target_##_name = \ | ||
505 | __CONFIGFS_ATTR(_name, S_IRUGO, show_##_name, NULL) | ||
506 | |||
507 | #define NETCONSOLE_TARGET_ATTR_RW(_name) \ | ||
508 | static struct netconsole_target_attr netconsole_target_##_name = \ | ||
509 | __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, show_##_name, store_##_name) | ||
510 | |||
511 | NETCONSOLE_TARGET_ATTR_RW(enabled); | ||
512 | NETCONSOLE_TARGET_ATTR_RW(dev_name); | ||
513 | NETCONSOLE_TARGET_ATTR_RW(local_port); | ||
514 | NETCONSOLE_TARGET_ATTR_RW(remote_port); | ||
515 | NETCONSOLE_TARGET_ATTR_RW(local_ip); | ||
516 | NETCONSOLE_TARGET_ATTR_RW(remote_ip); | ||
517 | NETCONSOLE_TARGET_ATTR_RO(local_mac); | ||
518 | NETCONSOLE_TARGET_ATTR_RW(remote_mac); | ||
519 | |||
520 | static 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 | |||
536 | static void netconsole_target_release(struct config_item *item) | ||
537 | { | ||
538 | kfree(to_target(item)); | ||
539 | } | ||
540 | |||
541 | static 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 | |||
556 | static 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 | |||
572 | static 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 | |||
578 | static 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 | |||
588 | static 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 | |||
621 | static 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 | |||
641 | static struct configfs_group_operations netconsole_subsys_group_ops = { | ||
642 | .make_item = make_netconsole_target, | ||
643 | .drop_item = drop_netconsole_target, | ||
644 | }; | ||
645 | |||
646 | static 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 */ | ||
652 | static 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 */ |
124 | static int netconsole_netdev_event(struct notifier_block *this, | 664 | static 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 | ||
197 | static int __init init_netconsole(void) | 741 | static 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 | ||
228 | out: | ||
229 | return err; | 773 | return err; |
230 | 774 | ||
775 | undonotifier: | ||
776 | unregister_netdevice_notifier(&netconsole_netdev_notifier); | ||
777 | |||
231 | fail: | 778 | fail: |
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 | ||