aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/netconsole.c
diff options
context:
space:
mode:
authorSatyam Sharma <satyam@infradead.org>2007-08-10 18:33:40 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-10-10 19:48:06 -0400
commitb5427c27173e128dda1541bd9d3b05df79af5882 (patch)
tree96d71faf39ff39002335cd4d8c9ef8ae1ad49a62 /drivers/net/netconsole.c
parent17951f34b0970b05e29fd93a5b93fa05ec71308b (diff)
[NET] netconsole: Support multiple logging targets
Based upon initial work by Keiichi Kii <k-keiichi@bx.jp.nec.com>. This patch introduces support for multiple targets, independent of CONFIG_NETCONSOLE_DYNAMIC -- this is useful even in the default case and (including the infrastructure introduced in previous patches) doesn't really add too many bytes to module text. All the complexity (and size) comes with the dynamic reconfigurability / userspace interface patch, and so it's plausible users may want to keep this enabled but that disabled (say to avoid a dependency on CONFIG_CONFIGFS_FS too). Also update documentation to mention the use of ";" separator to specify multiple logging targets in the boot/module option string. Brief overview: We maintain a target_list (and corresponding lock). Get rid of the static "default_target" and introduce allocation and release functions for our netconsole_target objects (but keeping sure to preserve previous behaviour such as default values). During init_netconsole(), ";" is used as the separator to identify multiple target specifications in the boot/module option string. The target specifications are parsed and netpolls setup. During exit, the target_list is torn down and all items released. Signed-off-by: Satyam Sharma <satyam@infradead.org> Signed-off-by: Keiichi Kii <k-keiichi@bx.jp.nec.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: David S. Miller <davem@davemloft.net>
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);