Commit | Line | Data |
---|---|---|
800f879a AT |
1 | /* |
2 | * Copyright 2010-2017 Intel Corporation. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License, version 2, | |
6 | * as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * General Public License for more details. | |
12 | * | |
13 | * Disclaimer: The codes contained in these modules may be specific to | |
14 | * the Intel Software Development Platform codenamed Knights Ferry, | |
15 | * and the Intel product codenamed Knights Corner, and are not backward | |
16 | * compatible with other Intel products. Additionally, Intel will NOT | |
17 | * support the codes or instruction set in future products. | |
18 | * | |
19 | * Intel offers no warranty of any kind regarding the code. This code is | |
20 | * licensed on an "AS IS" basis and Intel is not obligated to provide | |
21 | * any support, assistance, installation, training, or other services | |
22 | * of any kind. Intel is also not obligated to provide any updates, | |
23 | * enhancements or extensions. Intel specifically disclaims any warranty | |
24 | * of merchantability, non-infringement, fitness for any particular | |
25 | * purpose, and any other warranty. | |
26 | * | |
27 | * Further, Intel disclaims all liability of any kind, including but | |
28 | * not limited to liability for infringement of any proprietary rights, | |
29 | * relating to the use of the code, even if Intel is notified of the | |
30 | * possibility of such liability. Except as expressly stated in an Intel | |
31 | * license agreement provided with this code and agreed upon with Intel, | |
32 | * no license, express or implied, by estoppel or otherwise, to any | |
33 | * intellectual property rights is granted herein. | |
34 | */ | |
35 | ||
36 | #include <linux/ip.h> | |
37 | #include <linux/tcp.h> | |
38 | #include <linux/list.h> | |
39 | ||
40 | #include "mic/micveth.h" | |
41 | ||
42 | #define PWR_MGMT_NO_POLL_AFTER_LINKS_UP 1 | |
43 | ||
44 | /* #define HOST */ | |
45 | #define SBOX_MMIO_LENGTH (64 * 1024) | |
46 | ||
47 | /* Host - Card link initialization rotocol | |
48 | * Card comes up and writes MICVETH_LINK_UP_MAGIC to scratch 14 & 15 | |
49 | * Host detects that the card side interface is up and writes the | |
50 | * 1) address of the tx/rx descriptor ring buffer to scratch 14 & 15 | |
51 | * 2) last 2 octets of the MAC address (allows the host to identify | |
52 | * the board number based on its mac address) | |
53 | */ | |
54 | ||
55 | /* Host - Card descriptor queue/ring buffer (from the perspective of the host) | |
56 | * | |
57 | * There is a transmit and a receive queue. Each queue entry has | |
58 | * a physical address and a length. | |
59 | * | |
60 | * Packet transmission | |
61 | * The host adds a queue entry with the physical address of the skb and its | |
62 | * length and updates the write pointer. The receive side on the card sees the | |
63 | * new entry, allocates a new skb, maps the host's skb, copies it to a locally | |
64 | * allocated skb and updates the read pointer. The host side later frees up skbs | |
65 | * starting from a cached read pointer upto the read pointer | |
66 | * | |
67 | * Packet reception | |
68 | * The host "posts" skbs to the rx queue. The transmit routine on the card | |
69 | * copies its local skb to the host skb, updates the write pointer and frees | |
70 | * its local skb | |
71 | */ | |
72 | ||
73 | /* Vnet interrupts are now functional (with vnet=dma module parameter). In the | |
74 | main flow of the driver all polling in the interrupt mode has been | |
75 | eliminated. However, polling is still happening in clientpoll() routine which | |
76 | tracks if the link is up or down. This can also be replaced by an interrupt | |
77 | driven mechanism which will be done in the future. Apart from this, only | |
78 | limited testing has been done in the interrupt mode, especially with respect | |
79 | to sharing the interrupt with scif. Therefore, for now the default mode of | |
80 | operation is still left as poll in micstart. | |
81 | */ | |
82 | ||
83 | #define SBOX_SDBIC0_DBREQ_BIT 0x80000000 | |
84 | ||
85 | ||
86 | #ifdef HOST | |
87 | #else | |
88 | struct skb_node { | |
89 | struct list_head list; | |
90 | struct sk_buff *skb; | |
91 | }; | |
92 | ||
93 | /* List of skbs to be transmitted - global for now assumes KN* has a single interface */ | |
94 | struct list_head skb_list; | |
95 | LIST_HEAD(skb_list); | |
96 | #endif | |
97 | ||
98 | static void _micveth_process_descriptors(micveth_info_t *veth_info); | |
99 | ||
100 | #ifdef HOST | |
101 | #else | |
102 | static int micveth_xmit_enqueue(struct sk_buff *skb, struct net_device *dev, micveth_info_t *veth_info); | |
103 | static int micveth_xmit_dequeue(struct net_device *dev, micveth_info_t *veth_info); | |
104 | static struct sk_buff *dequeue_skb(micveth_info_t *veth_info); | |
105 | static void micvnet_tx_dequeue_handler(struct work_struct *work); | |
106 | ||
107 | int micveth_start(mic_ctx_t *mic_ctx); | |
108 | void micveth_stop(mic_ctx_t *mic_ctx); | |
109 | static int micveth_start_dev(struct net_device *dev); | |
110 | static int micveth_stop_dev(struct net_device *dev); | |
111 | #endif | |
112 | ||
113 | static void micveth_clientpoll(struct work_struct *work); | |
114 | static void micveth_poll(struct work_struct *work); | |
115 | static irqreturn_t micvnet_host_intr_handler(int irq, void *cookie); | |
116 | static void micvnet_intr_bh_handler(struct work_struct *work); | |
117 | static void micveth_send_intr(micveth_info_t *veth_info); | |
118 | int get_sbox_irq(int index); | |
119 | ||
120 | #ifdef HOST | |
121 | #else | |
122 | static mic_ctx_t mic_ctx_g; | |
123 | #endif | |
124 | ||
125 | micveth_t micveth; | |
126 | ||
127 | static int | |
128 | micveth_set_address(struct net_device *dev, void *p) | |
129 | { | |
130 | struct sockaddr *sa = p; | |
131 | ||
132 | if (!is_valid_ether_addr(sa->sa_data)) | |
133 | return -EADDRNOTAVAIL; | |
134 | ||
135 | memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); | |
136 | return 0; | |
137 | } | |
138 | ||
139 | static void | |
140 | micveth_multicast_list(struct net_device *dev) | |
141 | { | |
142 | } | |
143 | ||
144 | #ifdef HOST | |
145 | #else | |
146 | /* Enqueues an skb for transmission. This is necessary because micveth_xmit is called in | |
147 | interrupt context and we cannot call ioremap_nocache from interrupt context. */ | |
148 | static int | |
149 | micveth_xmit_enqueue(struct sk_buff *skb, struct net_device *dev, micveth_info_t *veth_info) | |
150 | { | |
151 | struct skb_node *new_node = kmalloc(sizeof(*new_node), GFP_ATOMIC); | |
152 | ||
153 | if (!new_node) | |
154 | return ENOMEM; | |
155 | new_node->skb = skb; | |
156 | spin_lock(&veth_info->vi_txlock); | |
157 | list_add_tail(&new_node->list, &skb_list); | |
158 | spin_unlock(&veth_info->vi_txlock); | |
159 | return 0; | |
160 | } | |
161 | ||
162 | /* Dequeues a skb enqueued by micveth_xmit_enqueue */ | |
163 | static struct sk_buff * | |
164 | dequeue_skb(micveth_info_t *veth_info) | |
165 | { | |
166 | struct sk_buff *skb = NULL; | |
167 | struct skb_node *skb_node = NULL; | |
168 | ||
169 | spin_lock_bh(&veth_info->vi_txlock); | |
170 | if (!list_empty(&skb_list)) | |
171 | { | |
172 | skb_node = list_entry(skb_list.next, struct skb_node , list); | |
173 | list_del(&skb_node->list); | |
174 | skb = skb_node->skb; | |
175 | } | |
176 | spin_unlock_bh(&veth_info->vi_txlock); | |
177 | ||
178 | if (skb_node) | |
179 | kfree(skb_node); | |
180 | return skb; | |
181 | } | |
182 | ||
183 | /* Transmits skbs that have been enqueued by the by micveth_xmit_enqueue */ | |
184 | static int | |
185 | micveth_xmit_dequeue(struct net_device *dev, micveth_info_t *veth_info) | |
186 | { | |
187 | veth_ring_t *ring; | |
188 | ring_queue_t *tx_queue; | |
189 | ring_desc_t *desc; | |
190 | int next_tail; | |
191 | void *dst; | |
192 | struct sk_buff *skb; | |
193 | ||
194 | while ((skb = dequeue_skb(veth_info))) { | |
195 | ring = veth_info->ring_ptr; | |
196 | tx_queue = &ring->r_rx; | |
197 | ||
198 | next_tail = (tx_queue->rq_tail + 1) % tx_queue->rq_length; | |
199 | if (next_tail == tx_queue->rq_head) { | |
200 | printk(KERN_WARNING "dropping packet\n"); | |
201 | /* queue_full situation - just drop the packet and let the stack retry */ | |
202 | return 1; | |
203 | } | |
204 | ||
205 | desc = &tx_queue->rq_descs[tx_queue->rq_tail]; | |
206 | dst = ioremap_nocache(desc->rd_phys, skb->len); | |
207 | if (!dst) { | |
208 | tx_queue->rq_tail = (tx_queue->rq_tail + 1) % tx_queue->rq_length; | |
209 | dev_kfree_skb(skb); | |
210 | dev->stats.tx_dropped++; | |
211 | continue; | |
212 | } | |
213 | desc->rd_length = skb->len; | |
214 | desc->rd_valid = 1; | |
215 | memcpy(dst, skb->data, skb->len); | |
216 | /* | |
217 | * Need a write memory barrier between copying the skb data to | |
218 | * the buffer and updating the tail pointer. NOT an smp_wmb(), | |
219 | * because this memory barrier needs to be done even if there is | |
220 | * a single CPU in the system. | |
221 | * | |
222 | * No need for the serializing request (Si bug workaround in | |
223 | * KNF), since the buffer exists in host memory. If the buffer | |
224 | * lives in card memory, and this code is running on the host, we | |
225 | * would need extra barriers and a "serializing request" on any write. | |
226 | */ | |
227 | wmb(); | |
228 | tx_queue->rq_tail = (tx_queue->rq_tail + 1) % tx_queue->rq_length; | |
229 | iounmap(dst); | |
230 | dev_kfree_skb(skb); | |
231 | ||
232 | if (mic_vnet_mode == VNET_MODE_INTR) { | |
233 | micveth_send_intr(veth_info); | |
234 | } | |
235 | } | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | static void | |
241 | micvnet_tx_dequeue_handler(struct work_struct *work) | |
242 | { | |
243 | micveth_info_t *veth_info = container_of(work, micveth_info_t, vi_txws); | |
244 | struct net_device *dev_veth = veth_info->vi_netdev; | |
245 | ||
246 | micveth_xmit_dequeue(dev_veth, veth_info); | |
247 | } | |
248 | #endif | |
249 | ||
250 | #ifdef HOST | |
251 | #else // card | |
252 | /* Transmit callback */ | |
253 | static int | |
254 | micveth_xmit(struct sk_buff *skb, struct net_device *dev) | |
255 | { | |
256 | micveth_info_t *veth_info; | |
257 | ||
258 | if (be16_to_cpu(skb->protocol) == ETH_P_IPV6) { | |
259 | kfree_skb(skb); | |
260 | dev->stats.tx_dropped++; | |
261 | return NETDEV_TX_OK; | |
262 | } | |
263 | ||
264 | dev->stats.tx_packets++; | |
265 | dev->stats.tx_bytes += skb->len; | |
266 | ||
267 | veth_info = &micveth.lv_info[0]; | |
268 | if (veth_info->vi_state == VETH_STATE_LINKUP) { | |
269 | if (micveth_xmit_enqueue(skb, dev, veth_info)) { | |
270 | dev_kfree_skb(skb); | |
271 | dev->stats.tx_dropped++; | |
272 | } | |
273 | } else { | |
274 | dev_kfree_skb(skb); | |
275 | } | |
276 | ||
277 | /* Reuse the interrupt workqueue to also queue tx dequeue tasks */ | |
278 | queue_work(veth_info->vi_wq, &veth_info->vi_txws); | |
279 | ||
280 | return NETDEV_TX_OK; | |
281 | } | |
282 | #endif | |
283 | ||
284 | static int | |
285 | micveth_change_mtu(struct net_device *dev, int new_mtu) | |
286 | { | |
287 | dev->mtu = new_mtu; | |
288 | return 0; | |
289 | } | |
290 | ||
291 | ||
292 | /* Start callback */ | |
293 | static int | |
294 | micveth_start_dev(struct net_device *dev) | |
295 | { | |
296 | micveth_info_t *veth_info = dev->ml_priv; | |
297 | ||
298 | micveth_start(veth_info->mic_ctx); | |
299 | return 0; | |
300 | } | |
301 | ||
302 | /* Stop callback */ | |
303 | static int | |
304 | micveth_stop_dev(struct net_device *dev) | |
305 | { | |
306 | micveth_info_t *veth_info = dev->ml_priv; | |
307 | ||
308 | micveth_stop(veth_info->mic_ctx); | |
309 | return 0; | |
310 | } | |
311 | ||
312 | #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28) | |
313 | static const struct net_device_ops veth_netdev_ops = { | |
314 | .ndo_open = micveth_start_dev, | |
315 | .ndo_stop = micveth_stop_dev, | |
316 | .ndo_start_xmit = micveth_xmit, | |
317 | .ndo_validate_addr = eth_validate_addr, | |
318 | .ndo_set_multicast_list = micveth_multicast_list, | |
319 | .ndo_set_mac_address = micveth_set_address, | |
320 | .ndo_change_mtu = micveth_change_mtu, | |
321 | }; | |
322 | #endif | |
323 | ||
324 | static void | |
325 | micveth_setup(struct net_device *dev) | |
326 | { | |
327 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) | |
328 | dev->hard_start_xmit = micveth_xmit; | |
329 | dev->set_multicast_list = micveth_multicast_list; | |
330 | dev->set_mac_address = micveth_set_address; | |
331 | #endif | |
332 | ether_setup(dev); | |
333 | ||
334 | /* Initialize the device structure. */ | |
335 | #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28) | |
336 | dev->netdev_ops = &veth_netdev_ops; | |
337 | #endif | |
338 | dev->destructor = free_netdev; | |
339 | ||
340 | /* Fill in device structure with ethernet-generic values. */ | |
341 | dev->mtu = (MICVETH_MAX_PACKET_SIZE); | |
342 | dev->tx_queue_len = 0; | |
343 | dev->flags &= ~IFF_MULTICAST; | |
344 | random_ether_addr(dev->dev_addr); | |
345 | } | |
346 | ||
347 | static int | |
348 | micveth_validate(struct nlattr *tb[], struct nlattr *data[]) | |
349 | { | |
350 | if (tb[IFLA_ADDRESS]) { | |
351 | if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) | |
352 | return -EINVAL; | |
353 | if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) | |
354 | return -EADDRNOTAVAIL; | |
355 | } | |
356 | return 0; | |
357 | } | |
358 | ||
359 | static struct rtnl_link_ops micveth_link_ops __read_mostly = { | |
360 | .kind = "micveth", | |
361 | .setup = micveth_setup, | |
362 | .validate = micveth_validate, | |
363 | }; | |
364 | ||
365 | static int | |
366 | micveth_probe_int(micveth_info_t *veth_info, mic_ctx_t *mic_ctx) | |
367 | { | |
368 | struct net_device *dev_veth; | |
369 | int err = 0; | |
370 | ||
371 | veth_info->vi_sbox = ioremap_nocache(SBOX_BASE, SBOX_MMIO_LENGTH); | |
372 | veth_info->vi_scratch14 = (uint32_t *)(veth_info->vi_sbox + SBOX_SCRATCH14); | |
373 | veth_info->vi_scratch15 = (uint32_t *)(veth_info->vi_sbox + SBOX_SCRATCH14); | |
374 | writel(0x55, veth_info->vi_sbox + SBOX_DCR); | |
375 | ||
376 | veth_info->mic_ctx = mic_ctx; | |
377 | mic_ctx->bi_vethinfo = (void *)veth_info; | |
378 | ||
379 | spin_lock_init(&veth_info->vi_txlock); | |
380 | spin_lock_init(&veth_info->vi_rxlock); | |
381 | ||
382 | if (mic_vnet_mode == VNET_MODE_POLL) | |
383 | INIT_DELAYED_WORK(&veth_info->vi_poll, micveth_poll); | |
384 | ||
385 | snprintf(veth_info->vi_wqname, sizeof(veth_info->vi_wqname), | |
386 | "VNET INTR %d", 0); | |
387 | veth_info->vi_wq = create_singlethread_workqueue(veth_info->vi_wqname); | |
388 | INIT_WORK(&veth_info->vi_txws, micvnet_tx_dequeue_handler); | |
389 | ||
390 | if (mic_vnet_mode == VNET_MODE_INTR) { | |
391 | if ((err = request_irq(get_sbox_irq(VNET_SBOX_INT_IDX), | |
392 | micvnet_host_intr_handler, IRQF_DISABLED, | |
393 | "micveth intr", veth_info))) { | |
394 | printk(KERN_ERR "%s: interrupt registration failed\n", __func__); | |
395 | return err; | |
396 | } | |
397 | INIT_WORK(&veth_info->vi_bh, micvnet_intr_bh_handler); | |
398 | } | |
399 | ||
400 | // Set the current sk_buff allocation size | |
401 | veth_info->vi_skb_mtu = MICVETH_MAX_PACKET_SIZE + 32; | |
402 | ||
403 | #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) | |
404 | if ((dev_veth = alloc_netdev(sizeof(micveth_info_t), "mic%d", micveth_setup)) == NULL) { | |
405 | #else | |
406 | if ((dev_veth = alloc_netdev(sizeof(micveth_info_t), "mic%d", NET_NAME_UNKNOWN, micveth_setup)) == NULL) { | |
407 | #endif | |
408 | return -ENOMEM; | |
409 | } | |
410 | ||
411 | veth_info->vi_netdev = dev_veth; | |
412 | dev_veth->ml_priv = veth_info; | |
413 | dev_veth->rtnl_link_ops = &micveth_link_ops; | |
414 | ||
415 | if ((err = register_netdev(dev_veth)) < 0) { | |
416 | printk("register netdev failed %d\n", err); | |
417 | free_netdev(dev_veth); | |
418 | return err; | |
419 | } | |
420 | ||
421 | veth_info->vi_state = VETH_STATE_INITIALIZED; | |
422 | ||
423 | /* Inform host after completing initialization */ | |
424 | printk("%s: writing magic to SC14 and SC15\n", __FUNCTION__); | |
425 | writel(MICVETH_LINK_UP_MAGIC, veth_info->vi_sbox + SBOX_SCRATCH14); | |
426 | writel(MICVETH_LINK_UP_MAGIC, veth_info->vi_sbox + SBOX_SCRATCH15); | |
427 | ||
428 | return 0; | |
429 | } | |
430 | ||
431 | void | |
432 | micveth_remove_int(mic_ctx_t *mic_ctx) | |
433 | { | |
434 | micveth_stop(mic_ctx); | |
435 | } | |
436 | ||
437 | static int __init | |
438 | micveth_create_int(int num_bds, struct device *dev) | |
439 | { | |
440 | int bd; | |
441 | int err = 0; | |
442 | ||
443 | printk("micveth_init(%d)\n", num_bds); | |
444 | ||
445 | micveth.lv_num_interfaces = num_bds; | |
446 | micveth.lv_num_clients = num_bds; | |
447 | micveth.lv_active_clients = 0; | |
448 | micveth.lv_num_links_remaining = num_bds; | |
449 | ||
450 | if ((err = rtnl_link_register(&micveth_link_ops))) { | |
451 | printk(KERN_ERR "%s: rtnl_link_register failed!\n", __func__); | |
452 | return err; | |
453 | } | |
454 | ||
455 | // Allocate space for the control of each device in the system. | |
456 | micveth.lv_info = kmalloc(sizeof(micveth_info_t) * num_bds, GFP_KERNEL); | |
457 | if (!micveth.lv_info) { | |
458 | printk(KERN_ERR "%s: micveth_info alloc failed!\n", __func__); | |
459 | return -ENOMEM; | |
460 | } | |
461 | ||
462 | // Initialize state mutex. Overloaded use for several fields. | |
463 | mutex_init(&micveth.lv_state_mutex); | |
464 | ||
465 | // Setup of timer for probeing active mic clients. When the total active board | |
466 | // count is zero the poll is not running. | |
467 | micveth.lv_pollstate = CLIENT_POLL_STOPPED; | |
468 | INIT_DELAYED_WORK(&micveth.lv_poll, micveth_clientpoll); | |
469 | init_waitqueue_head(&micveth.lv_wq); | |
470 | ||
471 | // Init each of the existing boards. | |
472 | for (bd = 0; bd < num_bds; bd++) { | |
473 | #ifdef HOST | |
474 | micveth_probe_int(&micveth.lv_info[bd], &mic_data.dd_bi[bd]->bi_ctx); | |
475 | #else | |
476 | micveth_probe_int(&micveth.lv_info[bd], &mic_ctx_g); | |
477 | #endif | |
478 | } | |
479 | ||
480 | return err; | |
481 | } | |
482 | ||
483 | static void | |
484 | micveth_exit_int(void) | |
485 | { | |
486 | micveth_info_t *veth_info = &micveth.lv_info[0]; | |
487 | #ifdef HOST | |
488 | #endif | |
489 | micveth_stop(veth_info->mic_ctx); | |
490 | ||
491 | destroy_workqueue(veth_info->vi_wq); | |
492 | rtnl_link_unregister(&micveth_link_ops); | |
493 | ||
494 | #ifdef HOST | |
495 | #else // card | |
496 | iounmap((void *)veth_info->ring_ptr); | |
497 | iounmap(veth_info->vi_sbox); | |
498 | #endif | |
499 | ||
500 | kfree(micveth.lv_info); | |
501 | } | |
502 | ||
503 | /* Card side - tell the host that the interface is up */ | |
504 | static int | |
505 | micveth_start_int(mic_ctx_t *mic_ctx) | |
506 | { | |
507 | micveth_info_t *veth_info = &micveth.lv_info[mic_ctx->bi_id]; | |
508 | ||
509 | // Eventuall (very soon) most of the descriptor allocation for a board will be done here | |
510 | if (veth_info->vi_state != VETH_STATE_INITIALIZED) | |
511 | return 0; | |
512 | ||
513 | mutex_lock(&micveth.lv_state_mutex); | |
514 | ||
515 | if (micveth.lv_pollstate == CLIENT_POLL_STOPPED) { | |
516 | schedule_delayed_work(&micveth.lv_poll, msecs_to_jiffies(MICVETH_CLIENT_TIMER_DELAY)); | |
517 | micveth.lv_pollstate = CLIENT_POLL_RUNNING; | |
518 | } | |
519 | ||
520 | micveth.lv_active_clients++; | |
521 | mutex_unlock(&micveth.lv_state_mutex); | |
522 | ||
523 | veth_info->vi_state = VETH_STATE_LINKDOWN; | |
524 | ||
525 | return 0; | |
526 | } | |
527 | ||
528 | /* Card side - tell the host that the interface is down */ | |
529 | static void | |
530 | micveth_stop_int(mic_ctx_t *mic_ctx) | |
531 | { | |
532 | micveth_info_t *veth_info = (micveth_info_t *)(mic_ctx->bi_vethinfo); | |
533 | ||
534 | if (veth_info->vi_state == VETH_STATE_INITIALIZED) | |
535 | return; | |
536 | ||
537 | mutex_lock(&micveth.lv_state_mutex); | |
538 | micveth.lv_active_clients--; | |
539 | veth_info->vi_state = VETH_STATE_INITIALIZED; | |
540 | ||
541 | if (micveth.lv_active_clients) { | |
542 | mutex_unlock(&micveth.lv_state_mutex); | |
543 | return; | |
544 | } | |
545 | ||
546 | micveth.lv_num_links_remaining = micveth.lv_num_clients; | |
547 | ||
548 | #if PWR_MGMT_NO_POLL_AFTER_LINKS_UP | |
549 | micveth.lv_pollstate = CLIENT_POLL_STOPPED; | |
550 | mutex_unlock(&micveth.lv_state_mutex); | |
551 | #else | |
552 | micveth.lv_pollstate = CLIENT_POLL_STOPPING; | |
553 | mutex_unlock(&micveth.lv_state_mutex); | |
554 | wait_event(micveth.lv_wq, micveth.lv_pollstate == CLIENT_POLL_STOPPED); | |
555 | #endif | |
556 | ||
557 | #ifdef HOST | |
558 | #else // card | |
559 | writel(MICVETH_LINK_DOWN_MAGIC, veth_info->vi_sbox + SBOX_SCRATCH14); | |
560 | writel(MICVETH_LINK_DOWN_MAGIC, veth_info->vi_sbox + SBOX_SCRATCH15); | |
561 | #endif | |
562 | } | |
563 | ||
564 | #ifdef HOST | |
565 | #else // card | |
566 | /* Link detection */ | |
567 | static void | |
568 | micveth_clientpoll(struct work_struct *work) | |
569 | { | |
570 | micveth_info_t *veth_info; | |
571 | mic_ctx_t *mic_ctx; | |
572 | uint32_t scratch14; | |
573 | uint32_t scratch15; | |
574 | struct net_device *dev_veth; | |
575 | veth_info = &micveth.lv_info[0]; | |
576 | dev_veth = veth_info->vi_netdev; | |
577 | mic_ctx = veth_info->mic_ctx; | |
578 | mutex_lock(&micveth.lv_state_mutex); | |
579 | ||
580 | if (micveth.lv_pollstate == CLIENT_POLL_STOPPING) { | |
581 | micveth.lv_pollstate = CLIENT_POLL_STOPPED; | |
582 | mutex_unlock(&micveth.lv_state_mutex); | |
583 | wake_up(&micveth.lv_wq); | |
584 | return; | |
585 | } | |
586 | ||
587 | if (veth_info->vi_state == VETH_STATE_LINKUP) { | |
588 | scratch14 = readl(veth_info->vi_sbox + SBOX_SCRATCH14); | |
589 | scratch15 = readl(veth_info->vi_sbox + SBOX_SCRATCH15); | |
590 | ||
591 | if ((MICVETH_LINK_DOWN_MAGIC == scratch14) && | |
592 | (MICVETH_LINK_DOWN_MAGIC == scratch15)) { | |
593 | veth_info->vi_state = VETH_STATE_LINKDOWN; | |
594 | } | |
595 | } else { | |
596 | scratch14 = readl(veth_info->vi_sbox + SBOX_SCRATCH14); | |
597 | scratch15 = readl(veth_info->vi_sbox + SBOX_SCRATCH15); | |
598 | ||
599 | if ((MICVETH_LINK_UP_MAGIC != scratch14) && | |
600 | (MICVETH_LINK_UP_MAGIC != scratch15)) { | |
601 | printk("micveth_clientpoll(): SC14 and SC15 changed from MAGIC, I got the RB addresses!\n"); | |
602 | writel(MICVETH_LINK_UP_MAGIC, veth_info->vi_sbox + SBOX_SCRATCH14); | |
603 | writel(MICVETH_LINK_UP_MAGIC, veth_info->vi_sbox + SBOX_SCRATCH15); | |
604 | dev_veth->dev_addr[4] = (scratch15 >> 24) & 0xff; | |
605 | dev_veth->dev_addr[5] = (scratch15 >> 16) & 0xff; | |
606 | veth_info->vi_ring.phys = ((uint64_t)(scratch15 & 0xffff) << 32) | scratch14; | |
607 | veth_info->vi_ring.phys |= (1ULL << 39); | |
608 | veth_info->vi_ring.length = sizeof(veth_ring_t); | |
609 | veth_info->ring_ptr = ioremap_nocache(veth_info->vi_ring.phys, veth_info->vi_ring.length); | |
610 | BUG_ON(veth_info->ring_ptr == NULL); | |
611 | ||
612 | printk("micveth_clientpoll(): VETH_STATE_LINKUP\n"); | |
613 | veth_info->vi_state = VETH_STATE_LINKUP; | |
614 | if (mic_vnet_mode == VNET_MODE_POLL) { | |
615 | printk("micveth_clientpoll(): poll for work now !!\n"); | |
616 | schedule_delayed_work(&veth_info->vi_poll, msecs_to_jiffies(MICVETH_POLL_TIMER_DELAY)); | |
617 | } | |
618 | ||
619 | micveth.lv_num_links_remaining--; | |
620 | } | |
621 | } | |
622 | mutex_unlock(&micveth.lv_state_mutex); | |
623 | ||
624 | #if PWR_MGMT_NO_POLL_AFTER_LINKS_UP | |
625 | if (micveth.lv_num_links_remaining) | |
626 | #endif | |
627 | schedule_delayed_work(&micveth.lv_poll, msecs_to_jiffies(MICVETH_CLIENT_TIMER_DELAY)); | |
628 | } | |
629 | #endif | |
630 | extern struct sk_buff *jsp_dbg1; | |
631 | ||
632 | #ifdef HOST | |
633 | #else // card | |
634 | static irqreturn_t | |
635 | micvnet_host_intr_handler(int irq, void *cookie) | |
636 | { | |
637 | micveth_info_t *veth_info = cookie; | |
638 | queue_work(veth_info->vi_wq, &veth_info->vi_bh); | |
639 | return IRQ_HANDLED; | |
640 | } | |
641 | ||
642 | /* Ring host doorbell 3 interrupt */ | |
643 | static void | |
644 | micveth_send_intr(micveth_info_t *veth_info) | |
645 | { | |
646 | uint32_t db_reg; | |
647 | ||
648 | // Ring host doorbell 3 interrupt | |
649 | db_reg = readl(veth_info->vi_sbox + SBOX_SDBIC3) | SBOX_SDBIC0_DBREQ_BIT; | |
650 | writel(db_reg, veth_info->vi_sbox + SBOX_SDBIC3); | |
651 | } | |
652 | ||
653 | static void | |
654 | _micveth_process_descriptors(micveth_info_t *veth_info) | |
655 | { | |
656 | veth_ring_t *ring = veth_info->ring_ptr; | |
657 | ring_queue_t *rx_queue = &ring->r_tx; | |
658 | ring_desc_t desc; | |
659 | struct sk_buff *skb; | |
660 | void *pkt; | |
661 | int receive_skb = 0; | |
662 | int err; | |
663 | ||
664 | if (veth_info->vi_state != VETH_STATE_LINKUP) { | |
665 | return; | |
666 | } | |
667 | ||
668 | spin_lock(&veth_info->vi_rxlock); | |
669 | ||
670 | while (rx_queue->rq_head != rx_queue->rq_tail) { | |
671 | desc = rx_queue->rq_descs[rx_queue->rq_head]; | |
672 | ||
673 | veth_info->vi_netdev->stats.rx_packets++; | |
674 | veth_info->vi_netdev->stats.rx_bytes += desc.rd_length; | |
675 | ||
676 | pkt = ioremap_nocache(desc.rd_phys, desc.rd_length); | |
677 | if (pkt == NULL) { | |
678 | veth_info->vi_netdev->stats.rx_dropped++; | |
679 | goto update_ring; | |
680 | } | |
681 | ||
682 | /* handle jumbo frame */ | |
683 | if (desc.rd_length > ETH_DATA_LEN) | |
684 | skb = dev_alloc_skb(veth_info->vi_skb_mtu); | |
685 | else | |
686 | skb = dev_alloc_skb(ETH_DATA_LEN + 32); | |
687 | if (skb == NULL) { | |
688 | veth_info->vi_netdev->stats.rx_dropped++; | |
689 | iounmap(pkt); | |
690 | goto update_ring; | |
691 | } | |
692 | ||
693 | memcpy(skb_put(skb,desc.rd_length), pkt, desc.rd_length); | |
694 | iounmap(pkt); | |
695 | skb->dev = veth_info->vi_netdev; | |
696 | skb->protocol = eth_type_trans(skb, skb->dev); | |
697 | skb->ip_summed = CHECKSUM_NONE; | |
698 | local_bh_disable(); | |
699 | err = netif_receive_skb(skb); | |
700 | err = err; | |
701 | local_bh_enable(); | |
702 | /* | |
703 | * Need a general memory barrier between copying the data from | |
704 | * the buffer and updating the head pointer. It's the general | |
705 | * mb() because we're ordering the read of the data with the write. | |
706 | * | |
707 | * No need for the serializing request (Si bug workaround in | |
708 | * KNF), since the buffer exists in host memory. If the buffer | |
709 | * lives in card memory, and this code is running on the host, we | |
710 | * would need extra barriers and a "serializing request" on any write. | |
711 | */ | |
712 | mb(); | |
713 | update_ring: | |
714 | rx_queue->rq_head = (rx_queue->rq_head + 1) % rx_queue->rq_length; | |
715 | receive_skb++; | |
716 | } | |
717 | ||
718 | /* Send intr to TX so that pending SKB's can be freed */ | |
719 | if (receive_skb && mic_vnet_mode == VNET_MODE_INTR) { | |
720 | micveth_send_intr(veth_info); | |
721 | } | |
722 | ||
723 | spin_unlock(&veth_info->vi_rxlock); | |
724 | ||
725 | if (mic_vnet_mode == VNET_MODE_POLL) { | |
726 | schedule_delayed_work(&veth_info->vi_poll, msecs_to_jiffies(MICVETH_POLL_TIMER_DELAY)); | |
727 | } | |
728 | } | |
729 | ||
730 | static void | |
731 | micvnet_intr_bh_handler(struct work_struct *work) | |
732 | { | |
733 | micveth_info_t *veth_info = container_of(work, micveth_info_t, vi_bh); | |
734 | _micveth_process_descriptors(veth_info); | |
735 | } | |
736 | ||
737 | static void | |
738 | micveth_poll(struct work_struct *work) | |
739 | { | |
740 | micveth_info_t *veth_info = container_of(work, micveth_info_t, vi_poll.work); | |
741 | ||
742 | _micveth_process_descriptors(veth_info); | |
743 | } | |
744 | ||
745 | #endif | |
746 | ||
747 | #ifdef HOST | |
748 | #else // card | |
749 | static int __init | |
750 | micveth_module_init_int(void) | |
751 | { | |
752 | mic_ctx_t *mic_ctx = &mic_ctx_g; | |
753 | int ret = 0; | |
754 | ||
755 | printk("micveth_probe()\n"); | |
756 | memset(mic_ctx, 0, sizeof(*mic_ctx)); | |
757 | mic_ctx->bi_id = 0; | |
758 | ||
759 | if ((ret = micveth_init(NULL))) | |
760 | return ret; | |
761 | if ((ret = micveth_init_legacy(1, NULL))) | |
762 | return ret; | |
763 | ||
764 | return 0; | |
765 | } | |
766 | ||
767 | static void __exit | |
768 | micveth_module_exit_int(void) | |
769 | { | |
770 | micveth_exit(); | |
771 | } | |
772 | #endif | |
773 | ||
774 | /* | |
775 | VNET driver public API. These are simply wrappers which either invoke the old | |
776 | interrupt/poll mode functions or the new DMA mode functions. These are temporary and | |
777 | will be phased out with the old interrupt/poll mode so only the DMA mode will be around | |
778 | eventually. | |
779 | */ | |
780 | int __init | |
781 | micveth_init(struct device *dev) | |
782 | { | |
783 | if (mic_vnet_mode == VNET_MODE_DMA) | |
784 | return micvnet_init(dev); | |
785 | /* Intr/poll modes use micveth_init_legacy */ | |
786 | return 0; | |
787 | } | |
788 | ||
789 | int __init | |
790 | micveth_init_legacy(int num_bds, struct device *dev) | |
791 | { | |
792 | if (mic_vnet_mode != VNET_MODE_DMA) | |
793 | return micveth_create_int(num_bds, dev); | |
794 | /* DMA mode uses micveth_create */ | |
795 | return 0; | |
796 | } | |
797 | ||
798 | void | |
799 | micveth_exit(void) | |
800 | { | |
801 | if (mic_vnet_mode == VNET_MODE_DMA) | |
802 | micvnet_exit(); | |
803 | else | |
804 | micveth_exit_int(); | |
805 | } | |
806 | ||
807 | int | |
808 | micveth_probe(mic_ctx_t *mic_ctx) | |
809 | { | |
810 | if (mic_vnet_mode == VNET_MODE_DMA) | |
811 | return micvnet_probe(mic_ctx); | |
812 | /* No support for micveth_probe in legacy intr/poll modes */ | |
813 | return 0; | |
814 | } | |
815 | ||
816 | void | |
817 | micveth_remove(mic_ctx_t *mic_ctx) | |
818 | { | |
819 | if (mic_vnet_mode == VNET_MODE_DMA) | |
820 | micvnet_remove(mic_ctx); | |
821 | /* No support for micveth_remove in legacy intr/poll modes */ | |
822 | } | |
823 | ||
824 | int | |
825 | micveth_start(mic_ctx_t *mic_ctx) | |
826 | { | |
827 | if (mic_vnet_mode == VNET_MODE_DMA) | |
828 | return micvnet_start(mic_ctx); | |
829 | else | |
830 | return micveth_start_int(mic_ctx); | |
831 | } | |
832 | ||
833 | void | |
834 | micveth_stop(mic_ctx_t *mic_ctx) | |
835 | { | |
836 | if (mic_vnet_mode == VNET_MODE_DMA) | |
837 | micvnet_stop(mic_ctx); | |
838 | else | |
839 | micveth_stop_int(mic_ctx); | |
840 | } | |
841 | ||
842 | static int __init | |
843 | micveth_module_init(void) | |
844 | { | |
845 | printk("vnet: mode: %s, buffers: %d\n", | |
846 | mic_vnet_modes[mic_vnet_mode], vnet_num_buffers); | |
847 | ||
848 | if (mic_vnet_mode == VNET_MODE_DMA) | |
849 | return micvnet_module_init(); | |
850 | else | |
851 | return micveth_module_init_int(); | |
852 | } | |
853 | ||
854 | static void __exit | |
855 | micveth_module_exit(void) | |
856 | { | |
857 | if (mic_vnet_mode == VNET_MODE_DMA) | |
858 | micvnet_module_exit(); | |
859 | else | |
860 | micveth_module_exit_int(); | |
861 | } | |
862 | ||
863 | #ifdef HOST | |
864 | #else // card | |
865 | module_init(micveth_module_init); | |
866 | module_exit(micveth_module_exit); | |
867 | ||
868 | MODULE_LICENSE("GPL"); | |
869 | #endif |