aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/netconsole.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/netconsole.c')
-rw-r--r--drivers/net/netconsole.c171
1 files changed, 131 insertions, 40 deletions
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 55570988250b..458c4d674a9e 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -62,44 +62,93 @@ static int __init option_setup(char *opt)
62__setup("netconsole=", option_setup); 62__setup("netconsole=", option_setup);
63#endif /* MODULE */ 63#endif /* MODULE */
64 64
65/* Linked list of all configured targets */
66static LIST_HEAD(target_list);
67
68/* This needs to be a spinlock because write_msg() cannot sleep */
69static DEFINE_SPINLOCK(target_list_lock);
70
65/** 71/**
66 * struct netconsole_target - Represents a configured netconsole target. 72 * struct netconsole_target - Represents a configured netconsole target.
73 * @list: Links this target into the target_list.
67 * @np: The netpoll structure for this target. 74 * @np: The netpoll structure for this target.
68 */ 75 */
69struct netconsole_target { 76struct netconsole_target {
77 struct list_head list;
70 struct netpoll np; 78 struct netpoll np;
71}; 79};
72 80
73static struct netconsole_target default_target = { 81/* Allocate new target and setup netpoll for it */
74 .np = { 82static struct netconsole_target *alloc_target(char *target_config)
75 .name = "netconsole", 83{
76 .dev_name = "eth0", 84 int err = -ENOMEM;
77 .local_port = 6665, 85 struct netconsole_target *nt;
78 .remote_port = 6666, 86
79 .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 87 /* Allocate and initialize with defaults */
80 }, 88 nt = kzalloc(sizeof(*nt), GFP_KERNEL);
81}; 89 if (!nt) {
90 printk(KERN_ERR "netconsole: failed to allocate memory\n");
91 goto fail;
92 }
93
94 nt->np.name = "netconsole";
95 strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
96 nt->np.local_port = 6665;
97 nt->np.remote_port = 6666;
98 memset(nt->np.remote_mac, 0xff, ETH_ALEN);
99
100 /* Parse parameters and setup netpoll */
101 err = netpoll_parse_options(&nt->np, target_config);
102 if (err)
103 goto fail;
104
105 err = netpoll_setup(&nt->np);
106 if (err)
107 goto fail;
108
109 return nt;
110
111fail:
112 kfree(nt);
113 return ERR_PTR(err);
114}
115
116/* Cleanup netpoll for given target and free it */
117static void free_target(struct netconsole_target *nt)
118{
119 netpoll_cleanup(&nt->np);
120 kfree(nt);
121}
82 122
83/* Handle network interface device notifications */ 123/* Handle network interface device notifications */
84static int netconsole_netdev_event(struct notifier_block *this, 124static int netconsole_netdev_event(struct notifier_block *this,
85 unsigned long event, 125 unsigned long event,
86 void *ptr) 126 void *ptr)
87{ 127{
128 unsigned long flags;
129 struct netconsole_target *nt;
88 struct net_device *dev = ptr; 130 struct net_device *dev = ptr;
89 struct netconsole_target *nt = &default_target;
90 131
91 if (nt->np.dev == dev) { 132 if (!(event == NETDEV_CHANGEADDR || event == NETDEV_CHANGENAME))
92 switch (event) { 133 goto done;
93 case NETDEV_CHANGEADDR: 134
94 memcpy(nt->np.local_mac, dev->dev_addr, ETH_ALEN); 135 spin_lock_irqsave(&target_list_lock, flags);
95 break; 136 list_for_each_entry(nt, &target_list, list) {
137 if (nt->np.dev == dev) {
138 switch (event) {
139 case NETDEV_CHANGEADDR:
140 memcpy(nt->np.local_mac, dev->dev_addr, ETH_ALEN);
141 break;
96 142
97 case NETDEV_CHANGENAME: 143 case NETDEV_CHANGENAME:
98 strlcpy(nt->np.dev_name, dev->name, IFNAMSIZ); 144 strlcpy(nt->np.dev_name, dev->name, IFNAMSIZ);
99 break; 145 break;
146 }
100 } 147 }
101 } 148 }
149 spin_unlock_irqrestore(&target_list_lock, flags);
102 150
151done:
103 return NOTIFY_DONE; 152 return NOTIFY_DONE;
104} 153}
105 154
@@ -111,18 +160,32 @@ static void write_msg(struct console *con, const char *msg, unsigned int len)
111{ 160{
112 int frag, left; 161 int frag, left;
113 unsigned long flags; 162 unsigned long flags;
114 struct netconsole_target *nt = &default_target; 163 struct netconsole_target *nt;
115 164 const char *tmp;
116 if (netif_running(nt->np.dev)) { 165
117 local_irq_save(flags); 166 /* Avoid taking lock and disabling interrupts unnecessarily */
118 for (left = len; left;) { 167 if (list_empty(&target_list))
119 frag = min(left, MAX_PRINT_CHUNK); 168 return;
120 netpoll_send_udp(&nt->np, msg, frag); 169
121 msg += frag; 170 spin_lock_irqsave(&target_list_lock, flags);
122 left -= frag; 171 list_for_each_entry(nt, &target_list, list) {
172 if (netif_running(nt->np.dev)) {
173 /*
174 * We nest this inside the for-each-target loop above
175 * so that we're able to get as much logging out to
176 * at least one target if we die inside here, instead
177 * of unnecessarily keeping all targets in lock-step.
178 */
179 tmp = msg;
180 for (left = len; left;) {
181 frag = min(left, MAX_PRINT_CHUNK);
182 netpoll_send_udp(&nt->np, tmp, frag);
183 tmp += frag;
184 left -= frag;
185 }
123 } 186 }
124 local_irq_restore(flags);
125 } 187 }
188 spin_unlock_irqrestore(&target_list_lock, flags);
126} 189}
127 190
128static struct console netconsole = { 191static struct console netconsole = {
@@ -134,39 +197,67 @@ static struct console netconsole = {
134static int __init init_netconsole(void) 197static int __init init_netconsole(void)
135{ 198{
136 int err = 0; 199 int err = 0;
137 struct netconsole_target *nt = &default_target; 200 struct netconsole_target *nt, *tmp;
201 unsigned long flags;
202 char *target_config;
203 char *input = config;
138 204
139 if (!strnlen(config, MAX_PARAM_LENGTH)) { 205 if (!strnlen(input, MAX_PARAM_LENGTH)) {
140 printk(KERN_INFO "netconsole: not configured, aborting\n"); 206 printk(KERN_INFO "netconsole: not configured, aborting\n");
141 goto out; 207 goto out;
142 } 208 }
143 209
144 err = netpoll_parse_options(&nt->np, config); 210 while ((target_config = strsep(&input, ";"))) {
145 if (err) 211 nt = alloc_target(target_config);
146 goto out; 212 if (IS_ERR(nt)) {
147 213 err = PTR_ERR(nt);
148 err = netpoll_setup(&nt->np); 214 goto fail;
149 if (err) 215 }
150 goto out; 216 spin_lock_irqsave(&target_list_lock, flags);
217 list_add(&nt->list, &target_list);
218 spin_unlock_irqrestore(&target_list_lock, flags);
219 }
151 220
152 err = register_netdevice_notifier(&netconsole_netdev_notifier); 221 err = register_netdevice_notifier(&netconsole_netdev_notifier);
153 if (err) 222 if (err)
154 goto out; 223 goto fail;
155 224
156 register_console(&netconsole); 225 register_console(&netconsole);
157 printk(KERN_INFO "netconsole: network logging started\n"); 226 printk(KERN_INFO "netconsole: network logging started\n");
158 227
159out: 228out:
160 return err; 229 return err;
230
231fail:
232 printk(KERN_ERR "netconsole: cleaning up\n");
233
234 /*
235 * Remove all targets and destroy them. Skipping the list
236 * lock is safe here, and netpoll_cleanup() will sleep.
237 */
238 list_for_each_entry_safe(nt, tmp, &target_list, list) {
239 list_del(&nt->list);
240 free_target(nt);
241 }
242
243 return err;
161} 244}
162 245
163static void __exit cleanup_netconsole(void) 246static void __exit cleanup_netconsole(void)
164{ 247{
165 struct netconsole_target *nt = &default_target; 248 struct netconsole_target *nt, *tmp;
166 249
167 unregister_console(&netconsole); 250 unregister_console(&netconsole);
168 unregister_netdevice_notifier(&netconsole_netdev_notifier); 251 unregister_netdevice_notifier(&netconsole_netdev_notifier);
169 netpoll_cleanup(&nt->np); 252
253 /*
254 * Remove all targets and destroy them. Skipping the list
255 * lock is safe here, and netpoll_cleanup() will sleep.
256 */
257 list_for_each_entry_safe(nt, tmp, &target_list, list) {
258 list_del(&nt->list);
259 free_target(nt);
260 }
170} 261}
171 262
172module_init(init_netconsole); 263module_init(init_netconsole);