blob: f6be7b29c845ca7bc3841f193fc3152623538144 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/**************************************************************************/
2/* */
3/* IBM eServer i/pSeries Virtual Ethernet Device Driver */
4/* Copyright (C) 2003 IBM Corp. */
5/* Originally written by Dave Larson (larson1@us.ibm.com) */
6/* Maintained by Santiago Leon (santil@us.ibm.com) */
7/* */
8/* This program is free software; you can redistribute it and/or modify */
9/* it under the terms of the GNU General Public License as published by */
10/* the Free Software Foundation; either version 2 of the License, or */
11/* (at your option) any later version. */
12/* */
13/* This program is distributed in the hope that it will be useful, */
14/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
15/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
16/* GNU General Public License for more details. */
17/* */
18/* You should have received a copy of the GNU General Public License */
19/* along with this program; if not, write to the Free Software */
20/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */
21/* USA */
22/* */
23/* This module contains the implementation of a virtual ethernet device */
24/* for use with IBM i/pSeries LPAR Linux. It utilizes the logical LAN */
25/* option of the RS/6000 Platform Architechture to interface with virtual */
26/* ethernet NICs that are presented to the partition by the hypervisor. */
Jeff Garzikd7fbeba2006-05-24 01:31:14 -040027/* */
Linus Torvalds1da177e2005-04-16 15:20:36 -070028/**************************************************************************/
29/*
30 TODO:
Linus Torvalds1da177e2005-04-16 15:20:36 -070031 - add support for sysfs
32 - possibly remove procfs support
33*/
34
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <linux/types.h>
37#include <linux/errno.h>
38#include <linux/ioport.h>
39#include <linux/dma-mapping.h>
40#include <linux/kernel.h>
41#include <linux/netdevice.h>
42#include <linux/etherdevice.h>
43#include <linux/skbuff.h>
44#include <linux/init.h>
45#include <linux/delay.h>
46#include <linux/mm.h>
47#include <linux/ethtool.h>
48#include <linux/proc_fs.h>
Brian Kingf4ff2872007-09-15 13:36:07 -070049#include <linux/in.h>
50#include <linux/ip.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020051#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#include <asm/semaphore.h>
53#include <asm/hvcall.h>
54#include <asm/atomic.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#include <asm/vio.h>
56#include <asm/uaccess.h>
57#include <linux/seq_file.h>
58
59#include "ibmveth.h"
60
Anton Blanchard6af37fa2005-11-11 14:02:04 +110061#undef DEBUG
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
63#define ibmveth_printk(fmt, args...) \
Olof Johanssonf98baff2006-04-12 15:22:29 -050064 printk(KERN_DEBUG "%s: " fmt, __FILE__, ## args)
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66#define ibmveth_error_printk(fmt, args...) \
67 printk(KERN_ERR "(%s:%3.3d ua:%x) ERROR: " fmt, __FILE__, __LINE__ , adapter->vdev->unit_address, ## args)
68
69#ifdef DEBUG
70#define ibmveth_debug_printk_no_adapter(fmt, args...) \
71 printk(KERN_DEBUG "(%s:%3.3d): " fmt, __FILE__, __LINE__ , ## args)
72#define ibmveth_debug_printk(fmt, args...) \
73 printk(KERN_DEBUG "(%s:%3.3d ua:%x): " fmt, __FILE__, __LINE__ , adapter->vdev->unit_address, ## args)
74#define ibmveth_assert(expr) \
75 if(!(expr)) { \
76 printk(KERN_DEBUG "assertion failed (%s:%3.3d ua:%x): %s\n", __FILE__, __LINE__, adapter->vdev->unit_address, #expr); \
77 BUG(); \
78 }
79#else
80#define ibmveth_debug_printk_no_adapter(fmt, args...)
81#define ibmveth_debug_printk(fmt, args...)
Jeff Garzikd7fbeba2006-05-24 01:31:14 -040082#define ibmveth_assert(expr)
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#endif
84
85static int ibmveth_open(struct net_device *dev);
86static int ibmveth_close(struct net_device *dev);
87static int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
Stephen Hemmingerbea33482007-10-03 16:41:36 -070088static int ibmveth_poll(struct napi_struct *napi, int budget);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *dev);
90static struct net_device_stats *ibmveth_get_stats(struct net_device *dev);
91static void ibmveth_set_multicast_list(struct net_device *dev);
92static int ibmveth_change_mtu(struct net_device *dev, int new_mtu);
93static void ibmveth_proc_register_driver(void);
94static void ibmveth_proc_unregister_driver(void);
95static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter);
96static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter);
David Howells7d12e782006-10-05 14:55:46 +010097static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance);
Michael Ellerman493a6842007-04-17 13:12:55 +100098static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter);
Santiago Leon860f2422006-04-25 11:19:59 -050099static struct kobj_type ktype_veth_pool;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
101#ifdef CONFIG_PROC_FS
Eric W. Biederman457c4cb2007-09-12 12:01:34 +0200102#define IBMVETH_PROC_DIR "ibmveth"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103static struct proc_dir_entry *ibmveth_proc_dir;
104#endif
105
106static const char ibmveth_driver_name[] = "ibmveth";
107static const char ibmveth_driver_string[] = "IBM i/pSeries Virtual Ethernet Driver";
108#define ibmveth_driver_version "1.03"
109
110MODULE_AUTHOR("Santiago Leon <santil@us.ibm.com>");
111MODULE_DESCRIPTION("IBM i/pSeries Virtual Ethernet Driver");
112MODULE_LICENSE("GPL");
113MODULE_VERSION(ibmveth_driver_version);
114
Brian Kingddbb4de2007-08-17 09:16:43 -0500115struct ibmveth_stat {
116 char name[ETH_GSTRING_LEN];
117 int offset;
118};
119
120#define IBMVETH_STAT_OFF(stat) offsetof(struct ibmveth_adapter, stat)
121#define IBMVETH_GET_STAT(a, off) *((u64 *)(((unsigned long)(a)) + off))
122
123struct ibmveth_stat ibmveth_stats[] = {
124 { "replenish_task_cycles", IBMVETH_STAT_OFF(replenish_task_cycles) },
125 { "replenish_no_mem", IBMVETH_STAT_OFF(replenish_no_mem) },
126 { "replenish_add_buff_failure", IBMVETH_STAT_OFF(replenish_add_buff_failure) },
127 { "replenish_add_buff_success", IBMVETH_STAT_OFF(replenish_add_buff_success) },
128 { "rx_invalid_buffer", IBMVETH_STAT_OFF(rx_invalid_buffer) },
129 { "rx_no_buffer", IBMVETH_STAT_OFF(rx_no_buffer) },
Brian Kingddbb4de2007-08-17 09:16:43 -0500130 { "tx_map_failed", IBMVETH_STAT_OFF(tx_map_failed) },
131 { "tx_send_failed", IBMVETH_STAT_OFF(tx_send_failed) },
132};
133
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134/* simple methods of getting data from the current rxq entry */
135static inline int ibmveth_rxq_pending_buffer(struct ibmveth_adapter *adapter)
136{
137 return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].toggle == adapter->rx_queue.toggle);
138}
139
140static inline int ibmveth_rxq_buffer_valid(struct ibmveth_adapter *adapter)
141{
142 return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].valid);
143}
144
145static inline int ibmveth_rxq_frame_offset(struct ibmveth_adapter *adapter)
146{
147 return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].offset);
148}
149
150static inline int ibmveth_rxq_frame_length(struct ibmveth_adapter *adapter)
151{
152 return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].length);
153}
154
Brian Kingf4ff2872007-09-15 13:36:07 -0700155static inline int ibmveth_rxq_csum_good(struct ibmveth_adapter *adapter)
156{
157 return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].csum_good);
158}
159
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160/* setup the initial settings for a buffer pool */
Santiago Leon860f2422006-04-25 11:19:59 -0500161static void ibmveth_init_buffer_pool(struct ibmveth_buff_pool *pool, u32 pool_index, u32 pool_size, u32 buff_size, u32 pool_active)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162{
163 pool->size = pool_size;
164 pool->index = pool_index;
165 pool->buff_size = buff_size;
166 pool->threshold = pool_size / 2;
Santiago Leon860f2422006-04-25 11:19:59 -0500167 pool->active = pool_active;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168}
169
170/* allocate and setup an buffer pool - called during open */
171static int ibmveth_alloc_buffer_pool(struct ibmveth_buff_pool *pool)
172{
173 int i;
174
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400175 pool->free_map = kmalloc(sizeof(u16) * pool->size, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
177 if(!pool->free_map) {
178 return -1;
179 }
180
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400181 pool->dma_addr = kmalloc(sizeof(dma_addr_t) * pool->size, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 if(!pool->dma_addr) {
183 kfree(pool->free_map);
184 pool->free_map = NULL;
185 return -1;
186 }
187
188 pool->skbuff = kmalloc(sizeof(void*) * pool->size, GFP_KERNEL);
189
190 if(!pool->skbuff) {
191 kfree(pool->dma_addr);
192 pool->dma_addr = NULL;
193
194 kfree(pool->free_map);
195 pool->free_map = NULL;
196 return -1;
197 }
198
199 memset(pool->skbuff, 0, sizeof(void*) * pool->size);
200 memset(pool->dma_addr, 0, sizeof(dma_addr_t) * pool->size);
201
202 for(i = 0; i < pool->size; ++i) {
203 pool->free_map[i] = i;
204 }
205
206 atomic_set(&pool->available, 0);
207 pool->producer_index = 0;
208 pool->consumer_index = 0;
209
210 return 0;
211}
212
213/* replenish the buffers for a pool. note that we don't need to
214 * skb_reserve these since they are used for incoming...
215 */
216static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struct ibmveth_buff_pool *pool)
217{
218 u32 i;
219 u32 count = pool->size - atomic_read(&pool->available);
220 u32 buffers_added = 0;
221
222 mb();
223
224 for(i = 0; i < count; ++i) {
225 struct sk_buff *skb;
226 unsigned int free_index, index;
227 u64 correlator;
228 union ibmveth_buf_desc desc;
229 unsigned long lpar_rc;
230 dma_addr_t dma_addr;
231
232 skb = alloc_skb(pool->buff_size, GFP_ATOMIC);
233
234 if(!skb) {
235 ibmveth_debug_printk("replenish: unable to allocate skb\n");
236 adapter->replenish_no_mem++;
237 break;
238 }
239
David Gibson047a66d2006-10-21 10:24:13 -0700240 free_index = pool->consumer_index;
241 pool->consumer_index = (pool->consumer_index + 1) % pool->size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 index = pool->free_map[free_index];
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400243
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 ibmveth_assert(index != IBM_VETH_INVALID_MAP);
245 ibmveth_assert(pool->skbuff[index] == NULL);
246
247 dma_addr = dma_map_single(&adapter->vdev->dev, skb->data,
248 pool->buff_size, DMA_FROM_DEVICE);
249
250 pool->free_map[free_index] = IBM_VETH_INVALID_MAP;
251 pool->dma_addr[index] = dma_addr;
252 pool->skbuff[index] = skb;
253
254 correlator = ((u64)pool->index << 32) | index;
255 *(u64*)skb->data = correlator;
256
257 desc.desc = 0;
258 desc.fields.valid = 1;
259 desc.fields.length = pool->buff_size;
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400260 desc.fields.address = dma_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
262 lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc);
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400263
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200264 if(lpar_rc != H_SUCCESS) {
Santiago Leon82702d32005-10-26 10:47:23 -0600265 pool->free_map[free_index] = index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 pool->skbuff[index] = NULL;
Santiago Leon751ae212006-10-03 12:24:45 -0500267 if (pool->consumer_index == 0)
268 pool->consumer_index = pool->size - 1;
269 else
270 pool->consumer_index--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 dma_unmap_single(&adapter->vdev->dev,
272 pool->dma_addr[index], pool->buff_size,
273 DMA_FROM_DEVICE);
274 dev_kfree_skb_any(skb);
275 adapter->replenish_add_buff_failure++;
276 break;
277 } else {
278 buffers_added++;
279 adapter->replenish_add_buff_success++;
280 }
281 }
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400282
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 mb();
284 atomic_add(buffers_added, &(pool->available));
285}
286
Santiago Leone2adbcb2005-10-26 10:47:08 -0600287/* replenish routine */
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400288static void ibmveth_replenish_task(struct ibmveth_adapter *adapter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289{
Santiago Leonb6d35182005-10-26 10:47:01 -0600290 int i;
291
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 adapter->replenish_task_cycles++;
293
Santiago Leonb6d35182005-10-26 10:47:01 -0600294 for(i = 0; i < IbmVethNumBufferPools; i++)
295 if(adapter->rx_buff_pool[i].active)
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400296 ibmveth_replenish_buffer_pool(adapter,
Santiago Leonb6d35182005-10-26 10:47:01 -0600297 &adapter->rx_buff_pool[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299 adapter->rx_no_buffer = *(u64*)(((char*)adapter->buffer_list_addr) + 4096 - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300}
301
302/* empty and free ana buffer pool - also used to do cleanup in error paths */
303static void ibmveth_free_buffer_pool(struct ibmveth_adapter *adapter, struct ibmveth_buff_pool *pool)
304{
305 int i;
306
Jesper Juhlb4558ea2005-10-28 16:53:13 -0400307 kfree(pool->free_map);
308 pool->free_map = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310 if(pool->skbuff && pool->dma_addr) {
311 for(i = 0; i < pool->size; ++i) {
312 struct sk_buff *skb = pool->skbuff[i];
313 if(skb) {
314 dma_unmap_single(&adapter->vdev->dev,
315 pool->dma_addr[i],
316 pool->buff_size,
317 DMA_FROM_DEVICE);
318 dev_kfree_skb_any(skb);
319 pool->skbuff[i] = NULL;
320 }
321 }
322 }
323
324 if(pool->dma_addr) {
325 kfree(pool->dma_addr);
326 pool->dma_addr = NULL;
327 }
328
329 if(pool->skbuff) {
330 kfree(pool->skbuff);
331 pool->skbuff = NULL;
332 }
333}
334
335/* remove a buffer from a pool */
336static void ibmveth_remove_buffer_from_pool(struct ibmveth_adapter *adapter, u64 correlator)
337{
338 unsigned int pool = correlator >> 32;
339 unsigned int index = correlator & 0xffffffffUL;
340 unsigned int free_index;
341 struct sk_buff *skb;
342
343 ibmveth_assert(pool < IbmVethNumBufferPools);
344 ibmveth_assert(index < adapter->rx_buff_pool[pool].size);
345
346 skb = adapter->rx_buff_pool[pool].skbuff[index];
347
348 ibmveth_assert(skb != NULL);
349
350 adapter->rx_buff_pool[pool].skbuff[index] = NULL;
351
352 dma_unmap_single(&adapter->vdev->dev,
353 adapter->rx_buff_pool[pool].dma_addr[index],
354 adapter->rx_buff_pool[pool].buff_size,
355 DMA_FROM_DEVICE);
356
David Gibson047a66d2006-10-21 10:24:13 -0700357 free_index = adapter->rx_buff_pool[pool].producer_index;
358 adapter->rx_buff_pool[pool].producer_index
359 = (adapter->rx_buff_pool[pool].producer_index + 1)
360 % adapter->rx_buff_pool[pool].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 adapter->rx_buff_pool[pool].free_map[free_index] = index;
362
363 mb();
364
365 atomic_dec(&(adapter->rx_buff_pool[pool].available));
366}
367
368/* get the current buffer on the rx queue */
369static inline struct sk_buff *ibmveth_rxq_get_buffer(struct ibmveth_adapter *adapter)
370{
371 u64 correlator = adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator;
372 unsigned int pool = correlator >> 32;
373 unsigned int index = correlator & 0xffffffffUL;
374
375 ibmveth_assert(pool < IbmVethNumBufferPools);
376 ibmveth_assert(index < adapter->rx_buff_pool[pool].size);
377
378 return adapter->rx_buff_pool[pool].skbuff[index];
379}
380
381/* recycle the current buffer on the rx queue */
382static void ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter)
383{
384 u32 q_index = adapter->rx_queue.index;
385 u64 correlator = adapter->rx_queue.queue_addr[q_index].correlator;
386 unsigned int pool = correlator >> 32;
387 unsigned int index = correlator & 0xffffffffUL;
388 union ibmveth_buf_desc desc;
389 unsigned long lpar_rc;
390
391 ibmveth_assert(pool < IbmVethNumBufferPools);
392 ibmveth_assert(index < adapter->rx_buff_pool[pool].size);
393
Santiago Leonb6d35182005-10-26 10:47:01 -0600394 if(!adapter->rx_buff_pool[pool].active) {
395 ibmveth_rxq_harvest_buffer(adapter);
396 ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[pool]);
397 return;
398 }
399
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 desc.desc = 0;
401 desc.fields.valid = 1;
402 desc.fields.length = adapter->rx_buff_pool[pool].buff_size;
403 desc.fields.address = adapter->rx_buff_pool[pool].dma_addr[index];
404
405 lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc);
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400406
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200407 if(lpar_rc != H_SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 ibmveth_debug_printk("h_add_logical_lan_buffer failed during recycle rc=%ld", lpar_rc);
409 ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator);
410 }
411
412 if(++adapter->rx_queue.index == adapter->rx_queue.num_slots) {
413 adapter->rx_queue.index = 0;
414 adapter->rx_queue.toggle = !adapter->rx_queue.toggle;
415 }
416}
417
Michael Ellerman493a6842007-04-17 13:12:55 +1000418static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
420 ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator);
421
422 if(++adapter->rx_queue.index == adapter->rx_queue.num_slots) {
423 adapter->rx_queue.index = 0;
424 adapter->rx_queue.toggle = !adapter->rx_queue.toggle;
425 }
426}
427
428static void ibmveth_cleanup(struct ibmveth_adapter *adapter)
429{
Santiago Leonb6d35182005-10-26 10:47:01 -0600430 int i;
431
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 if(adapter->buffer_list_addr != NULL) {
433 if(!dma_mapping_error(adapter->buffer_list_dma)) {
434 dma_unmap_single(&adapter->vdev->dev,
435 adapter->buffer_list_dma, 4096,
436 DMA_BIDIRECTIONAL);
437 adapter->buffer_list_dma = DMA_ERROR_CODE;
438 }
439 free_page((unsigned long)adapter->buffer_list_addr);
440 adapter->buffer_list_addr = NULL;
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400441 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
443 if(adapter->filter_list_addr != NULL) {
444 if(!dma_mapping_error(adapter->filter_list_dma)) {
445 dma_unmap_single(&adapter->vdev->dev,
446 adapter->filter_list_dma, 4096,
447 DMA_BIDIRECTIONAL);
448 adapter->filter_list_dma = DMA_ERROR_CODE;
449 }
450 free_page((unsigned long)adapter->filter_list_addr);
451 adapter->filter_list_addr = NULL;
452 }
453
454 if(adapter->rx_queue.queue_addr != NULL) {
455 if(!dma_mapping_error(adapter->rx_queue.queue_dma)) {
456 dma_unmap_single(&adapter->vdev->dev,
457 adapter->rx_queue.queue_dma,
458 adapter->rx_queue.queue_len,
459 DMA_BIDIRECTIONAL);
460 adapter->rx_queue.queue_dma = DMA_ERROR_CODE;
461 }
462 kfree(adapter->rx_queue.queue_addr);
463 adapter->rx_queue.queue_addr = NULL;
464 }
465
Santiago Leonb6d35182005-10-26 10:47:01 -0600466 for(i = 0; i<IbmVethNumBufferPools; i++)
Santiago Leon860f2422006-04-25 11:19:59 -0500467 if (adapter->rx_buff_pool[i].active)
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400468 ibmveth_free_buffer_pool(adapter,
Santiago Leon860f2422006-04-25 11:19:59 -0500469 &adapter->rx_buff_pool[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470}
471
Michael Ellermanbbedefc2006-10-03 12:24:23 -0500472static int ibmveth_register_logical_lan(struct ibmveth_adapter *adapter,
473 union ibmveth_buf_desc rxq_desc, u64 mac_address)
474{
475 int rc, try_again = 1;
476
477 /* After a kexec the adapter will still be open, so our attempt to
478 * open it will fail. So if we get a failure we free the adapter and
479 * try again, but only once. */
480retry:
481 rc = h_register_logical_lan(adapter->vdev->unit_address,
482 adapter->buffer_list_dma, rxq_desc.desc,
483 adapter->filter_list_dma, mac_address);
484
485 if (rc != H_SUCCESS && try_again) {
486 do {
487 rc = h_free_logical_lan(adapter->vdev->unit_address);
488 } while (H_IS_LONG_BUSY(rc) || (rc == H_BUSY));
489
490 try_again = 0;
491 goto retry;
492 }
493
494 return rc;
495}
496
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497static int ibmveth_open(struct net_device *netdev)
498{
499 struct ibmveth_adapter *adapter = netdev->priv;
500 u64 mac_address = 0;
Santiago Leonb6d35182005-10-26 10:47:01 -0600501 int rxq_entries = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 unsigned long lpar_rc;
503 int rc;
504 union ibmveth_buf_desc rxq_desc;
Santiago Leonb6d35182005-10-26 10:47:01 -0600505 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
507 ibmveth_debug_printk("open starting\n");
508
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700509 napi_enable(&adapter->napi);
510
Santiago Leonb6d35182005-10-26 10:47:01 -0600511 for(i = 0; i<IbmVethNumBufferPools; i++)
512 rxq_entries += adapter->rx_buff_pool[i].size;
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 adapter->buffer_list_addr = (void*) get_zeroed_page(GFP_KERNEL);
515 adapter->filter_list_addr = (void*) get_zeroed_page(GFP_KERNEL);
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400516
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 if(!adapter->buffer_list_addr || !adapter->filter_list_addr) {
518 ibmveth_error_printk("unable to allocate filter or buffer list pages\n");
519 ibmveth_cleanup(adapter);
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700520 napi_disable(&adapter->napi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 return -ENOMEM;
522 }
523
524 adapter->rx_queue.queue_len = sizeof(struct ibmveth_rx_q_entry) * rxq_entries;
525 adapter->rx_queue.queue_addr = kmalloc(adapter->rx_queue.queue_len, GFP_KERNEL);
526
527 if(!adapter->rx_queue.queue_addr) {
528 ibmveth_error_printk("unable to allocate rx queue pages\n");
529 ibmveth_cleanup(adapter);
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700530 napi_disable(&adapter->napi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 return -ENOMEM;
532 }
533
534 adapter->buffer_list_dma = dma_map_single(&adapter->vdev->dev,
535 adapter->buffer_list_addr, 4096, DMA_BIDIRECTIONAL);
536 adapter->filter_list_dma = dma_map_single(&adapter->vdev->dev,
537 adapter->filter_list_addr, 4096, DMA_BIDIRECTIONAL);
538 adapter->rx_queue.queue_dma = dma_map_single(&adapter->vdev->dev,
539 adapter->rx_queue.queue_addr,
540 adapter->rx_queue.queue_len, DMA_BIDIRECTIONAL);
541
542 if((dma_mapping_error(adapter->buffer_list_dma) ) ||
543 (dma_mapping_error(adapter->filter_list_dma)) ||
544 (dma_mapping_error(adapter->rx_queue.queue_dma))) {
545 ibmveth_error_printk("unable to map filter or buffer list pages\n");
546 ibmveth_cleanup(adapter);
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700547 napi_disable(&adapter->napi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 return -ENOMEM;
549 }
550
551 adapter->rx_queue.index = 0;
552 adapter->rx_queue.num_slots = rxq_entries;
553 adapter->rx_queue.toggle = 1;
554
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 memcpy(&mac_address, netdev->dev_addr, netdev->addr_len);
556 mac_address = mac_address >> 16;
557
558 rxq_desc.desc = 0;
559 rxq_desc.fields.valid = 1;
560 rxq_desc.fields.length = adapter->rx_queue.queue_len;
561 rxq_desc.fields.address = adapter->rx_queue.queue_dma;
562
563 ibmveth_debug_printk("buffer list @ 0x%p\n", adapter->buffer_list_addr);
564 ibmveth_debug_printk("filter list @ 0x%p\n", adapter->filter_list_addr);
565 ibmveth_debug_printk("receive q @ 0x%p\n", adapter->rx_queue.queue_addr);
566
Santiago Leon4347ef12006-10-03 12:24:34 -0500567 h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE);
568
Michael Ellermanbbedefc2006-10-03 12:24:23 -0500569 lpar_rc = ibmveth_register_logical_lan(adapter, rxq_desc, mac_address);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200571 if(lpar_rc != H_SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 ibmveth_error_printk("h_register_logical_lan failed with %ld\n", lpar_rc);
Stephen Rothwell8168f902005-10-25 16:56:43 +1000573 ibmveth_error_printk("buffer TCE:0x%lx filter TCE:0x%lx rxq desc:0x%lx MAC:0x%lx\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 adapter->buffer_list_dma,
575 adapter->filter_list_dma,
576 rxq_desc.desc,
577 mac_address);
578 ibmveth_cleanup(adapter);
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700579 napi_disable(&adapter->napi);
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400580 return -ENONET;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 }
582
Santiago Leon860f2422006-04-25 11:19:59 -0500583 for(i = 0; i<IbmVethNumBufferPools; i++) {
584 if(!adapter->rx_buff_pool[i].active)
585 continue;
586 if (ibmveth_alloc_buffer_pool(&adapter->rx_buff_pool[i])) {
587 ibmveth_error_printk("unable to alloc pool\n");
588 adapter->rx_buff_pool[i].active = 0;
589 ibmveth_cleanup(adapter);
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700590 napi_disable(&adapter->napi);
Santiago Leon860f2422006-04-25 11:19:59 -0500591 return -ENOMEM ;
592 }
593 }
594
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 ibmveth_debug_printk("registering irq 0x%x\n", netdev->irq);
596 if((rc = request_irq(netdev->irq, &ibmveth_interrupt, 0, netdev->name, netdev)) != 0) {
597 ibmveth_error_printk("unable to request irq 0x%x, rc %d\n", netdev->irq, rc);
598 do {
599 rc = h_free_logical_lan(adapter->vdev->unit_address);
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200600 } while (H_IS_LONG_BUSY(rc) || (rc == H_BUSY));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
602 ibmveth_cleanup(adapter);
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700603 napi_disable(&adapter->napi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 return rc;
605 }
606
Santiago Leone2adbcb2005-10-26 10:47:08 -0600607 ibmveth_debug_printk("initial replenish cycle\n");
David Howells7d12e782006-10-05 14:55:46 +0100608 ibmveth_interrupt(netdev->irq, netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609
Santiago Leone2adbcb2005-10-26 10:47:08 -0600610 netif_start_queue(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
612 ibmveth_debug_printk("open complete\n");
613
614 return 0;
615}
616
617static int ibmveth_close(struct net_device *netdev)
618{
619 struct ibmveth_adapter *adapter = netdev->priv;
620 long lpar_rc;
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 ibmveth_debug_printk("close starting\n");
623
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700624 napi_disable(&adapter->napi);
625
Santiago Leon860f2422006-04-25 11:19:59 -0500626 if (!adapter->pool_config)
627 netif_stop_queue(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
629 free_irq(netdev->irq, netdev);
630
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 do {
632 lpar_rc = h_free_logical_lan(adapter->vdev->unit_address);
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200633 } while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200635 if(lpar_rc != H_SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 {
637 ibmveth_error_printk("h_free_logical_lan failed with %lx, continuing with close\n",
638 lpar_rc);
639 }
640
641 adapter->rx_no_buffer = *(u64*)(((char*)adapter->buffer_list_addr) + 4096 - 8);
642
643 ibmveth_cleanup(adapter);
644
645 ibmveth_debug_printk("close complete\n");
646
647 return 0;
648}
649
650static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) {
651 cmd->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE);
652 cmd->advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_FIBRE);
653 cmd->speed = SPEED_1000;
654 cmd->duplex = DUPLEX_FULL;
655 cmd->port = PORT_FIBRE;
656 cmd->phy_address = 0;
657 cmd->transceiver = XCVR_INTERNAL;
658 cmd->autoneg = AUTONEG_ENABLE;
659 cmd->maxtxpkt = 0;
660 cmd->maxrxpkt = 1;
661 return 0;
662}
663
664static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) {
665 strncpy(info->driver, ibmveth_driver_name, sizeof(info->driver) - 1);
666 strncpy(info->version, ibmveth_driver_version, sizeof(info->version) - 1);
667}
668
669static u32 netdev_get_link(struct net_device *dev) {
670 return 1;
671}
672
Brian King5fc7e012007-08-17 09:16:31 -0500673static void ibmveth_set_rx_csum_flags(struct net_device *dev, u32 data)
674{
675 struct ibmveth_adapter *adapter = dev->priv;
676
677 if (data)
678 adapter->rx_csum = 1;
679 else {
680 /*
681 * Since the ibmveth firmware interface does not have the concept of
682 * separate tx/rx checksum offload enable, if rx checksum is disabled
683 * we also have to disable tx checksum offload. Once we disable rx
684 * checksum offload, we are no longer allowed to send tx buffers that
685 * are not properly checksummed.
686 */
687 adapter->rx_csum = 0;
688 dev->features &= ~NETIF_F_IP_CSUM;
689 }
690}
691
692static void ibmveth_set_tx_csum_flags(struct net_device *dev, u32 data)
693{
694 struct ibmveth_adapter *adapter = dev->priv;
695
696 if (data) {
697 dev->features |= NETIF_F_IP_CSUM;
698 adapter->rx_csum = 1;
699 } else
700 dev->features &= ~NETIF_F_IP_CSUM;
701}
702
703static int ibmveth_set_csum_offload(struct net_device *dev, u32 data,
704 void (*done) (struct net_device *, u32))
705{
706 struct ibmveth_adapter *adapter = dev->priv;
707 union ibmveth_illan_attributes set_attr, clr_attr, ret_attr;
708 long ret;
709 int rc1 = 0, rc2 = 0;
710 int restart = 0;
711
712 if (netif_running(dev)) {
713 restart = 1;
714 adapter->pool_config = 1;
715 ibmveth_close(dev);
716 adapter->pool_config = 0;
717 }
718
719 set_attr.desc = 0;
720 clr_attr.desc = 0;
721
722 if (data)
723 set_attr.fields.tcp_csum_offload_ipv4 = 1;
724 else
725 clr_attr.fields.tcp_csum_offload_ipv4 = 1;
726
727 ret = h_illan_attributes(adapter->vdev->unit_address, 0, 0, &ret_attr.desc);
728
729 if (ret == H_SUCCESS && !ret_attr.fields.active_trunk &&
730 !ret_attr.fields.trunk_priority &&
731 ret_attr.fields.csum_offload_padded_pkt_support) {
732 ret = h_illan_attributes(adapter->vdev->unit_address, clr_attr.desc,
733 set_attr.desc, &ret_attr.desc);
734
735 if (ret != H_SUCCESS) {
736 rc1 = -EIO;
737 ibmveth_error_printk("unable to change checksum offload settings."
738 " %d rc=%ld\n", data, ret);
739
740 ret = h_illan_attributes(adapter->vdev->unit_address,
741 set_attr.desc, clr_attr.desc, &ret_attr.desc);
742 } else
743 done(dev, data);
744 } else {
745 rc1 = -EIO;
746 ibmveth_error_printk("unable to change checksum offload settings."
747 " %d rc=%ld ret_attr=%lx\n", data, ret, ret_attr.desc);
748 }
749
750 if (restart)
751 rc2 = ibmveth_open(dev);
752
753 return rc1 ? rc1 : rc2;
754}
755
756static int ibmveth_set_rx_csum(struct net_device *dev, u32 data)
757{
758 struct ibmveth_adapter *adapter = dev->priv;
759
760 if ((data && adapter->rx_csum) || (!data && !adapter->rx_csum))
761 return 0;
762
763 return ibmveth_set_csum_offload(dev, data, ibmveth_set_rx_csum_flags);
764}
765
766static int ibmveth_set_tx_csum(struct net_device *dev, u32 data)
767{
768 struct ibmveth_adapter *adapter = dev->priv;
769 int rc = 0;
770
771 if (data && (dev->features & NETIF_F_IP_CSUM))
772 return 0;
773 if (!data && !(dev->features & NETIF_F_IP_CSUM))
774 return 0;
775
776 if (data && !adapter->rx_csum)
777 rc = ibmveth_set_csum_offload(dev, data, ibmveth_set_tx_csum_flags);
778 else
779 ibmveth_set_tx_csum_flags(dev, data);
780
781 return rc;
782}
783
784static u32 ibmveth_get_rx_csum(struct net_device *dev)
785{
786 struct ibmveth_adapter *adapter = dev->priv;
787 return adapter->rx_csum;
788}
789
Brian Kingddbb4de2007-08-17 09:16:43 -0500790static void ibmveth_get_strings(struct net_device *dev, u32 stringset, u8 *data)
791{
792 int i;
793
794 if (stringset != ETH_SS_STATS)
795 return;
796
797 for (i = 0; i < ARRAY_SIZE(ibmveth_stats); i++, data += ETH_GSTRING_LEN)
798 memcpy(data, ibmveth_stats[i].name, ETH_GSTRING_LEN);
799}
800
801static int ibmveth_get_stats_count(struct net_device *dev)
802{
803 return ARRAY_SIZE(ibmveth_stats);
804}
805
806static void ibmveth_get_ethtool_stats(struct net_device *dev,
807 struct ethtool_stats *stats, u64 *data)
808{
809 int i;
810 struct ibmveth_adapter *adapter = dev->priv;
811
812 for (i = 0; i < ARRAY_SIZE(ibmveth_stats); i++)
813 data[i] = IBMVETH_GET_STAT(adapter, ibmveth_stats[i].offset);
814}
815
Jeff Garzik7282d492006-09-13 14:30:00 -0400816static const struct ethtool_ops netdev_ethtool_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 .get_drvinfo = netdev_get_drvinfo,
818 .get_settings = netdev_get_settings,
819 .get_link = netdev_get_link,
820 .get_sg = ethtool_op_get_sg,
821 .get_tx_csum = ethtool_op_get_tx_csum,
Brian King5fc7e012007-08-17 09:16:31 -0500822 .set_tx_csum = ibmveth_set_tx_csum,
823 .get_rx_csum = ibmveth_get_rx_csum,
824 .set_rx_csum = ibmveth_set_rx_csum,
Brian King80e53672007-08-17 09:16:37 -0500825 .get_tso = ethtool_op_get_tso,
826 .get_ufo = ethtool_op_get_ufo,
Brian Kingddbb4de2007-08-17 09:16:43 -0500827 .get_strings = ibmveth_get_strings,
828 .get_stats_count = ibmveth_get_stats_count,
829 .get_ethtool_stats = ibmveth_get_ethtool_stats,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830};
831
832static int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
833{
834 return -EOPNOTSUPP;
835}
836
837#define page_offset(v) ((unsigned long)(v) & ((1 << 12) - 1))
838
839static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev)
840{
841 struct ibmveth_adapter *adapter = netdev->priv;
Brian King3449a2a2007-08-17 09:16:49 -0500842 union ibmveth_buf_desc desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 unsigned long lpar_rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 unsigned long correlator;
Santiago Leon60296d92005-10-26 10:47:16 -0600845 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 unsigned int retry_count;
Santiago Leon60296d92005-10-26 10:47:16 -0600847 unsigned int tx_dropped = 0;
848 unsigned int tx_bytes = 0;
849 unsigned int tx_packets = 0;
850 unsigned int tx_send_failed = 0;
851 unsigned int tx_map_failed = 0;
852
Brian King3449a2a2007-08-17 09:16:49 -0500853 desc.desc = 0;
854 desc.fields.length = skb->len;
855 desc.fields.address = dma_map_single(&adapter->vdev->dev, skb->data,
856 desc.fields.length, DMA_TO_DEVICE);
857 desc.fields.valid = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
Brian Kingf4ff2872007-09-15 13:36:07 -0700859 if (skb->ip_summed == CHECKSUM_PARTIAL &&
860 ip_hdr(skb)->protocol != IPPROTO_TCP && skb_checksum_help(skb)) {
861 ibmveth_error_printk("tx: failed to checksum packet\n");
862 tx_dropped++;
863 goto out;
864 }
865
866 if (skb->ip_summed == CHECKSUM_PARTIAL) {
867 unsigned char *buf = skb_transport_header(skb) + skb->csum_offset;
868
Brian King3449a2a2007-08-17 09:16:49 -0500869 desc.fields.no_csum = 1;
870 desc.fields.csum_good = 1;
Brian Kingf4ff2872007-09-15 13:36:07 -0700871
872 /* Need to zero out the checksum */
873 buf[0] = 0;
874 buf[1] = 0;
875 }
876
Brian King3449a2a2007-08-17 09:16:49 -0500877 if (dma_mapping_error(desc.fields.address)) {
878 ibmveth_error_printk("tx: unable to map xmit buffer\n");
Santiago Leon60296d92005-10-26 10:47:16 -0600879 tx_map_failed++;
880 tx_dropped++;
881 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 }
883
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 /* send the frame. Arbitrarily set retrycount to 1024 */
885 correlator = 0;
886 retry_count = 1024;
887 do {
888 lpar_rc = h_send_logical_lan(adapter->vdev->unit_address,
Brian King3449a2a2007-08-17 09:16:49 -0500889 desc.desc, 0, 0, 0, 0, 0,
890 correlator, &correlator);
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200891 } while ((lpar_rc == H_BUSY) && (retry_count--));
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400892
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200893 if(lpar_rc != H_SUCCESS && lpar_rc != H_DROPPED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 ibmveth_error_printk("tx: h_send_logical_lan failed with rc=%ld\n", lpar_rc);
Brian King3449a2a2007-08-17 09:16:49 -0500895 ibmveth_error_printk("tx: valid=%d, len=%d, address=0x%08x\n",
896 desc.fields.valid, desc.fields.length, desc.fields.address);
Santiago Leon60296d92005-10-26 10:47:16 -0600897 tx_send_failed++;
898 tx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 } else {
Santiago Leon60296d92005-10-26 10:47:16 -0600900 tx_packets++;
901 tx_bytes += skb->len;
Santiago Leon0abe7912005-10-26 10:46:53 -0600902 netdev->trans_start = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 }
904
Brian King3449a2a2007-08-17 09:16:49 -0500905 dma_unmap_single(&adapter->vdev->dev, desc.fields.address,
906 desc.fields.length, DMA_TO_DEVICE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Santiago Leon60296d92005-10-26 10:47:16 -0600908out: spin_lock_irqsave(&adapter->stats_lock, flags);
909 adapter->stats.tx_dropped += tx_dropped;
910 adapter->stats.tx_bytes += tx_bytes;
911 adapter->stats.tx_packets += tx_packets;
912 adapter->tx_send_failed += tx_send_failed;
913 adapter->tx_map_failed += tx_map_failed;
914 spin_unlock_irqrestore(&adapter->stats_lock, flags);
915
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 dev_kfree_skb(skb);
917 return 0;
918}
919
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700920static int ibmveth_poll(struct napi_struct *napi, int budget)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921{
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700922 struct ibmveth_adapter *adapter = container_of(napi, struct ibmveth_adapter, napi);
923 struct net_device *netdev = adapter->netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 int frames_processed = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 unsigned long lpar_rc;
926
927 restart_poll:
928 do {
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700929 struct sk_buff *skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700931 if (!ibmveth_rxq_pending_buffer(adapter))
932 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700934 rmb();
935 if (!ibmveth_rxq_buffer_valid(adapter)) {
936 wmb(); /* suggested by larson1 */
937 adapter->rx_invalid_buffer++;
938 ibmveth_debug_printk("recycling invalid buffer\n");
939 ibmveth_rxq_recycle_buffer(adapter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 } else {
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700941 int length = ibmveth_rxq_frame_length(adapter);
942 int offset = ibmveth_rxq_frame_offset(adapter);
Brian Kingf4ff2872007-09-15 13:36:07 -0700943 int csum_good = ibmveth_rxq_csum_good(adapter);
944
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700945 skb = ibmveth_rxq_get_buffer(adapter);
Brian Kingf4ff2872007-09-15 13:36:07 -0700946 if (csum_good)
947 skb->ip_summed = CHECKSUM_UNNECESSARY;
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700948
949 ibmveth_rxq_harvest_buffer(adapter);
950
951 skb_reserve(skb, offset);
952 skb_put(skb, length);
953 skb->protocol = eth_type_trans(skb, netdev);
954
955 netif_receive_skb(skb); /* send it up */
956
957 adapter->stats.rx_packets++;
958 adapter->stats.rx_bytes += length;
959 frames_processed++;
960 netdev->last_rx = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 }
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700962 } while (frames_processed < budget);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963
Santiago Leone2adbcb2005-10-26 10:47:08 -0600964 ibmveth_replenish_task(adapter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700966 if (frames_processed < budget) {
967 /* We think we are done - reenable interrupts,
968 * then check once more to make sure we are done.
969 */
970 lpar_rc = h_vio_signal(adapter->vdev->unit_address,
971 VIO_IRQ_ENABLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200973 ibmveth_assert(lpar_rc == H_SUCCESS);
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700974
975 netif_rx_complete(netdev, napi);
976
977 if (ibmveth_rxq_pending_buffer(adapter) &&
978 netif_rx_reschedule(netdev, napi)) {
979 lpar_rc = h_vio_signal(adapter->vdev->unit_address,
980 VIO_IRQ_DISABLE);
981 goto restart_poll;
982 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 }
984
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700985 return frames_processed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986}
987
David Howells7d12e782006-10-05 14:55:46 +0100988static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance)
Jeff Garzikd7fbeba2006-05-24 01:31:14 -0400989{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 struct net_device *netdev = dev_instance;
991 struct ibmveth_adapter *adapter = netdev->priv;
992 unsigned long lpar_rc;
993
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700994 if (netif_rx_schedule_prep(netdev, &adapter->napi)) {
995 lpar_rc = h_vio_signal(adapter->vdev->unit_address,
996 VIO_IRQ_DISABLE);
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200997 ibmveth_assert(lpar_rc == H_SUCCESS);
Stephen Hemmingerbea33482007-10-03 16:41:36 -0700998 __netif_rx_schedule(netdev, &adapter->napi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 }
1000 return IRQ_HANDLED;
1001}
1002
1003static struct net_device_stats *ibmveth_get_stats(struct net_device *dev)
1004{
1005 struct ibmveth_adapter *adapter = dev->priv;
1006 return &adapter->stats;
1007}
1008
1009static void ibmveth_set_multicast_list(struct net_device *netdev)
1010{
1011 struct ibmveth_adapter *adapter = netdev->priv;
1012 unsigned long lpar_rc;
1013
1014 if((netdev->flags & IFF_PROMISC) || (netdev->mc_count > adapter->mcastFilterSize)) {
1015 lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
1016 IbmVethMcastEnableRecv |
1017 IbmVethMcastDisableFiltering,
1018 0);
Segher Boessenkool706c8c92006-03-30 14:49:40 +02001019 if(lpar_rc != H_SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 ibmveth_error_printk("h_multicast_ctrl rc=%ld when entering promisc mode\n", lpar_rc);
1021 }
1022 } else {
1023 struct dev_mc_list *mclist = netdev->mc_list;
1024 int i;
1025 /* clear the filter table & disable filtering */
1026 lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
1027 IbmVethMcastEnableRecv |
1028 IbmVethMcastDisableFiltering |
1029 IbmVethMcastClearFilterTable,
1030 0);
Segher Boessenkool706c8c92006-03-30 14:49:40 +02001031 if(lpar_rc != H_SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 ibmveth_error_printk("h_multicast_ctrl rc=%ld when attempting to clear filter table\n", lpar_rc);
1033 }
1034 /* add the addresses to the filter table */
1035 for(i = 0; i < netdev->mc_count; ++i, mclist = mclist->next) {
1036 // add the multicast address to the filter table
1037 unsigned long mcast_addr = 0;
1038 memcpy(((char *)&mcast_addr)+2, mclist->dmi_addr, 6);
1039 lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
1040 IbmVethMcastAddFilter,
1041 mcast_addr);
Segher Boessenkool706c8c92006-03-30 14:49:40 +02001042 if(lpar_rc != H_SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 ibmveth_error_printk("h_multicast_ctrl rc=%ld when adding an entry to the filter table\n", lpar_rc);
1044 }
1045 }
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001046
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 /* re-enable filtering */
1048 lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
1049 IbmVethMcastEnableFiltering,
1050 0);
Segher Boessenkool706c8c92006-03-30 14:49:40 +02001051 if(lpar_rc != H_SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 ibmveth_error_printk("h_multicast_ctrl rc=%ld when enabling filtering\n", lpar_rc);
1053 }
1054 }
1055}
1056
1057static int ibmveth_change_mtu(struct net_device *dev, int new_mtu)
1058{
Santiago Leonb6d35182005-10-26 10:47:01 -06001059 struct ibmveth_adapter *adapter = dev->priv;
Santiago Leon860f2422006-04-25 11:19:59 -05001060 int new_mtu_oh = new_mtu + IBMVETH_BUFF_OH;
Brian Kingce6eea52007-06-08 14:05:17 -05001061 int reinit = 0;
1062 int i, rc;
Santiago Leonb6d35182005-10-26 10:47:01 -06001063
Santiago Leon860f2422006-04-25 11:19:59 -05001064 if (new_mtu < IBMVETH_MAX_MTU)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 return -EINVAL;
Santiago Leonb6d35182005-10-26 10:47:01 -06001066
Brian Kingce6eea52007-06-08 14:05:17 -05001067 for (i = 0; i < IbmVethNumBufferPools; i++)
1068 if (new_mtu_oh < adapter->rx_buff_pool[i].buff_size)
1069 break;
1070
1071 if (i == IbmVethNumBufferPools)
1072 return -EINVAL;
1073
Santiago Leon860f2422006-04-25 11:19:59 -05001074 /* Look for an active buffer pool that can hold the new MTU */
Santiago Leonb6d35182005-10-26 10:47:01 -06001075 for(i = 0; i<IbmVethNumBufferPools; i++) {
Brian Kingce6eea52007-06-08 14:05:17 -05001076 if (!adapter->rx_buff_pool[i].active) {
1077 adapter->rx_buff_pool[i].active = 1;
1078 reinit = 1;
1079 }
1080
Santiago Leon860f2422006-04-25 11:19:59 -05001081 if (new_mtu_oh < adapter->rx_buff_pool[i].buff_size) {
Brian Kingce6eea52007-06-08 14:05:17 -05001082 if (reinit && netif_running(adapter->netdev)) {
1083 adapter->pool_config = 1;
1084 ibmveth_close(adapter->netdev);
1085 adapter->pool_config = 0;
1086 dev->mtu = new_mtu;
1087 if ((rc = ibmveth_open(adapter->netdev)))
1088 return rc;
1089 } else
1090 dev->mtu = new_mtu;
Santiago Leon860f2422006-04-25 11:19:59 -05001091 return 0;
Santiago Leonb6d35182005-10-26 10:47:01 -06001092 }
Santiago Leonb6d35182005-10-26 10:47:01 -06001093 }
Santiago Leon860f2422006-04-25 11:19:59 -05001094 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095}
1096
Santiago Leon6b422372006-10-03 12:24:28 -05001097#ifdef CONFIG_NET_POLL_CONTROLLER
1098static void ibmveth_poll_controller(struct net_device *dev)
1099{
1100 ibmveth_replenish_task(dev->priv);
Andrew Morton5f771132006-10-10 14:33:30 -07001101 ibmveth_interrupt(dev->irq, dev);
Santiago Leon6b422372006-10-03 12:24:28 -05001102}
1103#endif
1104
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
1106{
Santiago Leonb6d35182005-10-26 10:47:01 -06001107 int rc, i;
Brian Kingf4ff2872007-09-15 13:36:07 -07001108 long ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 struct net_device *netdev;
Mariusz Kozlowski9dc83af2007-08-06 23:44:03 +02001110 struct ibmveth_adapter *adapter;
Brian Kingf4ff2872007-09-15 13:36:07 -07001111 union ibmveth_illan_attributes set_attr, ret_attr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112
1113 unsigned char *mac_addr_p;
1114 unsigned int *mcastFilterSize_p;
1115
1116
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001117 ibmveth_debug_printk_no_adapter("entering ibmveth_probe for UA 0x%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 dev->unit_address);
1119
Michael Ellerman493a6842007-04-17 13:12:55 +10001120 mac_addr_p = (unsigned char *) vio_get_attribute(dev,
1121 VETH_MAC_ADDR, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 if(!mac_addr_p) {
1123 printk(KERN_ERR "(%s:%3.3d) ERROR: Can't find VETH_MAC_ADDR "
1124 "attribute\n", __FILE__, __LINE__);
1125 return 0;
1126 }
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001127
Michael Ellerman493a6842007-04-17 13:12:55 +10001128 mcastFilterSize_p = (unsigned int *) vio_get_attribute(dev,
1129 VETH_MCAST_FILTER_SIZE, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 if(!mcastFilterSize_p) {
1131 printk(KERN_ERR "(%s:%3.3d) ERROR: Can't find "
1132 "VETH_MCAST_FILTER_SIZE attribute\n",
1133 __FILE__, __LINE__);
1134 return 0;
1135 }
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001136
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 netdev = alloc_etherdev(sizeof(struct ibmveth_adapter));
1138
1139 if(!netdev)
1140 return -ENOMEM;
1141
1142 SET_MODULE_OWNER(netdev);
1143
1144 adapter = netdev->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 dev->dev.driver_data = netdev;
1146
1147 adapter->vdev = dev;
1148 adapter->netdev = netdev;
1149 adapter->mcastFilterSize= *mcastFilterSize_p;
Santiago Leon860f2422006-04-25 11:19:59 -05001150 adapter->pool_config = 0;
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001151
Stephen Hemmingerbea33482007-10-03 16:41:36 -07001152 netif_napi_add(netdev, &adapter->napi, ibmveth_poll, 16);
1153
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 /* Some older boxes running PHYP non-natively have an OF that
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001155 returns a 8-byte local-mac-address field (and the first
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 2 bytes have to be ignored) while newer boxes' OF return
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001157 a 6-byte field. Note that IEEE 1275 specifies that
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 local-mac-address must be a 6-byte field.
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001159 The RPA doc specifies that the first byte must be 10b, so
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 we'll just look for it to solve this 8 vs. 6 byte field issue */
1161
1162 if ((*mac_addr_p & 0x3) != 0x02)
1163 mac_addr_p += 2;
1164
1165 adapter->mac_addr = 0;
1166 memcpy(&adapter->mac_addr, mac_addr_p, 6);
1167
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 netdev->irq = dev->irq;
1169 netdev->open = ibmveth_open;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 netdev->stop = ibmveth_close;
1171 netdev->hard_start_xmit = ibmveth_start_xmit;
1172 netdev->get_stats = ibmveth_get_stats;
1173 netdev->set_multicast_list = ibmveth_set_multicast_list;
1174 netdev->do_ioctl = ibmveth_ioctl;
1175 netdev->ethtool_ops = &netdev_ethtool_ops;
1176 netdev->change_mtu = ibmveth_change_mtu;
1177 SET_NETDEV_DEV(netdev, &dev->dev);
Santiago Leon6b422372006-10-03 12:24:28 -05001178#ifdef CONFIG_NET_POLL_CONTROLLER
1179 netdev->poll_controller = ibmveth_poll_controller;
1180#endif
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001181 netdev->features |= NETIF_F_LLTX;
Santiago Leon60296d92005-10-26 10:47:16 -06001182 spin_lock_init(&adapter->stats_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183
1184 memcpy(&netdev->dev_addr, &adapter->mac_addr, netdev->addr_len);
1185
Santiago Leon860f2422006-04-25 11:19:59 -05001186 for(i = 0; i<IbmVethNumBufferPools; i++) {
1187 struct kobject *kobj = &adapter->rx_buff_pool[i].kobj;
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001188 ibmveth_init_buffer_pool(&adapter->rx_buff_pool[i], i,
1189 pool_count[i], pool_size[i],
Santiago Leon860f2422006-04-25 11:19:59 -05001190 pool_active[i]);
1191 kobj->parent = &dev->dev.kobj;
1192 sprintf(kobj->name, "pool%d", i);
1193 kobj->ktype = &ktype_veth_pool;
1194 kobject_register(kobj);
1195 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196
1197 ibmveth_debug_printk("adapter @ 0x%p\n", adapter);
1198
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 adapter->buffer_list_dma = DMA_ERROR_CODE;
1200 adapter->filter_list_dma = DMA_ERROR_CODE;
1201 adapter->rx_queue.queue_dma = DMA_ERROR_CODE;
1202
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 ibmveth_debug_printk("registering netdev...\n");
1204
Brian Kingf4ff2872007-09-15 13:36:07 -07001205 ret = h_illan_attributes(dev->unit_address, 0, 0, &ret_attr.desc);
1206
1207 if (ret == H_SUCCESS && !ret_attr.fields.active_trunk &&
1208 !ret_attr.fields.trunk_priority &&
1209 ret_attr.fields.csum_offload_padded_pkt_support) {
1210 set_attr.desc = 0;
1211 set_attr.fields.tcp_csum_offload_ipv4 = 1;
1212
1213 ret = h_illan_attributes(dev->unit_address, 0, set_attr.desc,
1214 &ret_attr.desc);
1215
Brian King5fc7e012007-08-17 09:16:31 -05001216 if (ret == H_SUCCESS) {
1217 adapter->rx_csum = 1;
Brian Kingf4ff2872007-09-15 13:36:07 -07001218 netdev->features |= NETIF_F_IP_CSUM;
Brian King5fc7e012007-08-17 09:16:31 -05001219 } else
Brian Kingf4ff2872007-09-15 13:36:07 -07001220 ret = h_illan_attributes(dev->unit_address, set_attr.desc,
1221 0, &ret_attr.desc);
1222 }
1223
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 rc = register_netdev(netdev);
1225
1226 if(rc) {
1227 ibmveth_debug_printk("failed to register netdev rc=%d\n", rc);
1228 free_netdev(netdev);
1229 return rc;
1230 }
1231
1232 ibmveth_debug_printk("registered\n");
1233
1234 ibmveth_proc_register_adapter(adapter);
1235
1236 return 0;
1237}
1238
1239static int __devexit ibmveth_remove(struct vio_dev *dev)
1240{
1241 struct net_device *netdev = dev->dev.driver_data;
1242 struct ibmveth_adapter *adapter = netdev->priv;
Santiago Leon860f2422006-04-25 11:19:59 -05001243 int i;
1244
1245 for(i = 0; i<IbmVethNumBufferPools; i++)
1246 kobject_unregister(&adapter->rx_buff_pool[i].kobj);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247
1248 unregister_netdev(netdev);
1249
1250 ibmveth_proc_unregister_adapter(adapter);
1251
1252 free_netdev(netdev);
1253 return 0;
1254}
1255
1256#ifdef CONFIG_PROC_FS
1257static void ibmveth_proc_register_driver(void)
1258{
Eric W. Biederman457c4cb2007-09-12 12:01:34 +02001259 ibmveth_proc_dir = proc_mkdir(IBMVETH_PROC_DIR, init_net.proc_net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 if (ibmveth_proc_dir) {
1261 SET_MODULE_OWNER(ibmveth_proc_dir);
1262 }
1263}
1264
1265static void ibmveth_proc_unregister_driver(void)
1266{
Eric W. Biederman457c4cb2007-09-12 12:01:34 +02001267 remove_proc_entry(IBMVETH_PROC_DIR, init_net.proc_net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268}
1269
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001270static void *ibmveth_seq_start(struct seq_file *seq, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271{
1272 if (*pos == 0) {
1273 return (void *)1;
1274 } else {
1275 return NULL;
1276 }
1277}
1278
1279static void *ibmveth_seq_next(struct seq_file *seq, void *v, loff_t *pos)
1280{
1281 ++*pos;
1282 return NULL;
1283}
1284
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001285static void ibmveth_seq_stop(struct seq_file *seq, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286{
1287}
1288
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001289static int ibmveth_seq_show(struct seq_file *seq, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
1291 struct ibmveth_adapter *adapter = seq->private;
1292 char *current_mac = ((char*) &adapter->netdev->dev_addr);
1293 char *firmware_mac = ((char*) &adapter->mac_addr) ;
1294
1295 seq_printf(seq, "%s %s\n\n", ibmveth_driver_string, ibmveth_driver_version);
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001296
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 seq_printf(seq, "Unit Address: 0x%x\n", adapter->vdev->unit_address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 seq_printf(seq, "Current MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
1299 current_mac[0], current_mac[1], current_mac[2],
1300 current_mac[3], current_mac[4], current_mac[5]);
1301 seq_printf(seq, "Firmware MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
1302 firmware_mac[0], firmware_mac[1], firmware_mac[2],
1303 firmware_mac[3], firmware_mac[4], firmware_mac[5]);
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001304
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 seq_printf(seq, "\nAdapter Statistics:\n");
Brian King3449a2a2007-08-17 09:16:49 -05001306 seq_printf(seq, " TX: vio_map_single failres: %ld\n", adapter->tx_map_failed);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 seq_printf(seq, " send failures: %ld\n", adapter->tx_send_failed);
1308 seq_printf(seq, " RX: replenish task cycles: %ld\n", adapter->replenish_task_cycles);
1309 seq_printf(seq, " alloc_skb_failures: %ld\n", adapter->replenish_no_mem);
1310 seq_printf(seq, " add buffer failures: %ld\n", adapter->replenish_add_buff_failure);
1311 seq_printf(seq, " invalid buffers: %ld\n", adapter->rx_invalid_buffer);
1312 seq_printf(seq, " no buffers: %ld\n", adapter->rx_no_buffer);
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001313
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 return 0;
1315}
1316static struct seq_operations ibmveth_seq_ops = {
1317 .start = ibmveth_seq_start,
1318 .next = ibmveth_seq_next,
1319 .stop = ibmveth_seq_stop,
1320 .show = ibmveth_seq_show,
1321};
1322
1323static int ibmveth_proc_open(struct inode *inode, struct file *file)
1324{
1325 struct seq_file *seq;
1326 struct proc_dir_entry *proc;
1327 int rc;
1328
1329 rc = seq_open(file, &ibmveth_seq_ops);
1330 if (!rc) {
1331 /* recover the pointer buried in proc_dir_entry data */
1332 seq = file->private_data;
1333 proc = PDE(inode);
1334 seq->private = proc->data;
1335 }
1336 return rc;
1337}
1338
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08001339static const struct file_operations ibmveth_proc_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 .owner = THIS_MODULE,
1341 .open = ibmveth_proc_open,
1342 .read = seq_read,
1343 .llseek = seq_lseek,
1344 .release = seq_release,
1345};
1346
1347static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter)
1348{
1349 struct proc_dir_entry *entry;
1350 if (ibmveth_proc_dir) {
Santiago Leon03a85d02006-10-03 12:24:39 -05001351 char u_addr[10];
1352 sprintf(u_addr, "%x", adapter->vdev->unit_address);
1353 entry = create_proc_entry(u_addr, S_IFREG, ibmveth_proc_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 if (!entry) {
1355 ibmveth_error_printk("Cannot create adapter proc entry");
1356 } else {
1357 entry->data = (void *) adapter;
1358 entry->proc_fops = &ibmveth_proc_fops;
1359 SET_MODULE_OWNER(entry);
1360 }
1361 }
1362 return;
1363}
1364
1365static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter)
1366{
1367 if (ibmveth_proc_dir) {
Santiago Leon03a85d02006-10-03 12:24:39 -05001368 char u_addr[10];
1369 sprintf(u_addr, "%x", adapter->vdev->unit_address);
1370 remove_proc_entry(u_addr, ibmveth_proc_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 }
1372}
1373
1374#else /* CONFIG_PROC_FS */
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001375static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376{
1377}
1378
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001379static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380{
1381}
1382static void ibmveth_proc_register_driver(void)
1383{
1384}
1385
1386static void ibmveth_proc_unregister_driver(void)
1387{
1388}
1389#endif /* CONFIG_PROC_FS */
1390
Santiago Leon860f2422006-04-25 11:19:59 -05001391static struct attribute veth_active_attr;
1392static struct attribute veth_num_attr;
1393static struct attribute veth_size_attr;
1394
1395static ssize_t veth_pool_show(struct kobject * kobj,
1396 struct attribute * attr, char * buf)
1397{
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001398 struct ibmveth_buff_pool *pool = container_of(kobj,
Santiago Leon860f2422006-04-25 11:19:59 -05001399 struct ibmveth_buff_pool,
1400 kobj);
1401
1402 if (attr == &veth_active_attr)
1403 return sprintf(buf, "%d\n", pool->active);
1404 else if (attr == &veth_num_attr)
1405 return sprintf(buf, "%d\n", pool->size);
1406 else if (attr == &veth_size_attr)
1407 return sprintf(buf, "%d\n", pool->buff_size);
1408 return 0;
1409}
1410
1411static ssize_t veth_pool_store(struct kobject * kobj, struct attribute * attr,
1412const char * buf, size_t count)
1413{
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001414 struct ibmveth_buff_pool *pool = container_of(kobj,
Santiago Leon860f2422006-04-25 11:19:59 -05001415 struct ibmveth_buff_pool,
1416 kobj);
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001417 struct net_device *netdev =
Santiago Leon860f2422006-04-25 11:19:59 -05001418 container_of(kobj->parent, struct device, kobj)->driver_data;
1419 struct ibmveth_adapter *adapter = netdev->priv;
1420 long value = simple_strtol(buf, NULL, 10);
1421 long rc;
1422
1423 if (attr == &veth_active_attr) {
1424 if (value && !pool->active) {
Brian King4aa9c932007-06-08 14:05:16 -05001425 if (netif_running(netdev)) {
1426 if(ibmveth_alloc_buffer_pool(pool)) {
1427 ibmveth_error_printk("unable to alloc pool\n");
1428 return -ENOMEM;
1429 }
1430 pool->active = 1;
1431 adapter->pool_config = 1;
1432 ibmveth_close(netdev);
1433 adapter->pool_config = 0;
1434 if ((rc = ibmveth_open(netdev)))
1435 return rc;
1436 } else
1437 pool->active = 1;
Santiago Leon860f2422006-04-25 11:19:59 -05001438 } else if (!value && pool->active) {
1439 int mtu = netdev->mtu + IBMVETH_BUFF_OH;
1440 int i;
1441 /* Make sure there is a buffer pool with buffers that
1442 can hold a packet of the size of the MTU */
Brian King76b9cfc2007-08-03 13:55:19 +10001443 for (i = 0; i < IbmVethNumBufferPools; i++) {
Santiago Leon860f2422006-04-25 11:19:59 -05001444 if (pool == &adapter->rx_buff_pool[i])
1445 continue;
1446 if (!adapter->rx_buff_pool[i].active)
1447 continue;
Brian King76b9cfc2007-08-03 13:55:19 +10001448 if (mtu <= adapter->rx_buff_pool[i].buff_size)
1449 break;
Santiago Leon860f2422006-04-25 11:19:59 -05001450 }
Brian King76b9cfc2007-08-03 13:55:19 +10001451
1452 if (i == IbmVethNumBufferPools) {
Santiago Leon860f2422006-04-25 11:19:59 -05001453 ibmveth_error_printk("no active pool >= MTU\n");
1454 return -EPERM;
1455 }
Brian King76b9cfc2007-08-03 13:55:19 +10001456
1457 pool->active = 0;
1458 if (netif_running(netdev)) {
1459 adapter->pool_config = 1;
1460 ibmveth_close(netdev);
1461 adapter->pool_config = 0;
1462 if ((rc = ibmveth_open(netdev)))
1463 return rc;
1464 }
Santiago Leon860f2422006-04-25 11:19:59 -05001465 }
1466 } else if (attr == &veth_num_attr) {
1467 if (value <= 0 || value > IBMVETH_MAX_POOL_COUNT)
1468 return -EINVAL;
1469 else {
Brian King4aa9c932007-06-08 14:05:16 -05001470 if (netif_running(netdev)) {
1471 adapter->pool_config = 1;
1472 ibmveth_close(netdev);
1473 adapter->pool_config = 0;
1474 pool->size = value;
1475 if ((rc = ibmveth_open(netdev)))
1476 return rc;
1477 } else
1478 pool->size = value;
Santiago Leon860f2422006-04-25 11:19:59 -05001479 }
1480 } else if (attr == &veth_size_attr) {
1481 if (value <= IBMVETH_BUFF_OH || value > IBMVETH_MAX_BUF_SIZE)
1482 return -EINVAL;
1483 else {
Brian King4aa9c932007-06-08 14:05:16 -05001484 if (netif_running(netdev)) {
1485 adapter->pool_config = 1;
1486 ibmveth_close(netdev);
1487 adapter->pool_config = 0;
1488 pool->buff_size = value;
1489 if ((rc = ibmveth_open(netdev)))
1490 return rc;
1491 } else
1492 pool->buff_size = value;
Santiago Leon860f2422006-04-25 11:19:59 -05001493 }
1494 }
1495
1496 /* kick the interrupt handler to allocate/deallocate pools */
David Howells7d12e782006-10-05 14:55:46 +01001497 ibmveth_interrupt(netdev->irq, netdev);
Santiago Leon860f2422006-04-25 11:19:59 -05001498 return count;
1499}
1500
1501
1502#define ATTR(_name, _mode) \
1503 struct attribute veth_##_name##_attr = { \
Tejun Heo7b595752007-06-14 03:45:17 +09001504 .name = __stringify(_name), .mode = _mode, \
Santiago Leon860f2422006-04-25 11:19:59 -05001505 };
1506
1507static ATTR(active, 0644);
1508static ATTR(num, 0644);
1509static ATTR(size, 0644);
1510
1511static struct attribute * veth_pool_attrs[] = {
1512 &veth_active_attr,
1513 &veth_num_attr,
1514 &veth_size_attr,
1515 NULL,
1516};
1517
1518static struct sysfs_ops veth_pool_ops = {
1519 .show = veth_pool_show,
1520 .store = veth_pool_store,
1521};
1522
1523static struct kobj_type ktype_veth_pool = {
1524 .release = NULL,
1525 .sysfs_ops = &veth_pool_ops,
1526 .default_attrs = veth_pool_attrs,
1527};
1528
1529
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530static struct vio_device_id ibmveth_device_table[] __devinitdata= {
1531 { "network", "IBM,l-lan"},
Stephen Rothwellfb120da2005-08-17 16:42:59 +10001532 { "", "" }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534MODULE_DEVICE_TABLE(vio, ibmveth_device_table);
1535
1536static struct vio_driver ibmveth_driver = {
Stephen Rothwell6fdf5392005-10-24 14:53:21 +10001537 .id_table = ibmveth_device_table,
1538 .probe = ibmveth_probe,
1539 .remove = ibmveth_remove,
1540 .driver = {
1541 .name = ibmveth_driver_name,
Stephen Rothwell915124d2005-10-24 15:12:22 +10001542 .owner = THIS_MODULE,
Stephen Rothwell6fdf5392005-10-24 14:53:21 +10001543 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544};
1545
1546static int __init ibmveth_module_init(void)
1547{
1548 ibmveth_printk("%s: %s %s\n", ibmveth_driver_name, ibmveth_driver_string, ibmveth_driver_version);
1549
1550 ibmveth_proc_register_driver();
1551
1552 return vio_register_driver(&ibmveth_driver);
1553}
1554
1555static void __exit ibmveth_module_exit(void)
1556{
1557 vio_unregister_driver(&ibmveth_driver);
1558 ibmveth_proc_unregister_driver();
Jeff Garzikd7fbeba2006-05-24 01:31:14 -04001559}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560
1561module_init(ibmveth_module_init);
1562module_exit(ibmveth_module_exit);