blob: 8f876f6ab367c7c1aef89cfb4efa30b4e03bf4c1 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#undef DEBUG
35
36#include <linux/module.h>
37#include <linux/init.h>
38#include <linux/kernel.h>
39#include <linux/slab.h>
40#include <linux/errno.h>
41#include <linux/types.h>
42#include <linux/interrupt.h>
43#include <linux/timer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <linux/bitops.h>
45
46#include <linux/signal.h>
47#include <linux/string.h>
48#include <linux/device.h>
49
50#include <linux/ip.h>
51#include <linux/if_arp.h>
52#include <linux/tcp.h>
53#include <linux/skbuff.h>
54#include <linux/ctype.h>
55#include <net/dst.h>
56
57#include <asm/io.h>
58#include <asm/uaccess.h>
59
Martin Schwidefskyeebce382007-02-08 13:50:33 -080060#include <net/iucv/iucv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#include "fsm.h"
62
63MODULE_AUTHOR
64 ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)");
65MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
66
Martin Schwidefskyeebce382007-02-08 13:50:33 -080067/**
68 * Debug Facility stuff
69 */
70#define IUCV_DBF_SETUP_NAME "iucv_setup"
71#define IUCV_DBF_SETUP_LEN 32
72#define IUCV_DBF_SETUP_PAGES 2
73#define IUCV_DBF_SETUP_NR_AREAS 1
74#define IUCV_DBF_SETUP_LEVEL 3
75
76#define IUCV_DBF_DATA_NAME "iucv_data"
77#define IUCV_DBF_DATA_LEN 128
78#define IUCV_DBF_DATA_PAGES 2
79#define IUCV_DBF_DATA_NR_AREAS 1
80#define IUCV_DBF_DATA_LEVEL 2
81
82#define IUCV_DBF_TRACE_NAME "iucv_trace"
83#define IUCV_DBF_TRACE_LEN 16
84#define IUCV_DBF_TRACE_PAGES 4
85#define IUCV_DBF_TRACE_NR_AREAS 1
86#define IUCV_DBF_TRACE_LEVEL 3
87
88#define IUCV_DBF_TEXT(name,level,text) \
89 do { \
90 debug_text_event(iucv_dbf_##name,level,text); \
91 } while (0)
92
93#define IUCV_DBF_HEX(name,level,addr,len) \
94 do { \
95 debug_event(iucv_dbf_##name,level,(void*)(addr),len); \
96 } while (0)
97
98DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf);
99
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100100/* Allow to sort out low debug levels early to avoid wasted sprints */
101static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
102{
103 return (level <= dbf_grp->level);
104}
105
106#define IUCV_DBF_TEXT_(name, level, text...) \
107 do { \
108 if (iucv_dbf_passes(iucv_dbf_##name, level)) { \
109 char* iucv_dbf_txt_buf = \
110 get_cpu_var(iucv_dbf_txt_buf); \
111 sprintf(iucv_dbf_txt_buf, text); \
112 debug_text_event(iucv_dbf_##name, level, \
113 iucv_dbf_txt_buf); \
114 put_cpu_var(iucv_dbf_txt_buf); \
115 } \
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800116 } while (0)
117
118#define IUCV_DBF_SPRINTF(name,level,text...) \
119 do { \
120 debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
121 debug_sprintf_event(iucv_dbf_trace, level, text ); \
122 } while (0)
123
124/**
125 * some more debug stuff
126 */
127#define IUCV_HEXDUMP16(importance,header,ptr) \
128PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
129 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
130 *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
131 *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
132 *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
133 *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
134 *(((char*)ptr)+12),*(((char*)ptr)+13), \
135 *(((char*)ptr)+14),*(((char*)ptr)+15)); \
136PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
137 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
138 *(((char*)ptr)+16),*(((char*)ptr)+17), \
139 *(((char*)ptr)+18),*(((char*)ptr)+19), \
140 *(((char*)ptr)+20),*(((char*)ptr)+21), \
141 *(((char*)ptr)+22),*(((char*)ptr)+23), \
142 *(((char*)ptr)+24),*(((char*)ptr)+25), \
143 *(((char*)ptr)+26),*(((char*)ptr)+27), \
144 *(((char*)ptr)+28),*(((char*)ptr)+29), \
145 *(((char*)ptr)+30),*(((char*)ptr)+31));
146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147#define PRINTK_HEADER " iucv: " /* for debugging */
148
149static struct device_driver netiucv_driver = {
Cornelia Huck22195102008-02-08 13:09:02 +0100150 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 .name = "netiucv",
152 .bus = &iucv_bus,
153};
154
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800155static int netiucv_callback_connreq(struct iucv_path *,
156 u8 ipvmid[8], u8 ipuser[16]);
157static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
158static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
159static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
160static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
161static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
162static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
163
164static struct iucv_handler netiucv_handler = {
165 .path_pending = netiucv_callback_connreq,
166 .path_complete = netiucv_callback_connack,
167 .path_severed = netiucv_callback_connrej,
168 .path_quiesced = netiucv_callback_connsusp,
169 .path_resumed = netiucv_callback_connres,
170 .message_pending = netiucv_callback_rx,
171 .message_complete = netiucv_callback_txdone
172};
173
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174/**
175 * Per connection profiling data
176 */
177struct connection_profile {
178 unsigned long maxmulti;
179 unsigned long maxcqueue;
180 unsigned long doios_single;
181 unsigned long doios_multi;
182 unsigned long txlen;
183 unsigned long tx_time;
184 struct timespec send_stamp;
185 unsigned long tx_pending;
186 unsigned long tx_max_pending;
187};
188
189/**
190 * Representation of one iucv connection
191 */
192struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800193 struct list_head list;
194 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 struct sk_buff *rx_buff;
196 struct sk_buff *tx_buff;
197 struct sk_buff_head collect_queue;
198 struct sk_buff_head commit_queue;
199 spinlock_t collect_lock;
200 int collect_len;
201 int max_buffsize;
202 fsm_timer timer;
203 fsm_instance *fsm;
204 struct net_device *netdev;
205 struct connection_profile prof;
206 char userid[9];
207};
208
209/**
210 * Linked list of all connection structs.
211 */
Denis Chengc11ca972008-01-26 14:11:13 +0100212static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200213static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
215/**
216 * Representation of event-data for the
217 * connection state machine.
218 */
219struct iucv_event {
220 struct iucv_connection *conn;
221 void *data;
222};
223
224/**
225 * Private part of the network device structure
226 */
227struct netiucv_priv {
228 struct net_device_stats stats;
229 unsigned long tbusy;
230 fsm_instance *fsm;
231 struct iucv_connection *conn;
232 struct device *dev;
233};
234
235/**
236 * Link level header for a packet.
237 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800238struct ll_header {
239 u16 next;
240};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800242#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243#define NETIUCV_BUFSIZE_MAX 32768
244#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
245#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
246#define NETIUCV_MTU_DEFAULT 9216
247#define NETIUCV_QUEUELEN_DEFAULT 50
248#define NETIUCV_TIMEOUT_5SEC 5000
249
250/**
251 * Compatibility macros for busy handling
252 * of network devices.
253 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800254static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800256 struct netiucv_priv *priv = netdev_priv(dev);
257 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 netif_wake_queue(dev);
259}
260
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800261static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800263 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800265 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266}
267
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800268static u8 iucvMagic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
270 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
271};
272
273/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 * Convert an iucv userId to its printable
275 * form (strip whitespace at end).
276 *
277 * @param An iucv userId
278 *
279 * @returns The printable string (static data!!)
280 */
Martin Schwidefskyd4614622007-06-20 13:03:57 +0200281static char *netiucv_printname(char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282{
283 static char tmp[9];
284 char *p = tmp;
285 memcpy(tmp, name, 8);
286 tmp[8] = '\0';
287 while (*p && (!isspace(*p)))
288 p++;
289 *p = '\0';
290 return tmp;
291}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400292
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293/**
294 * States of the interface statemachine.
295 */
296enum dev_states {
297 DEV_STATE_STOPPED,
298 DEV_STATE_STARTWAIT,
299 DEV_STATE_STOPWAIT,
300 DEV_STATE_RUNNING,
301 /**
302 * MUST be always the last element!!
303 */
304 NR_DEV_STATES
305};
306
307static const char *dev_state_names[] = {
308 "Stopped",
309 "StartWait",
310 "StopWait",
311 "Running",
312};
313
314/**
315 * Events of the interface statemachine.
316 */
317enum dev_events {
318 DEV_EVENT_START,
319 DEV_EVENT_STOP,
320 DEV_EVENT_CONUP,
321 DEV_EVENT_CONDOWN,
322 /**
323 * MUST be always the last element!!
324 */
325 NR_DEV_EVENTS
326};
327
328static const char *dev_event_names[] = {
329 "Start",
330 "Stop",
331 "Connection up",
332 "Connection down",
333};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400334
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335/**
336 * Events of the connection statemachine
337 */
338enum conn_events {
339 /**
340 * Events, representing callbacks from
341 * lowlevel iucv layer)
342 */
343 CONN_EVENT_CONN_REQ,
344 CONN_EVENT_CONN_ACK,
345 CONN_EVENT_CONN_REJ,
346 CONN_EVENT_CONN_SUS,
347 CONN_EVENT_CONN_RES,
348 CONN_EVENT_RX,
349 CONN_EVENT_TXDONE,
350
351 /**
352 * Events, representing errors return codes from
353 * calls to lowlevel iucv layer
354 */
355
356 /**
357 * Event, representing timer expiry.
358 */
359 CONN_EVENT_TIMER,
360
361 /**
362 * Events, representing commands from upper levels.
363 */
364 CONN_EVENT_START,
365 CONN_EVENT_STOP,
366
367 /**
368 * MUST be always the last element!!
369 */
370 NR_CONN_EVENTS,
371};
372
373static const char *conn_event_names[] = {
374 "Remote connection request",
375 "Remote connection acknowledge",
376 "Remote connection reject",
377 "Connection suspended",
378 "Connection resumed",
379 "Data received",
380 "Data sent",
381
382 "Timer",
383
384 "Start",
385 "Stop",
386};
387
388/**
389 * States of the connection statemachine.
390 */
391enum conn_states {
392 /**
393 * Connection not assigned to any device,
394 * initial state, invalid
395 */
396 CONN_STATE_INVALID,
397
398 /**
399 * Userid assigned but not operating
400 */
401 CONN_STATE_STOPPED,
402
403 /**
404 * Connection registered,
405 * no connection request sent yet,
406 * no connection request received
407 */
408 CONN_STATE_STARTWAIT,
409
410 /**
411 * Connection registered and connection request sent,
412 * no acknowledge and no connection request received yet.
413 */
414 CONN_STATE_SETUPWAIT,
415
416 /**
417 * Connection up and running idle
418 */
419 CONN_STATE_IDLE,
420
421 /**
422 * Data sent, awaiting CONN_EVENT_TXDONE
423 */
424 CONN_STATE_TX,
425
426 /**
427 * Error during registration.
428 */
429 CONN_STATE_REGERR,
430
431 /**
432 * Error during registration.
433 */
434 CONN_STATE_CONNERR,
435
436 /**
437 * MUST be always the last element!!
438 */
439 NR_CONN_STATES,
440};
441
442static const char *conn_state_names[] = {
443 "Invalid",
444 "Stopped",
445 "StartWait",
446 "SetupWait",
447 "Idle",
448 "TX",
449 "Terminating",
450 "Registration error",
451 "Connect error",
452};
453
Jeff Garzike82b0f22006-05-26 21:58:38 -0400454
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455/**
456 * Debug Facility Stuff
457 */
458static debug_info_t *iucv_dbf_setup = NULL;
459static debug_info_t *iucv_dbf_data = NULL;
460static debug_info_t *iucv_dbf_trace = NULL;
461
462DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
463
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800464static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465{
466 if (iucv_dbf_setup)
467 debug_unregister(iucv_dbf_setup);
468 if (iucv_dbf_data)
469 debug_unregister(iucv_dbf_data);
470 if (iucv_dbf_trace)
471 debug_unregister(iucv_dbf_trace);
472}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800473static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474{
475 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700476 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 IUCV_DBF_SETUP_NR_AREAS,
478 IUCV_DBF_SETUP_LEN);
479 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700480 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 IUCV_DBF_DATA_NR_AREAS,
482 IUCV_DBF_DATA_LEN);
483 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700484 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 IUCV_DBF_TRACE_NR_AREAS,
486 IUCV_DBF_TRACE_LEN);
487
488 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
489 (iucv_dbf_trace == NULL)) {
490 iucv_unregister_dbf_views();
491 return -ENOMEM;
492 }
493 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
494 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
495
496 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
497 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
498
499 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
500 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
501
502 return 0;
503}
504
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800505/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800507 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800509static void netiucv_callback_rx(struct iucv_path *path,
510 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800512 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 struct iucv_event ev;
514
515 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800516 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
518}
519
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800520static void netiucv_callback_txdone(struct iucv_path *path,
521 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800523 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 struct iucv_event ev;
525
526 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800527 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
529}
530
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800531static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800533 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800535 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536}
537
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800538static int netiucv_callback_connreq(struct iucv_path *path,
539 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800541 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 struct iucv_event ev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800543 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800545 if (memcmp(iucvMagic, ipuser, sizeof(ipuser)))
546 /* ipuser must match iucvMagic. */
547 return -EINVAL;
548 rc = -EINVAL;
549 read_lock_bh(&iucv_connection_rwlock);
550 list_for_each_entry(conn, &iucv_connection_list, list) {
551 if (strncmp(ipvmid, conn->userid, 8))
552 continue;
553 /* Found a matching connection for this path. */
554 conn->path = path;
555 ev.conn = conn;
556 ev.data = path;
557 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
558 rc = 0;
559 }
560 read_unlock_bh(&iucv_connection_rwlock);
561 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562}
563
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800564static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800566 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800568 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569}
570
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800571static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800573 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800575 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576}
577
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800578static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800580 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800582 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583}
584
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100586 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100588static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589{
590}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400591
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800592/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800594 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
596/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800597 * netiucv_unpack_skb
598 * @conn: The connection where this skb has been received.
599 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800601 * Unpack a just received skb and hand it over to upper layers.
602 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800604static void netiucv_unpack_skb(struct iucv_connection *conn,
605 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606{
607 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800608 struct netiucv_priv *privptr = netdev_priv(dev);
609 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
611 skb_put(pskb, NETIUCV_HDRLEN);
612 pskb->dev = dev;
613 pskb->ip_summed = CHECKSUM_NONE;
614 pskb->protocol = ntohs(ETH_P_IP);
615
616 while (1) {
617 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800618 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619
620 if (!header->next)
621 break;
622
623 skb_pull(pskb, NETIUCV_HDRLEN);
624 header->next -= offset;
625 offset += header->next;
626 header->next -= NETIUCV_HDRLEN;
627 if (skb_tailroom(pskb) < header->next) {
628 PRINT_WARN("%s: Illegal next field in iucv header: "
629 "%d > %d\n",
630 dev->name, header->next, skb_tailroom(pskb));
631 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) {
639 PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n",
640 dev->name);
641 IUCV_DBF_TEXT(data, 2,
642 "Out of memory in netiucv_unpack_skb\n");
643 privptr->stats.rx_dropped++;
644 return;
645 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300646 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
647 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700648 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 skb->dev = pskb->dev;
650 skb->protocol = pskb->protocol;
651 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800652 privptr->stats.rx_packets++;
653 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 /*
655 * Since receiving is always initiated from a tasklet (in iucv.c),
656 * we must use netif_rx_ni() instead of netif_rx()
657 */
658 netif_rx_ni(skb);
659 dev->last_rx = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 skb_pull(pskb, header->next);
661 skb_put(pskb, NETIUCV_HDRLEN);
662 }
663}
664
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800665static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800667 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800669 struct iucv_message *msg = ev->data;
670 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 int rc;
672
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200673 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
675 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800676 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 PRINT_WARN("Received data for unlinked connection\n");
678 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800679 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 return;
681 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800682 if (msg->length > conn->max_buffsize) {
683 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 privptr->stats.rx_dropped++;
685 PRINT_WARN("msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800686 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800688 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 return;
690 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700691 conn->rx_buff->data = conn->rx_buff->head;
692 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800694 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
695 msg->length, NULL);
696 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 privptr->stats.rx_errors++;
698 PRINT_WARN("iucv_receive returned %08x\n", rc);
699 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
700 return;
701 }
702 netiucv_unpack_skb(conn, conn->rx_buff);
703}
704
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800705static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800707 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800709 struct iucv_message *msg = ev->data;
710 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800712 u32 single_flag = msg->tag;
713 u32 txbytes = 0;
714 u32 txpackets = 0;
715 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 struct sk_buff *skb;
717 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800718 struct ll_header header;
719 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200721 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800723 if (conn && conn->netdev)
724 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 conn->prof.tx_pending--;
726 if (single_flag) {
727 if ((skb = skb_dequeue(&conn->commit_queue))) {
728 atomic_dec(&skb->users);
729 dev_kfree_skb_any(skb);
730 if (privptr) {
731 privptr->stats.tx_packets++;
732 privptr->stats.tx_bytes +=
733 (skb->len - NETIUCV_HDRLEN
734 - NETIUCV_HDRLEN);
735 }
736 }
737 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700738 conn->tx_buff->data = conn->tx_buff->head;
739 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 conn->tx_buff->len = 0;
741 spin_lock_irqsave(&conn->collect_lock, saveflags);
742 while ((skb = skb_dequeue(&conn->collect_queue))) {
743 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
744 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
745 NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300746 skb_copy_from_linear_data(skb,
747 skb_put(conn->tx_buff, skb->len),
748 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 txbytes += skb->len;
750 txpackets++;
751 stat_maxcq++;
752 atomic_dec(&skb->users);
753 dev_kfree_skb_any(skb);
754 }
755 if (conn->collect_len > conn->prof.maxmulti)
756 conn->prof.maxmulti = conn->collect_len;
757 conn->collect_len = 0;
758 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800759 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800761 return;
762 }
763
764 header.next = 0;
765 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
john stultz2c6b47d2007-07-24 17:47:43 -0700766 conn->prof.send_stamp = current_kernel_time();
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800767 txmsg.class = 0;
768 txmsg.tag = 0;
769 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
770 conn->tx_buff->data, conn->tx_buff->len);
771 conn->prof.doios_multi++;
772 conn->prof.txlen += conn->tx_buff->len;
773 conn->prof.tx_pending++;
774 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
775 conn->prof.tx_max_pending = conn->prof.tx_pending;
776 if (rc) {
777 conn->prof.tx_pending--;
778 fsm_newstate(fi, CONN_STATE_IDLE);
779 if (privptr)
780 privptr->stats.tx_errors += txpackets;
781 PRINT_WARN("iucv_send returned %08x\n", rc);
782 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
783 } else {
784 if (privptr) {
785 privptr->stats.tx_packets += txpackets;
786 privptr->stats.tx_bytes += txbytes;
787 }
788 if (stat_maxcq > conn->prof.maxcqueue)
789 conn->prof.maxcqueue = stat_maxcq;
790 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791}
792
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800793static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800795 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800797 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800799 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200802 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800804 conn->path = path;
805 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
806 path->flags = 0;
807 rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 if (rc) {
809 PRINT_WARN("%s: IUCV accept failed with error %d\n",
810 netdev->name, rc);
811 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
812 return;
813 }
814 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800815 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
817}
818
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800819static void conn_action_connreject(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_event *ev = arg;
822 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200824 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800825 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826}
827
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800828static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800830 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800832 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200834 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 fsm_deltimer(&conn->timer);
836 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800837 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
839}
840
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800841static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800843 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200845 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800847 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 fsm_newstate(fi, CONN_STATE_STARTWAIT);
849}
850
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800851static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800853 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800855 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200857 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
859 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800860 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 PRINT_INFO("%s: Remote dropped connection\n", netdev->name);
862 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800863 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 fsm_newstate(fi, CONN_STATE_STARTWAIT);
865 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
866}
867
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800868static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800870 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 int rc;
872
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200873 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800875 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 PRINT_DEBUG("%s('%s'): connecting ...\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800877 conn->netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800879 /*
880 * We must set the state before calling iucv_connect because the
881 * callback handler could be called at any point after the connection
882 * request is sent
883 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
885 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800886 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
887 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
888 NULL, iucvMagic, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800890 case 0:
891 conn->netdev->tx_queue_len = conn->path->msglim;
892 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
893 CONN_EVENT_TIMER, conn);
894 return;
895 case 11:
896 PRINT_INFO("%s: User %s is currently not available.\n",
897 conn->netdev->name,
898 netiucv_printname(conn->userid));
899 fsm_newstate(fi, CONN_STATE_STARTWAIT);
900 break;
901 case 12:
902 PRINT_INFO("%s: User %s is currently not ready.\n",
903 conn->netdev->name,
904 netiucv_printname(conn->userid));
905 fsm_newstate(fi, CONN_STATE_STARTWAIT);
906 break;
907 case 13:
908 PRINT_WARN("%s: Too many IUCV connections.\n",
909 conn->netdev->name);
910 fsm_newstate(fi, CONN_STATE_CONNERR);
911 break;
912 case 14:
913 PRINT_WARN("%s: User %s has too many IUCV connections.\n",
914 conn->netdev->name,
915 netiucv_printname(conn->userid));
916 fsm_newstate(fi, CONN_STATE_CONNERR);
917 break;
918 case 15:
919 PRINT_WARN("%s: No IUCV authorization in CP directory.\n",
920 conn->netdev->name);
921 fsm_newstate(fi, CONN_STATE_CONNERR);
922 break;
923 default:
924 PRINT_WARN("%s: iucv_connect returned error %d\n",
925 conn->netdev->name, rc);
926 fsm_newstate(fi, CONN_STATE_CONNERR);
927 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 }
929 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800930 kfree(conn->path);
931 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932}
933
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800934static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935{
936 struct sk_buff *skb;
937
938 while ((skb = skb_dequeue(q))) {
939 atomic_dec(&skb->users);
940 dev_kfree_skb_any(skb);
941 }
942}
943
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800944static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800946 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 struct iucv_connection *conn = ev->conn;
948 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800949 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200951 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952
953 fsm_deltimer(&conn->timer);
954 fsm_newstate(fi, CONN_STATE_STOPPED);
955 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800956 if (conn->path) {
957 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
958 iucv_path_sever(conn->path, iucvMagic);
959 kfree(conn->path);
960 conn->path = NULL;
961 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 netiucv_purge_skb_queue(&conn->commit_queue);
963 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
964}
965
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800966static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800968 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 struct net_device *netdev = conn->netdev;
970
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800971 PRINT_WARN("%s: Cannot connect without username\n", netdev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 IUCV_DBF_TEXT(data, 2, "conn_action_inval called\n");
973}
974
975static const fsm_node conn_fsm[] = {
976 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
977 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
978
979 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
980 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
981 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
982 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
983 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
984 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
985 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
986
987 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
988 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
989 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
990 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
991 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
992
993 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
994 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
995
996 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
997 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
998 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
999
1000 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
1001 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1002
1003 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1004 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1005};
1006
1007static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1008
Jeff Garzike82b0f22006-05-26 21:58:38 -04001009
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001010/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001012 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013
1014/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001015 * dev_action_start
1016 * @fi: An instance of an interface statemachine.
1017 * @event: The event, just happened.
1018 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001020 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001022static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001024 struct net_device *dev = arg;
1025 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001027 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001030 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031}
1032
1033/**
1034 * Shutdown connection by sending CONN_EVENT_STOP to it.
1035 *
1036 * @param fi An instance of an interface statemachine.
1037 * @param event The event, just happened.
1038 * @param arg Generic pointer, casted from struct net_device * upon call.
1039 */
1040static void
1041dev_action_stop(fsm_instance *fi, int event, void *arg)
1042{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001043 struct net_device *dev = arg;
1044 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 struct iucv_event ev;
1046
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001047 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
1049 ev.conn = privptr->conn;
1050
1051 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1052 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1053}
1054
1055/**
1056 * Called from connection statemachine
1057 * when a connection is up and running.
1058 *
1059 * @param fi An instance of an interface statemachine.
1060 * @param event The event, just happened.
1061 * @param arg Generic pointer, casted from struct net_device * upon call.
1062 */
1063static void
1064dev_action_connup(fsm_instance *fi, int event, void *arg)
1065{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001066 struct net_device *dev = arg;
1067 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001069 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070
1071 switch (fsm_getstate(fi)) {
1072 case DEV_STATE_STARTWAIT:
1073 fsm_newstate(fi, DEV_STATE_RUNNING);
1074 PRINT_INFO("%s: connected with remote side %s\n",
1075 dev->name, privptr->conn->userid);
1076 IUCV_DBF_TEXT(setup, 3,
1077 "connection is up and running\n");
1078 break;
1079 case DEV_STATE_STOPWAIT:
1080 PRINT_INFO(
1081 "%s: got connection UP event during shutdown!\n",
1082 dev->name);
1083 IUCV_DBF_TEXT(data, 2,
1084 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1085 break;
1086 }
1087}
1088
1089/**
1090 * Called from connection statemachine
1091 * when a connection has been shutdown.
1092 *
1093 * @param fi An instance of an interface statemachine.
1094 * @param event The event, just happened.
1095 * @param arg Generic pointer, casted from struct net_device * upon call.
1096 */
1097static void
1098dev_action_conndown(fsm_instance *fi, int event, void *arg)
1099{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001100 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101
1102 switch (fsm_getstate(fi)) {
1103 case DEV_STATE_RUNNING:
1104 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1105 break;
1106 case DEV_STATE_STOPWAIT:
1107 fsm_newstate(fi, DEV_STATE_STOPPED);
1108 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1109 break;
1110 }
1111}
1112
1113static const fsm_node dev_fsm[] = {
1114 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1115
1116 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1117 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1118
1119 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1120 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1121
1122 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1123 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001124 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125};
1126
1127static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1128
1129/**
1130 * Transmit a packet.
1131 * This is a helper function for netiucv_tx().
1132 *
1133 * @param conn Connection to be used for sending.
1134 * @param skb Pointer to struct sk_buff of packet to send.
1135 * The linklevel header has already been set up
1136 * by netiucv_tx().
1137 *
1138 * @return 0 on success, -ERRNO on failure. (Never fails.)
1139 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001140static int netiucv_transmit_skb(struct iucv_connection *conn,
1141 struct sk_buff *skb)
1142{
1143 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001145 struct ll_header header;
1146 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147
1148 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1149 int l = skb->len + NETIUCV_HDRLEN;
1150
1151 spin_lock_irqsave(&conn->collect_lock, saveflags);
1152 if (conn->collect_len + l >
1153 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1154 rc = -EBUSY;
1155 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001156 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 } else {
1158 atomic_inc(&skb->users);
1159 skb_queue_tail(&conn->collect_queue, skb);
1160 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001161 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 }
1163 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1164 } else {
1165 struct sk_buff *nskb = skb;
1166 /**
1167 * Copy the skb to a new allocated skb in lowmem only if the
1168 * data is located above 2G in memory or tailroom is < 2.
1169 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001170 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1171 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 int copied = 0;
1173 if (hi || (skb_tailroom(skb) < 2)) {
1174 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1175 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1176 if (!nskb) {
1177 PRINT_WARN("%s: Could not allocate tx_skb\n",
1178 conn->netdev->name);
1179 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1180 rc = -ENOMEM;
1181 return rc;
1182 } else {
1183 skb_reserve(nskb, NETIUCV_HDRLEN);
1184 memcpy(skb_put(nskb, skb->len),
1185 skb->data, skb->len);
1186 }
1187 copied = 1;
1188 }
1189 /**
1190 * skb now is below 2G and has enough room. Add headers.
1191 */
1192 header.next = nskb->len + NETIUCV_HDRLEN;
1193 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1194 header.next = 0;
1195 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1196
1197 fsm_newstate(conn->fsm, CONN_STATE_TX);
john stultz2c6b47d2007-07-24 17:47:43 -07001198 conn->prof.send_stamp = current_kernel_time();
Jeff Garzike82b0f22006-05-26 21:58:38 -04001199
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001200 msg.tag = 1;
1201 msg.class = 0;
1202 rc = iucv_message_send(conn->path, &msg, 0, 0,
1203 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 conn->prof.doios_single++;
1205 conn->prof.txlen += skb->len;
1206 conn->prof.tx_pending++;
1207 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1208 conn->prof.tx_max_pending = conn->prof.tx_pending;
1209 if (rc) {
1210 struct netiucv_priv *privptr;
1211 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1212 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001213 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 if (privptr)
1215 privptr->stats.tx_errors++;
1216 if (copied)
1217 dev_kfree_skb(nskb);
1218 else {
1219 /**
1220 * Remove our headers. They get added
1221 * again on retransmit.
1222 */
1223 skb_pull(skb, NETIUCV_HDRLEN);
1224 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1225 }
1226 PRINT_WARN("iucv_send returned %08x\n", rc);
1227 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1228 } else {
1229 if (copied)
1230 dev_kfree_skb(skb);
1231 atomic_inc(&nskb->users);
1232 skb_queue_tail(&conn->commit_queue, nskb);
1233 }
1234 }
1235
1236 return rc;
1237}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001238
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001239/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001241 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242
1243/**
1244 * Open an interface.
1245 * Called from generic network layer when ifconfig up is run.
1246 *
1247 * @param dev Pointer to interface struct.
1248 *
1249 * @return 0 on success, -ERRNO on failure. (Never fails.)
1250 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001251static int netiucv_open(struct net_device *dev)
1252{
1253 struct netiucv_priv *priv = netdev_priv(dev);
1254
1255 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 return 0;
1257}
1258
1259/**
1260 * Close an interface.
1261 * Called from generic network layer when ifconfig down is run.
1262 *
1263 * @param dev Pointer to interface struct.
1264 *
1265 * @return 0 on success, -ERRNO on failure. (Never fails.)
1266 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001267static int netiucv_close(struct net_device *dev)
1268{
1269 struct netiucv_priv *priv = netdev_priv(dev);
1270
1271 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 return 0;
1273}
1274
1275/**
1276 * Start transmission of a packet.
1277 * Called from generic network device layer.
1278 *
1279 * @param skb Pointer to buffer containing the packet.
1280 * @param dev Pointer to interface struct.
1281 *
1282 * @return 0 if packet consumed, !0 if packet rejected.
1283 * Note: If we return !0, then the packet is free'd by
1284 * the generic network layer.
1285 */
1286static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1287{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001288 struct netiucv_priv *privptr = netdev_priv(dev);
1289 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001291 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 /**
1293 * Some sanity checks ...
1294 */
1295 if (skb == NULL) {
1296 PRINT_WARN("%s: NULL sk_buff passed\n", dev->name);
1297 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1298 privptr->stats.tx_dropped++;
1299 return 0;
1300 }
1301 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
1302 PRINT_WARN("%s: Got sk_buff with head room < %ld bytes\n",
1303 dev->name, NETIUCV_HDRLEN);
1304 IUCV_DBF_TEXT(data, 2,
1305 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1306 dev_kfree_skb(skb);
1307 privptr->stats.tx_dropped++;
1308 return 0;
1309 }
1310
1311 /**
1312 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001313 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 */
1315 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Ursula Braun651bbc62007-06-20 13:01:30 +02001316 if (!in_atomic())
1317 fsm_event(privptr->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 dev_kfree_skb(skb);
1319 privptr->stats.tx_dropped++;
1320 privptr->stats.tx_errors++;
1321 privptr->stats.tx_carrier_errors++;
1322 return 0;
1323 }
1324
1325 if (netiucv_test_and_set_busy(dev)) {
1326 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
1327 return -EBUSY;
1328 }
1329 dev->trans_start = jiffies;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001330 rc = netiucv_transmit_skb(privptr->conn, skb) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 netiucv_clear_busy(dev);
1332 return rc;
1333}
1334
1335/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001336 * netiucv_stats
1337 * @dev: Pointer to interface struct.
1338 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 * Returns interface statistics of a device.
1340 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001341 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001343static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001345 struct netiucv_priv *priv = netdev_priv(dev);
1346
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001347 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001348 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349}
1350
1351/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001352 * netiucv_change_mtu
1353 * @dev: Pointer to interface struct.
1354 * @new_mtu: The new MTU to use for this interface.
1355 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 * Sets MTU of an interface.
1357 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001358 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 * (valid range is 576 .. NETIUCV_MTU_MAX).
1360 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001361static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001363 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001364 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1366 return -EINVAL;
1367 }
1368 dev->mtu = new_mtu;
1369 return 0;
1370}
1371
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001372/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001374 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001376static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1377 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378{
1379 struct netiucv_priv *priv = dev->driver_data;
1380
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001381 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382 return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));
1383}
1384
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001385static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1386 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387{
1388 struct netiucv_priv *priv = dev->driver_data;
1389 struct net_device *ndev = priv->conn->netdev;
1390 char *p;
1391 char *tmp;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001392 char username[9];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 int i;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001394 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001396 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001397 if (count > 9) {
1398 PRINT_WARN("netiucv: username too long (%d)!\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001400 "%d is length of username\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 return -EINVAL;
1402 }
1403
1404 tmp = strsep((char **) &buf, "\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001405 for (i = 0, p = tmp; i < 8 && *p; i++, p++) {
1406 if (isalnum(*p) || (*p == '$')) {
Frank Pavlic16a83b32006-09-15 16:25:19 +02001407 username[i]= toupper(*p);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001408 continue;
1409 }
1410 if (*p == '\n') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 /* trailing lf, grr */
1412 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001414 PRINT_WARN("netiucv: Invalid char %c in username!\n", *p);
1415 IUCV_DBF_TEXT_(setup, 2,
1416 "username: invalid character %c\n", *p);
1417 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001419 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001421 username[8] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001423 if (memcmp(username, priv->conn->userid, 9) &&
1424 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1425 /* username changed while the interface is active. */
1426 PRINT_WARN("netiucv: device %s active, connected to %s\n",
1427 dev->bus_id, priv->conn->userid);
1428 PRINT_WARN("netiucv: user cannot be updated\n");
1429 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
1430 return -EBUSY;
1431 }
1432 read_lock_bh(&iucv_connection_rwlock);
1433 list_for_each_entry(cp, &iucv_connection_list, list) {
1434 if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
1435 read_unlock_bh(&iucv_connection_rwlock);
1436 PRINT_WARN("netiucv: Connection to %s already "
1437 "exists\n", username);
1438 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 }
1440 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001441 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 memcpy(priv->conn->userid, username, 9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444}
1445
1446static DEVICE_ATTR(user, 0644, user_show, user_write);
1447
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001448static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1449 char *buf)
1450{ struct netiucv_priv *priv = dev->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001452 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1454}
1455
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001456static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1457 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458{
1459 struct netiucv_priv *priv = dev->driver_data;
1460 struct net_device *ndev = priv->conn->netdev;
1461 char *e;
1462 int bs1;
1463
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001464 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 if (count >= 39)
1466 return -EINVAL;
1467
1468 bs1 = simple_strtoul(buf, &e, 0);
1469
1470 if (e && (!isspace(*e))) {
1471 PRINT_WARN("netiucv: Invalid character in buffer!\n");
1472 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
1473 return -EINVAL;
1474 }
1475 if (bs1 > NETIUCV_BUFSIZE_MAX) {
1476 PRINT_WARN("netiucv: Given buffer size %d too large.\n",
1477 bs1);
1478 IUCV_DBF_TEXT_(setup, 2,
1479 "buffer_write: buffer size %d too large\n",
1480 bs1);
1481 return -EINVAL;
1482 }
1483 if ((ndev->flags & IFF_RUNNING) &&
1484 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
1485 PRINT_WARN("netiucv: Given buffer size %d too small.\n",
1486 bs1);
1487 IUCV_DBF_TEXT_(setup, 2,
1488 "buffer_write: buffer size %d too small\n",
1489 bs1);
1490 return -EINVAL;
1491 }
1492 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
1493 PRINT_WARN("netiucv: Given buffer size %d too small.\n",
1494 bs1);
1495 IUCV_DBF_TEXT_(setup, 2,
1496 "buffer_write: buffer size %d too small\n",
1497 bs1);
1498 return -EINVAL;
1499 }
1500
1501 priv->conn->max_buffsize = bs1;
1502 if (!(ndev->flags & IFF_RUNNING))
1503 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1504
1505 return count;
1506
1507}
1508
1509static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1510
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001511static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1512 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513{
1514 struct netiucv_priv *priv = dev->driver_data;
1515
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001516 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1518}
1519
1520static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1521
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001522static ssize_t conn_fsm_show (struct device *dev,
1523 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524{
1525 struct netiucv_priv *priv = dev->driver_data;
1526
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001527 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1529}
1530
1531static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1532
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001533static ssize_t maxmulti_show (struct device *dev,
1534 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535{
1536 struct netiucv_priv *priv = dev->driver_data;
1537
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001538 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1540}
1541
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001542static ssize_t maxmulti_write (struct device *dev,
1543 struct device_attribute *attr,
1544 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545{
1546 struct netiucv_priv *priv = dev->driver_data;
1547
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001548 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 priv->conn->prof.maxmulti = 0;
1550 return count;
1551}
1552
1553static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1554
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001555static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1556 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557{
1558 struct netiucv_priv *priv = dev->driver_data;
1559
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001560 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1562}
1563
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001564static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1565 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566{
1567 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001568
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001569 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 priv->conn->prof.maxcqueue = 0;
1571 return count;
1572}
1573
1574static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1575
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001576static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1577 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578{
1579 struct netiucv_priv *priv = dev->driver_data;
1580
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001581 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1583}
1584
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001585static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1586 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587{
1588 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001589
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001590 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 priv->conn->prof.doios_single = 0;
1592 return count;
1593}
1594
1595static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1596
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001597static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1598 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599{
1600 struct netiucv_priv *priv = dev->driver_data;
1601
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001602 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1604}
1605
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001606static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1607 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608{
1609 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001610
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001611 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 priv->conn->prof.doios_multi = 0;
1613 return count;
1614}
1615
1616static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1617
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001618static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1619 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620{
1621 struct netiucv_priv *priv = dev->driver_data;
1622
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001623 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1625}
1626
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001627static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1628 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629{
1630 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001631
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001632 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 priv->conn->prof.txlen = 0;
1634 return count;
1635}
1636
1637static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1638
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001639static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1640 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641{
1642 struct netiucv_priv *priv = dev->driver_data;
1643
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001644 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1646}
1647
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001648static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1649 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650{
1651 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001652
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001653 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 priv->conn->prof.tx_time = 0;
1655 return count;
1656}
1657
1658static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1659
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001660static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1661 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662{
1663 struct netiucv_priv *priv = dev->driver_data;
1664
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001665 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1667}
1668
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001669static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1670 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671{
1672 struct netiucv_priv *priv = dev->driver_data;
1673
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001674 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 priv->conn->prof.tx_pending = 0;
1676 return count;
1677}
1678
1679static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1680
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001681static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1682 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683{
1684 struct netiucv_priv *priv = dev->driver_data;
1685
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001686 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1688}
1689
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001690static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1691 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692{
1693 struct netiucv_priv *priv = dev->driver_data;
1694
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001695 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 priv->conn->prof.tx_max_pending = 0;
1697 return count;
1698}
1699
1700static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1701
1702static struct attribute *netiucv_attrs[] = {
1703 &dev_attr_buffer.attr,
1704 &dev_attr_user.attr,
1705 NULL,
1706};
1707
1708static struct attribute_group netiucv_attr_group = {
1709 .attrs = netiucv_attrs,
1710};
1711
1712static struct attribute *netiucv_stat_attrs[] = {
1713 &dev_attr_device_fsm_state.attr,
1714 &dev_attr_connection_fsm_state.attr,
1715 &dev_attr_max_tx_buffer_used.attr,
1716 &dev_attr_max_chained_skbs.attr,
1717 &dev_attr_tx_single_write_ops.attr,
1718 &dev_attr_tx_multi_write_ops.attr,
1719 &dev_attr_netto_bytes.attr,
1720 &dev_attr_max_tx_io_time.attr,
1721 &dev_attr_tx_pending.attr,
1722 &dev_attr_tx_max_pending.attr,
1723 NULL,
1724};
1725
1726static struct attribute_group netiucv_stat_attr_group = {
1727 .name = "stats",
1728 .attrs = netiucv_stat_attrs,
1729};
1730
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001731static int netiucv_add_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732{
1733 int ret;
1734
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001735 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group);
1737 if (ret)
1738 return ret;
1739 ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group);
1740 if (ret)
1741 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1742 return ret;
1743}
1744
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001745static void netiucv_remove_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001747 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748 sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group);
1749 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1750}
1751
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001752static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001754 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001755 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 int ret;
1757
1758
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001759 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760
1761 if (dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 snprintf(dev->bus_id, BUS_ID_SIZE, "net%s", ndev->name);
1763 dev->bus = &iucv_bus;
1764 dev->parent = iucv_root;
1765 /*
1766 * The release function could be called after the
1767 * module has been unloaded. It's _only_ task is to
1768 * free the struct. Therefore, we specify kfree()
1769 * directly here. (Probably a little bit obfuscating
1770 * but legitime ...).
1771 */
1772 dev->release = (void (*)(struct device *))kfree;
1773 dev->driver = &netiucv_driver;
1774 } else
1775 return -ENOMEM;
1776
1777 ret = device_register(dev);
1778
1779 if (ret)
1780 return ret;
1781 ret = netiucv_add_files(dev);
1782 if (ret)
1783 goto out_unreg;
1784 priv->dev = dev;
1785 dev->driver_data = priv;
1786 return 0;
1787
1788out_unreg:
1789 device_unregister(dev);
1790 return ret;
1791}
1792
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001793static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001795 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 netiucv_remove_files(dev);
1797 device_unregister(dev);
1798}
1799
1800/**
1801 * Allocate and initialize a new connection structure.
1802 * Add it to the list of netiucv connections;
1803 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001804static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
1805 char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001807 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001809 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1810 if (!conn)
1811 goto out;
1812 skb_queue_head_init(&conn->collect_queue);
1813 skb_queue_head_init(&conn->commit_queue);
1814 spin_lock_init(&conn->collect_lock);
1815 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1816 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001818 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1819 if (!conn->rx_buff)
1820 goto out_conn;
1821 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1822 if (!conn->tx_buff)
1823 goto out_rx;
1824 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1825 conn_event_names, NR_CONN_STATES,
1826 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1827 GFP_KERNEL);
1828 if (!conn->fsm)
1829 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001831 fsm_settimer(conn->fsm, &conn->timer);
1832 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001834 if (username) {
1835 memcpy(conn->userid, username, 9);
1836 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001838
1839 write_lock_bh(&iucv_connection_rwlock);
1840 list_add_tail(&conn->list, &iucv_connection_list);
1841 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001843
1844out_tx:
1845 kfree_skb(conn->tx_buff);
1846out_rx:
1847 kfree_skb(conn->rx_buff);
1848out_conn:
1849 kfree(conn);
1850out:
1851 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852}
1853
1854/**
1855 * Release a connection structure and remove it from the
1856 * list of netiucv connections.
1857 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001858static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001860 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001861 write_lock_bh(&iucv_connection_rwlock);
1862 list_del_init(&conn->list);
1863 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001864 fsm_deltimer(&conn->timer);
1865 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001866 if (conn->path) {
1867 iucv_path_sever(conn->path, iucvMagic);
1868 kfree(conn->path);
1869 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001871 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001872 kfree_fsm(conn->fsm);
1873 kfree_skb(conn->rx_buff);
1874 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875}
1876
1877/**
1878 * Release everything of a net device.
1879 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001880static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001882 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001884 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885
1886 if (!dev)
1887 return;
1888
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 if (privptr) {
1890 if (privptr->conn)
1891 netiucv_remove_connection(privptr->conn);
1892 if (privptr->fsm)
1893 kfree_fsm(privptr->fsm);
1894 privptr->conn = NULL; privptr->fsm = NULL;
1895 /* privptr gets freed by free_netdev() */
1896 }
1897 free_netdev(dev);
1898}
1899
1900/**
1901 * Initialize a net device. (Called from kernel in alloc_netdev())
1902 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001903static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 dev->mtu = NETIUCV_MTU_DEFAULT;
1906 dev->hard_start_xmit = netiucv_tx;
1907 dev->open = netiucv_open;
1908 dev->stop = netiucv_close;
1909 dev->get_stats = netiucv_stats;
1910 dev->change_mtu = netiucv_change_mtu;
1911 dev->destructor = netiucv_free_netdevice;
1912 dev->hard_header_len = NETIUCV_HDRLEN;
1913 dev->addr_len = 0;
1914 dev->type = ARPHRD_SLIP;
1915 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1916 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917}
1918
1919/**
1920 * Allocate and initialize everything of a net device.
1921 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001922static struct net_device *netiucv_init_netdevice(char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923{
1924 struct netiucv_priv *privptr;
1925 struct net_device *dev;
1926
1927 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
1928 netiucv_setup_netdevice);
1929 if (!dev)
1930 return NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001931 if (dev_alloc_name(dev, dev->name) < 0)
1932 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001934 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
1936 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
1937 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001938 if (!privptr->fsm)
1939 goto out_netdev;
1940
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 privptr->conn = netiucv_new_connection(dev, username);
1942 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001944 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 }
1946 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001948
1949out_fsm:
1950 kfree_fsm(privptr->fsm);
1951out_netdev:
1952 free_netdev(dev);
1953 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954}
1955
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001956static ssize_t conn_write(struct device_driver *drv,
1957 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001959 const char *p;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001960 char username[9];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001961 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001963 struct netiucv_priv *priv;
1964 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001966 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 if (count>9) {
1968 PRINT_WARN("netiucv: username too long (%d)!\n", (int)count);
1969 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1970 return -EINVAL;
1971 }
1972
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001973 for (i = 0, p = buf; i < 8 && *p; i++, p++) {
1974 if (isalnum(*p) || *p == '$') {
1975 username[i] = toupper(*p);
1976 continue;
1977 }
1978 if (*p == '\n')
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979 /* trailing lf, grr */
1980 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001981 PRINT_WARN("netiucv: Invalid character in username!\n");
1982 IUCV_DBF_TEXT_(setup, 2,
1983 "conn_write: invalid character %c\n", *p);
1984 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001986 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001988 username[8] = '\0';
1989
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001990 read_lock_bh(&iucv_connection_rwlock);
1991 list_for_each_entry(cp, &iucv_connection_list, list) {
1992 if (!strncmp(username, cp->userid, 9)) {
1993 read_unlock_bh(&iucv_connection_rwlock);
1994 PRINT_WARN("netiucv: Connection to %s already "
1995 "exists\n", username);
1996 return -EEXIST;
1997 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02001998 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001999 read_unlock_bh(&iucv_connection_rwlock);
2000
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 dev = netiucv_init_netdevice(username);
2002 if (!dev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002003 PRINT_WARN("netiucv: Could not allocate network device "
2004 "structure for user '%s'\n",
2005 netiucv_printname(username));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2007 return -ENODEV;
2008 }
2009
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002010 rc = netiucv_register_device(dev);
2011 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002013 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 goto out_free_ndev;
2015 }
2016
2017 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002018 priv = netdev_priv(dev);
2019 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002021 rc = register_netdev(dev);
2022 if (rc)
2023 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024
2025 PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002026
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 return count;
2028
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002029out_unreg:
2030 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031out_free_ndev:
2032 PRINT_WARN("netiucv: Could not register '%s'\n", dev->name);
2033 IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n");
2034 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002035 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036}
2037
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002038static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002040static ssize_t remove_write (struct device_driver *drv,
2041 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002043 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044 struct net_device *ndev;
2045 struct netiucv_priv *priv;
2046 struct device *dev;
2047 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002048 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 int i;
2050
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002051 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052
2053 if (count >= IFNAMSIZ)
Frank Pavlic16a83b32006-09-15 16:25:19 +02002054 count = IFNAMSIZ - 1;;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002056 for (i = 0, p = buf; i < count && *p; i++, p++) {
2057 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 /* trailing lf, grr */
2059 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002060 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 }
2062 name[i] = '\0';
2063
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002064 read_lock_bh(&iucv_connection_rwlock);
2065 list_for_each_entry(cp, &iucv_connection_list, list) {
2066 ndev = cp->netdev;
2067 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002069 if (strncmp(name, ndev->name, count))
2070 continue;
2071 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002073 PRINT_WARN("netiucv: net device %s active with peer "
2074 "%s\n", ndev->name, priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 PRINT_WARN("netiucv: %s cannot be removed\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002076 ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
2078 return -EBUSY;
2079 }
2080 unregister_netdev(ndev);
2081 netiucv_unregister_device(dev);
2082 return count;
2083 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002084 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 PRINT_WARN("netiucv: net device %s unknown\n", name);
2086 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2087 return -EINVAL;
2088}
2089
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002090static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002092static struct attribute * netiucv_drv_attrs[] = {
2093 &driver_attr_connection.attr,
2094 &driver_attr_remove.attr,
2095 NULL,
2096};
2097
2098static struct attribute_group netiucv_drv_attr_group = {
2099 .attrs = netiucv_drv_attrs,
2100};
2101
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002102static struct attribute_group *netiucv_drv_attr_groups[] = {
2103 &netiucv_drv_attr_group,
2104 NULL,
2105};
2106
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002107static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108{
Heiko Carstense018ba12006-02-01 03:06:31 -08002109 PRINT_INFO("NETIUCV driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110}
2111
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002112static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002114 struct iucv_connection *cp;
2115 struct net_device *ndev;
2116 struct netiucv_priv *priv;
2117 struct device *dev;
2118
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002119 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002120 while (!list_empty(&iucv_connection_list)) {
2121 cp = list_entry(iucv_connection_list.next,
2122 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002123 ndev = cp->netdev;
2124 priv = netdev_priv(ndev);
2125 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126
2127 unregister_netdev(ndev);
2128 netiucv_unregister_device(dev);
2129 }
2130
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002132 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 iucv_unregister_dbf_views();
2134
2135 PRINT_INFO("NETIUCV driver unloaded\n");
2136 return;
2137}
2138
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002139static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002141 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002142
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002143 rc = iucv_register_dbf_views();
2144 if (rc)
2145 goto out;
2146 rc = iucv_register(&netiucv_handler, 1);
2147 if (rc)
2148 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002149 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002150 rc = driver_register(&netiucv_driver);
2151 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 PRINT_ERR("NETIUCV: failed to register driver.\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002153 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2154 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 }
2156
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002157 netiucv_banner();
2158 return rc;
2159
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002160out_iucv:
2161 iucv_unregister(&netiucv_handler, 1);
2162out_dbf:
2163 iucv_unregister_dbf_views();
2164out:
2165 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002167
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168module_init(netiucv_init);
2169module_exit(netiucv_exit);
2170MODULE_LICENSE("GPL");