]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: David Riddoch <driddoch@solarflare.com> |
2 | commit d96c061bfd1839e34e136de0555564520acc97af | |
3 | Author: Steve Hodgson <shodgson@solarflare.com> | |
4 | Date: Mon Jul 14 15:38:47 2008 +0100 | |
5 | ||
6 | Subject: sfc: Driverlink API for exporting hardware features to client drivers | |
7 | ||
8 | References: FATE#303479 | |
9 | Acked-by: jbeulich@novell.com | |
10 | ||
11 | Index: head-2008-08-18/drivers/net/sfc/Makefile | |
12 | =================================================================== | |
13 | --- head-2008-08-18.orig/drivers/net/sfc/Makefile 2008-08-18 10:16:43.000000000 +0200 | |
14 | +++ head-2008-08-18/drivers/net/sfc/Makefile 2008-08-18 10:16:46.000000000 +0200 | |
15 | @@ -1,5 +1,5 @@ | |
16 | sfc-y += efx.o falcon.o tx.o rx.o falcon_xmac.o \ | |
17 | selftest.o ethtool.o xfp_phy.o \ | |
18 | - mdio_10g.o tenxpress.o boards.o sfe4001.o | |
19 | - | |
20 | + mdio_10g.o tenxpress.o boards.o sfe4001.o \ | |
21 | + driverlink.o | |
22 | obj-$(CONFIG_SFC) += sfc.o | |
23 | Index: head-2008-08-18/drivers/net/sfc/driverlink.c | |
24 | =================================================================== | |
25 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
26 | +++ head-2008-08-18/drivers/net/sfc/driverlink.c 2008-08-18 10:16:46.000000000 +0200 | |
27 | @@ -0,0 +1,367 @@ | |
28 | +/**************************************************************************** | |
29 | + * Driver for Solarflare Solarstorm network controllers and boards | |
30 | + * Copyright 2005 Fen Systems Ltd. | |
31 | + * Copyright 2005-2008 Solarflare Communications Inc. | |
32 | + * | |
33 | + * This program is free software; you can redistribute it and/or modify it | |
34 | + * under the terms of the GNU General Public License version 2 as published | |
35 | + * by the Free Software Foundation, incorporated herein by reference. | |
36 | + */ | |
37 | + | |
38 | +#include <linux/module.h> | |
39 | +#include <linux/list.h> | |
40 | +#include <linux/skbuff.h> | |
41 | +#include <linux/rtnetlink.h> | |
42 | +#include "net_driver.h" | |
43 | +#include "efx.h" | |
44 | +#include "driverlink_api.h" | |
45 | +#include "driverlink.h" | |
46 | + | |
47 | +/* Protects @efx_driverlink_lock and @efx_driver_list */ | |
48 | +static DEFINE_MUTEX(efx_driverlink_lock); | |
49 | + | |
50 | +/* List of all registered drivers */ | |
51 | +static LIST_HEAD(efx_driver_list); | |
52 | + | |
53 | +/* List of all registered Efx ports */ | |
54 | +static LIST_HEAD(efx_port_list); | |
55 | + | |
56 | +/** | |
57 | + * Driver link handle used internally to track devices | |
58 | + * @efx_dev: driverlink device handle exported to consumers | |
59 | + * @efx: efx_nic backing the driverlink device | |
60 | + * @port_node: per-device list head | |
61 | + * @driver_node: per-driver list head | |
62 | + */ | |
63 | +struct efx_dl_handle { | |
64 | + struct efx_dl_device efx_dev; | |
65 | + struct efx_nic *efx; | |
66 | + struct list_head port_node; | |
67 | + struct list_head driver_node; | |
68 | +}; | |
69 | + | |
70 | +static struct efx_dl_handle *efx_dl_handle(struct efx_dl_device *efx_dev) | |
71 | +{ | |
72 | + return container_of(efx_dev, struct efx_dl_handle, efx_dev); | |
73 | +} | |
74 | + | |
75 | +/* Remove an Efx device, and call the driver's remove() callback if | |
76 | + * present. The caller must hold @efx_driverlink_lock. */ | |
77 | +static void efx_dl_del_device(struct efx_dl_device *efx_dev) | |
78 | +{ | |
79 | + struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); | |
80 | + | |
81 | + EFX_INFO(efx_handle->efx, "%s driverlink client unregistering\n", | |
82 | + efx_dev->driver->name); | |
83 | + | |
84 | + if (efx_dev->driver->remove) | |
85 | + efx_dev->driver->remove(efx_dev); | |
86 | + | |
87 | + list_del(&efx_handle->driver_node); | |
88 | + list_del(&efx_handle->port_node); | |
89 | + | |
90 | + kfree(efx_handle); | |
91 | +} | |
92 | + | |
93 | +/* Attempt to probe the given device with the driver, creating a | |
94 | + * new &struct efx_dl_device. If the probe routine returns an error, | |
95 | + * then the &struct efx_dl_device is destroyed */ | |
96 | +static void efx_dl_try_add_device(struct efx_nic *efx, | |
97 | + struct efx_dl_driver *driver) | |
98 | +{ | |
99 | + struct efx_dl_handle *efx_handle; | |
100 | + struct efx_dl_device *efx_dev; | |
101 | + int rc; | |
102 | + | |
103 | + efx_handle = kzalloc(sizeof(*efx_handle), GFP_KERNEL); | |
104 | + if (!efx_handle) | |
105 | + goto fail; | |
106 | + efx_dev = &efx_handle->efx_dev; | |
107 | + efx_handle->efx = efx; | |
108 | + efx_dev->driver = driver; | |
109 | + efx_dev->pci_dev = efx->pci_dev; | |
110 | + INIT_LIST_HEAD(&efx_handle->port_node); | |
111 | + INIT_LIST_HEAD(&efx_handle->driver_node); | |
112 | + | |
113 | + rc = driver->probe(efx_dev, efx->net_dev, | |
114 | + efx->dl_info, efx->silicon_rev); | |
115 | + if (rc) | |
116 | + goto fail; | |
117 | + | |
118 | + list_add_tail(&efx_handle->driver_node, &driver->device_list); | |
119 | + list_add_tail(&efx_handle->port_node, &efx->dl_device_list); | |
120 | + | |
121 | + EFX_INFO(efx, "%s driverlink client registered\n", driver->name); | |
122 | + return; | |
123 | + | |
124 | + fail: | |
125 | + EFX_INFO(efx, "%s driverlink client skipped\n", driver->name); | |
126 | + | |
127 | + kfree(efx_handle); | |
128 | +} | |
129 | + | |
130 | +/* Unregister a driver from the driverlink layer, calling the | |
131 | + * driver's remove() callback for every attached device */ | |
132 | +void efx_dl_unregister_driver(struct efx_dl_driver *driver) | |
133 | +{ | |
134 | + struct efx_dl_handle *efx_handle, *efx_handle_n; | |
135 | + | |
136 | + printk(KERN_INFO "Efx driverlink unregistering %s driver\n", | |
137 | + driver->name); | |
138 | + | |
139 | + mutex_lock(&efx_driverlink_lock); | |
140 | + | |
141 | + list_for_each_entry_safe(efx_handle, efx_handle_n, | |
142 | + &driver->device_list, driver_node) | |
143 | + efx_dl_del_device(&efx_handle->efx_dev); | |
144 | + | |
145 | + list_del(&driver->node); | |
146 | + | |
147 | + mutex_unlock(&efx_driverlink_lock); | |
148 | +} | |
149 | +EXPORT_SYMBOL(efx_dl_unregister_driver); | |
150 | + | |
151 | +/* Register a new driver with the driverlink layer. The driver's | |
152 | + * probe routine will be called for every attached nic. */ | |
153 | +int efx_dl_register_driver(struct efx_dl_driver *driver) | |
154 | +{ | |
155 | + struct efx_nic *efx; | |
156 | + int rc; | |
157 | + | |
158 | + printk(KERN_INFO "Efx driverlink registering %s driver\n", | |
159 | + driver->name); | |
160 | + | |
161 | + INIT_LIST_HEAD(&driver->node); | |
162 | + INIT_LIST_HEAD(&driver->device_list); | |
163 | + | |
164 | + rc = mutex_lock_interruptible(&efx_driverlink_lock); | |
165 | + if (rc) | |
166 | + return rc; | |
167 | + | |
168 | + list_add_tail(&driver->node, &efx_driver_list); | |
169 | + list_for_each_entry(efx, &efx_port_list, dl_node) | |
170 | + efx_dl_try_add_device(efx, driver); | |
171 | + | |
172 | + mutex_unlock(&efx_driverlink_lock); | |
173 | + | |
174 | + return 0; | |
175 | +} | |
176 | +EXPORT_SYMBOL(efx_dl_register_driver); | |
177 | + | |
178 | +void efx_dl_unregister_nic(struct efx_nic *efx) | |
179 | +{ | |
180 | + struct efx_dl_handle *efx_handle, *efx_handle_n; | |
181 | + | |
182 | + mutex_lock(&efx_driverlink_lock); | |
183 | + | |
184 | + list_for_each_entry_safe_reverse(efx_handle, efx_handle_n, | |
185 | + &efx->dl_device_list, | |
186 | + port_node) | |
187 | + efx_dl_del_device(&efx_handle->efx_dev); | |
188 | + | |
189 | + list_del(&efx->dl_node); | |
190 | + | |
191 | + mutex_unlock(&efx_driverlink_lock); | |
192 | +} | |
193 | + | |
194 | +int efx_dl_register_nic(struct efx_nic *efx) | |
195 | +{ | |
196 | + struct efx_dl_driver *driver; | |
197 | + int rc; | |
198 | + | |
199 | + rc = mutex_lock_interruptible(&efx_driverlink_lock); | |
200 | + if (rc) | |
201 | + return rc; | |
202 | + | |
203 | + list_add_tail(&efx->dl_node, &efx_port_list); | |
204 | + list_for_each_entry(driver, &efx_driver_list, node) | |
205 | + efx_dl_try_add_device(efx, driver); | |
206 | + | |
207 | + mutex_unlock(&efx_driverlink_lock); | |
208 | + | |
209 | + return 0; | |
210 | +} | |
211 | + | |
212 | +/* Dummy callback implementations. | |
213 | + * To avoid a branch point on the fast-path, the callbacks are always | |
214 | + * implemented - they are never NULL. | |
215 | + */ | |
216 | +static enum efx_veto efx_dummy_tx_packet_callback(struct efx_dl_device *efx_dev, | |
217 | + struct sk_buff *skb) | |
218 | +{ | |
219 | + return EFX_ALLOW_PACKET; | |
220 | +} | |
221 | + | |
222 | +static enum efx_veto efx_dummy_rx_packet_callback(struct efx_dl_device *efx_dev, | |
223 | + const char *pkt_buf, int len) | |
224 | +{ | |
225 | + return EFX_ALLOW_PACKET; | |
226 | +} | |
227 | + | |
228 | +static int efx_dummy_request_mtu_callback(struct efx_dl_device *efx_dev, | |
229 | + int new_mtu) | |
230 | +{ | |
231 | + return 0; | |
232 | +} | |
233 | + | |
234 | +static void efx_dummy_mtu_changed_callback(struct efx_dl_device *efx_dev, | |
235 | + int mtu) | |
236 | +{ | |
237 | + return; | |
238 | +} | |
239 | + | |
240 | +static void efx_dummy_event_callback(struct efx_dl_device *efx_dev, void *event) | |
241 | +{ | |
242 | + return; | |
243 | +} | |
244 | + | |
245 | +struct efx_dl_callbacks efx_default_callbacks = { | |
246 | + .tx_packet = efx_dummy_tx_packet_callback, | |
247 | + .rx_packet = efx_dummy_rx_packet_callback, | |
248 | + .request_mtu = efx_dummy_request_mtu_callback, | |
249 | + .mtu_changed = efx_dummy_mtu_changed_callback, | |
250 | + .event = efx_dummy_event_callback, | |
251 | +}; | |
252 | + | |
253 | +void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev, | |
254 | + struct efx_dl_callbacks *callbacks) | |
255 | +{ | |
256 | + struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); | |
257 | + struct efx_nic *efx = efx_handle->efx; | |
258 | + | |
259 | + efx_suspend(efx); | |
260 | + | |
261 | + EFX_INFO(efx, "removing callback hooks into %s driver\n", | |
262 | + efx_dev->driver->name); | |
263 | + | |
264 | + if (callbacks->tx_packet) { | |
265 | + BUG_ON(efx->dl_cb_dev.tx_packet != efx_dev); | |
266 | + efx->dl_cb.tx_packet = efx_default_callbacks.tx_packet; | |
267 | + efx->dl_cb_dev.tx_packet = NULL; | |
268 | + } | |
269 | + if (callbacks->rx_packet) { | |
270 | + BUG_ON(efx->dl_cb_dev.rx_packet != efx_dev); | |
271 | + efx->dl_cb.rx_packet = efx_default_callbacks.rx_packet; | |
272 | + efx->dl_cb_dev.rx_packet = NULL; | |
273 | + } | |
274 | + if (callbacks->request_mtu) { | |
275 | + BUG_ON(efx->dl_cb_dev.request_mtu != efx_dev); | |
276 | + efx->dl_cb.request_mtu = efx_default_callbacks.request_mtu; | |
277 | + efx->dl_cb_dev.request_mtu = NULL; | |
278 | + } | |
279 | + if (callbacks->mtu_changed) { | |
280 | + BUG_ON(efx->dl_cb_dev.mtu_changed != efx_dev); | |
281 | + efx->dl_cb.mtu_changed = efx_default_callbacks.mtu_changed; | |
282 | + efx->dl_cb_dev.mtu_changed = NULL; | |
283 | + } | |
284 | + if (callbacks->event) { | |
285 | + BUG_ON(efx->dl_cb_dev.event != efx_dev); | |
286 | + efx->dl_cb.event = efx_default_callbacks.event; | |
287 | + efx->dl_cb_dev.event = NULL; | |
288 | + } | |
289 | + | |
290 | + efx_resume(efx); | |
291 | +} | |
292 | +EXPORT_SYMBOL(efx_dl_unregister_callbacks); | |
293 | + | |
294 | +int efx_dl_register_callbacks(struct efx_dl_device *efx_dev, | |
295 | + struct efx_dl_callbacks *callbacks) | |
296 | +{ | |
297 | + struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); | |
298 | + struct efx_nic *efx = efx_handle->efx; | |
299 | + int rc = 0; | |
300 | + | |
301 | + efx_suspend(efx); | |
302 | + | |
303 | + /* Check that the requested callbacks are not already hooked. */ | |
304 | + if ((callbacks->tx_packet && efx->dl_cb_dev.tx_packet) || | |
305 | + (callbacks->rx_packet && efx->dl_cb_dev.rx_packet) || | |
306 | + (callbacks->request_mtu && efx->dl_cb_dev.request_mtu) || | |
307 | + (callbacks->mtu_changed && efx->dl_cb_dev.mtu_changed) || | |
308 | + (callbacks->event && efx->dl_cb_dev.event)) { | |
309 | + rc = -EBUSY; | |
310 | + goto out; | |
311 | + } | |
312 | + | |
313 | + EFX_INFO(efx, "adding callback hooks to %s driver\n", | |
314 | + efx_dev->driver->name); | |
315 | + | |
316 | + /* Hook in the requested callbacks, leaving any NULL members | |
317 | + * referencing the members of @efx_default_callbacks */ | |
318 | + if (callbacks->tx_packet) { | |
319 | + efx->dl_cb.tx_packet = callbacks->tx_packet; | |
320 | + efx->dl_cb_dev.tx_packet = efx_dev; | |
321 | + } | |
322 | + if (callbacks->rx_packet) { | |
323 | + efx->dl_cb.rx_packet = callbacks->rx_packet; | |
324 | + efx->dl_cb_dev.rx_packet = efx_dev; | |
325 | + } | |
326 | + if (callbacks->request_mtu) { | |
327 | + efx->dl_cb.request_mtu = callbacks->request_mtu; | |
328 | + efx->dl_cb_dev.request_mtu = efx_dev; | |
329 | + } | |
330 | + if (callbacks->mtu_changed) { | |
331 | + efx->dl_cb.mtu_changed = callbacks->mtu_changed; | |
332 | + efx->dl_cb_dev.mtu_changed = efx_dev; | |
333 | + } | |
334 | + if (callbacks->event) { | |
335 | + efx->dl_cb.event = callbacks->event; | |
336 | + efx->dl_cb_dev.event = efx_dev; | |
337 | + } | |
338 | + | |
339 | + out: | |
340 | + efx_resume(efx); | |
341 | + | |
342 | + return rc; | |
343 | +} | |
344 | +EXPORT_SYMBOL(efx_dl_register_callbacks); | |
345 | + | |
346 | +void efx_dl_schedule_reset(struct efx_dl_device *efx_dev) | |
347 | +{ | |
348 | + struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); | |
349 | + struct efx_nic *efx = efx_handle->efx; | |
350 | + | |
351 | + efx_schedule_reset(efx, RESET_TYPE_ALL); | |
352 | +} | |
353 | +EXPORT_SYMBOL(efx_dl_schedule_reset); | |
354 | + | |
355 | +void efx_dl_reset_unlock(void) | |
356 | +{ | |
357 | + mutex_unlock(&efx_driverlink_lock); | |
358 | +} | |
359 | + | |
360 | +/* Suspend ready for reset, serialising against all the driverlink interfacse | |
361 | + * and calling the suspend() callback of every registered driver */ | |
362 | +void efx_dl_reset_suspend(struct efx_nic *efx) | |
363 | +{ | |
364 | + struct efx_dl_handle *efx_handle; | |
365 | + struct efx_dl_device *efx_dev; | |
366 | + | |
367 | + mutex_lock(&efx_driverlink_lock); | |
368 | + | |
369 | + list_for_each_entry_reverse(efx_handle, | |
370 | + &efx->dl_device_list, | |
371 | + port_node) { | |
372 | + efx_dev = &efx_handle->efx_dev; | |
373 | + if (efx_dev->driver->reset_suspend) | |
374 | + efx_dev->driver->reset_suspend(efx_dev); | |
375 | + } | |
376 | +} | |
377 | + | |
378 | +/* Resume after a reset, calling the resume() callback of every registered | |
379 | + * driver, and releasing @Efx_driverlink_lock acquired in | |
380 | + * efx_dl_reset_resume() */ | |
381 | +void efx_dl_reset_resume(struct efx_nic *efx, int ok) | |
382 | +{ | |
383 | + struct efx_dl_handle *efx_handle; | |
384 | + struct efx_dl_device *efx_dev; | |
385 | + | |
386 | + list_for_each_entry(efx_handle, &efx->dl_device_list, | |
387 | + port_node) { | |
388 | + efx_dev = &efx_handle->efx_dev; | |
389 | + if (efx_dev->driver->reset_resume) | |
390 | + efx_dev->driver->reset_resume(efx_dev, ok); | |
391 | + } | |
392 | + | |
393 | + mutex_unlock(&efx_driverlink_lock); | |
394 | +} | |
395 | Index: head-2008-08-18/drivers/net/sfc/driverlink.h | |
396 | =================================================================== | |
397 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
398 | +++ head-2008-08-18/drivers/net/sfc/driverlink.h 2008-08-18 10:16:46.000000000 +0200 | |
399 | @@ -0,0 +1,43 @@ | |
400 | +/**************************************************************************** | |
401 | + * Driver for Solarflare Solarstorm network controllers and boards | |
402 | + * Copyright 2005 Fen Systems Ltd. | |
403 | + * Copyright 2006-2008 Solarflare Communications Inc. | |
404 | + * | |
405 | + * This program is free software; you can redistribute it and/or modify it | |
406 | + * under the terms of the GNU General Public License version 2 as published | |
407 | + * by the Free Software Foundation, incorporated herein by reference. | |
408 | + */ | |
409 | + | |
410 | +#ifndef EFX_DRIVERLINK_H | |
411 | +#define EFX_DRIVERLINK_H | |
412 | + | |
413 | +/* Forward declarations */ | |
414 | +struct efx_dl_device; | |
415 | +struct efx_nic; | |
416 | + | |
417 | +/* Efx callback devices | |
418 | + * | |
419 | + * A list of the devices that own each callback. The partner to | |
420 | + * struct efx_dl_callbacks. | |
421 | + */ | |
422 | +struct efx_dl_cb_devices { | |
423 | + struct efx_dl_device *tx_packet; | |
424 | + struct efx_dl_device *rx_packet; | |
425 | + struct efx_dl_device *request_mtu; | |
426 | + struct efx_dl_device *mtu_changed; | |
427 | + struct efx_dl_device *event; | |
428 | +}; | |
429 | + | |
430 | +extern struct efx_dl_callbacks efx_default_callbacks; | |
431 | + | |
432 | +#define EFX_DL_CALLBACK(_port, _name, ...) \ | |
433 | + (_port)->dl_cb._name((_port)->dl_cb_dev._name, __VA_ARGS__) | |
434 | + | |
435 | +extern int efx_dl_register_nic(struct efx_nic *efx); | |
436 | +extern void efx_dl_unregister_nic(struct efx_nic *efx); | |
437 | + | |
438 | +/* Suspend and resume client drivers over a hardware reset */ | |
439 | +extern void efx_dl_reset_suspend(struct efx_nic *efx); | |
440 | +extern void efx_dl_reset_resume(struct efx_nic *efx, int ok); | |
441 | + | |
442 | +#endif /* EFX_DRIVERLINK_H */ | |
443 | Index: head-2008-08-18/drivers/net/sfc/driverlink_api.h | |
444 | =================================================================== | |
445 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
446 | +++ head-2008-08-18/drivers/net/sfc/driverlink_api.h 2008-08-18 10:16:46.000000000 +0200 | |
447 | @@ -0,0 +1,303 @@ | |
448 | +/**************************************************************************** | |
449 | + * Driver for Solarflare Solarstorm network controllers and boards | |
450 | + * Copyright 2005-2006 Fen Systems Ltd. | |
451 | + * Copyright 2005-2008 Solarflare Communications Inc. | |
452 | + * | |
453 | + * This program is free software; you can redistribute it and/or modify it | |
454 | + * under the terms of the GNU General Public License version 2 as published | |
455 | + * by the Free Software Foundation, incorporated herein by reference. | |
456 | + */ | |
457 | + | |
458 | +#ifndef EFX_DRIVERLINK_API_H | |
459 | +#define EFX_DRIVERLINK_API_H | |
460 | + | |
461 | +#include <linux/list.h> | |
462 | + | |
463 | +/* Forward declarations */ | |
464 | +struct pci_dev; | |
465 | +struct net_device; | |
466 | +struct sk_buff; | |
467 | +struct efx_dl_device; | |
468 | +struct efx_dl_device_info; | |
469 | + | |
470 | +/* An extra safeguard in addition to symbol versioning */ | |
471 | +#define EFX_DRIVERLINK_API_VERSION 2 | |
472 | + | |
473 | +/** | |
474 | + * struct efx_dl_driver - An Efx driverlink device driver | |
475 | + * | |
476 | + * A driverlink client defines and initializes as many instances of | |
477 | + * efx_dl_driver as required, registering each one with | |
478 | + * efx_dl_register_driver(). | |
479 | + * | |
480 | + * @name: Name of the driver | |
481 | + * @probe: Called when device added | |
482 | + * The client should use the @def_info linked list and @silicon_rev | |
483 | + * to determine if they wish to attach to this device. | |
484 | + * Context: process, driverlink semaphore held | |
485 | + * @remove: Called when device removed | |
486 | + * The client must ensure the finish all operations with this | |
487 | + * device before returning from this method. | |
488 | + * Context: process, driverlink semaphore held | |
489 | + * @reset_suspend: Called before device is reset | |
490 | + * Called immediately before a hardware reset. The client must stop all | |
491 | + * hardware processing before returning from this method. Callbacks will | |
492 | + * be inactive when this method is called. | |
493 | + * Context: process, driverlink semaphore held. rtnl_lock may be held | |
494 | + * @reset_resume: Called after device is reset | |
495 | + * Called after a hardware reset. If @ok is true, the client should | |
496 | + * state and resume normal operations. If @ok is false, the client should | |
497 | + * abandon use of the hardware resources. remove() will still be called. | |
498 | + * Context: process, driverlink semaphore held. rtnl_lock may be held | |
499 | + */ | |
500 | +struct efx_dl_driver { | |
501 | + const char *name; | |
502 | + | |
503 | + int (*probe) (struct efx_dl_device *efx_dl_dev, | |
504 | + const struct net_device *net_dev, | |
505 | + const struct efx_dl_device_info *dev_info, | |
506 | + const char *silicon_rev); | |
507 | + void (*remove) (struct efx_dl_device *efx_dev); | |
508 | + void (*reset_suspend) (struct efx_dl_device *efx_dev); | |
509 | + void (*reset_resume) (struct efx_dl_device *efx_dev, int ok); | |
510 | + | |
511 | +/* private: */ | |
512 | + struct list_head node; | |
513 | + struct list_head device_list; | |
514 | +}; | |
515 | + | |
516 | +/** | |
517 | + * enum efx_dl_device_info_type - Device information identifier. | |
518 | + * | |
519 | + * Used to identify each item in the &struct efx_dl_device_info linked list | |
520 | + * provided to each driverlink client in the probe() @dev_info member. | |
521 | + * | |
522 | + * @EFX_DL_FALCON_RESOURCES: Information type is &struct efx_dl_falcon_resources | |
523 | + */ | |
524 | +enum efx_dl_device_info_type { | |
525 | + /** Falcon resources available for export */ | |
526 | + EFX_DL_FALCON_RESOURCES = 0, | |
527 | +}; | |
528 | + | |
529 | +/** | |
530 | + * struct efx_dl_device_info - device information structure | |
531 | + * | |
532 | + * @next: Link to next structure, if any | |
533 | + * @type: Type code for this structure | |
534 | + */ | |
535 | +struct efx_dl_device_info { | |
536 | + struct efx_dl_device_info *next; | |
537 | + enum efx_dl_device_info_type type; | |
538 | +}; | |
539 | + | |
540 | +/** | |
541 | + * enum efx_dl_falcon_resource_flags - Falcon resource information flags. | |
542 | + * | |
543 | + * Flags that describe hardware variations for the current Falcon device. | |
544 | + * | |
545 | + * @EFX_DL_FALCON_DUAL_FUNC: Port is dual-function. | |
546 | + * Certain silicon revisions have two pci functions, and require | |
547 | + * certain hardware resources to be accessed via the secondary | |
548 | + * function | |
549 | + * @EFX_DL_FALCON_USE_MSI: Port is initialised to use MSI/MSI-X interrupts. | |
550 | + * Falcon supports traditional legacy interrupts and MSI/MSI-X | |
551 | + * interrupts. The choice is made at run time by the sfc driver, and | |
552 | + * notified to the clients by this enumeration | |
553 | + */ | |
554 | +enum efx_dl_falcon_resource_flags { | |
555 | + EFX_DL_FALCON_DUAL_FUNC = 0x1, | |
556 | + EFX_DL_FALCON_USE_MSI = 0x2, | |
557 | +}; | |
558 | + | |
559 | +/** | |
560 | + * struct efx_dl_falcon_resources - Falcon resource information. | |
561 | + * | |
562 | + * This structure describes Falcon hardware resources available for | |
563 | + * use by a driverlink driver. | |
564 | + * | |
565 | + * @hdr: Resource linked list header | |
566 | + * @biu_lock: Register access lock. | |
567 | + * Some Falcon revisions require register access for configuration | |
568 | + * registers to be serialised between ports and PCI functions. | |
569 | + * The sfc driver will provide the appropriate lock semantics for | |
570 | + * the underlying hardware. | |
571 | + * @buffer_table_min: First available buffer table entry | |
572 | + * @buffer_table_lim: Last available buffer table entry + 1 | |
573 | + * @evq_timer_min: First available event queue with timer | |
574 | + * @evq_timer_lim: Last available event queue with timer + 1 | |
575 | + * @evq_int_min: First available event queue with interrupt | |
576 | + * @evq_int_lim: Last available event queue with interrupt + 1 | |
577 | + * @rxq_min: First available RX queue | |
578 | + * @rxq_lim: Last available RX queue + 1 | |
579 | + * @txq_min: First available TX queue | |
580 | + * @txq_lim: Last available TX queue + 1 | |
581 | + * @flags: Hardware variation flags | |
582 | + */ | |
583 | +struct efx_dl_falcon_resources { | |
584 | + struct efx_dl_device_info hdr; | |
585 | + spinlock_t *biu_lock; | |
586 | + unsigned buffer_table_min; | |
587 | + unsigned buffer_table_lim; | |
588 | + unsigned evq_timer_min; | |
589 | + unsigned evq_timer_lim; | |
590 | + unsigned evq_int_min; | |
591 | + unsigned evq_int_lim; | |
592 | + unsigned rxq_min; | |
593 | + unsigned rxq_lim; | |
594 | + unsigned txq_min; | |
595 | + unsigned txq_lim; | |
596 | + enum efx_dl_falcon_resource_flags flags; | |
597 | +}; | |
598 | + | |
599 | +/** | |
600 | + * struct efx_dl_device - An Efx driverlink device. | |
601 | + * | |
602 | + * @pci_dev: PCI device used by the sfc driver. | |
603 | + * @priv: Driver private data | |
604 | + * Driverlink clients can use this to store a pointer to their | |
605 | + * internal per-device data structure. Each (driver, device) | |
606 | + * tuple has a separate &struct efx_dl_device, so clients can use | |
607 | + * this @priv field independently. | |
608 | + * @driver: Efx driverlink driver for this device | |
609 | + */ | |
610 | +struct efx_dl_device { | |
611 | + struct pci_dev *pci_dev; | |
612 | + void *priv; | |
613 | + struct efx_dl_driver *driver; | |
614 | +}; | |
615 | + | |
616 | +/** | |
617 | + * enum efx_veto - Packet veto request flag. | |
618 | + * | |
619 | + * This is the return type for the rx_packet() and tx_packet() methods | |
620 | + * in &struct efx_dl_callbacks. | |
621 | + * | |
622 | + * @EFX_ALLOW_PACKET: Packet may be transmitted/received | |
623 | + * @EFX_VETO_PACKET: Packet must not be transmitted/received | |
624 | + */ | |
625 | +enum efx_veto { | |
626 | + EFX_ALLOW_PACKET = 0, | |
627 | + EFX_VETO_PACKET = 1, | |
628 | +}; | |
629 | + | |
630 | +/** | |
631 | + * struct efx_dl_callbacks - Efx callbacks | |
632 | + * | |
633 | + * This is a tighly controlled set of simple callbacks, that are attached | |
634 | + * to the sfc driver via efx_dl_register_callbacks(). They export just enough | |
635 | + * state to allow clients to make use of the available hardware resources. | |
636 | + * | |
637 | + * For efficiency, only one client can hook each callback. Since these | |
638 | + * callbacks are called on packet transmit and reception paths, and the | |
639 | + * sfc driver may have multiple tx and rx queues per port, clients should | |
640 | + * avoid acquiring locks or allocating memory. | |
641 | + * | |
642 | + * @tx_packet: Called when packet is about to be transmitted | |
643 | + * Called for every packet about to be transmitted, providing means | |
644 | + * for the client to snoop traffic, and veto transmission by returning | |
645 | + * %EFX_VETO_PACKET (the sfc driver will subsequently free the skb). | |
646 | + * Context: tasklet, netif_tx_lock held | |
647 | + * @rx_packet: Called when packet is received | |
648 | + * Called for every received packet (after LRO), allowing the client | |
649 | + * to snoop every received packet (on every rx queue), and veto | |
650 | + * reception by returning %EFX_VETO_PACKET. | |
651 | + * Context: tasklet | |
652 | + * @request_mtu: Called to request MTU change. | |
653 | + * Called whenever the user requests the net_dev mtu to be changed. | |
654 | + * If the client returns an error, the mtu change is aborted. The sfc | |
655 | + * driver guarantees that no other callbacks are running. | |
656 | + * Context: process, rtnl_lock held. | |
657 | + * @mtu_changed: Called when MTU has been changed. | |
658 | + * Called after the mtu has been successfully changed, always after | |
659 | + * a previous call to request_mtu(). The sfc driver guarantees that no | |
660 | + * other callbacks are running. | |
661 | + * Context: process, rtnl_lock held. | |
662 | + * @event: Called when a hardware NIC event is not understood by the sfc driver. | |
663 | + * Context: tasklet. | |
664 | + */ | |
665 | +struct efx_dl_callbacks { | |
666 | + enum efx_veto (*tx_packet) (struct efx_dl_device *efx_dev, | |
667 | + struct sk_buff *skb); | |
668 | + enum efx_veto (*rx_packet) (struct efx_dl_device *efx_dev, | |
669 | + const char *pkt_hdr, int pkt_len); | |
670 | + int (*request_mtu) (struct efx_dl_device *efx_dev, int new_mtu); | |
671 | + void (*mtu_changed) (struct efx_dl_device *efx_dev, int mtu); | |
672 | + void (*event) (struct efx_dl_device *efx_dev, void *p_event); | |
673 | +}; | |
674 | + | |
675 | +/* Include API version number in symbol used for efx_dl_register_driver */ | |
676 | +#define efx_dl_stringify_1(x, y) x ## y | |
677 | +#define efx_dl_stringify_2(x, y) efx_dl_stringify_1(x, y) | |
678 | +#define efx_dl_register_driver \ | |
679 | + efx_dl_stringify_2(efx_dl_register_driver_api_ver_, \ | |
680 | + EFX_DRIVERLINK_API_VERSION) | |
681 | + | |
682 | +/* Exported driverlink api used to register and unregister the client driver | |
683 | + * and any callbacks [only one per port allowed], and to allow a client driver | |
684 | + * to request reset to recover from an error condition. | |
685 | + * | |
686 | + * All of these functions acquire the driverlink semaphore, so must not be | |
687 | + * called from an efx_dl_driver or efx_dl_callbacks member, and must be called | |
688 | + * from process context. | |
689 | + */ | |
690 | +extern int efx_dl_register_driver(struct efx_dl_driver *driver); | |
691 | + | |
692 | +extern void efx_dl_unregister_driver(struct efx_dl_driver *driver); | |
693 | + | |
694 | +extern int efx_dl_register_callbacks(struct efx_dl_device *efx_dev, | |
695 | + struct efx_dl_callbacks *callbacks); | |
696 | + | |
697 | +extern void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev, | |
698 | + struct efx_dl_callbacks *callbacks); | |
699 | + | |
700 | +/* Schedule a reset without grabbing any locks */ | |
701 | +extern void efx_dl_schedule_reset(struct efx_dl_device *efx_dev); | |
702 | + | |
703 | +/** | |
704 | + * efx_dl_for_each_device_info_matching - iterate an efx_dl_device_info list | |
705 | + * @_dev_info: Pointer to first &struct efx_dl_device_info | |
706 | + * @_type: Type code to look for | |
707 | + * @_info_type: Structure type corresponding to type code | |
708 | + * @_field: Name of &struct efx_dl_device_info field in the type | |
709 | + * @_p: Iterator variable | |
710 | + * | |
711 | + * Example: | |
712 | + * struct efx_dl_falcon_resources *res; | |
713 | + * efx_dl_for_each_device_info_matching(dev_info, EFX_DL_FALCON_RESOURCES, | |
714 | + * struct efx_dl_falcon_resources, | |
715 | + * hdr, res) { | |
716 | + * if (res->flags & EFX_DL_FALCON_DUAL_FUNC) | |
717 | + * .... | |
718 | + * } | |
719 | + */ | |
720 | +#define efx_dl_for_each_device_info_matching(_dev_info, _type, \ | |
721 | + _info_type, _field, _p) \ | |
722 | + for ((_p) = container_of((_dev_info), _info_type, _field); \ | |
723 | + (_p) != NULL; \ | |
724 | + (_p) = container_of((_p)->_field.next, _info_type, _field))\ | |
725 | + if ((_p)->_field.type != _type) \ | |
726 | + continue; \ | |
727 | + else | |
728 | + | |
729 | +/** | |
730 | + * efx_dl_search_device_info - search an efx_dl_device_info list | |
731 | + * @_dev_info: Pointer to first &struct efx_dl_device_info | |
732 | + * @_type: Type code to look for | |
733 | + * @_info_type: Structure type corresponding to type code | |
734 | + * @_field: Name of &struct efx_dl_device_info member in this type | |
735 | + * @_p: Result variable | |
736 | + * | |
737 | + * Example: | |
738 | + * struct efx_dl_falcon_resources *res; | |
739 | + * efx_dl_search_device_info(dev_info, EFX_DL_FALCON_RESOURCES, | |
740 | + * struct efx_dl_falcon_resources, hdr, res); | |
741 | + * if (res) | |
742 | + * .... | |
743 | + */ | |
744 | +#define efx_dl_search_device_info(_dev_info, _type, _info_type, \ | |
745 | + _field, _p) \ | |
746 | + efx_dl_for_each_device_info_matching((_dev_info), (_type), \ | |
747 | + _info_type, _field, (_p)) \ | |
748 | + break; | |
749 | + | |
750 | +#endif /* EFX_DRIVERLINK_API_H */ | |
751 | Index: head-2008-08-18/drivers/net/sfc/efx.c | |
752 | =================================================================== | |
753 | --- head-2008-08-18.orig/drivers/net/sfc/efx.c 2008-08-18 10:16:43.000000000 +0200 | |
754 | +++ head-2008-08-18/drivers/net/sfc/efx.c 2008-08-18 10:16:46.000000000 +0200 | |
755 | @@ -1427,6 +1427,11 @@ static int efx_change_mtu(struct net_dev | |
756 | ||
757 | efx_stop_all(efx); | |
758 | ||
759 | + /* Ask driverlink client if we can change MTU */ | |
760 | + rc = EFX_DL_CALLBACK(efx, request_mtu, new_mtu); | |
761 | + if (rc) | |
762 | + goto out; | |
763 | + | |
764 | EFX_LOG(efx, "changing MTU to %d\n", new_mtu); | |
765 | ||
766 | efx_fini_channels(efx); | |
767 | @@ -1435,6 +1440,10 @@ static int efx_change_mtu(struct net_dev | |
768 | if (rc) | |
769 | goto fail; | |
770 | ||
771 | + /* Notify driverlink client of new MTU */ | |
772 | + EFX_DL_CALLBACK(efx, mtu_changed, new_mtu); | |
773 | + | |
774 | + out: | |
775 | efx_start_all(efx); | |
776 | return rc; | |
777 | ||
778 | @@ -1587,6 +1596,23 @@ static void efx_unregister_netdev(struct | |
779 | * Device reset and suspend | |
780 | * | |
781 | **************************************************************************/ | |
782 | +/* Serialise access to the driverlink callbacks, by quiescing event processing | |
783 | + * (without flushing the descriptor queues), and acquiring the rtnl_lock */ | |
784 | +void efx_suspend(struct efx_nic *efx) | |
785 | +{ | |
786 | + EFX_LOG(efx, "suspending operations\n"); | |
787 | + | |
788 | + rtnl_lock(); | |
789 | + efx_stop_all(efx); | |
790 | +} | |
791 | + | |
792 | +void efx_resume(struct efx_nic *efx) | |
793 | +{ | |
794 | + EFX_LOG(efx, "resuming operations\n"); | |
795 | + | |
796 | + efx_start_all(efx); | |
797 | + rtnl_unlock(); | |
798 | +} | |
799 | ||
800 | /* The final hardware and software finalisation before reset. */ | |
801 | static int efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) | |
802 | @@ -1649,8 +1675,8 @@ static int efx_reset(struct efx_nic *efx | |
803 | enum reset_type method = efx->reset_pending; | |
804 | int rc; | |
805 | ||
806 | - /* Serialise with kernel interfaces */ | |
807 | rtnl_lock(); | |
808 | + efx_dl_reset_suspend(efx); | |
809 | ||
810 | /* If we're not RUNNING then don't reset. Leave the reset_pending | |
811 | * flag set so that efx_pci_probe_main will be retried */ | |
812 | @@ -1717,6 +1743,7 @@ static int efx_reset(struct efx_nic *efx | |
813 | efx_start_all(efx); | |
814 | ||
815 | unlock_rtnl: | |
816 | + efx_dl_reset_resume(efx, 1); | |
817 | rtnl_unlock(); | |
818 | return 0; | |
819 | ||
820 | @@ -1729,6 +1756,7 @@ static int efx_reset(struct efx_nic *efx | |
821 | efx->state = STATE_DISABLED; | |
822 | ||
823 | mutex_unlock(&efx->mac_lock); | |
824 | + efx_dl_reset_resume(efx, 0); | |
825 | rtnl_unlock(); | |
826 | efx_unregister_netdev(efx); | |
827 | efx_fini_port(efx); | |
828 | @@ -1871,6 +1899,9 @@ static int efx_init_struct(struct efx_ni | |
829 | mutex_init(&efx->mac_lock); | |
830 | efx->phy_op = &efx_dummy_phy_operations; | |
831 | efx->mii.dev = net_dev; | |
832 | + INIT_LIST_HEAD(&efx->dl_node); | |
833 | + INIT_LIST_HEAD(&efx->dl_device_list); | |
834 | + efx->dl_cb = efx_default_callbacks; | |
835 | INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work); | |
836 | atomic_set(&efx->netif_stop_count, 1); | |
837 | ||
838 | @@ -1990,6 +2021,7 @@ static void efx_pci_remove(struct pci_de | |
839 | efx = pci_get_drvdata(pci_dev); | |
840 | if (!efx) | |
841 | return; | |
842 | + efx_dl_unregister_nic(efx); | |
843 | ||
844 | /* Mark the NIC as fini, then stop the interface */ | |
845 | rtnl_lock(); | |
846 | @@ -2157,8 +2189,15 @@ static int __devinit efx_pci_probe(struc | |
847 | ||
848 | EFX_LOG(efx, "initialisation successful\n"); | |
849 | ||
850 | + /* Register with driverlink layer */ | |
851 | + rc = efx_dl_register_nic(efx); | |
852 | + if (rc) | |
853 | + goto fail6; | |
854 | + | |
855 | return 0; | |
856 | ||
857 | + fail6: | |
858 | + efx_unregister_netdev(efx); | |
859 | fail5: | |
860 | efx_pci_remove_main(efx); | |
861 | fail4: | |
862 | Index: head-2008-08-18/drivers/net/sfc/falcon.c | |
863 | =================================================================== | |
864 | --- head-2008-08-18.orig/drivers/net/sfc/falcon.c 2008-08-18 10:16:43.000000000 +0200 | |
865 | +++ head-2008-08-18/drivers/net/sfc/falcon.c 2008-08-18 10:16:46.000000000 +0200 | |
866 | @@ -36,12 +36,12 @@ | |
867 | ||
868 | /** | |
869 | * struct falcon_nic_data - Falcon NIC state | |
870 | - * @next_buffer_table: First available buffer table id | |
871 | + * @resources: Resource information for driverlink client | |
872 | * @pci_dev2: The secondary PCI device if present | |
873 | * @i2c_data: Operations and state for I2C bit-bashing algorithm | |
874 | */ | |
875 | struct falcon_nic_data { | |
876 | - unsigned next_buffer_table; | |
877 | + struct efx_dl_falcon_resources resources; | |
878 | struct pci_dev *pci_dev2; | |
879 | struct i2c_algo_bit_data i2c_data; | |
880 | }; | |
881 | @@ -322,8 +322,8 @@ static int falcon_alloc_special_buffer(s | |
882 | memset(buffer->addr, 0xff, len); | |
883 | ||
884 | /* Select new buffer ID */ | |
885 | - buffer->index = nic_data->next_buffer_table; | |
886 | - nic_data->next_buffer_table += buffer->entries; | |
887 | + buffer->index = nic_data->resources.buffer_table_min; | |
888 | + nic_data->resources.buffer_table_min += buffer->entries; | |
889 | ||
890 | EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " | |
891 | "(virt %p phys %lx)\n", buffer->index, | |
892 | @@ -1115,10 +1115,12 @@ static void falcon_handle_driver_event(s | |
893 | case TX_DESCQ_FLS_DONE_EV_DECODE: | |
894 | EFX_TRACE(efx, "channel %d TXQ %d flushed\n", | |
895 | channel->channel, ev_sub_data); | |
896 | + EFX_DL_CALLBACK(efx, event, event); | |
897 | break; | |
898 | case RX_DESCQ_FLS_DONE_EV_DECODE: | |
899 | EFX_TRACE(efx, "channel %d RXQ %d flushed\n", | |
900 | channel->channel, ev_sub_data); | |
901 | + EFX_DL_CALLBACK(efx, event, event); | |
902 | break; | |
903 | case EVQ_INIT_DONE_EV_DECODE: | |
904 | EFX_LOG(efx, "channel %d EVQ %d initialised\n", | |
905 | @@ -1127,14 +1129,17 @@ static void falcon_handle_driver_event(s | |
906 | case SRM_UPD_DONE_EV_DECODE: | |
907 | EFX_TRACE(efx, "channel %d SRAM update done\n", | |
908 | channel->channel); | |
909 | + EFX_DL_CALLBACK(efx, event, event); | |
910 | break; | |
911 | case WAKE_UP_EV_DECODE: | |
912 | EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n", | |
913 | channel->channel, ev_sub_data); | |
914 | + EFX_DL_CALLBACK(efx, event, event); | |
915 | break; | |
916 | case TIMER_EV_DECODE: | |
917 | EFX_TRACE(efx, "channel %d RX queue %d timer expired\n", | |
918 | channel->channel, ev_sub_data); | |
919 | + EFX_DL_CALLBACK(efx, event, event); | |
920 | break; | |
921 | case RX_RECOVERY_EV_DECODE: | |
922 | EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. " | |
923 | @@ -1159,6 +1164,7 @@ static void falcon_handle_driver_event(s | |
924 | EFX_TRACE(efx, "channel %d unknown driver event code %d " | |
925 | "data %04x\n", channel->channel, ev_sub_code, | |
926 | ev_sub_data); | |
927 | + EFX_DL_CALLBACK(efx, event, event); | |
928 | break; | |
929 | } | |
930 | } | |
931 | @@ -2371,6 +2377,59 @@ static int falcon_probe_nvconfig(struct | |
932 | return rc; | |
933 | } | |
934 | ||
935 | +/* Looks at available SRAM resources and silicon revision, and works out | |
936 | + * how many queues we can support, and where things like descriptor caches | |
937 | + * should live. */ | |
938 | +static int falcon_dimension_resources(struct efx_nic *efx) | |
939 | +{ | |
940 | + unsigned internal_dcs_entries; | |
941 | + struct falcon_nic_data *nic_data = efx->nic_data; | |
942 | + struct efx_dl_falcon_resources *res = &nic_data->resources; | |
943 | + | |
944 | + /* Fill out the driverlink resource list */ | |
945 | + res->hdr.type = EFX_DL_FALCON_RESOURCES; | |
946 | + res->biu_lock = &efx->biu_lock; | |
947 | + efx->dl_info = &res->hdr; | |
948 | + | |
949 | + /* NB. The minimum values get increased as this driver initialises | |
950 | + * its resources, so this should prevent any overlap. | |
951 | + */ | |
952 | + switch (falcon_rev(efx)) { | |
953 | + case FALCON_REV_A1: | |
954 | + res->rxq_min = 16; | |
955 | + res->txq_min = 16; | |
956 | + res->evq_int_min = 4; | |
957 | + res->evq_int_lim = 5; | |
958 | + res->evq_timer_min = 5; | |
959 | + res->evq_timer_lim = 4096; | |
960 | + internal_dcs_entries = 8192; | |
961 | + break; | |
962 | + case FALCON_REV_B0: | |
963 | + default: | |
964 | + res->rxq_min = 0; | |
965 | + res->txq_min = 0; | |
966 | + res->evq_int_min = 0; | |
967 | + res->evq_int_lim = 64; | |
968 | + res->evq_timer_min = 64; | |
969 | + res->evq_timer_lim = 4096; | |
970 | + internal_dcs_entries = 4096; | |
971 | + break; | |
972 | + } | |
973 | + | |
974 | + /* Internal SRAM only for now */ | |
975 | + res->rxq_lim = internal_dcs_entries / RX_DC_ENTRIES; | |
976 | + res->txq_lim = internal_dcs_entries / TX_DC_ENTRIES; | |
977 | + res->buffer_table_lim = 8192; | |
978 | + | |
979 | + if (FALCON_IS_DUAL_FUNC(efx)) | |
980 | + res->flags |= EFX_DL_FALCON_DUAL_FUNC; | |
981 | + | |
982 | + if (EFX_INT_MODE_USE_MSI(efx)) | |
983 | + res->flags |= EFX_DL_FALCON_USE_MSI; | |
984 | + | |
985 | + return 0; | |
986 | +} | |
987 | + | |
988 | /* Probe the NIC variant (revision, ASIC vs FPGA, function count, port | |
989 | * count, port speed). Set workaround and feature flags accordingly. | |
990 | */ | |
991 | @@ -2403,10 +2462,12 @@ static int falcon_probe_nic_variant(stru | |
992 | EFX_ERR(efx, "1G mode not supported\n"); | |
993 | return -ENODEV; | |
994 | } | |
995 | + efx->silicon_rev = "falcon/a1"; | |
996 | break; | |
997 | } | |
998 | ||
999 | case FALCON_REV_B0: | |
1000 | + efx->silicon_rev = "falcon/b0"; | |
1001 | break; | |
1002 | ||
1003 | default: | |
1004 | @@ -2472,6 +2533,10 @@ int falcon_probe_nic(struct efx_nic *efx | |
1005 | if (rc) | |
1006 | goto fail5; | |
1007 | ||
1008 | + rc = falcon_dimension_resources(efx); | |
1009 | + if (rc) | |
1010 | + goto fail6; | |
1011 | + | |
1012 | /* Initialise I2C adapter */ | |
1013 | efx->i2c_adap.owner = THIS_MODULE; | |
1014 | nic_data->i2c_data = falcon_i2c_bit_operations; | |
1015 | @@ -2481,10 +2546,12 @@ int falcon_probe_nic(struct efx_nic *efx | |
1016 | strlcpy(efx->i2c_adap.name, "SFC4000 GPIO", sizeof(efx->i2c_adap.name)); | |
1017 | rc = i2c_bit_add_bus(&efx->i2c_adap); | |
1018 | if (rc) | |
1019 | - goto fail5; | |
1020 | + goto fail6; | |
1021 | ||
1022 | return 0; | |
1023 | ||
1024 | + fail6: | |
1025 | + efx->dl_info = NULL; | |
1026 | fail5: | |
1027 | falcon_free_buffer(efx, &efx->irq_status); | |
1028 | fail4: | |
1029 | @@ -2675,6 +2742,7 @@ void falcon_remove_nic(struct efx_nic *e | |
1030 | /* Tear down the private nic state */ | |
1031 | kfree(efx->nic_data); | |
1032 | efx->nic_data = NULL; | |
1033 | + efx->dl_info = NULL; | |
1034 | } | |
1035 | ||
1036 | void falcon_update_nic_stats(struct efx_nic *efx) | |
1037 | Index: head-2008-08-18/drivers/net/sfc/net_driver.h | |
1038 | =================================================================== | |
1039 | --- head-2008-08-18.orig/drivers/net/sfc/net_driver.h 2008-08-18 10:16:43.000000000 +0200 | |
1040 | +++ head-2008-08-18/drivers/net/sfc/net_driver.h 2008-08-18 10:16:46.000000000 +0200 | |
1041 | @@ -30,6 +30,8 @@ | |
1042 | ||
1043 | #include "enum.h" | |
1044 | #include "bitfield.h" | |
1045 | +#include "driverlink_api.h" | |
1046 | +#include "driverlink.h" | |
1047 | ||
1048 | #define EFX_MAX_LRO_DESCRIPTORS 8 | |
1049 | #define EFX_MAX_LRO_AGGR MAX_SKB_FRAGS | |
1050 | @@ -676,6 +678,12 @@ union efx_multicast_hash { | |
1051 | * @loopback_mode: Loopback status | |
1052 | * @loopback_modes: Supported loopback mode bitmask | |
1053 | * @loopback_selftest: Offline self-test private state | |
1054 | + * @silicon_rev: Silicon revision description for driverlink | |
1055 | + * @dl_info: Linked list of hardware parameters exposed through driverlink | |
1056 | + * @dl_node: Driverlink port list | |
1057 | + * @dl_device_list: Driverlink device list | |
1058 | + * @dl_cb: Driverlink callbacks table | |
1059 | + * @dl_cb_dev: Driverlink callback owner devices | |
1060 | * | |
1061 | * The @priv field of the corresponding &struct net_device points to | |
1062 | * this. | |
1063 | @@ -752,6 +760,13 @@ struct efx_nic { | |
1064 | unsigned int loopback_modes; | |
1065 | ||
1066 | void *loopback_selftest; | |
1067 | + | |
1068 | + const char *silicon_rev; | |
1069 | + struct efx_dl_device_info *dl_info; | |
1070 | + struct list_head dl_node; | |
1071 | + struct list_head dl_device_list; | |
1072 | + struct efx_dl_callbacks dl_cb; | |
1073 | + struct efx_dl_cb_devices dl_cb_dev; | |
1074 | }; | |
1075 | ||
1076 | static inline int efx_dev_registered(struct efx_nic *efx) | |
1077 | Index: head-2008-08-18/drivers/net/sfc/rx.c | |
1078 | =================================================================== | |
1079 | --- head-2008-08-18.orig/drivers/net/sfc/rx.c 2008-08-18 10:16:43.000000000 +0200 | |
1080 | +++ head-2008-08-18/drivers/net/sfc/rx.c 2008-08-18 10:16:46.000000000 +0200 | |
1081 | @@ -549,8 +549,22 @@ static inline void efx_rx_packet__check_ | |
1082 | static inline void efx_rx_packet_lro(struct efx_channel *channel, | |
1083 | struct efx_rx_buffer *rx_buf) | |
1084 | { | |
1085 | + struct efx_nic *efx = channel->efx; | |
1086 | struct net_lro_mgr *lro_mgr = &channel->lro_mgr; | |
1087 | void *priv = channel; | |
1088 | + enum efx_veto veto; | |
1089 | + | |
1090 | + /* It would be faster if we had access to packets at the | |
1091 | + * other side of generic LRO. Unfortunately, there isn't | |
1092 | + * an obvious interface to this, so veto packets before LRO */ | |
1093 | + veto = EFX_DL_CALLBACK(efx, rx_packet, rx_buf->data, rx_buf->len); | |
1094 | + if (unlikely(veto)) { | |
1095 | + EFX_TRACE(efx, "LRO RX vetoed by driverlink %s driver\n", | |
1096 | + efx->dl_cb_dev.rx_packet->driver->name); | |
1097 | + /* Free the buffer now */ | |
1098 | + efx_free_rx_buffer(efx, rx_buf); | |
1099 | + return; | |
1100 | + } | |
1101 | ||
1102 | /* Pass the skb/page into the LRO engine */ | |
1103 | if (rx_buf->page) { | |
1104 | @@ -686,6 +700,7 @@ void __efx_rx_packet(struct efx_channel | |
1105 | struct efx_rx_buffer *rx_buf, int checksummed) | |
1106 | { | |
1107 | struct efx_nic *efx = channel->efx; | |
1108 | + enum efx_veto veto; | |
1109 | struct sk_buff *skb; | |
1110 | int lro = efx->net_dev->features & NETIF_F_LRO; | |
1111 | ||
1112 | @@ -723,6 +738,16 @@ void __efx_rx_packet(struct efx_channel | |
1113 | goto done; | |
1114 | } | |
1115 | ||
1116 | + /* Allow callback to veto the packet */ | |
1117 | + veto = EFX_DL_CALLBACK(efx, rx_packet, rx_buf->data, rx_buf->len); | |
1118 | + if (unlikely(veto)) { | |
1119 | + EFX_LOG(efx, "RX vetoed by driverlink %s driver\n", | |
1120 | + efx->dl_cb_dev.rx_packet->driver->name); | |
1121 | + /* Free the buffer now */ | |
1122 | + efx_free_rx_buffer(efx, rx_buf); | |
1123 | + goto done; | |
1124 | + } | |
1125 | + | |
1126 | /* Form an skb if required */ | |
1127 | if (rx_buf->page) { | |
1128 | int hdr_len = min(rx_buf->len, EFX_SKB_HEADERS); | |
1129 | Index: head-2008-08-18/drivers/net/sfc/tx.c | |
1130 | =================================================================== | |
1131 | --- head-2008-08-18.orig/drivers/net/sfc/tx.c 2008-08-18 10:16:43.000000000 +0200 | |
1132 | +++ head-2008-08-18/drivers/net/sfc/tx.c 2008-08-18 10:16:46.000000000 +0200 | |
1133 | @@ -368,7 +368,21 @@ inline int efx_xmit(struct efx_nic *efx, | |
1134 | int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev) | |
1135 | { | |
1136 | struct efx_nic *efx = net_dev->priv; | |
1137 | - return efx_xmit(efx, &efx->tx_queue[0], skb); | |
1138 | + struct efx_tx_queue *tx_queue = &efx->tx_queue[0]; | |
1139 | + enum efx_veto veto; | |
1140 | + | |
1141 | + /* See if driverlink wants to veto the packet. */ | |
1142 | + veto = EFX_DL_CALLBACK(efx, tx_packet, skb); | |
1143 | + if (unlikely(veto)) { | |
1144 | + EFX_TRACE(efx, "TX queue %d packet vetoed by " | |
1145 | + "driverlink %s driver\n", tx_queue->queue, | |
1146 | + efx->dl_cb_dev.tx_packet->driver->name); | |
1147 | + /* Free the skb; nothing else will do it */ | |
1148 | + dev_kfree_skb_any(skb); | |
1149 | + return NETDEV_TX_OK; | |
1150 | + } | |
1151 | + | |
1152 | + return efx_xmit(efx, tx_queue, skb); | |
1153 | } | |
1154 | ||
1155 | void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) |