blob: e4ba6a0372acb6360124839afe6e651c1a6cec87 [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) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 dev_kfree_skb(skb);
1317 privptr->stats.tx_dropped++;
1318 privptr->stats.tx_errors++;
1319 privptr->stats.tx_carrier_errors++;
1320 return 0;
1321 }
1322
1323 if (netiucv_test_and_set_busy(dev)) {
1324 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
1325 return -EBUSY;
1326 }
1327 dev->trans_start = jiffies;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001328 rc = netiucv_transmit_skb(privptr->conn, skb) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 netiucv_clear_busy(dev);
1330 return rc;
1331}
1332
1333/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001334 * netiucv_stats
1335 * @dev: Pointer to interface struct.
1336 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 * Returns interface statistics of a device.
1338 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001339 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001341static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001343 struct netiucv_priv *priv = netdev_priv(dev);
1344
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001345 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001346 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347}
1348
1349/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001350 * netiucv_change_mtu
1351 * @dev: Pointer to interface struct.
1352 * @new_mtu: The new MTU to use for this interface.
1353 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 * Sets MTU of an interface.
1355 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001356 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 * (valid range is 576 .. NETIUCV_MTU_MAX).
1358 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001359static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001361 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001362 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1364 return -EINVAL;
1365 }
1366 dev->mtu = new_mtu;
1367 return 0;
1368}
1369
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001370/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001372 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001374static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1375 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376{
1377 struct netiucv_priv *priv = dev->driver_data;
1378
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001379 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));
1381}
1382
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001383static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1384 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385{
1386 struct netiucv_priv *priv = dev->driver_data;
1387 struct net_device *ndev = priv->conn->netdev;
1388 char *p;
1389 char *tmp;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001390 char username[9];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 int i;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001392 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001394 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001395 if (count > 9) {
1396 PRINT_WARN("netiucv: username too long (%d)!\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001398 "%d is length of username\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 return -EINVAL;
1400 }
1401
1402 tmp = strsep((char **) &buf, "\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001403 for (i = 0, p = tmp; i < 8 && *p; i++, p++) {
1404 if (isalnum(*p) || (*p == '$')) {
Frank Pavlic16a83b32006-09-15 16:25:19 +02001405 username[i]= toupper(*p);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001406 continue;
1407 }
1408 if (*p == '\n') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 /* trailing lf, grr */
1410 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001412 PRINT_WARN("netiucv: Invalid char %c in username!\n", *p);
1413 IUCV_DBF_TEXT_(setup, 2,
1414 "username: invalid character %c\n", *p);
1415 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001417 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001419 username[8] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001421 if (memcmp(username, priv->conn->userid, 9) &&
1422 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1423 /* username changed while the interface is active. */
1424 PRINT_WARN("netiucv: device %s active, connected to %s\n",
1425 dev->bus_id, priv->conn->userid);
1426 PRINT_WARN("netiucv: user cannot be updated\n");
1427 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
1428 return -EBUSY;
1429 }
1430 read_lock_bh(&iucv_connection_rwlock);
1431 list_for_each_entry(cp, &iucv_connection_list, list) {
1432 if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
1433 read_unlock_bh(&iucv_connection_rwlock);
1434 PRINT_WARN("netiucv: Connection to %s already "
1435 "exists\n", username);
1436 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 }
1438 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001439 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 memcpy(priv->conn->userid, username, 9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442}
1443
1444static DEVICE_ATTR(user, 0644, user_show, user_write);
1445
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001446static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1447 char *buf)
1448{ struct netiucv_priv *priv = dev->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001450 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1452}
1453
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001454static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1455 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456{
1457 struct netiucv_priv *priv = dev->driver_data;
1458 struct net_device *ndev = priv->conn->netdev;
1459 char *e;
1460 int bs1;
1461
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001462 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 if (count >= 39)
1464 return -EINVAL;
1465
1466 bs1 = simple_strtoul(buf, &e, 0);
1467
1468 if (e && (!isspace(*e))) {
1469 PRINT_WARN("netiucv: Invalid character in buffer!\n");
1470 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
1471 return -EINVAL;
1472 }
1473 if (bs1 > NETIUCV_BUFSIZE_MAX) {
1474 PRINT_WARN("netiucv: Given buffer size %d too large.\n",
1475 bs1);
1476 IUCV_DBF_TEXT_(setup, 2,
1477 "buffer_write: buffer size %d too large\n",
1478 bs1);
1479 return -EINVAL;
1480 }
1481 if ((ndev->flags & IFF_RUNNING) &&
1482 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
1483 PRINT_WARN("netiucv: Given buffer size %d too small.\n",
1484 bs1);
1485 IUCV_DBF_TEXT_(setup, 2,
1486 "buffer_write: buffer size %d too small\n",
1487 bs1);
1488 return -EINVAL;
1489 }
1490 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
1491 PRINT_WARN("netiucv: Given buffer size %d too small.\n",
1492 bs1);
1493 IUCV_DBF_TEXT_(setup, 2,
1494 "buffer_write: buffer size %d too small\n",
1495 bs1);
1496 return -EINVAL;
1497 }
1498
1499 priv->conn->max_buffsize = bs1;
1500 if (!(ndev->flags & IFF_RUNNING))
1501 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1502
1503 return count;
1504
1505}
1506
1507static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1508
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001509static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1510 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511{
1512 struct netiucv_priv *priv = dev->driver_data;
1513
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001514 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1516}
1517
1518static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1519
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001520static ssize_t conn_fsm_show (struct device *dev,
1521 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522{
1523 struct netiucv_priv *priv = dev->driver_data;
1524
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001525 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1527}
1528
1529static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1530
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001531static ssize_t maxmulti_show (struct device *dev,
1532 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533{
1534 struct netiucv_priv *priv = dev->driver_data;
1535
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001536 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1538}
1539
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001540static ssize_t maxmulti_write (struct device *dev,
1541 struct device_attribute *attr,
1542 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543{
1544 struct netiucv_priv *priv = dev->driver_data;
1545
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001546 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 priv->conn->prof.maxmulti = 0;
1548 return count;
1549}
1550
1551static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1552
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001553static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1554 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555{
1556 struct netiucv_priv *priv = dev->driver_data;
1557
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001558 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1560}
1561
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001562static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1563 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564{
1565 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001566
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001567 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 priv->conn->prof.maxcqueue = 0;
1569 return count;
1570}
1571
1572static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1573
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001574static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1575 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576{
1577 struct netiucv_priv *priv = dev->driver_data;
1578
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001579 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1581}
1582
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001583static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1584 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585{
1586 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001587
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001588 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 priv->conn->prof.doios_single = 0;
1590 return count;
1591}
1592
1593static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1594
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001595static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1596 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597{
1598 struct netiucv_priv *priv = dev->driver_data;
1599
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001600 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1602}
1603
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001604static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1605 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606{
1607 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001608
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001609 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 priv->conn->prof.doios_multi = 0;
1611 return count;
1612}
1613
1614static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1615
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001616static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1617 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618{
1619 struct netiucv_priv *priv = dev->driver_data;
1620
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001621 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1623}
1624
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001625static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1626 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627{
1628 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001629
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001630 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 priv->conn->prof.txlen = 0;
1632 return count;
1633}
1634
1635static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1636
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001637static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1638 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
1640 struct netiucv_priv *priv = dev->driver_data;
1641
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001642 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1644}
1645
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001646static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1647 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648{
1649 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001650
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001651 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 priv->conn->prof.tx_time = 0;
1653 return count;
1654}
1655
1656static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1657
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001658static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1659 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660{
1661 struct netiucv_priv *priv = dev->driver_data;
1662
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001663 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1665}
1666
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001667static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1668 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669{
1670 struct netiucv_priv *priv = dev->driver_data;
1671
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001672 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 priv->conn->prof.tx_pending = 0;
1674 return count;
1675}
1676
1677static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1678
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001679static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1680 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681{
1682 struct netiucv_priv *priv = dev->driver_data;
1683
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001684 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1686}
1687
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001688static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1689 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690{
1691 struct netiucv_priv *priv = dev->driver_data;
1692
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001693 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 priv->conn->prof.tx_max_pending = 0;
1695 return count;
1696}
1697
1698static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1699
1700static struct attribute *netiucv_attrs[] = {
1701 &dev_attr_buffer.attr,
1702 &dev_attr_user.attr,
1703 NULL,
1704};
1705
1706static struct attribute_group netiucv_attr_group = {
1707 .attrs = netiucv_attrs,
1708};
1709
1710static struct attribute *netiucv_stat_attrs[] = {
1711 &dev_attr_device_fsm_state.attr,
1712 &dev_attr_connection_fsm_state.attr,
1713 &dev_attr_max_tx_buffer_used.attr,
1714 &dev_attr_max_chained_skbs.attr,
1715 &dev_attr_tx_single_write_ops.attr,
1716 &dev_attr_tx_multi_write_ops.attr,
1717 &dev_attr_netto_bytes.attr,
1718 &dev_attr_max_tx_io_time.attr,
1719 &dev_attr_tx_pending.attr,
1720 &dev_attr_tx_max_pending.attr,
1721 NULL,
1722};
1723
1724static struct attribute_group netiucv_stat_attr_group = {
1725 .name = "stats",
1726 .attrs = netiucv_stat_attrs,
1727};
1728
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001729static int netiucv_add_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730{
1731 int ret;
1732
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001733 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group);
1735 if (ret)
1736 return ret;
1737 ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group);
1738 if (ret)
1739 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1740 return ret;
1741}
1742
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001743static void netiucv_remove_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001745 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group);
1747 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1748}
1749
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001750static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001752 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001753 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 int ret;
1755
1756
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001757 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758
1759 if (dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 snprintf(dev->bus_id, BUS_ID_SIZE, "net%s", ndev->name);
1761 dev->bus = &iucv_bus;
1762 dev->parent = iucv_root;
1763 /*
1764 * The release function could be called after the
1765 * module has been unloaded. It's _only_ task is to
1766 * free the struct. Therefore, we specify kfree()
1767 * directly here. (Probably a little bit obfuscating
1768 * but legitime ...).
1769 */
1770 dev->release = (void (*)(struct device *))kfree;
1771 dev->driver = &netiucv_driver;
1772 } else
1773 return -ENOMEM;
1774
1775 ret = device_register(dev);
1776
1777 if (ret)
1778 return ret;
1779 ret = netiucv_add_files(dev);
1780 if (ret)
1781 goto out_unreg;
1782 priv->dev = dev;
1783 dev->driver_data = priv;
1784 return 0;
1785
1786out_unreg:
1787 device_unregister(dev);
1788 return ret;
1789}
1790
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001791static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001793 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794 netiucv_remove_files(dev);
1795 device_unregister(dev);
1796}
1797
1798/**
1799 * Allocate and initialize a new connection structure.
1800 * Add it to the list of netiucv connections;
1801 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001802static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
1803 char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001805 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001807 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1808 if (!conn)
1809 goto out;
1810 skb_queue_head_init(&conn->collect_queue);
1811 skb_queue_head_init(&conn->commit_queue);
1812 spin_lock_init(&conn->collect_lock);
1813 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1814 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001816 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1817 if (!conn->rx_buff)
1818 goto out_conn;
1819 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1820 if (!conn->tx_buff)
1821 goto out_rx;
1822 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1823 conn_event_names, NR_CONN_STATES,
1824 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1825 GFP_KERNEL);
1826 if (!conn->fsm)
1827 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001829 fsm_settimer(conn->fsm, &conn->timer);
1830 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001832 if (username) {
1833 memcpy(conn->userid, username, 9);
1834 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001836
1837 write_lock_bh(&iucv_connection_rwlock);
1838 list_add_tail(&conn->list, &iucv_connection_list);
1839 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001841
1842out_tx:
1843 kfree_skb(conn->tx_buff);
1844out_rx:
1845 kfree_skb(conn->rx_buff);
1846out_conn:
1847 kfree(conn);
1848out:
1849 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850}
1851
1852/**
1853 * Release a connection structure and remove it from the
1854 * list of netiucv connections.
1855 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001856static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001858 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001859 write_lock_bh(&iucv_connection_rwlock);
1860 list_del_init(&conn->list);
1861 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001862 fsm_deltimer(&conn->timer);
1863 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001864 if (conn->path) {
1865 iucv_path_sever(conn->path, iucvMagic);
1866 kfree(conn->path);
1867 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001869 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001870 kfree_fsm(conn->fsm);
1871 kfree_skb(conn->rx_buff);
1872 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873}
1874
1875/**
1876 * Release everything of a net device.
1877 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001878static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001880 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001882 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883
1884 if (!dev)
1885 return;
1886
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 if (privptr) {
1888 if (privptr->conn)
1889 netiucv_remove_connection(privptr->conn);
1890 if (privptr->fsm)
1891 kfree_fsm(privptr->fsm);
1892 privptr->conn = NULL; privptr->fsm = NULL;
1893 /* privptr gets freed by free_netdev() */
1894 }
1895 free_netdev(dev);
1896}
1897
1898/**
1899 * Initialize a net device. (Called from kernel in alloc_netdev())
1900 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001901static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903 dev->mtu = NETIUCV_MTU_DEFAULT;
1904 dev->hard_start_xmit = netiucv_tx;
1905 dev->open = netiucv_open;
1906 dev->stop = netiucv_close;
1907 dev->get_stats = netiucv_stats;
1908 dev->change_mtu = netiucv_change_mtu;
1909 dev->destructor = netiucv_free_netdevice;
1910 dev->hard_header_len = NETIUCV_HDRLEN;
1911 dev->addr_len = 0;
1912 dev->type = ARPHRD_SLIP;
1913 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1914 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915}
1916
1917/**
1918 * Allocate and initialize everything of a net device.
1919 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001920static struct net_device *netiucv_init_netdevice(char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921{
1922 struct netiucv_priv *privptr;
1923 struct net_device *dev;
1924
1925 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
1926 netiucv_setup_netdevice);
1927 if (!dev)
1928 return NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001929 if (dev_alloc_name(dev, dev->name) < 0)
1930 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001932 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
1934 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
1935 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001936 if (!privptr->fsm)
1937 goto out_netdev;
1938
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 privptr->conn = netiucv_new_connection(dev, username);
1940 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001942 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 }
1944 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001946
1947out_fsm:
1948 kfree_fsm(privptr->fsm);
1949out_netdev:
1950 free_netdev(dev);
1951 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952}
1953
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001954static ssize_t conn_write(struct device_driver *drv,
1955 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001957 const char *p;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001958 char username[9];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001959 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001961 struct netiucv_priv *priv;
1962 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001964 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965 if (count>9) {
1966 PRINT_WARN("netiucv: username too long (%d)!\n", (int)count);
1967 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1968 return -EINVAL;
1969 }
1970
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001971 for (i = 0, p = buf; i < 8 && *p; i++, p++) {
1972 if (isalnum(*p) || *p == '$') {
1973 username[i] = toupper(*p);
1974 continue;
1975 }
1976 if (*p == '\n')
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 /* trailing lf, grr */
1978 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001979 PRINT_WARN("netiucv: Invalid character in username!\n");
1980 IUCV_DBF_TEXT_(setup, 2,
1981 "conn_write: invalid character %c\n", *p);
1982 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001984 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001986 username[8] = '\0';
1987
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001988 read_lock_bh(&iucv_connection_rwlock);
1989 list_for_each_entry(cp, &iucv_connection_list, list) {
1990 if (!strncmp(username, cp->userid, 9)) {
1991 read_unlock_bh(&iucv_connection_rwlock);
1992 PRINT_WARN("netiucv: Connection to %s already "
1993 "exists\n", username);
1994 return -EEXIST;
1995 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02001996 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001997 read_unlock_bh(&iucv_connection_rwlock);
1998
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 dev = netiucv_init_netdevice(username);
2000 if (!dev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002001 PRINT_WARN("netiucv: Could not allocate network device "
2002 "structure for user '%s'\n",
2003 netiucv_printname(username));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2005 return -ENODEV;
2006 }
2007
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002008 rc = netiucv_register_device(dev);
2009 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002011 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 goto out_free_ndev;
2013 }
2014
2015 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002016 priv = netdev_priv(dev);
2017 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002019 rc = register_netdev(dev);
2020 if (rc)
2021 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022
2023 PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002024
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 return count;
2026
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002027out_unreg:
2028 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029out_free_ndev:
2030 PRINT_WARN("netiucv: Could not register '%s'\n", dev->name);
2031 IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n");
2032 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002033 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034}
2035
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002036static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002038static ssize_t remove_write (struct device_driver *drv,
2039 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002041 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 struct net_device *ndev;
2043 struct netiucv_priv *priv;
2044 struct device *dev;
2045 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002046 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 int i;
2048
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002049 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050
2051 if (count >= IFNAMSIZ)
Frank Pavlic16a83b32006-09-15 16:25:19 +02002052 count = IFNAMSIZ - 1;;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002054 for (i = 0, p = buf; i < count && *p; i++, p++) {
2055 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 /* trailing lf, grr */
2057 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002058 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 }
2060 name[i] = '\0';
2061
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002062 read_lock_bh(&iucv_connection_rwlock);
2063 list_for_each_entry(cp, &iucv_connection_list, list) {
2064 ndev = cp->netdev;
2065 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002067 if (strncmp(name, ndev->name, count))
2068 continue;
2069 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002071 PRINT_WARN("netiucv: net device %s active with peer "
2072 "%s\n", ndev->name, priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 PRINT_WARN("netiucv: %s cannot be removed\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002074 ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
2076 return -EBUSY;
2077 }
2078 unregister_netdev(ndev);
2079 netiucv_unregister_device(dev);
2080 return count;
2081 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002082 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 PRINT_WARN("netiucv: net device %s unknown\n", name);
2084 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2085 return -EINVAL;
2086}
2087
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002088static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002090static struct attribute * netiucv_drv_attrs[] = {
2091 &driver_attr_connection.attr,
2092 &driver_attr_remove.attr,
2093 NULL,
2094};
2095
2096static struct attribute_group netiucv_drv_attr_group = {
2097 .attrs = netiucv_drv_attrs,
2098};
2099
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002100static struct attribute_group *netiucv_drv_attr_groups[] = {
2101 &netiucv_drv_attr_group,
2102 NULL,
2103};
2104
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002105static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106{
Heiko Carstense018ba12006-02-01 03:06:31 -08002107 PRINT_INFO("NETIUCV driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108}
2109
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002110static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002112 struct iucv_connection *cp;
2113 struct net_device *ndev;
2114 struct netiucv_priv *priv;
2115 struct device *dev;
2116
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002117 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002118 while (!list_empty(&iucv_connection_list)) {
2119 cp = list_entry(iucv_connection_list.next,
2120 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002121 ndev = cp->netdev;
2122 priv = netdev_priv(ndev);
2123 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124
2125 unregister_netdev(ndev);
2126 netiucv_unregister_device(dev);
2127 }
2128
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002130 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 iucv_unregister_dbf_views();
2132
2133 PRINT_INFO("NETIUCV driver unloaded\n");
2134 return;
2135}
2136
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002137static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002139 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002140
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002141 rc = iucv_register_dbf_views();
2142 if (rc)
2143 goto out;
2144 rc = iucv_register(&netiucv_handler, 1);
2145 if (rc)
2146 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002147 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002148 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002149 rc = driver_register(&netiucv_driver);
2150 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 PRINT_ERR("NETIUCV: failed to register driver.\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002152 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2153 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154 }
2155
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002156 netiucv_banner();
2157 return rc;
2158
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002159out_iucv:
2160 iucv_unregister(&netiucv_handler, 1);
2161out_dbf:
2162 iucv_unregister_dbf_views();
2163out:
2164 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002166
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167module_init(netiucv_init);
2168module_exit(netiucv_exit);
2169MODULE_LICENSE("GPL");