blob: be716e45f7acddcddae29f694252177a89c2eb9b [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * IUCV network driver
3 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08004 * Copyright 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
6 *
Cornelia Huck4ce3b302006-01-14 13:21:04 -08007 * Sysfs integration and all bugs therein by Cornelia Huck
8 * (cornelia.huck@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 *
10 * Documentation used:
11 * the source of the original IUCV driver by:
12 * Stefan Hegewald <hegewald@de.ibm.com>
13 * Hartmut Penner <hpenner@de.ibm.com>
14 * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
15 * Martin Schwidefsky (schwidefsky@de.ibm.com)
16 * Alan Altmark (Alan_Altmark@us.ibm.com) Sept. 2000
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2, or (at your option)
21 * any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070032 */
Jeff Garzike82b0f22006-05-26 21:58:38 -040033
Ursula Braun8f7c5022008-12-25 13:39:47 +010034#define KMSG_COMPONENT "netiucv"
35#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#undef DEBUG
38
39#include <linux/module.h>
40#include <linux/init.h>
41#include <linux/kernel.h>
42#include <linux/slab.h>
43#include <linux/errno.h>
44#include <linux/types.h>
45#include <linux/interrupt.h>
46#include <linux/timer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#include <linux/bitops.h>
48
49#include <linux/signal.h>
50#include <linux/string.h>
51#include <linux/device.h>
52
53#include <linux/ip.h>
54#include <linux/if_arp.h>
55#include <linux/tcp.h>
56#include <linux/skbuff.h>
57#include <linux/ctype.h>
58#include <net/dst.h>
59
60#include <asm/io.h>
61#include <asm/uaccess.h>
62
Martin Schwidefskyeebce382007-02-08 13:50:33 -080063#include <net/iucv/iucv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070064#include "fsm.h"
65
66MODULE_AUTHOR
67 ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)");
68MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
69
Martin Schwidefskyeebce382007-02-08 13:50:33 -080070/**
71 * Debug Facility stuff
72 */
73#define IUCV_DBF_SETUP_NAME "iucv_setup"
74#define IUCV_DBF_SETUP_LEN 32
75#define IUCV_DBF_SETUP_PAGES 2
76#define IUCV_DBF_SETUP_NR_AREAS 1
77#define IUCV_DBF_SETUP_LEVEL 3
78
79#define IUCV_DBF_DATA_NAME "iucv_data"
80#define IUCV_DBF_DATA_LEN 128
81#define IUCV_DBF_DATA_PAGES 2
82#define IUCV_DBF_DATA_NR_AREAS 1
83#define IUCV_DBF_DATA_LEVEL 2
84
85#define IUCV_DBF_TRACE_NAME "iucv_trace"
86#define IUCV_DBF_TRACE_LEN 16
87#define IUCV_DBF_TRACE_PAGES 4
88#define IUCV_DBF_TRACE_NR_AREAS 1
89#define IUCV_DBF_TRACE_LEVEL 3
90
91#define IUCV_DBF_TEXT(name,level,text) \
92 do { \
93 debug_text_event(iucv_dbf_##name,level,text); \
94 } while (0)
95
96#define IUCV_DBF_HEX(name,level,addr,len) \
97 do { \
98 debug_event(iucv_dbf_##name,level,(void*)(addr),len); \
99 } while (0)
100
101DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf);
102
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100103/* Allow to sort out low debug levels early to avoid wasted sprints */
104static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
105{
106 return (level <= dbf_grp->level);
107}
108
109#define IUCV_DBF_TEXT_(name, level, text...) \
110 do { \
111 if (iucv_dbf_passes(iucv_dbf_##name, level)) { \
112 char* iucv_dbf_txt_buf = \
113 get_cpu_var(iucv_dbf_txt_buf); \
114 sprintf(iucv_dbf_txt_buf, text); \
115 debug_text_event(iucv_dbf_##name, level, \
116 iucv_dbf_txt_buf); \
117 put_cpu_var(iucv_dbf_txt_buf); \
118 } \
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800119 } while (0)
120
121#define IUCV_DBF_SPRINTF(name,level,text...) \
122 do { \
123 debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
124 debug_sprintf_event(iucv_dbf_trace, level, text ); \
125 } while (0)
126
127/**
128 * some more debug stuff
129 */
130#define IUCV_HEXDUMP16(importance,header,ptr) \
131PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
132 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
133 *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
134 *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
135 *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
136 *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
137 *(((char*)ptr)+12),*(((char*)ptr)+13), \
138 *(((char*)ptr)+14),*(((char*)ptr)+15)); \
139PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
140 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
141 *(((char*)ptr)+16),*(((char*)ptr)+17), \
142 *(((char*)ptr)+18),*(((char*)ptr)+19), \
143 *(((char*)ptr)+20),*(((char*)ptr)+21), \
144 *(((char*)ptr)+22),*(((char*)ptr)+23), \
145 *(((char*)ptr)+24),*(((char*)ptr)+25), \
146 *(((char*)ptr)+26),*(((char*)ptr)+27), \
147 *(((char*)ptr)+28),*(((char*)ptr)+29), \
148 *(((char*)ptr)+30),*(((char*)ptr)+31));
149
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150#define PRINTK_HEADER " iucv: " /* for debugging */
151
152static struct device_driver netiucv_driver = {
Cornelia Huck22195102008-02-08 13:09:02 +0100153 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 .name = "netiucv",
155 .bus = &iucv_bus,
156};
157
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800158static int netiucv_callback_connreq(struct iucv_path *,
159 u8 ipvmid[8], u8 ipuser[16]);
160static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
161static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
162static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
163static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
164static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
165static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
166
167static struct iucv_handler netiucv_handler = {
168 .path_pending = netiucv_callback_connreq,
169 .path_complete = netiucv_callback_connack,
170 .path_severed = netiucv_callback_connrej,
171 .path_quiesced = netiucv_callback_connsusp,
172 .path_resumed = netiucv_callback_connres,
173 .message_pending = netiucv_callback_rx,
174 .message_complete = netiucv_callback_txdone
175};
176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177/**
178 * Per connection profiling data
179 */
180struct connection_profile {
181 unsigned long maxmulti;
182 unsigned long maxcqueue;
183 unsigned long doios_single;
184 unsigned long doios_multi;
185 unsigned long txlen;
186 unsigned long tx_time;
187 struct timespec send_stamp;
188 unsigned long tx_pending;
189 unsigned long tx_max_pending;
190};
191
192/**
193 * Representation of one iucv connection
194 */
195struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800196 struct list_head list;
197 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 struct sk_buff *rx_buff;
199 struct sk_buff *tx_buff;
200 struct sk_buff_head collect_queue;
201 struct sk_buff_head commit_queue;
202 spinlock_t collect_lock;
203 int collect_len;
204 int max_buffsize;
205 fsm_timer timer;
206 fsm_instance *fsm;
207 struct net_device *netdev;
208 struct connection_profile prof;
209 char userid[9];
210};
211
212/**
213 * Linked list of all connection structs.
214 */
Denis Chengc11ca972008-01-26 14:11:13 +0100215static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200216static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217
218/**
219 * Representation of event-data for the
220 * connection state machine.
221 */
222struct iucv_event {
223 struct iucv_connection *conn;
224 void *data;
225};
226
227/**
228 * Private part of the network device structure
229 */
230struct netiucv_priv {
231 struct net_device_stats stats;
232 unsigned long tbusy;
233 fsm_instance *fsm;
234 struct iucv_connection *conn;
235 struct device *dev;
236};
237
238/**
239 * Link level header for a packet.
240 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800241struct ll_header {
242 u16 next;
243};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800245#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246#define NETIUCV_BUFSIZE_MAX 32768
247#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
248#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
249#define NETIUCV_MTU_DEFAULT 9216
250#define NETIUCV_QUEUELEN_DEFAULT 50
251#define NETIUCV_TIMEOUT_5SEC 5000
252
253/**
254 * Compatibility macros for busy handling
255 * of network devices.
256 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800257static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800259 struct netiucv_priv *priv = netdev_priv(dev);
260 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 netif_wake_queue(dev);
262}
263
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800264static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800266 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800268 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269}
270
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800271static u8 iucvMagic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
273 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
274};
275
276/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 * Convert an iucv userId to its printable
278 * form (strip whitespace at end).
279 *
280 * @param An iucv userId
281 *
282 * @returns The printable string (static data!!)
283 */
Martin Schwidefskyd4614622007-06-20 13:03:57 +0200284static char *netiucv_printname(char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285{
286 static char tmp[9];
287 char *p = tmp;
288 memcpy(tmp, name, 8);
289 tmp[8] = '\0';
290 while (*p && (!isspace(*p)))
291 p++;
292 *p = '\0';
293 return tmp;
294}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296/**
297 * States of the interface statemachine.
298 */
299enum dev_states {
300 DEV_STATE_STOPPED,
301 DEV_STATE_STARTWAIT,
302 DEV_STATE_STOPWAIT,
303 DEV_STATE_RUNNING,
304 /**
305 * MUST be always the last element!!
306 */
307 NR_DEV_STATES
308};
309
310static const char *dev_state_names[] = {
311 "Stopped",
312 "StartWait",
313 "StopWait",
314 "Running",
315};
316
317/**
318 * Events of the interface statemachine.
319 */
320enum dev_events {
321 DEV_EVENT_START,
322 DEV_EVENT_STOP,
323 DEV_EVENT_CONUP,
324 DEV_EVENT_CONDOWN,
325 /**
326 * MUST be always the last element!!
327 */
328 NR_DEV_EVENTS
329};
330
331static const char *dev_event_names[] = {
332 "Start",
333 "Stop",
334 "Connection up",
335 "Connection down",
336};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400337
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338/**
339 * Events of the connection statemachine
340 */
341enum conn_events {
342 /**
343 * Events, representing callbacks from
344 * lowlevel iucv layer)
345 */
346 CONN_EVENT_CONN_REQ,
347 CONN_EVENT_CONN_ACK,
348 CONN_EVENT_CONN_REJ,
349 CONN_EVENT_CONN_SUS,
350 CONN_EVENT_CONN_RES,
351 CONN_EVENT_RX,
352 CONN_EVENT_TXDONE,
353
354 /**
355 * Events, representing errors return codes from
356 * calls to lowlevel iucv layer
357 */
358
359 /**
360 * Event, representing timer expiry.
361 */
362 CONN_EVENT_TIMER,
363
364 /**
365 * Events, representing commands from upper levels.
366 */
367 CONN_EVENT_START,
368 CONN_EVENT_STOP,
369
370 /**
371 * MUST be always the last element!!
372 */
373 NR_CONN_EVENTS,
374};
375
376static const char *conn_event_names[] = {
377 "Remote connection request",
378 "Remote connection acknowledge",
379 "Remote connection reject",
380 "Connection suspended",
381 "Connection resumed",
382 "Data received",
383 "Data sent",
384
385 "Timer",
386
387 "Start",
388 "Stop",
389};
390
391/**
392 * States of the connection statemachine.
393 */
394enum conn_states {
395 /**
396 * Connection not assigned to any device,
397 * initial state, invalid
398 */
399 CONN_STATE_INVALID,
400
401 /**
402 * Userid assigned but not operating
403 */
404 CONN_STATE_STOPPED,
405
406 /**
407 * Connection registered,
408 * no connection request sent yet,
409 * no connection request received
410 */
411 CONN_STATE_STARTWAIT,
412
413 /**
414 * Connection registered and connection request sent,
415 * no acknowledge and no connection request received yet.
416 */
417 CONN_STATE_SETUPWAIT,
418
419 /**
420 * Connection up and running idle
421 */
422 CONN_STATE_IDLE,
423
424 /**
425 * Data sent, awaiting CONN_EVENT_TXDONE
426 */
427 CONN_STATE_TX,
428
429 /**
430 * Error during registration.
431 */
432 CONN_STATE_REGERR,
433
434 /**
435 * Error during registration.
436 */
437 CONN_STATE_CONNERR,
438
439 /**
440 * MUST be always the last element!!
441 */
442 NR_CONN_STATES,
443};
444
445static const char *conn_state_names[] = {
446 "Invalid",
447 "Stopped",
448 "StartWait",
449 "SetupWait",
450 "Idle",
451 "TX",
452 "Terminating",
453 "Registration error",
454 "Connect error",
455};
456
Jeff Garzike82b0f22006-05-26 21:58:38 -0400457
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458/**
459 * Debug Facility Stuff
460 */
461static debug_info_t *iucv_dbf_setup = NULL;
462static debug_info_t *iucv_dbf_data = NULL;
463static debug_info_t *iucv_dbf_trace = NULL;
464
465DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
466
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800467static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468{
469 if (iucv_dbf_setup)
470 debug_unregister(iucv_dbf_setup);
471 if (iucv_dbf_data)
472 debug_unregister(iucv_dbf_data);
473 if (iucv_dbf_trace)
474 debug_unregister(iucv_dbf_trace);
475}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800476static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
478 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700479 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 IUCV_DBF_SETUP_NR_AREAS,
481 IUCV_DBF_SETUP_LEN);
482 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700483 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 IUCV_DBF_DATA_NR_AREAS,
485 IUCV_DBF_DATA_LEN);
486 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700487 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 IUCV_DBF_TRACE_NR_AREAS,
489 IUCV_DBF_TRACE_LEN);
490
491 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
492 (iucv_dbf_trace == NULL)) {
493 iucv_unregister_dbf_views();
494 return -ENOMEM;
495 }
496 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
497 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
498
499 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
500 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
501
502 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
503 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
504
505 return 0;
506}
507
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800508/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800510 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800512static void netiucv_callback_rx(struct iucv_path *path,
513 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800515 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 struct iucv_event ev;
517
518 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800519 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
521}
522
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800523static void netiucv_callback_txdone(struct iucv_path *path,
524 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800526 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 struct iucv_event ev;
528
529 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800530 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
532}
533
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800534static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800536 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800538 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539}
540
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800541static int netiucv_callback_connreq(struct iucv_path *path,
542 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800544 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 struct iucv_event ev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800546 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800548 if (memcmp(iucvMagic, ipuser, sizeof(ipuser)))
549 /* ipuser must match iucvMagic. */
550 return -EINVAL;
551 rc = -EINVAL;
552 read_lock_bh(&iucv_connection_rwlock);
553 list_for_each_entry(conn, &iucv_connection_list, list) {
554 if (strncmp(ipvmid, conn->userid, 8))
555 continue;
556 /* Found a matching connection for this path. */
557 conn->path = path;
558 ev.conn = conn;
559 ev.data = path;
560 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
561 rc = 0;
562 }
563 read_unlock_bh(&iucv_connection_rwlock);
564 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800567static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800569 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800571 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
573
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800574static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800576 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800578 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579}
580
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800581static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800583 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800585 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586}
587
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100589 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100591static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
593}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400594
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800595/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800597 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
599/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800600 * netiucv_unpack_skb
601 * @conn: The connection where this skb has been received.
602 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800604 * Unpack a just received skb and hand it over to upper layers.
605 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800607static void netiucv_unpack_skb(struct iucv_connection *conn,
608 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609{
610 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800611 struct netiucv_priv *privptr = netdev_priv(dev);
612 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
614 skb_put(pskb, NETIUCV_HDRLEN);
615 pskb->dev = dev;
616 pskb->ip_summed = CHECKSUM_NONE;
617 pskb->protocol = ntohs(ETH_P_IP);
618
619 while (1) {
620 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800621 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
623 if (!header->next)
624 break;
625
626 skb_pull(pskb, NETIUCV_HDRLEN);
627 header->next -= offset;
628 offset += header->next;
629 header->next -= NETIUCV_HDRLEN;
630 if (skb_tailroom(pskb) < header->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
632 header->next, skb_tailroom(pskb));
633 return;
634 }
635 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700636 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 skb = dev_alloc_skb(pskb->len);
638 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 IUCV_DBF_TEXT(data, 2,
640 "Out of memory in netiucv_unpack_skb\n");
641 privptr->stats.rx_dropped++;
642 return;
643 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300644 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
645 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700646 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 skb->dev = pskb->dev;
648 skb->protocol = pskb->protocol;
649 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800650 privptr->stats.rx_packets++;
651 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 /*
653 * Since receiving is always initiated from a tasklet (in iucv.c),
654 * we must use netif_rx_ni() instead of netif_rx()
655 */
656 netif_rx_ni(skb);
657 dev->last_rx = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 skb_pull(pskb, header->next);
659 skb_put(pskb, NETIUCV_HDRLEN);
660 }
661}
662
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800663static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800665 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800667 struct iucv_message *msg = ev->data;
668 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 int rc;
670
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200671 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
673 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800674 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800676 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 return;
678 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800679 if (msg->length > conn->max_buffsize) {
680 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 privptr->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800683 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 return;
685 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700686 conn->rx_buff->data = conn->rx_buff->head;
687 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800689 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
690 msg->length, NULL);
691 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 privptr->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
694 return;
695 }
696 netiucv_unpack_skb(conn, conn->rx_buff);
697}
698
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800699static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800701 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800703 struct iucv_message *msg = ev->data;
704 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800706 u32 single_flag = msg->tag;
707 u32 txbytes = 0;
708 u32 txpackets = 0;
709 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 struct sk_buff *skb;
711 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800712 struct ll_header header;
713 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200715 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800717 if (conn && conn->netdev)
718 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 conn->prof.tx_pending--;
720 if (single_flag) {
721 if ((skb = skb_dequeue(&conn->commit_queue))) {
722 atomic_dec(&skb->users);
723 dev_kfree_skb_any(skb);
724 if (privptr) {
725 privptr->stats.tx_packets++;
726 privptr->stats.tx_bytes +=
727 (skb->len - NETIUCV_HDRLEN
728 - NETIUCV_HDRLEN);
729 }
730 }
731 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700732 conn->tx_buff->data = conn->tx_buff->head;
733 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 conn->tx_buff->len = 0;
735 spin_lock_irqsave(&conn->collect_lock, saveflags);
736 while ((skb = skb_dequeue(&conn->collect_queue))) {
737 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
738 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
739 NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300740 skb_copy_from_linear_data(skb,
741 skb_put(conn->tx_buff, skb->len),
742 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 txbytes += skb->len;
744 txpackets++;
745 stat_maxcq++;
746 atomic_dec(&skb->users);
747 dev_kfree_skb_any(skb);
748 }
749 if (conn->collect_len > conn->prof.maxmulti)
750 conn->prof.maxmulti = conn->collect_len;
751 conn->collect_len = 0;
752 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800753 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800755 return;
756 }
757
758 header.next = 0;
759 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
john stultz2c6b47d2007-07-24 17:47:43 -0700760 conn->prof.send_stamp = current_kernel_time();
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800761 txmsg.class = 0;
762 txmsg.tag = 0;
763 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
764 conn->tx_buff->data, conn->tx_buff->len);
765 conn->prof.doios_multi++;
766 conn->prof.txlen += conn->tx_buff->len;
767 conn->prof.tx_pending++;
768 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
769 conn->prof.tx_max_pending = conn->prof.tx_pending;
770 if (rc) {
771 conn->prof.tx_pending--;
772 fsm_newstate(fi, CONN_STATE_IDLE);
773 if (privptr)
774 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800775 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
776 } else {
777 if (privptr) {
778 privptr->stats.tx_packets += txpackets;
779 privptr->stats.tx_bytes += txbytes;
780 }
781 if (stat_maxcq > conn->prof.maxcqueue)
782 conn->prof.maxcqueue = stat_maxcq;
783 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784}
785
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800786static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800788 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800790 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800792 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200795 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800797 conn->path = path;
798 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
799 path->flags = 0;
800 rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
803 return;
804 }
805 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800806 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
808}
809
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800810static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800812 struct iucv_event *ev = arg;
813 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200815 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800816 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817}
818
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800819static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800821 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800823 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200825 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 fsm_deltimer(&conn->timer);
827 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800828 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
830}
831
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800832static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800834 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200836 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800838 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 fsm_newstate(fi, CONN_STATE_STARTWAIT);
840}
841
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800842static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800844 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800846 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200848 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849
850 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800851 iucv_path_sever(conn->path, NULL);
Ursula Braun8f7c5022008-12-25 13:39:47 +0100852 dev_info(privptr->dev, "The peer interface of the IUCV device"
853 " has closed the connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800855 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 fsm_newstate(fi, CONN_STATE_STARTWAIT);
857 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
858}
859
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800860static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800862 struct iucv_connection *conn = arg;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100863 struct net_device *netdev = conn->netdev;
864 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 int rc;
866
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200867 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800869 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Ursula Braunf082bca2008-07-14 09:59:30 +0200870 IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n",
Ursula Braun8f7c5022008-12-25 13:39:47 +0100871 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800873 /*
874 * We must set the state before calling iucv_connect because the
875 * callback handler could be called at any point after the connection
876 * request is sent
877 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
879 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800880 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
881 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
882 NULL, iucvMagic, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800884 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100885 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800886 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
887 CONN_EVENT_TIMER, conn);
888 return;
889 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100890 dev_warn(privptr->dev,
891 "The IUCV device failed to connect to z/VM guest %s\n",
892 netiucv_printname(conn->userid));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800893 fsm_newstate(fi, CONN_STATE_STARTWAIT);
894 break;
895 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100896 dev_warn(privptr->dev,
897 "The IUCV device failed to connect to the peer on z/VM"
898 " guest %s\n", netiucv_printname(conn->userid));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800899 fsm_newstate(fi, CONN_STATE_STARTWAIT);
900 break;
901 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100902 dev_err(privptr->dev,
903 "Connecting the IUCV device would exceed the maximum"
904 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800905 fsm_newstate(fi, CONN_STATE_CONNERR);
906 break;
907 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100908 dev_err(privptr->dev,
909 "z/VM guest %s has too many IUCV connections"
910 " to connect with the IUCV device\n",
911 netiucv_printname(conn->userid));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800912 fsm_newstate(fi, CONN_STATE_CONNERR);
913 break;
914 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100915 dev_err(privptr->dev,
916 "The IUCV device cannot connect to a z/VM guest with no"
917 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800918 fsm_newstate(fi, CONN_STATE_CONNERR);
919 break;
920 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100921 dev_err(privptr->dev,
922 "Connecting the IUCV device failed with error %d\n",
923 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800924 fsm_newstate(fi, CONN_STATE_CONNERR);
925 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 }
927 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800928 kfree(conn->path);
929 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930}
931
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800932static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933{
934 struct sk_buff *skb;
935
936 while ((skb = skb_dequeue(q))) {
937 atomic_dec(&skb->users);
938 dev_kfree_skb_any(skb);
939 }
940}
941
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800942static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800944 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 struct iucv_connection *conn = ev->conn;
946 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800947 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200949 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950
951 fsm_deltimer(&conn->timer);
952 fsm_newstate(fi, CONN_STATE_STOPPED);
953 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800954 if (conn->path) {
955 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
956 iucv_path_sever(conn->path, iucvMagic);
957 kfree(conn->path);
958 conn->path = NULL;
959 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 netiucv_purge_skb_queue(&conn->commit_queue);
961 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
962}
963
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800964static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800966 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 struct net_device *netdev = conn->netdev;
968
Ursula Braunf082bca2008-07-14 09:59:30 +0200969 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
970 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971}
972
973static const fsm_node conn_fsm[] = {
974 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
975 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
976
977 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
978 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
979 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
980 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
981 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
982 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
983 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
984
985 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
986 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
987 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
988 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
989 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
990
991 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
992 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
993
994 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
995 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
996 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
997
998 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
999 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1000
1001 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1002 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1003};
1004
1005static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1006
Jeff Garzike82b0f22006-05-26 21:58:38 -04001007
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001008/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001010 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011
1012/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001013 * dev_action_start
1014 * @fi: An instance of an interface statemachine.
1015 * @event: The event, just happened.
1016 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001018 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001020static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001022 struct net_device *dev = arg;
1023 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001025 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001028 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029}
1030
1031/**
1032 * Shutdown connection by sending CONN_EVENT_STOP to it.
1033 *
1034 * @param fi An instance of an interface statemachine.
1035 * @param event The event, just happened.
1036 * @param arg Generic pointer, casted from struct net_device * upon call.
1037 */
1038static void
1039dev_action_stop(fsm_instance *fi, int event, void *arg)
1040{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001041 struct net_device *dev = arg;
1042 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 struct iucv_event ev;
1044
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001045 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046
1047 ev.conn = privptr->conn;
1048
1049 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1050 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1051}
1052
1053/**
1054 * Called from connection statemachine
1055 * when a connection is up and running.
1056 *
1057 * @param fi An instance of an interface statemachine.
1058 * @param event The event, just happened.
1059 * @param arg Generic pointer, casted from struct net_device * upon call.
1060 */
1061static void
1062dev_action_connup(fsm_instance *fi, int event, void *arg)
1063{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001064 struct net_device *dev = arg;
1065 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001067 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068
1069 switch (fsm_getstate(fi)) {
1070 case DEV_STATE_STARTWAIT:
1071 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001072 dev_info(privptr->dev,
1073 "The IUCV device has been connected"
1074 " successfully to %s\n", privptr->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 IUCV_DBF_TEXT(setup, 3,
1076 "connection is up and running\n");
1077 break;
1078 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 IUCV_DBF_TEXT(data, 2,
1080 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1081 break;
1082 }
1083}
1084
1085/**
1086 * Called from connection statemachine
1087 * when a connection has been shutdown.
1088 *
1089 * @param fi An instance of an interface statemachine.
1090 * @param event The event, just happened.
1091 * @param arg Generic pointer, casted from struct net_device * upon call.
1092 */
1093static void
1094dev_action_conndown(fsm_instance *fi, int event, void *arg)
1095{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001096 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097
1098 switch (fsm_getstate(fi)) {
1099 case DEV_STATE_RUNNING:
1100 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1101 break;
1102 case DEV_STATE_STOPWAIT:
1103 fsm_newstate(fi, DEV_STATE_STOPPED);
1104 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1105 break;
1106 }
1107}
1108
1109static const fsm_node dev_fsm[] = {
1110 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1111
1112 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1113 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1114
1115 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1116 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1117
1118 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1119 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001120 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121};
1122
1123static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1124
1125/**
1126 * Transmit a packet.
1127 * This is a helper function for netiucv_tx().
1128 *
1129 * @param conn Connection to be used for sending.
1130 * @param skb Pointer to struct sk_buff of packet to send.
1131 * The linklevel header has already been set up
1132 * by netiucv_tx().
1133 *
1134 * @return 0 on success, -ERRNO on failure. (Never fails.)
1135 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001136static int netiucv_transmit_skb(struct iucv_connection *conn,
1137 struct sk_buff *skb)
1138{
1139 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001141 struct ll_header header;
1142 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143
1144 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1145 int l = skb->len + NETIUCV_HDRLEN;
1146
1147 spin_lock_irqsave(&conn->collect_lock, saveflags);
1148 if (conn->collect_len + l >
1149 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1150 rc = -EBUSY;
1151 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001152 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 } else {
1154 atomic_inc(&skb->users);
1155 skb_queue_tail(&conn->collect_queue, skb);
1156 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001157 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 }
1159 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1160 } else {
1161 struct sk_buff *nskb = skb;
1162 /**
1163 * Copy the skb to a new allocated skb in lowmem only if the
1164 * data is located above 2G in memory or tailroom is < 2.
1165 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001166 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1167 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 int copied = 0;
1169 if (hi || (skb_tailroom(skb) < 2)) {
1170 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1171 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1172 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1174 rc = -ENOMEM;
1175 return rc;
1176 } else {
1177 skb_reserve(nskb, NETIUCV_HDRLEN);
1178 memcpy(skb_put(nskb, skb->len),
1179 skb->data, skb->len);
1180 }
1181 copied = 1;
1182 }
1183 /**
1184 * skb now is below 2G and has enough room. Add headers.
1185 */
1186 header.next = nskb->len + NETIUCV_HDRLEN;
1187 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1188 header.next = 0;
1189 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1190
1191 fsm_newstate(conn->fsm, CONN_STATE_TX);
john stultz2c6b47d2007-07-24 17:47:43 -07001192 conn->prof.send_stamp = current_kernel_time();
Jeff Garzike82b0f22006-05-26 21:58:38 -04001193
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001194 msg.tag = 1;
1195 msg.class = 0;
1196 rc = iucv_message_send(conn->path, &msg, 0, 0,
1197 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 conn->prof.doios_single++;
1199 conn->prof.txlen += skb->len;
1200 conn->prof.tx_pending++;
1201 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1202 conn->prof.tx_max_pending = conn->prof.tx_pending;
1203 if (rc) {
1204 struct netiucv_priv *privptr;
1205 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1206 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001207 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 if (privptr)
1209 privptr->stats.tx_errors++;
1210 if (copied)
1211 dev_kfree_skb(nskb);
1212 else {
1213 /**
1214 * Remove our headers. They get added
1215 * again on retransmit.
1216 */
1217 skb_pull(skb, NETIUCV_HDRLEN);
1218 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1219 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1221 } else {
1222 if (copied)
1223 dev_kfree_skb(skb);
1224 atomic_inc(&nskb->users);
1225 skb_queue_tail(&conn->commit_queue, nskb);
1226 }
1227 }
1228
1229 return rc;
1230}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001231
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001232/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001234 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
1236/**
1237 * Open an interface.
1238 * Called from generic network layer when ifconfig up is run.
1239 *
1240 * @param dev Pointer to interface struct.
1241 *
1242 * @return 0 on success, -ERRNO on failure. (Never fails.)
1243 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001244static int netiucv_open(struct net_device *dev)
1245{
1246 struct netiucv_priv *priv = netdev_priv(dev);
1247
1248 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 return 0;
1250}
1251
1252/**
1253 * Close an interface.
1254 * Called from generic network layer when ifconfig down is run.
1255 *
1256 * @param dev Pointer to interface struct.
1257 *
1258 * @return 0 on success, -ERRNO on failure. (Never fails.)
1259 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001260static int netiucv_close(struct net_device *dev)
1261{
1262 struct netiucv_priv *priv = netdev_priv(dev);
1263
1264 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 return 0;
1266}
1267
1268/**
1269 * Start transmission of a packet.
1270 * Called from generic network device layer.
1271 *
1272 * @param skb Pointer to buffer containing the packet.
1273 * @param dev Pointer to interface struct.
1274 *
1275 * @return 0 if packet consumed, !0 if packet rejected.
1276 * Note: If we return !0, then the packet is free'd by
1277 * the generic network layer.
1278 */
1279static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1280{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001281 struct netiucv_priv *privptr = netdev_priv(dev);
1282 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001284 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 /**
1286 * Some sanity checks ...
1287 */
1288 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1290 privptr->stats.tx_dropped++;
1291 return 0;
1292 }
1293 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 IUCV_DBF_TEXT(data, 2,
1295 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1296 dev_kfree_skb(skb);
1297 privptr->stats.tx_dropped++;
1298 return 0;
1299 }
1300
1301 /**
1302 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001303 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 */
1305 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 dev_kfree_skb(skb);
1307 privptr->stats.tx_dropped++;
1308 privptr->stats.tx_errors++;
1309 privptr->stats.tx_carrier_errors++;
1310 return 0;
1311 }
1312
1313 if (netiucv_test_and_set_busy(dev)) {
1314 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001315 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 }
1317 dev->trans_start = jiffies;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001318 rc = netiucv_transmit_skb(privptr->conn, skb) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 netiucv_clear_busy(dev);
1320 return rc;
1321}
1322
1323/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001324 * netiucv_stats
1325 * @dev: Pointer to interface struct.
1326 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 * Returns interface statistics of a device.
1328 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001329 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001331static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001333 struct netiucv_priv *priv = netdev_priv(dev);
1334
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001335 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001336 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337}
1338
1339/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001340 * netiucv_change_mtu
1341 * @dev: Pointer to interface struct.
1342 * @new_mtu: The new MTU to use for this interface.
1343 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 * Sets MTU of an interface.
1345 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001346 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 * (valid range is 576 .. NETIUCV_MTU_MAX).
1348 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001349static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001351 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001352 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1354 return -EINVAL;
1355 }
1356 dev->mtu = new_mtu;
1357 return 0;
1358}
1359
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001360/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001362 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001364static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1365 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366{
1367 struct netiucv_priv *priv = dev->driver_data;
1368
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001369 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));
1371}
1372
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001373static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1374 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375{
1376 struct netiucv_priv *priv = dev->driver_data;
1377 struct net_device *ndev = priv->conn->netdev;
1378 char *p;
1379 char *tmp;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001380 char username[9];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 int i;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001382 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001384 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001385 if (count > 9) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001387 "%d is length of username\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 return -EINVAL;
1389 }
1390
1391 tmp = strsep((char **) &buf, "\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001392 for (i = 0, p = tmp; i < 8 && *p; i++, p++) {
1393 if (isalnum(*p) || (*p == '$')) {
Frank Pavlic16a83b32006-09-15 16:25:19 +02001394 username[i]= toupper(*p);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001395 continue;
1396 }
1397 if (*p == '\n') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 /* trailing lf, grr */
1399 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001401 IUCV_DBF_TEXT_(setup, 2,
1402 "username: invalid character %c\n", *p);
1403 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001405 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001407 username[8] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001409 if (memcmp(username, priv->conn->userid, 9) &&
1410 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1411 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001412 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001413 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001414 }
1415 read_lock_bh(&iucv_connection_rwlock);
1416 list_for_each_entry(cp, &iucv_connection_list, list) {
1417 if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
1418 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braunf082bca2008-07-14 09:59:30 +02001419 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection "
1420 "to %s already exists\n", username);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001421 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 }
1423 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001424 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 memcpy(priv->conn->userid, username, 9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427}
1428
1429static DEVICE_ATTR(user, 0644, user_show, user_write);
1430
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001431static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1432 char *buf)
1433{ struct netiucv_priv *priv = dev->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001435 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1437}
1438
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001439static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1440 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441{
1442 struct netiucv_priv *priv = dev->driver_data;
1443 struct net_device *ndev = priv->conn->netdev;
1444 char *e;
1445 int bs1;
1446
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001447 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 if (count >= 39)
1449 return -EINVAL;
1450
1451 bs1 = simple_strtoul(buf, &e, 0);
1452
1453 if (e && (!isspace(*e))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
1455 return -EINVAL;
1456 }
1457 if (bs1 > NETIUCV_BUFSIZE_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 IUCV_DBF_TEXT_(setup, 2,
1459 "buffer_write: buffer size %d too large\n",
1460 bs1);
1461 return -EINVAL;
1462 }
1463 if ((ndev->flags & IFF_RUNNING) &&
1464 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 IUCV_DBF_TEXT_(setup, 2,
1466 "buffer_write: buffer size %d too small\n",
1467 bs1);
1468 return -EINVAL;
1469 }
1470 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 IUCV_DBF_TEXT_(setup, 2,
1472 "buffer_write: buffer size %d too small\n",
1473 bs1);
1474 return -EINVAL;
1475 }
1476
1477 priv->conn->max_buffsize = bs1;
1478 if (!(ndev->flags & IFF_RUNNING))
1479 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1480
1481 return count;
1482
1483}
1484
1485static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1486
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001487static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1488 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489{
1490 struct netiucv_priv *priv = dev->driver_data;
1491
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001492 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1494}
1495
1496static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1497
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001498static ssize_t conn_fsm_show (struct device *dev,
1499 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500{
1501 struct netiucv_priv *priv = dev->driver_data;
1502
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001503 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1505}
1506
1507static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1508
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001509static ssize_t maxmulti_show (struct device *dev,
1510 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511{
1512 struct netiucv_priv *priv = dev->driver_data;
1513
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001514 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1516}
1517
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001518static ssize_t maxmulti_write (struct device *dev,
1519 struct device_attribute *attr,
1520 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521{
1522 struct netiucv_priv *priv = dev->driver_data;
1523
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001524 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 priv->conn->prof.maxmulti = 0;
1526 return count;
1527}
1528
1529static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1530
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001531static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1532 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533{
1534 struct netiucv_priv *priv = dev->driver_data;
1535
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001536 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1538}
1539
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001540static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1541 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542{
1543 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001544
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001545 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 priv->conn->prof.maxcqueue = 0;
1547 return count;
1548}
1549
1550static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1551
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001552static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1553 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554{
1555 struct netiucv_priv *priv = dev->driver_data;
1556
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001557 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1559}
1560
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001561static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1562 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563{
1564 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001565
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001566 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 priv->conn->prof.doios_single = 0;
1568 return count;
1569}
1570
1571static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1572
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001573static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1574 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575{
1576 struct netiucv_priv *priv = dev->driver_data;
1577
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001578 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1580}
1581
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001582static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1583 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584{
1585 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001586
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001587 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 priv->conn->prof.doios_multi = 0;
1589 return count;
1590}
1591
1592static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1593
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001594static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1595 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596{
1597 struct netiucv_priv *priv = dev->driver_data;
1598
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001599 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1601}
1602
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001603static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1604 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605{
1606 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001607
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001608 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 priv->conn->prof.txlen = 0;
1610 return count;
1611}
1612
1613static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1614
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001615static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1616 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617{
1618 struct netiucv_priv *priv = dev->driver_data;
1619
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001620 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1622}
1623
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001624static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1625 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626{
1627 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001628
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001629 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 priv->conn->prof.tx_time = 0;
1631 return count;
1632}
1633
1634static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1635
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001636static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1637 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638{
1639 struct netiucv_priv *priv = dev->driver_data;
1640
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001641 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1643}
1644
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001645static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1646 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647{
1648 struct netiucv_priv *priv = dev->driver_data;
1649
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001650 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 priv->conn->prof.tx_pending = 0;
1652 return count;
1653}
1654
1655static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1656
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001657static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1658 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659{
1660 struct netiucv_priv *priv = dev->driver_data;
1661
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001662 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1664}
1665
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001666static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1667 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668{
1669 struct netiucv_priv *priv = dev->driver_data;
1670
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001671 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 priv->conn->prof.tx_max_pending = 0;
1673 return count;
1674}
1675
1676static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1677
1678static struct attribute *netiucv_attrs[] = {
1679 &dev_attr_buffer.attr,
1680 &dev_attr_user.attr,
1681 NULL,
1682};
1683
1684static struct attribute_group netiucv_attr_group = {
1685 .attrs = netiucv_attrs,
1686};
1687
1688static struct attribute *netiucv_stat_attrs[] = {
1689 &dev_attr_device_fsm_state.attr,
1690 &dev_attr_connection_fsm_state.attr,
1691 &dev_attr_max_tx_buffer_used.attr,
1692 &dev_attr_max_chained_skbs.attr,
1693 &dev_attr_tx_single_write_ops.attr,
1694 &dev_attr_tx_multi_write_ops.attr,
1695 &dev_attr_netto_bytes.attr,
1696 &dev_attr_max_tx_io_time.attr,
1697 &dev_attr_tx_pending.attr,
1698 &dev_attr_tx_max_pending.attr,
1699 NULL,
1700};
1701
1702static struct attribute_group netiucv_stat_attr_group = {
1703 .name = "stats",
1704 .attrs = netiucv_stat_attrs,
1705};
1706
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001707static int netiucv_add_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708{
1709 int ret;
1710
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001711 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group);
1713 if (ret)
1714 return ret;
1715 ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group);
1716 if (ret)
1717 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1718 return ret;
1719}
1720
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001721static void netiucv_remove_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001723 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group);
1725 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1726}
1727
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001728static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001730 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001731 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 int ret;
1733
1734
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001735 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736
1737 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001738 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 dev->bus = &iucv_bus;
1740 dev->parent = iucv_root;
1741 /*
1742 * The release function could be called after the
1743 * module has been unloaded. It's _only_ task is to
1744 * free the struct. Therefore, we specify kfree()
1745 * directly here. (Probably a little bit obfuscating
1746 * but legitime ...).
1747 */
1748 dev->release = (void (*)(struct device *))kfree;
1749 dev->driver = &netiucv_driver;
1750 } else
1751 return -ENOMEM;
1752
1753 ret = device_register(dev);
1754
1755 if (ret)
1756 return ret;
1757 ret = netiucv_add_files(dev);
1758 if (ret)
1759 goto out_unreg;
1760 priv->dev = dev;
1761 dev->driver_data = priv;
1762 return 0;
1763
1764out_unreg:
1765 device_unregister(dev);
1766 return ret;
1767}
1768
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001769static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001771 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 netiucv_remove_files(dev);
1773 device_unregister(dev);
1774}
1775
1776/**
1777 * Allocate and initialize a new connection structure.
1778 * Add it to the list of netiucv connections;
1779 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001780static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
1781 char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001783 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001785 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1786 if (!conn)
1787 goto out;
1788 skb_queue_head_init(&conn->collect_queue);
1789 skb_queue_head_init(&conn->commit_queue);
1790 spin_lock_init(&conn->collect_lock);
1791 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1792 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001794 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1795 if (!conn->rx_buff)
1796 goto out_conn;
1797 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1798 if (!conn->tx_buff)
1799 goto out_rx;
1800 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1801 conn_event_names, NR_CONN_STATES,
1802 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1803 GFP_KERNEL);
1804 if (!conn->fsm)
1805 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001807 fsm_settimer(conn->fsm, &conn->timer);
1808 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001810 if (username) {
1811 memcpy(conn->userid, username, 9);
1812 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001814
1815 write_lock_bh(&iucv_connection_rwlock);
1816 list_add_tail(&conn->list, &iucv_connection_list);
1817 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001819
1820out_tx:
1821 kfree_skb(conn->tx_buff);
1822out_rx:
1823 kfree_skb(conn->rx_buff);
1824out_conn:
1825 kfree(conn);
1826out:
1827 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828}
1829
1830/**
1831 * Release a connection structure and remove it from the
1832 * list of netiucv connections.
1833 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001834static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001836 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001837 write_lock_bh(&iucv_connection_rwlock);
1838 list_del_init(&conn->list);
1839 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001840 fsm_deltimer(&conn->timer);
1841 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001842 if (conn->path) {
1843 iucv_path_sever(conn->path, iucvMagic);
1844 kfree(conn->path);
1845 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001847 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001848 kfree_fsm(conn->fsm);
1849 kfree_skb(conn->rx_buff);
1850 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851}
1852
1853/**
1854 * Release everything of a net device.
1855 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001856static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001858 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001860 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861
1862 if (!dev)
1863 return;
1864
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 if (privptr) {
1866 if (privptr->conn)
1867 netiucv_remove_connection(privptr->conn);
1868 if (privptr->fsm)
1869 kfree_fsm(privptr->fsm);
1870 privptr->conn = NULL; privptr->fsm = NULL;
1871 /* privptr gets freed by free_netdev() */
1872 }
1873 free_netdev(dev);
1874}
1875
1876/**
1877 * Initialize a net device. (Called from kernel in alloc_netdev())
1878 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001879static const struct net_device_ops netiucv_netdev_ops = {
1880 .ndo_open = netiucv_open,
1881 .ndo_stop = netiucv_close,
1882 .ndo_get_stats = netiucv_stats,
1883 .ndo_start_xmit = netiucv_tx,
1884 .ndo_change_mtu = netiucv_change_mtu,
1885};
1886
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001887static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 dev->mtu = NETIUCV_MTU_DEFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 dev->destructor = netiucv_free_netdevice;
1891 dev->hard_header_len = NETIUCV_HDRLEN;
1892 dev->addr_len = 0;
1893 dev->type = ARPHRD_SLIP;
1894 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1895 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001896 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897}
1898
1899/**
1900 * Allocate and initialize everything of a net device.
1901 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001902static struct net_device *netiucv_init_netdevice(char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903{
1904 struct netiucv_priv *privptr;
1905 struct net_device *dev;
1906
1907 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
1908 netiucv_setup_netdevice);
1909 if (!dev)
1910 return NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001911 if (dev_alloc_name(dev, dev->name) < 0)
1912 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001914 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
1916 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
1917 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001918 if (!privptr->fsm)
1919 goto out_netdev;
1920
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921 privptr->conn = netiucv_new_connection(dev, username);
1922 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001924 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 }
1926 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001928
1929out_fsm:
1930 kfree_fsm(privptr->fsm);
1931out_netdev:
1932 free_netdev(dev);
1933 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934}
1935
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001936static ssize_t conn_write(struct device_driver *drv,
1937 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001939 const char *p;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001940 char username[9];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001941 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001943 struct netiucv_priv *priv;
1944 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001946 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 if (count>9) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1949 return -EINVAL;
1950 }
1951
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001952 for (i = 0, p = buf; i < 8 && *p; i++, p++) {
1953 if (isalnum(*p) || *p == '$') {
1954 username[i] = toupper(*p);
1955 continue;
1956 }
1957 if (*p == '\n')
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 /* trailing lf, grr */
1959 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001960 IUCV_DBF_TEXT_(setup, 2,
1961 "conn_write: invalid character %c\n", *p);
1962 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001964 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001966 username[8] = '\0';
1967
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001968 read_lock_bh(&iucv_connection_rwlock);
1969 list_for_each_entry(cp, &iucv_connection_list, list) {
1970 if (!strncmp(username, cp->userid, 9)) {
1971 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braunf082bca2008-07-14 09:59:30 +02001972 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection "
1973 "to %s already exists\n", username);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001974 return -EEXIST;
1975 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02001976 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001977 read_unlock_bh(&iucv_connection_rwlock);
1978
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979 dev = netiucv_init_netdevice(username);
1980 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
1982 return -ENODEV;
1983 }
1984
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001985 rc = netiucv_register_device(dev);
1986 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001988 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 goto out_free_ndev;
1990 }
1991
1992 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001993 priv = netdev_priv(dev);
1994 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001996 rc = register_netdev(dev);
1997 if (rc)
1998 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999
Ursula Braun8f7c5022008-12-25 13:39:47 +01002000 dev_info(priv->dev, "The IUCV interface to %s has been"
2001 " established successfully\n", netiucv_printname(username));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002002
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 return count;
2004
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002005out_unreg:
2006 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002009 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010}
2011
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002012static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002014static ssize_t remove_write (struct device_driver *drv,
2015 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002017 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 struct net_device *ndev;
2019 struct netiucv_priv *priv;
2020 struct device *dev;
2021 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002022 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 int i;
2024
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002025 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026
2027 if (count >= IFNAMSIZ)
Frank Pavlic16a83b32006-09-15 16:25:19 +02002028 count = IFNAMSIZ - 1;;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002030 for (i = 0, p = buf; i < count && *p; i++, p++) {
2031 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 /* trailing lf, grr */
2033 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002034 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 }
2036 name[i] = '\0';
2037
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002038 read_lock_bh(&iucv_connection_rwlock);
2039 list_for_each_entry(cp, &iucv_connection_list, list) {
2040 ndev = cp->netdev;
2041 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002043 if (strncmp(name, ndev->name, count))
2044 continue;
2045 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002047 dev_warn(dev, "The IUCV device is connected"
2048 " to %s and cannot be removed\n",
2049 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002051 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 }
2053 unregister_netdev(ndev);
2054 netiucv_unregister_device(dev);
2055 return count;
2056 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002057 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2059 return -EINVAL;
2060}
2061
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002062static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002064static struct attribute * netiucv_drv_attrs[] = {
2065 &driver_attr_connection.attr,
2066 &driver_attr_remove.attr,
2067 NULL,
2068};
2069
2070static struct attribute_group netiucv_drv_attr_group = {
2071 .attrs = netiucv_drv_attrs,
2072};
2073
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002074static struct attribute_group *netiucv_drv_attr_groups[] = {
2075 &netiucv_drv_attr_group,
2076 NULL,
2077};
2078
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002079static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002081 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082}
2083
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002084static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002086 struct iucv_connection *cp;
2087 struct net_device *ndev;
2088 struct netiucv_priv *priv;
2089 struct device *dev;
2090
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002091 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002092 while (!list_empty(&iucv_connection_list)) {
2093 cp = list_entry(iucv_connection_list.next,
2094 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002095 ndev = cp->netdev;
2096 priv = netdev_priv(ndev);
2097 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098
2099 unregister_netdev(ndev);
2100 netiucv_unregister_device(dev);
2101 }
2102
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002104 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105 iucv_unregister_dbf_views();
2106
Ursula Braun8f7c5022008-12-25 13:39:47 +01002107 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108 return;
2109}
2110
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002111static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002113 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002114
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002115 rc = iucv_register_dbf_views();
2116 if (rc)
2117 goto out;
2118 rc = iucv_register(&netiucv_handler, 1);
2119 if (rc)
2120 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002121 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002122 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002123 rc = driver_register(&netiucv_driver);
2124 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002125 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2126 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127 }
2128
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002129 netiucv_banner();
2130 return rc;
2131
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002132out_iucv:
2133 iucv_unregister(&netiucv_handler, 1);
2134out_dbf:
2135 iucv_unregister_dbf_views();
2136out:
2137 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002139
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140module_init(netiucv_init);
2141module_exit(netiucv_exit);
2142MODULE_LICENSE("GPL");