blob: c7ea9381db9f63050b8589ee05faa466d295120e [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
100#define IUCV_DBF_TEXT_(name,level,text...) \
101 do { \
102 char* iucv_dbf_txt_buf = get_cpu_var(iucv_dbf_txt_buf); \
103 sprintf(iucv_dbf_txt_buf, text); \
104 debug_text_event(iucv_dbf_##name,level,iucv_dbf_txt_buf); \
105 put_cpu_var(iucv_dbf_txt_buf); \
106 } while (0)
107
108#define IUCV_DBF_SPRINTF(name,level,text...) \
109 do { \
110 debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
111 debug_sprintf_event(iucv_dbf_trace, level, text ); \
112 } while (0)
113
114/**
115 * some more debug stuff
116 */
117#define IUCV_HEXDUMP16(importance,header,ptr) \
118PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
119 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
120 *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
121 *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
122 *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
123 *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
124 *(((char*)ptr)+12),*(((char*)ptr)+13), \
125 *(((char*)ptr)+14),*(((char*)ptr)+15)); \
126PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
127 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
128 *(((char*)ptr)+16),*(((char*)ptr)+17), \
129 *(((char*)ptr)+18),*(((char*)ptr)+19), \
130 *(((char*)ptr)+20),*(((char*)ptr)+21), \
131 *(((char*)ptr)+22),*(((char*)ptr)+23), \
132 *(((char*)ptr)+24),*(((char*)ptr)+25), \
133 *(((char*)ptr)+26),*(((char*)ptr)+27), \
134 *(((char*)ptr)+28),*(((char*)ptr)+29), \
135 *(((char*)ptr)+30),*(((char*)ptr)+31));
136
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137#define PRINTK_HEADER " iucv: " /* for debugging */
138
139static struct device_driver netiucv_driver = {
140 .name = "netiucv",
141 .bus = &iucv_bus,
142};
143
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800144static int netiucv_callback_connreq(struct iucv_path *,
145 u8 ipvmid[8], u8 ipuser[16]);
146static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
147static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
148static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
149static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
150static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
151static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
152
153static struct iucv_handler netiucv_handler = {
154 .path_pending = netiucv_callback_connreq,
155 .path_complete = netiucv_callback_connack,
156 .path_severed = netiucv_callback_connrej,
157 .path_quiesced = netiucv_callback_connsusp,
158 .path_resumed = netiucv_callback_connres,
159 .message_pending = netiucv_callback_rx,
160 .message_complete = netiucv_callback_txdone
161};
162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163/**
164 * Per connection profiling data
165 */
166struct connection_profile {
167 unsigned long maxmulti;
168 unsigned long maxcqueue;
169 unsigned long doios_single;
170 unsigned long doios_multi;
171 unsigned long txlen;
172 unsigned long tx_time;
173 struct timespec send_stamp;
174 unsigned long tx_pending;
175 unsigned long tx_max_pending;
176};
177
178/**
179 * Representation of one iucv connection
180 */
181struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800182 struct list_head list;
183 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 struct sk_buff *rx_buff;
185 struct sk_buff *tx_buff;
186 struct sk_buff_head collect_queue;
187 struct sk_buff_head commit_queue;
188 spinlock_t collect_lock;
189 int collect_len;
190 int max_buffsize;
191 fsm_timer timer;
192 fsm_instance *fsm;
193 struct net_device *netdev;
194 struct connection_profile prof;
195 char userid[9];
196};
197
198/**
199 * Linked list of all connection structs.
200 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800201static struct list_head iucv_connection_list =
202 LIST_HEAD_INIT(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200203static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
205/**
206 * Representation of event-data for the
207 * connection state machine.
208 */
209struct iucv_event {
210 struct iucv_connection *conn;
211 void *data;
212};
213
214/**
215 * Private part of the network device structure
216 */
217struct netiucv_priv {
218 struct net_device_stats stats;
219 unsigned long tbusy;
220 fsm_instance *fsm;
221 struct iucv_connection *conn;
222 struct device *dev;
223};
224
225/**
226 * Link level header for a packet.
227 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800228struct ll_header {
229 u16 next;
230};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800232#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233#define NETIUCV_BUFSIZE_MAX 32768
234#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
235#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
236#define NETIUCV_MTU_DEFAULT 9216
237#define NETIUCV_QUEUELEN_DEFAULT 50
238#define NETIUCV_TIMEOUT_5SEC 5000
239
240/**
241 * Compatibility macros for busy handling
242 * of network devices.
243 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800244static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800246 struct netiucv_priv *priv = netdev_priv(dev);
247 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 netif_wake_queue(dev);
249}
250
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800251static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800253 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800255 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256}
257
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800258static u8 iucvMagic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
260 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
261};
262
263/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 * Convert an iucv userId to its printable
265 * form (strip whitespace at end).
266 *
267 * @param An iucv userId
268 *
269 * @returns The printable string (static data!!)
270 */
Martin Schwidefskyd4614622007-06-20 13:03:57 +0200271static char *netiucv_printname(char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272{
273 static char tmp[9];
274 char *p = tmp;
275 memcpy(tmp, name, 8);
276 tmp[8] = '\0';
277 while (*p && (!isspace(*p)))
278 p++;
279 *p = '\0';
280 return tmp;
281}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400282
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283/**
284 * States of the interface statemachine.
285 */
286enum dev_states {
287 DEV_STATE_STOPPED,
288 DEV_STATE_STARTWAIT,
289 DEV_STATE_STOPWAIT,
290 DEV_STATE_RUNNING,
291 /**
292 * MUST be always the last element!!
293 */
294 NR_DEV_STATES
295};
296
297static const char *dev_state_names[] = {
298 "Stopped",
299 "StartWait",
300 "StopWait",
301 "Running",
302};
303
304/**
305 * Events of the interface statemachine.
306 */
307enum dev_events {
308 DEV_EVENT_START,
309 DEV_EVENT_STOP,
310 DEV_EVENT_CONUP,
311 DEV_EVENT_CONDOWN,
312 /**
313 * MUST be always the last element!!
314 */
315 NR_DEV_EVENTS
316};
317
318static const char *dev_event_names[] = {
319 "Start",
320 "Stop",
321 "Connection up",
322 "Connection down",
323};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325/**
326 * Events of the connection statemachine
327 */
328enum conn_events {
329 /**
330 * Events, representing callbacks from
331 * lowlevel iucv layer)
332 */
333 CONN_EVENT_CONN_REQ,
334 CONN_EVENT_CONN_ACK,
335 CONN_EVENT_CONN_REJ,
336 CONN_EVENT_CONN_SUS,
337 CONN_EVENT_CONN_RES,
338 CONN_EVENT_RX,
339 CONN_EVENT_TXDONE,
340
341 /**
342 * Events, representing errors return codes from
343 * calls to lowlevel iucv layer
344 */
345
346 /**
347 * Event, representing timer expiry.
348 */
349 CONN_EVENT_TIMER,
350
351 /**
352 * Events, representing commands from upper levels.
353 */
354 CONN_EVENT_START,
355 CONN_EVENT_STOP,
356
357 /**
358 * MUST be always the last element!!
359 */
360 NR_CONN_EVENTS,
361};
362
363static const char *conn_event_names[] = {
364 "Remote connection request",
365 "Remote connection acknowledge",
366 "Remote connection reject",
367 "Connection suspended",
368 "Connection resumed",
369 "Data received",
370 "Data sent",
371
372 "Timer",
373
374 "Start",
375 "Stop",
376};
377
378/**
379 * States of the connection statemachine.
380 */
381enum conn_states {
382 /**
383 * Connection not assigned to any device,
384 * initial state, invalid
385 */
386 CONN_STATE_INVALID,
387
388 /**
389 * Userid assigned but not operating
390 */
391 CONN_STATE_STOPPED,
392
393 /**
394 * Connection registered,
395 * no connection request sent yet,
396 * no connection request received
397 */
398 CONN_STATE_STARTWAIT,
399
400 /**
401 * Connection registered and connection request sent,
402 * no acknowledge and no connection request received yet.
403 */
404 CONN_STATE_SETUPWAIT,
405
406 /**
407 * Connection up and running idle
408 */
409 CONN_STATE_IDLE,
410
411 /**
412 * Data sent, awaiting CONN_EVENT_TXDONE
413 */
414 CONN_STATE_TX,
415
416 /**
417 * Error during registration.
418 */
419 CONN_STATE_REGERR,
420
421 /**
422 * Error during registration.
423 */
424 CONN_STATE_CONNERR,
425
426 /**
427 * MUST be always the last element!!
428 */
429 NR_CONN_STATES,
430};
431
432static const char *conn_state_names[] = {
433 "Invalid",
434 "Stopped",
435 "StartWait",
436 "SetupWait",
437 "Idle",
438 "TX",
439 "Terminating",
440 "Registration error",
441 "Connect error",
442};
443
Jeff Garzike82b0f22006-05-26 21:58:38 -0400444
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445/**
446 * Debug Facility Stuff
447 */
448static debug_info_t *iucv_dbf_setup = NULL;
449static debug_info_t *iucv_dbf_data = NULL;
450static debug_info_t *iucv_dbf_trace = NULL;
451
452DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
453
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800454static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455{
456 if (iucv_dbf_setup)
457 debug_unregister(iucv_dbf_setup);
458 if (iucv_dbf_data)
459 debug_unregister(iucv_dbf_data);
460 if (iucv_dbf_trace)
461 debug_unregister(iucv_dbf_trace);
462}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800463static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
465 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700466 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 IUCV_DBF_SETUP_NR_AREAS,
468 IUCV_DBF_SETUP_LEN);
469 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700470 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 IUCV_DBF_DATA_NR_AREAS,
472 IUCV_DBF_DATA_LEN);
473 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700474 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 IUCV_DBF_TRACE_NR_AREAS,
476 IUCV_DBF_TRACE_LEN);
477
478 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
479 (iucv_dbf_trace == NULL)) {
480 iucv_unregister_dbf_views();
481 return -ENOMEM;
482 }
483 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
484 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
485
486 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
487 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
488
489 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
490 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
491
492 return 0;
493}
494
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800495/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800497 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800499static void netiucv_callback_rx(struct iucv_path *path,
500 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800502 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 struct iucv_event ev;
504
505 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800506 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
508}
509
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800510static void netiucv_callback_txdone(struct iucv_path *path,
511 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800513 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 struct iucv_event ev;
515
516 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800517 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
519}
520
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800521static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
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
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800525 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526}
527
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800528static int netiucv_callback_connreq(struct iucv_path *path,
529 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800531 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 struct iucv_event ev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800533 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800535 if (memcmp(iucvMagic, ipuser, sizeof(ipuser)))
536 /* ipuser must match iucvMagic. */
537 return -EINVAL;
538 rc = -EINVAL;
539 read_lock_bh(&iucv_connection_rwlock);
540 list_for_each_entry(conn, &iucv_connection_list, list) {
541 if (strncmp(ipvmid, conn->userid, 8))
542 continue;
543 /* Found a matching connection for this path. */
544 conn->path = path;
545 ev.conn = conn;
546 ev.data = path;
547 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
548 rc = 0;
549 }
550 read_unlock_bh(&iucv_connection_rwlock);
551 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552}
553
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800554static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800556 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800558 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559}
560
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800561static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800563 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800565 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566}
567
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800568static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800570 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800572 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573}
574
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575/**
576 * Dummy NOP action for all statemachines
577 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800578static void fsm_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579{
580}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400581
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800582/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800584 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
586/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800587 * netiucv_unpack_skb
588 * @conn: The connection where this skb has been received.
589 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800591 * Unpack a just received skb and hand it over to upper layers.
592 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800594static void netiucv_unpack_skb(struct iucv_connection *conn,
595 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596{
597 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800598 struct netiucv_priv *privptr = netdev_priv(dev);
599 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
601 skb_put(pskb, NETIUCV_HDRLEN);
602 pskb->dev = dev;
603 pskb->ip_summed = CHECKSUM_NONE;
604 pskb->protocol = ntohs(ETH_P_IP);
605
606 while (1) {
607 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800608 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609
610 if (!header->next)
611 break;
612
613 skb_pull(pskb, NETIUCV_HDRLEN);
614 header->next -= offset;
615 offset += header->next;
616 header->next -= NETIUCV_HDRLEN;
617 if (skb_tailroom(pskb) < header->next) {
618 PRINT_WARN("%s: Illegal next field in iucv header: "
619 "%d > %d\n",
620 dev->name, header->next, skb_tailroom(pskb));
621 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
622 header->next, skb_tailroom(pskb));
623 return;
624 }
625 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700626 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 skb = dev_alloc_skb(pskb->len);
628 if (!skb) {
629 PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n",
630 dev->name);
631 IUCV_DBF_TEXT(data, 2,
632 "Out of memory in netiucv_unpack_skb\n");
633 privptr->stats.rx_dropped++;
634 return;
635 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300636 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
637 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700638 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 skb->dev = pskb->dev;
640 skb->protocol = pskb->protocol;
641 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800642 privptr->stats.rx_packets++;
643 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 /*
645 * Since receiving is always initiated from a tasklet (in iucv.c),
646 * we must use netif_rx_ni() instead of netif_rx()
647 */
648 netif_rx_ni(skb);
649 dev->last_rx = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 skb_pull(pskb, header->next);
651 skb_put(pskb, NETIUCV_HDRLEN);
652 }
653}
654
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800655static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800657 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800659 struct iucv_message *msg = ev->data;
660 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 int rc;
662
663 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
664
665 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800666 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 PRINT_WARN("Received data for unlinked connection\n");
668 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800669 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 return;
671 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800672 if (msg->length > conn->max_buffsize) {
673 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 privptr->stats.rx_dropped++;
675 PRINT_WARN("msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800676 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800678 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 return;
680 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700681 conn->rx_buff->data = conn->rx_buff->head;
682 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800684 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
685 msg->length, NULL);
686 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 privptr->stats.rx_errors++;
688 PRINT_WARN("iucv_receive returned %08x\n", rc);
689 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
690 return;
691 }
692 netiucv_unpack_skb(conn, conn->rx_buff);
693}
694
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800695static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800697 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800699 struct iucv_message *msg = ev->data;
700 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800702 u32 single_flag = msg->tag;
703 u32 txbytes = 0;
704 u32 txpackets = 0;
705 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 struct sk_buff *skb;
707 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800708 struct ll_header header;
709 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
711 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
712
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800713 if (conn && conn->netdev)
714 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 conn->prof.tx_pending--;
716 if (single_flag) {
717 if ((skb = skb_dequeue(&conn->commit_queue))) {
718 atomic_dec(&skb->users);
719 dev_kfree_skb_any(skb);
720 if (privptr) {
721 privptr->stats.tx_packets++;
722 privptr->stats.tx_bytes +=
723 (skb->len - NETIUCV_HDRLEN
724 - NETIUCV_HDRLEN);
725 }
726 }
727 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700728 conn->tx_buff->data = conn->tx_buff->head;
729 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 conn->tx_buff->len = 0;
731 spin_lock_irqsave(&conn->collect_lock, saveflags);
732 while ((skb = skb_dequeue(&conn->collect_queue))) {
733 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
734 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
735 NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300736 skb_copy_from_linear_data(skb,
737 skb_put(conn->tx_buff, skb->len),
738 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 txbytes += skb->len;
740 txpackets++;
741 stat_maxcq++;
742 atomic_dec(&skb->users);
743 dev_kfree_skb_any(skb);
744 }
745 if (conn->collect_len > conn->prof.maxmulti)
746 conn->prof.maxmulti = conn->collect_len;
747 conn->collect_len = 0;
748 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800749 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800751 return;
752 }
753
754 header.next = 0;
755 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
john stultz2c6b47d2007-07-24 17:47:43 -0700756 conn->prof.send_stamp = current_kernel_time();
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800757 txmsg.class = 0;
758 txmsg.tag = 0;
759 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
760 conn->tx_buff->data, conn->tx_buff->len);
761 conn->prof.doios_multi++;
762 conn->prof.txlen += conn->tx_buff->len;
763 conn->prof.tx_pending++;
764 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
765 conn->prof.tx_max_pending = conn->prof.tx_pending;
766 if (rc) {
767 conn->prof.tx_pending--;
768 fsm_newstate(fi, CONN_STATE_IDLE);
769 if (privptr)
770 privptr->stats.tx_errors += txpackets;
771 PRINT_WARN("iucv_send returned %08x\n", rc);
772 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
773 } else {
774 if (privptr) {
775 privptr->stats.tx_packets += txpackets;
776 privptr->stats.tx_bytes += txbytes;
777 }
778 if (stat_maxcq > conn->prof.maxcqueue)
779 conn->prof.maxcqueue = stat_maxcq;
780 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781}
782
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800783static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800785 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800787 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800789 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
792 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
793
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800794 conn->path = path;
795 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
796 path->flags = 0;
797 rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 if (rc) {
799 PRINT_WARN("%s: IUCV accept failed with error %d\n",
800 netdev->name, rc);
801 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
802 return;
803 }
804 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800805 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
807}
808
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800809static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800811 struct iucv_event *ev = arg;
812 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
814 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800815 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816}
817
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800818static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800820 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800822 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
824 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 fsm_deltimer(&conn->timer);
826 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800827 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
829}
830
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800831static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800833 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
835 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800837 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 fsm_newstate(fi, CONN_STATE_STARTWAIT);
839}
840
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800841static void conn_action_connsever(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 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800845 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
847 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
848
849 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800850 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 PRINT_INFO("%s: Remote dropped connection\n", netdev->name);
852 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800853 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 fsm_newstate(fi, CONN_STATE_STARTWAIT);
855 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
856}
857
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800858static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800860 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 int rc;
862
863 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
864
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800865 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 PRINT_DEBUG("%s('%s'): connecting ...\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800867 conn->netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800869 /*
870 * We must set the state before calling iucv_connect because the
871 * callback handler could be called at any point after the connection
872 * request is sent
873 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874
875 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800876 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
877 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
878 NULL, iucvMagic, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800880 case 0:
881 conn->netdev->tx_queue_len = conn->path->msglim;
882 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
883 CONN_EVENT_TIMER, conn);
884 return;
885 case 11:
886 PRINT_INFO("%s: User %s is currently not available.\n",
887 conn->netdev->name,
888 netiucv_printname(conn->userid));
889 fsm_newstate(fi, CONN_STATE_STARTWAIT);
890 break;
891 case 12:
892 PRINT_INFO("%s: User %s is currently not ready.\n",
893 conn->netdev->name,
894 netiucv_printname(conn->userid));
895 fsm_newstate(fi, CONN_STATE_STARTWAIT);
896 break;
897 case 13:
898 PRINT_WARN("%s: Too many IUCV connections.\n",
899 conn->netdev->name);
900 fsm_newstate(fi, CONN_STATE_CONNERR);
901 break;
902 case 14:
903 PRINT_WARN("%s: User %s has too many IUCV connections.\n",
904 conn->netdev->name,
905 netiucv_printname(conn->userid));
906 fsm_newstate(fi, CONN_STATE_CONNERR);
907 break;
908 case 15:
909 PRINT_WARN("%s: No IUCV authorization in CP directory.\n",
910 conn->netdev->name);
911 fsm_newstate(fi, CONN_STATE_CONNERR);
912 break;
913 default:
914 PRINT_WARN("%s: iucv_connect returned error %d\n",
915 conn->netdev->name, rc);
916 fsm_newstate(fi, CONN_STATE_CONNERR);
917 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 }
919 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800920 kfree(conn->path);
921 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922}
923
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800924static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925{
926 struct sk_buff *skb;
927
928 while ((skb = skb_dequeue(q))) {
929 atomic_dec(&skb->users);
930 dev_kfree_skb_any(skb);
931 }
932}
933
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800934static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800936 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 struct iucv_connection *conn = ev->conn;
938 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800939 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940
941 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
942
943 fsm_deltimer(&conn->timer);
944 fsm_newstate(fi, CONN_STATE_STOPPED);
945 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800946 if (conn->path) {
947 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
948 iucv_path_sever(conn->path, iucvMagic);
949 kfree(conn->path);
950 conn->path = NULL;
951 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 netiucv_purge_skb_queue(&conn->commit_queue);
953 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
954}
955
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800956static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800958 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 struct net_device *netdev = conn->netdev;
960
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800961 PRINT_WARN("%s: Cannot connect without username\n", netdev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 IUCV_DBF_TEXT(data, 2, "conn_action_inval called\n");
963}
964
965static const fsm_node conn_fsm[] = {
966 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
967 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
968
969 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
970 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
971 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
972 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
973 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
974 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
975 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
976
977 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
978 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
979 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
980 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
981 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
982
983 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
984 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
985
986 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
987 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
988 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
989
990 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
991 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
992
993 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
994 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
995};
996
997static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
998
Jeff Garzike82b0f22006-05-26 21:58:38 -0400999
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001000/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001002 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
1004/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001005 * dev_action_start
1006 * @fi: An instance of an interface statemachine.
1007 * @event: The event, just happened.
1008 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001010 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001012static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001014 struct net_device *dev = arg;
1015 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016
1017 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1018
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001020 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021}
1022
1023/**
1024 * Shutdown connection by sending CONN_EVENT_STOP to it.
1025 *
1026 * @param fi An instance of an interface statemachine.
1027 * @param event The event, just happened.
1028 * @param arg Generic pointer, casted from struct net_device * upon call.
1029 */
1030static void
1031dev_action_stop(fsm_instance *fi, int event, void *arg)
1032{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001033 struct net_device *dev = arg;
1034 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 struct iucv_event ev;
1036
1037 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1038
1039 ev.conn = privptr->conn;
1040
1041 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1042 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1043}
1044
1045/**
1046 * Called from connection statemachine
1047 * when a connection is up and running.
1048 *
1049 * @param fi An instance of an interface statemachine.
1050 * @param event The event, just happened.
1051 * @param arg Generic pointer, casted from struct net_device * upon call.
1052 */
1053static void
1054dev_action_connup(fsm_instance *fi, int event, void *arg)
1055{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001056 struct net_device *dev = arg;
1057 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
1059 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1060
1061 switch (fsm_getstate(fi)) {
1062 case DEV_STATE_STARTWAIT:
1063 fsm_newstate(fi, DEV_STATE_RUNNING);
1064 PRINT_INFO("%s: connected with remote side %s\n",
1065 dev->name, privptr->conn->userid);
1066 IUCV_DBF_TEXT(setup, 3,
1067 "connection is up and running\n");
1068 break;
1069 case DEV_STATE_STOPWAIT:
1070 PRINT_INFO(
1071 "%s: got connection UP event during shutdown!\n",
1072 dev->name);
1073 IUCV_DBF_TEXT(data, 2,
1074 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1075 break;
1076 }
1077}
1078
1079/**
1080 * Called from connection statemachine
1081 * when a connection has been shutdown.
1082 *
1083 * @param fi An instance of an interface statemachine.
1084 * @param event The event, just happened.
1085 * @param arg Generic pointer, casted from struct net_device * upon call.
1086 */
1087static void
1088dev_action_conndown(fsm_instance *fi, int event, void *arg)
1089{
1090 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1091
1092 switch (fsm_getstate(fi)) {
1093 case DEV_STATE_RUNNING:
1094 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1095 break;
1096 case DEV_STATE_STOPWAIT:
1097 fsm_newstate(fi, DEV_STATE_STOPPED);
1098 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1099 break;
1100 }
1101}
1102
1103static const fsm_node dev_fsm[] = {
1104 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1105
1106 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1107 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1108
1109 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1110 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1111
1112 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1113 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
1114 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, fsm_action_nop },
1115};
1116
1117static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1118
1119/**
1120 * Transmit a packet.
1121 * This is a helper function for netiucv_tx().
1122 *
1123 * @param conn Connection to be used for sending.
1124 * @param skb Pointer to struct sk_buff of packet to send.
1125 * The linklevel header has already been set up
1126 * by netiucv_tx().
1127 *
1128 * @return 0 on success, -ERRNO on failure. (Never fails.)
1129 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001130static int netiucv_transmit_skb(struct iucv_connection *conn,
1131 struct sk_buff *skb)
1132{
1133 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001135 struct ll_header header;
1136 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137
1138 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1139 int l = skb->len + NETIUCV_HDRLEN;
1140
1141 spin_lock_irqsave(&conn->collect_lock, saveflags);
1142 if (conn->collect_len + l >
1143 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1144 rc = -EBUSY;
1145 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001146 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 } else {
1148 atomic_inc(&skb->users);
1149 skb_queue_tail(&conn->collect_queue, skb);
1150 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001151 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 }
1153 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1154 } else {
1155 struct sk_buff *nskb = skb;
1156 /**
1157 * Copy the skb to a new allocated skb in lowmem only if the
1158 * data is located above 2G in memory or tailroom is < 2.
1159 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001160 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1161 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 int copied = 0;
1163 if (hi || (skb_tailroom(skb) < 2)) {
1164 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1165 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1166 if (!nskb) {
1167 PRINT_WARN("%s: Could not allocate tx_skb\n",
1168 conn->netdev->name);
1169 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1170 rc = -ENOMEM;
1171 return rc;
1172 } else {
1173 skb_reserve(nskb, NETIUCV_HDRLEN);
1174 memcpy(skb_put(nskb, skb->len),
1175 skb->data, skb->len);
1176 }
1177 copied = 1;
1178 }
1179 /**
1180 * skb now is below 2G and has enough room. Add headers.
1181 */
1182 header.next = nskb->len + NETIUCV_HDRLEN;
1183 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1184 header.next = 0;
1185 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1186
1187 fsm_newstate(conn->fsm, CONN_STATE_TX);
john stultz2c6b47d2007-07-24 17:47:43 -07001188 conn->prof.send_stamp = current_kernel_time();
Jeff Garzike82b0f22006-05-26 21:58:38 -04001189
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001190 msg.tag = 1;
1191 msg.class = 0;
1192 rc = iucv_message_send(conn->path, &msg, 0, 0,
1193 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 conn->prof.doios_single++;
1195 conn->prof.txlen += skb->len;
1196 conn->prof.tx_pending++;
1197 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1198 conn->prof.tx_max_pending = conn->prof.tx_pending;
1199 if (rc) {
1200 struct netiucv_priv *privptr;
1201 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1202 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001203 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 if (privptr)
1205 privptr->stats.tx_errors++;
1206 if (copied)
1207 dev_kfree_skb(nskb);
1208 else {
1209 /**
1210 * Remove our headers. They get added
1211 * again on retransmit.
1212 */
1213 skb_pull(skb, NETIUCV_HDRLEN);
1214 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1215 }
1216 PRINT_WARN("iucv_send returned %08x\n", rc);
1217 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1218 } else {
1219 if (copied)
1220 dev_kfree_skb(skb);
1221 atomic_inc(&nskb->users);
1222 skb_queue_tail(&conn->commit_queue, nskb);
1223 }
1224 }
1225
1226 return rc;
1227}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001228
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001229/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001231 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232
1233/**
1234 * Open an interface.
1235 * Called from generic network layer when ifconfig up is run.
1236 *
1237 * @param dev Pointer to interface struct.
1238 *
1239 * @return 0 on success, -ERRNO on failure. (Never fails.)
1240 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001241static int netiucv_open(struct net_device *dev)
1242{
1243 struct netiucv_priv *priv = netdev_priv(dev);
1244
1245 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 return 0;
1247}
1248
1249/**
1250 * Close an interface.
1251 * Called from generic network layer when ifconfig down is run.
1252 *
1253 * @param dev Pointer to interface struct.
1254 *
1255 * @return 0 on success, -ERRNO on failure. (Never fails.)
1256 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001257static int netiucv_close(struct net_device *dev)
1258{
1259 struct netiucv_priv *priv = netdev_priv(dev);
1260
1261 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 return 0;
1263}
1264
1265/**
1266 * Start transmission of a packet.
1267 * Called from generic network device layer.
1268 *
1269 * @param skb Pointer to buffer containing the packet.
1270 * @param dev Pointer to interface struct.
1271 *
1272 * @return 0 if packet consumed, !0 if packet rejected.
1273 * Note: If we return !0, then the packet is free'd by
1274 * the generic network layer.
1275 */
1276static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1277{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001278 struct netiucv_priv *privptr = netdev_priv(dev);
1279 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280
1281 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1282 /**
1283 * Some sanity checks ...
1284 */
1285 if (skb == NULL) {
1286 PRINT_WARN("%s: NULL sk_buff passed\n", dev->name);
1287 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1288 privptr->stats.tx_dropped++;
1289 return 0;
1290 }
1291 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
1292 PRINT_WARN("%s: Got sk_buff with head room < %ld bytes\n",
1293 dev->name, NETIUCV_HDRLEN);
1294 IUCV_DBF_TEXT(data, 2,
1295 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1296 dev_kfree_skb(skb);
1297 privptr->stats.tx_dropped++;
1298 return 0;
1299 }
1300
1301 /**
1302 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001303 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 */
1305 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Ursula Braun651bbc62007-06-20 13:01:30 +02001306 if (!in_atomic())
1307 fsm_event(privptr->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 dev_kfree_skb(skb);
1309 privptr->stats.tx_dropped++;
1310 privptr->stats.tx_errors++;
1311 privptr->stats.tx_carrier_errors++;
1312 return 0;
1313 }
1314
1315 if (netiucv_test_and_set_busy(dev)) {
1316 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
1317 return -EBUSY;
1318 }
1319 dev->trans_start = jiffies;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001320 rc = netiucv_transmit_skb(privptr->conn, skb) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 netiucv_clear_busy(dev);
1322 return rc;
1323}
1324
1325/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001326 * netiucv_stats
1327 * @dev: Pointer to interface struct.
1328 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 * Returns interface statistics of a device.
1330 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001331 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001333static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001335 struct netiucv_priv *priv = netdev_priv(dev);
1336
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001338 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339}
1340
1341/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001342 * netiucv_change_mtu
1343 * @dev: Pointer to interface struct.
1344 * @new_mtu: The new MTU to use for this interface.
1345 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 * Sets MTU of an interface.
1347 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001348 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 * (valid range is 576 .. NETIUCV_MTU_MAX).
1350 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001351static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352{
1353 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001354 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1356 return -EINVAL;
1357 }
1358 dev->mtu = new_mtu;
1359 return 0;
1360}
1361
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001362/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001364 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001366static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1367 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368{
1369 struct netiucv_priv *priv = dev->driver_data;
1370
1371 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1372 return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));
1373}
1374
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001375static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1376 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377{
1378 struct netiucv_priv *priv = dev->driver_data;
1379 struct net_device *ndev = priv->conn->netdev;
1380 char *p;
1381 char *tmp;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001382 char username[9];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 int i;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001384 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385
1386 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001387 if (count > 9) {
1388 PRINT_WARN("netiucv: username too long (%d)!\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001390 "%d is length of username\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 return -EINVAL;
1392 }
1393
1394 tmp = strsep((char **) &buf, "\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001395 for (i = 0, p = tmp; i < 8 && *p; i++, p++) {
1396 if (isalnum(*p) || (*p == '$')) {
Frank Pavlic16a83b32006-09-15 16:25:19 +02001397 username[i]= toupper(*p);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001398 continue;
1399 }
1400 if (*p == '\n') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 /* trailing lf, grr */
1402 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001404 PRINT_WARN("netiucv: Invalid char %c in username!\n", *p);
1405 IUCV_DBF_TEXT_(setup, 2,
1406 "username: invalid character %c\n", *p);
1407 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001409 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001411 username[8] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001413 if (memcmp(username, priv->conn->userid, 9) &&
1414 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1415 /* username changed while the interface is active. */
1416 PRINT_WARN("netiucv: device %s active, connected to %s\n",
1417 dev->bus_id, priv->conn->userid);
1418 PRINT_WARN("netiucv: user cannot be updated\n");
1419 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
1420 return -EBUSY;
1421 }
1422 read_lock_bh(&iucv_connection_rwlock);
1423 list_for_each_entry(cp, &iucv_connection_list, list) {
1424 if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
1425 read_unlock_bh(&iucv_connection_rwlock);
1426 PRINT_WARN("netiucv: Connection to %s already "
1427 "exists\n", username);
1428 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 }
1430 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001431 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 memcpy(priv->conn->userid, username, 9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434}
1435
1436static DEVICE_ATTR(user, 0644, user_show, user_write);
1437
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001438static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1439 char *buf)
1440{ struct netiucv_priv *priv = dev->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441
1442 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1443 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1444}
1445
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001446static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1447 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448{
1449 struct netiucv_priv *priv = dev->driver_data;
1450 struct net_device *ndev = priv->conn->netdev;
1451 char *e;
1452 int bs1;
1453
1454 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1455 if (count >= 39)
1456 return -EINVAL;
1457
1458 bs1 = simple_strtoul(buf, &e, 0);
1459
1460 if (e && (!isspace(*e))) {
1461 PRINT_WARN("netiucv: Invalid character in buffer!\n");
1462 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
1463 return -EINVAL;
1464 }
1465 if (bs1 > NETIUCV_BUFSIZE_MAX) {
1466 PRINT_WARN("netiucv: Given buffer size %d too large.\n",
1467 bs1);
1468 IUCV_DBF_TEXT_(setup, 2,
1469 "buffer_write: buffer size %d too large\n",
1470 bs1);
1471 return -EINVAL;
1472 }
1473 if ((ndev->flags & IFF_RUNNING) &&
1474 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
1475 PRINT_WARN("netiucv: Given buffer size %d too small.\n",
1476 bs1);
1477 IUCV_DBF_TEXT_(setup, 2,
1478 "buffer_write: buffer size %d too small\n",
1479 bs1);
1480 return -EINVAL;
1481 }
1482 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
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
1491 priv->conn->max_buffsize = bs1;
1492 if (!(ndev->flags & IFF_RUNNING))
1493 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1494
1495 return count;
1496
1497}
1498
1499static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1500
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001501static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1502 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503{
1504 struct netiucv_priv *priv = dev->driver_data;
1505
1506 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1507 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1508}
1509
1510static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1511
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001512static ssize_t conn_fsm_show (struct device *dev,
1513 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514{
1515 struct netiucv_priv *priv = dev->driver_data;
1516
1517 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1518 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1519}
1520
1521static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1522
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001523static ssize_t maxmulti_show (struct device *dev,
1524 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525{
1526 struct netiucv_priv *priv = dev->driver_data;
1527
1528 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1529 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1530}
1531
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001532static ssize_t maxmulti_write (struct device *dev,
1533 struct device_attribute *attr,
1534 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535{
1536 struct netiucv_priv *priv = dev->driver_data;
1537
1538 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1539 priv->conn->prof.maxmulti = 0;
1540 return count;
1541}
1542
1543static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1544
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001545static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1546 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547{
1548 struct netiucv_priv *priv = dev->driver_data;
1549
1550 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1551 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1552}
1553
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001554static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1555 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556{
1557 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001558
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1560 priv->conn->prof.maxcqueue = 0;
1561 return count;
1562}
1563
1564static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1565
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001566static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1567 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568{
1569 struct netiucv_priv *priv = dev->driver_data;
1570
1571 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1572 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1573}
1574
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001575static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1576 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577{
1578 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001579
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1581 priv->conn->prof.doios_single = 0;
1582 return count;
1583}
1584
1585static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1586
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001587static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1588 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589{
1590 struct netiucv_priv *priv = dev->driver_data;
1591
1592 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1593 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1594}
1595
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001596static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1597 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598{
1599 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001600
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1602 priv->conn->prof.doios_multi = 0;
1603 return count;
1604}
1605
1606static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1607
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001608static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1609 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610{
1611 struct netiucv_priv *priv = dev->driver_data;
1612
1613 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1614 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1615}
1616
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001617static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1618 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619{
1620 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001621
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1623 priv->conn->prof.txlen = 0;
1624 return count;
1625}
1626
1627static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1628
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001629static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1630 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631{
1632 struct netiucv_priv *priv = dev->driver_data;
1633
1634 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1635 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1636}
1637
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001638static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1639 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640{
1641 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001642
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1644 priv->conn->prof.tx_time = 0;
1645 return count;
1646}
1647
1648static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1649
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001650static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1651 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652{
1653 struct netiucv_priv *priv = dev->driver_data;
1654
1655 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1656 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1657}
1658
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001659static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1660 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661{
1662 struct netiucv_priv *priv = dev->driver_data;
1663
1664 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1665 priv->conn->prof.tx_pending = 0;
1666 return count;
1667}
1668
1669static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1670
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001671static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1672 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673{
1674 struct netiucv_priv *priv = dev->driver_data;
1675
1676 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1677 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1678}
1679
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001680static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1681 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682{
1683 struct netiucv_priv *priv = dev->driver_data;
1684
1685 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1686 priv->conn->prof.tx_max_pending = 0;
1687 return count;
1688}
1689
1690static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1691
1692static struct attribute *netiucv_attrs[] = {
1693 &dev_attr_buffer.attr,
1694 &dev_attr_user.attr,
1695 NULL,
1696};
1697
1698static struct attribute_group netiucv_attr_group = {
1699 .attrs = netiucv_attrs,
1700};
1701
1702static struct attribute *netiucv_stat_attrs[] = {
1703 &dev_attr_device_fsm_state.attr,
1704 &dev_attr_connection_fsm_state.attr,
1705 &dev_attr_max_tx_buffer_used.attr,
1706 &dev_attr_max_chained_skbs.attr,
1707 &dev_attr_tx_single_write_ops.attr,
1708 &dev_attr_tx_multi_write_ops.attr,
1709 &dev_attr_netto_bytes.attr,
1710 &dev_attr_max_tx_io_time.attr,
1711 &dev_attr_tx_pending.attr,
1712 &dev_attr_tx_max_pending.attr,
1713 NULL,
1714};
1715
1716static struct attribute_group netiucv_stat_attr_group = {
1717 .name = "stats",
1718 .attrs = netiucv_stat_attrs,
1719};
1720
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001721static int netiucv_add_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722{
1723 int ret;
1724
1725 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1726 ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group);
1727 if (ret)
1728 return ret;
1729 ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group);
1730 if (ret)
1731 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1732 return ret;
1733}
1734
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001735static void netiucv_remove_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736{
1737 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1738 sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group);
1739 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1740}
1741
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001742static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001744 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001745 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 int ret;
1747
1748
1749 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1750
1751 if (dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 snprintf(dev->bus_id, BUS_ID_SIZE, "net%s", ndev->name);
1753 dev->bus = &iucv_bus;
1754 dev->parent = iucv_root;
1755 /*
1756 * The release function could be called after the
1757 * module has been unloaded. It's _only_ task is to
1758 * free the struct. Therefore, we specify kfree()
1759 * directly here. (Probably a little bit obfuscating
1760 * but legitime ...).
1761 */
1762 dev->release = (void (*)(struct device *))kfree;
1763 dev->driver = &netiucv_driver;
1764 } else
1765 return -ENOMEM;
1766
1767 ret = device_register(dev);
1768
1769 if (ret)
1770 return ret;
1771 ret = netiucv_add_files(dev);
1772 if (ret)
1773 goto out_unreg;
1774 priv->dev = dev;
1775 dev->driver_data = priv;
1776 return 0;
1777
1778out_unreg:
1779 device_unregister(dev);
1780 return ret;
1781}
1782
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001783static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784{
1785 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1786 netiucv_remove_files(dev);
1787 device_unregister(dev);
1788}
1789
1790/**
1791 * Allocate and initialize a new connection structure.
1792 * Add it to the list of netiucv connections;
1793 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001794static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
1795 char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001797 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001799 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1800 if (!conn)
1801 goto out;
1802 skb_queue_head_init(&conn->collect_queue);
1803 skb_queue_head_init(&conn->commit_queue);
1804 spin_lock_init(&conn->collect_lock);
1805 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1806 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001808 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1809 if (!conn->rx_buff)
1810 goto out_conn;
1811 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1812 if (!conn->tx_buff)
1813 goto out_rx;
1814 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1815 conn_event_names, NR_CONN_STATES,
1816 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1817 GFP_KERNEL);
1818 if (!conn->fsm)
1819 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001821 fsm_settimer(conn->fsm, &conn->timer);
1822 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001824 if (username) {
1825 memcpy(conn->userid, username, 9);
1826 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001828
1829 write_lock_bh(&iucv_connection_rwlock);
1830 list_add_tail(&conn->list, &iucv_connection_list);
1831 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001833
1834out_tx:
1835 kfree_skb(conn->tx_buff);
1836out_rx:
1837 kfree_skb(conn->rx_buff);
1838out_conn:
1839 kfree(conn);
1840out:
1841 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842}
1843
1844/**
1845 * Release a connection structure and remove it from the
1846 * list of netiucv connections.
1847 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001848static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001851 write_lock_bh(&iucv_connection_rwlock);
1852 list_del_init(&conn->list);
1853 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001854 fsm_deltimer(&conn->timer);
1855 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001856 if (conn->path) {
1857 iucv_path_sever(conn->path, iucvMagic);
1858 kfree(conn->path);
1859 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001861 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001862 kfree_fsm(conn->fsm);
1863 kfree_skb(conn->rx_buff);
1864 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865}
1866
1867/**
1868 * Release everything of a net device.
1869 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001870static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001872 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873
1874 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1875
1876 if (!dev)
1877 return;
1878
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 if (privptr) {
1880 if (privptr->conn)
1881 netiucv_remove_connection(privptr->conn);
1882 if (privptr->fsm)
1883 kfree_fsm(privptr->fsm);
1884 privptr->conn = NULL; privptr->fsm = NULL;
1885 /* privptr gets freed by free_netdev() */
1886 }
1887 free_netdev(dev);
1888}
1889
1890/**
1891 * Initialize a net device. (Called from kernel in alloc_netdev())
1892 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001893static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 dev->mtu = NETIUCV_MTU_DEFAULT;
1896 dev->hard_start_xmit = netiucv_tx;
1897 dev->open = netiucv_open;
1898 dev->stop = netiucv_close;
1899 dev->get_stats = netiucv_stats;
1900 dev->change_mtu = netiucv_change_mtu;
1901 dev->destructor = netiucv_free_netdevice;
1902 dev->hard_header_len = NETIUCV_HDRLEN;
1903 dev->addr_len = 0;
1904 dev->type = ARPHRD_SLIP;
1905 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1906 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907}
1908
1909/**
1910 * Allocate and initialize everything of a net device.
1911 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001912static struct net_device *netiucv_init_netdevice(char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913{
1914 struct netiucv_priv *privptr;
1915 struct net_device *dev;
1916
1917 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
1918 netiucv_setup_netdevice);
1919 if (!dev)
1920 return NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001921 if (dev_alloc_name(dev, dev->name) < 0)
1922 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001924 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
1926 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
1927 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001928 if (!privptr->fsm)
1929 goto out_netdev;
1930
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 privptr->conn = netiucv_new_connection(dev, username);
1932 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001934 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 }
1936 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001938
1939out_fsm:
1940 kfree_fsm(privptr->fsm);
1941out_netdev:
1942 free_netdev(dev);
1943 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944}
1945
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001946static ssize_t conn_write(struct device_driver *drv,
1947 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001949 const char *p;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001950 char username[9];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001951 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001953 struct netiucv_priv *priv;
1954 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955
1956 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1957 if (count>9) {
1958 PRINT_WARN("netiucv: username too long (%d)!\n", (int)count);
1959 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1960 return -EINVAL;
1961 }
1962
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001963 for (i = 0, p = buf; i < 8 && *p; i++, p++) {
1964 if (isalnum(*p) || *p == '$') {
1965 username[i] = toupper(*p);
1966 continue;
1967 }
1968 if (*p == '\n')
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969 /* trailing lf, grr */
1970 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001971 PRINT_WARN("netiucv: Invalid character in username!\n");
1972 IUCV_DBF_TEXT_(setup, 2,
1973 "conn_write: invalid character %c\n", *p);
1974 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001976 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001978 username[8] = '\0';
1979
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001980 read_lock_bh(&iucv_connection_rwlock);
1981 list_for_each_entry(cp, &iucv_connection_list, list) {
1982 if (!strncmp(username, cp->userid, 9)) {
1983 read_unlock_bh(&iucv_connection_rwlock);
1984 PRINT_WARN("netiucv: Connection to %s already "
1985 "exists\n", username);
1986 return -EEXIST;
1987 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02001988 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001989 read_unlock_bh(&iucv_connection_rwlock);
1990
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 dev = netiucv_init_netdevice(username);
1992 if (!dev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001993 PRINT_WARN("netiucv: Could not allocate network device "
1994 "structure for user '%s'\n",
1995 netiucv_printname(username));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
1997 return -ENODEV;
1998 }
1999
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002000 rc = netiucv_register_device(dev);
2001 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002003 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 goto out_free_ndev;
2005 }
2006
2007 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002008 priv = netdev_priv(dev);
2009 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002011 rc = register_netdev(dev);
2012 if (rc)
2013 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014
2015 PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002016
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 return count;
2018
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002019out_unreg:
2020 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021out_free_ndev:
2022 PRINT_WARN("netiucv: Could not register '%s'\n", dev->name);
2023 IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n");
2024 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002025 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026}
2027
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002028static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002030static ssize_t remove_write (struct device_driver *drv,
2031 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002033 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 struct net_device *ndev;
2035 struct netiucv_priv *priv;
2036 struct device *dev;
2037 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002038 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 int i;
2040
2041 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
2042
2043 if (count >= IFNAMSIZ)
Frank Pavlic16a83b32006-09-15 16:25:19 +02002044 count = IFNAMSIZ - 1;;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002046 for (i = 0, p = buf; i < count && *p; i++, p++) {
2047 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 /* trailing lf, grr */
2049 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002050 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 }
2052 name[i] = '\0';
2053
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002054 read_lock_bh(&iucv_connection_rwlock);
2055 list_for_each_entry(cp, &iucv_connection_list, list) {
2056 ndev = cp->netdev;
2057 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002059 if (strncmp(name, ndev->name, count))
2060 continue;
2061 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002063 PRINT_WARN("netiucv: net device %s active with peer "
2064 "%s\n", ndev->name, priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 PRINT_WARN("netiucv: %s cannot be removed\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002066 ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
2068 return -EBUSY;
2069 }
2070 unregister_netdev(ndev);
2071 netiucv_unregister_device(dev);
2072 return count;
2073 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002074 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 PRINT_WARN("netiucv: net device %s unknown\n", name);
2076 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2077 return -EINVAL;
2078}
2079
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002080static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002082static struct attribute * netiucv_drv_attrs[] = {
2083 &driver_attr_connection.attr,
2084 &driver_attr_remove.attr,
2085 NULL,
2086};
2087
2088static struct attribute_group netiucv_drv_attr_group = {
2089 .attrs = netiucv_drv_attrs,
2090};
2091
2092static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093{
Heiko Carstense018ba12006-02-01 03:06:31 -08002094 PRINT_INFO("NETIUCV driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095}
2096
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002097static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002099 struct iucv_connection *cp;
2100 struct net_device *ndev;
2101 struct netiucv_priv *priv;
2102 struct device *dev;
2103
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002105 while (!list_empty(&iucv_connection_list)) {
2106 cp = list_entry(iucv_connection_list.next,
2107 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002108 ndev = cp->netdev;
2109 priv = netdev_priv(ndev);
2110 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111
2112 unregister_netdev(ndev);
2113 netiucv_unregister_device(dev);
2114 }
2115
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002116 sysfs_remove_group(&netiucv_driver.kobj, &netiucv_drv_attr_group);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002118 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 iucv_unregister_dbf_views();
2120
2121 PRINT_INFO("NETIUCV driver unloaded\n");
2122 return;
2123}
2124
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002125static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002127 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002128
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002129 rc = iucv_register_dbf_views();
2130 if (rc)
2131 goto out;
2132 rc = iucv_register(&netiucv_handler, 1);
2133 if (rc)
2134 goto out_dbf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002136 rc = driver_register(&netiucv_driver);
2137 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138 PRINT_ERR("NETIUCV: failed to register driver.\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002139 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2140 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141 }
2142
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002143 rc = sysfs_create_group(&netiucv_driver.kobj, &netiucv_drv_attr_group);
2144 if (rc) {
2145 PRINT_ERR("NETIUCV: failed to add driver attributes.\n");
2146 IUCV_DBF_TEXT_(setup, 2,
2147 "ret %d - netiucv_drv_attr_group\n", rc);
2148 goto out_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002150 netiucv_banner();
2151 return rc;
2152
2153out_driver:
2154 driver_unregister(&netiucv_driver);
2155out_iucv:
2156 iucv_unregister(&netiucv_handler, 1);
2157out_dbf:
2158 iucv_unregister_dbf_views();
2159out:
2160 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002162
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163module_init(netiucv_init);
2164module_exit(netiucv_exit);
2165MODULE_LICENSE("GPL");