blob: cd42bd54988cd9645de2fcc902e8ea5274b80e14 [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
137static inline void iucv_hex_dump(unsigned char *buf, size_t len)
138{
139 size_t i;
140
141 for (i = 0; i < len; i++) {
142 if (i && !(i % 16))
143 printk("\n");
144 printk("%02x ", *(buf + i));
145 }
146 printk("\n");
147}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400148
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149#define PRINTK_HEADER " iucv: " /* for debugging */
150
151static struct device_driver netiucv_driver = {
152 .name = "netiucv",
153 .bus = &iucv_bus,
154};
155
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800156static int netiucv_callback_connreq(struct iucv_path *,
157 u8 ipvmid[8], u8 ipuser[16]);
158static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
159static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
160static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
161static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
162static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
163static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
164
165static struct iucv_handler netiucv_handler = {
166 .path_pending = netiucv_callback_connreq,
167 .path_complete = netiucv_callback_connack,
168 .path_severed = netiucv_callback_connrej,
169 .path_quiesced = netiucv_callback_connsusp,
170 .path_resumed = netiucv_callback_connres,
171 .message_pending = netiucv_callback_rx,
172 .message_complete = netiucv_callback_txdone
173};
174
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175/**
176 * Per connection profiling data
177 */
178struct connection_profile {
179 unsigned long maxmulti;
180 unsigned long maxcqueue;
181 unsigned long doios_single;
182 unsigned long doios_multi;
183 unsigned long txlen;
184 unsigned long tx_time;
185 struct timespec send_stamp;
186 unsigned long tx_pending;
187 unsigned long tx_max_pending;
188};
189
190/**
191 * Representation of one iucv connection
192 */
193struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800194 struct list_head list;
195 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 struct sk_buff *rx_buff;
197 struct sk_buff *tx_buff;
198 struct sk_buff_head collect_queue;
199 struct sk_buff_head commit_queue;
200 spinlock_t collect_lock;
201 int collect_len;
202 int max_buffsize;
203 fsm_timer timer;
204 fsm_instance *fsm;
205 struct net_device *netdev;
206 struct connection_profile prof;
207 char userid[9];
208};
209
210/**
211 * Linked list of all connection structs.
212 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800213static struct list_head iucv_connection_list =
214 LIST_HEAD_INIT(iucv_connection_list);
215static rwlock_t iucv_connection_rwlock = RW_LOCK_UNLOCKED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
217/**
218 * Representation of event-data for the
219 * connection state machine.
220 */
221struct iucv_event {
222 struct iucv_connection *conn;
223 void *data;
224};
225
226/**
227 * Private part of the network device structure
228 */
229struct netiucv_priv {
230 struct net_device_stats stats;
231 unsigned long tbusy;
232 fsm_instance *fsm;
233 struct iucv_connection *conn;
234 struct device *dev;
235};
236
237/**
238 * Link level header for a packet.
239 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800240struct ll_header {
241 u16 next;
242};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800244#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245#define NETIUCV_BUFSIZE_MAX 32768
246#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
247#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
248#define NETIUCV_MTU_DEFAULT 9216
249#define NETIUCV_QUEUELEN_DEFAULT 50
250#define NETIUCV_TIMEOUT_5SEC 5000
251
252/**
253 * Compatibility macros for busy handling
254 * of network devices.
255 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800256static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800258 struct netiucv_priv *priv = netdev_priv(dev);
259 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 netif_wake_queue(dev);
261}
262
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800263static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800265 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800267 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268}
269
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800270static u8 iucvMagic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
272 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
273};
274
275/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 * Convert an iucv userId to its printable
277 * form (strip whitespace at end).
278 *
279 * @param An iucv userId
280 *
281 * @returns The printable string (static data!!)
282 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800283static inline char *netiucv_printname(char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284{
285 static char tmp[9];
286 char *p = tmp;
287 memcpy(tmp, name, 8);
288 tmp[8] = '\0';
289 while (*p && (!isspace(*p)))
290 p++;
291 *p = '\0';
292 return tmp;
293}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400294
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295/**
296 * States of the interface statemachine.
297 */
298enum dev_states {
299 DEV_STATE_STOPPED,
300 DEV_STATE_STARTWAIT,
301 DEV_STATE_STOPWAIT,
302 DEV_STATE_RUNNING,
303 /**
304 * MUST be always the last element!!
305 */
306 NR_DEV_STATES
307};
308
309static const char *dev_state_names[] = {
310 "Stopped",
311 "StartWait",
312 "StopWait",
313 "Running",
314};
315
316/**
317 * Events of the interface statemachine.
318 */
319enum dev_events {
320 DEV_EVENT_START,
321 DEV_EVENT_STOP,
322 DEV_EVENT_CONUP,
323 DEV_EVENT_CONDOWN,
324 /**
325 * MUST be always the last element!!
326 */
327 NR_DEV_EVENTS
328};
329
330static const char *dev_event_names[] = {
331 "Start",
332 "Stop",
333 "Connection up",
334 "Connection down",
335};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400336
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337/**
338 * Events of the connection statemachine
339 */
340enum conn_events {
341 /**
342 * Events, representing callbacks from
343 * lowlevel iucv layer)
344 */
345 CONN_EVENT_CONN_REQ,
346 CONN_EVENT_CONN_ACK,
347 CONN_EVENT_CONN_REJ,
348 CONN_EVENT_CONN_SUS,
349 CONN_EVENT_CONN_RES,
350 CONN_EVENT_RX,
351 CONN_EVENT_TXDONE,
352
353 /**
354 * Events, representing errors return codes from
355 * calls to lowlevel iucv layer
356 */
357
358 /**
359 * Event, representing timer expiry.
360 */
361 CONN_EVENT_TIMER,
362
363 /**
364 * Events, representing commands from upper levels.
365 */
366 CONN_EVENT_START,
367 CONN_EVENT_STOP,
368
369 /**
370 * MUST be always the last element!!
371 */
372 NR_CONN_EVENTS,
373};
374
375static const char *conn_event_names[] = {
376 "Remote connection request",
377 "Remote connection acknowledge",
378 "Remote connection reject",
379 "Connection suspended",
380 "Connection resumed",
381 "Data received",
382 "Data sent",
383
384 "Timer",
385
386 "Start",
387 "Stop",
388};
389
390/**
391 * States of the connection statemachine.
392 */
393enum conn_states {
394 /**
395 * Connection not assigned to any device,
396 * initial state, invalid
397 */
398 CONN_STATE_INVALID,
399
400 /**
401 * Userid assigned but not operating
402 */
403 CONN_STATE_STOPPED,
404
405 /**
406 * Connection registered,
407 * no connection request sent yet,
408 * no connection request received
409 */
410 CONN_STATE_STARTWAIT,
411
412 /**
413 * Connection registered and connection request sent,
414 * no acknowledge and no connection request received yet.
415 */
416 CONN_STATE_SETUPWAIT,
417
418 /**
419 * Connection up and running idle
420 */
421 CONN_STATE_IDLE,
422
423 /**
424 * Data sent, awaiting CONN_EVENT_TXDONE
425 */
426 CONN_STATE_TX,
427
428 /**
429 * Error during registration.
430 */
431 CONN_STATE_REGERR,
432
433 /**
434 * Error during registration.
435 */
436 CONN_STATE_CONNERR,
437
438 /**
439 * MUST be always the last element!!
440 */
441 NR_CONN_STATES,
442};
443
444static const char *conn_state_names[] = {
445 "Invalid",
446 "Stopped",
447 "StartWait",
448 "SetupWait",
449 "Idle",
450 "TX",
451 "Terminating",
452 "Registration error",
453 "Connect error",
454};
455
Jeff Garzike82b0f22006-05-26 21:58:38 -0400456
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457/**
458 * Debug Facility Stuff
459 */
460static debug_info_t *iucv_dbf_setup = NULL;
461static debug_info_t *iucv_dbf_data = NULL;
462static debug_info_t *iucv_dbf_trace = NULL;
463
464DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
465
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800466static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467{
468 if (iucv_dbf_setup)
469 debug_unregister(iucv_dbf_setup);
470 if (iucv_dbf_data)
471 debug_unregister(iucv_dbf_data);
472 if (iucv_dbf_trace)
473 debug_unregister(iucv_dbf_trace);
474}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800475static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
477 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700478 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 IUCV_DBF_SETUP_NR_AREAS,
480 IUCV_DBF_SETUP_LEN);
481 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700482 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 IUCV_DBF_DATA_NR_AREAS,
484 IUCV_DBF_DATA_LEN);
485 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700486 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 IUCV_DBF_TRACE_NR_AREAS,
488 IUCV_DBF_TRACE_LEN);
489
490 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
491 (iucv_dbf_trace == NULL)) {
492 iucv_unregister_dbf_views();
493 return -ENOMEM;
494 }
495 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
496 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
497
498 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
499 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
500
501 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
502 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
503
504 return 0;
505}
506
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800507/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800509 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800511static void netiucv_callback_rx(struct iucv_path *path,
512 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800514 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 struct iucv_event ev;
516
517 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800518 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
520}
521
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800522static void netiucv_callback_txdone(struct iucv_path *path,
523 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800525 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 struct iucv_event ev;
527
528 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800529 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
531}
532
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800533static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800535 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800537 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538}
539
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800540static int netiucv_callback_connreq(struct iucv_path *path,
541 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800543 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 struct iucv_event ev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800545 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800547 if (memcmp(iucvMagic, ipuser, sizeof(ipuser)))
548 /* ipuser must match iucvMagic. */
549 return -EINVAL;
550 rc = -EINVAL;
551 read_lock_bh(&iucv_connection_rwlock);
552 list_for_each_entry(conn, &iucv_connection_list, list) {
553 if (strncmp(ipvmid, conn->userid, 8))
554 continue;
555 /* Found a matching connection for this path. */
556 conn->path = path;
557 ev.conn = conn;
558 ev.data = path;
559 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
560 rc = 0;
561 }
562 read_unlock_bh(&iucv_connection_rwlock);
563 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564}
565
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800566static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800568 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800570 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571}
572
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800573static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800575 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800577 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578}
579
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800580static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800582 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800584 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585}
586
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587/**
588 * Dummy NOP action for all statemachines
589 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800590static void fsm_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591{
592}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400593
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800594/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800596 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597
598/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800599 * netiucv_unpack_skb
600 * @conn: The connection where this skb has been received.
601 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800603 * Unpack a just received skb and hand it over to upper layers.
604 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800606static void netiucv_unpack_skb(struct iucv_connection *conn,
607 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608{
609 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800610 struct netiucv_priv *privptr = netdev_priv(dev);
611 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612
613 skb_put(pskb, NETIUCV_HDRLEN);
614 pskb->dev = dev;
615 pskb->ip_summed = CHECKSUM_NONE;
616 pskb->protocol = ntohs(ETH_P_IP);
617
618 while (1) {
619 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800620 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
622 if (!header->next)
623 break;
624
625 skb_pull(pskb, NETIUCV_HDRLEN);
626 header->next -= offset;
627 offset += header->next;
628 header->next -= NETIUCV_HDRLEN;
629 if (skb_tailroom(pskb) < header->next) {
630 PRINT_WARN("%s: Illegal next field in iucv header: "
631 "%d > %d\n",
632 dev->name, header->next, skb_tailroom(pskb));
633 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
634 header->next, skb_tailroom(pskb));
635 return;
636 }
637 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700638 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 skb = dev_alloc_skb(pskb->len);
640 if (!skb) {
641 PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n",
642 dev->name);
643 IUCV_DBF_TEXT(data, 2,
644 "Out of memory in netiucv_unpack_skb\n");
645 privptr->stats.rx_dropped++;
646 return;
647 }
648 memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700649 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 skb->dev = pskb->dev;
651 skb->protocol = pskb->protocol;
652 pskb->ip_summed = CHECKSUM_UNNECESSARY;
653 /*
654 * Since receiving is always initiated from a tasklet (in iucv.c),
655 * we must use netif_rx_ni() instead of netif_rx()
656 */
657 netif_rx_ni(skb);
658 dev->last_rx = jiffies;
659 privptr->stats.rx_packets++;
660 privptr->stats.rx_bytes += skb->len;
661 skb_pull(pskb, header->next);
662 skb_put(pskb, NETIUCV_HDRLEN);
663 }
664}
665
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800666static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800668 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800670 struct iucv_message *msg = ev->data;
671 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 int rc;
673
674 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
675
676 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800677 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 PRINT_WARN("Received data for unlinked connection\n");
679 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800680 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 return;
682 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800683 if (msg->length > conn->max_buffsize) {
684 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 privptr->stats.rx_dropped++;
686 PRINT_WARN("msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800687 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800689 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 return;
691 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700692 conn->rx_buff->data = conn->rx_buff->head;
693 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800695 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
696 msg->length, NULL);
697 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 privptr->stats.rx_errors++;
699 PRINT_WARN("iucv_receive returned %08x\n", rc);
700 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
701 return;
702 }
703 netiucv_unpack_skb(conn, conn->rx_buff);
704}
705
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800706static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800708 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800710 struct iucv_message *msg = ev->data;
711 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800713 u32 single_flag = msg->tag;
714 u32 txbytes = 0;
715 u32 txpackets = 0;
716 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 struct sk_buff *skb;
718 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800719 struct ll_header header;
720 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
722 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
723
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800724 if (conn && conn->netdev)
725 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 conn->prof.tx_pending--;
727 if (single_flag) {
728 if ((skb = skb_dequeue(&conn->commit_queue))) {
729 atomic_dec(&skb->users);
730 dev_kfree_skb_any(skb);
731 if (privptr) {
732 privptr->stats.tx_packets++;
733 privptr->stats.tx_bytes +=
734 (skb->len - NETIUCV_HDRLEN
735 - NETIUCV_HDRLEN);
736 }
737 }
738 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700739 conn->tx_buff->data = conn->tx_buff->head;
740 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 conn->tx_buff->len = 0;
742 spin_lock_irqsave(&conn->collect_lock, saveflags);
743 while ((skb = skb_dequeue(&conn->collect_queue))) {
744 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
745 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
746 NETIUCV_HDRLEN);
747 memcpy(skb_put(conn->tx_buff, skb->len), skb->data, skb->len);
748 txbytes += skb->len;
749 txpackets++;
750 stat_maxcq++;
751 atomic_dec(&skb->users);
752 dev_kfree_skb_any(skb);
753 }
754 if (conn->collect_len > conn->prof.maxmulti)
755 conn->prof.maxmulti = conn->collect_len;
756 conn->collect_len = 0;
757 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800758 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800760 return;
761 }
762
763 header.next = 0;
764 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
765 conn->prof.send_stamp = xtime;
766 txmsg.class = 0;
767 txmsg.tag = 0;
768 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
769 conn->tx_buff->data, conn->tx_buff->len);
770 conn->prof.doios_multi++;
771 conn->prof.txlen += conn->tx_buff->len;
772 conn->prof.tx_pending++;
773 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
774 conn->prof.tx_max_pending = conn->prof.tx_pending;
775 if (rc) {
776 conn->prof.tx_pending--;
777 fsm_newstate(fi, CONN_STATE_IDLE);
778 if (privptr)
779 privptr->stats.tx_errors += txpackets;
780 PRINT_WARN("iucv_send returned %08x\n", rc);
781 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
782 } else {
783 if (privptr) {
784 privptr->stats.tx_packets += txpackets;
785 privptr->stats.tx_bytes += txbytes;
786 }
787 if (stat_maxcq > conn->prof.maxcqueue)
788 conn->prof.maxcqueue = stat_maxcq;
789 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790}
791
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800792static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800794 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800796 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800798 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
801 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
802
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800803 conn->path = path;
804 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
805 path->flags = 0;
806 rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 if (rc) {
808 PRINT_WARN("%s: IUCV accept failed with error %d\n",
809 netdev->name, rc);
810 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
811 return;
812 }
813 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800814 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
816}
817
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800818static void conn_action_connreject(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_event *ev = arg;
821 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
823 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800824 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825}
826
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800827static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800829 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800831 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
833 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 fsm_deltimer(&conn->timer);
835 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800836 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
838}
839
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800840static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800842 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
844 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800846 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 fsm_newstate(fi, CONN_STATE_STARTWAIT);
848}
849
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800850static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800852 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800854 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
856 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
857
858 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800859 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 PRINT_INFO("%s: Remote dropped connection\n", netdev->name);
861 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800862 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 fsm_newstate(fi, CONN_STATE_STARTWAIT);
864 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
865}
866
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800867static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800869 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 int rc;
871
872 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
873
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800874 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 PRINT_DEBUG("%s('%s'): connecting ...\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800876 conn->netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800878 /*
879 * We must set the state before calling iucv_connect because the
880 * callback handler could be called at any point after the connection
881 * request is sent
882 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
884 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800885 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
886 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
887 NULL, iucvMagic, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800889 case 0:
890 conn->netdev->tx_queue_len = conn->path->msglim;
891 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
892 CONN_EVENT_TIMER, conn);
893 return;
894 case 11:
895 PRINT_INFO("%s: User %s is currently not available.\n",
896 conn->netdev->name,
897 netiucv_printname(conn->userid));
898 fsm_newstate(fi, CONN_STATE_STARTWAIT);
899 break;
900 case 12:
901 PRINT_INFO("%s: User %s is currently not ready.\n",
902 conn->netdev->name,
903 netiucv_printname(conn->userid));
904 fsm_newstate(fi, CONN_STATE_STARTWAIT);
905 break;
906 case 13:
907 PRINT_WARN("%s: Too many IUCV connections.\n",
908 conn->netdev->name);
909 fsm_newstate(fi, CONN_STATE_CONNERR);
910 break;
911 case 14:
912 PRINT_WARN("%s: User %s has too many IUCV connections.\n",
913 conn->netdev->name,
914 netiucv_printname(conn->userid));
915 fsm_newstate(fi, CONN_STATE_CONNERR);
916 break;
917 case 15:
918 PRINT_WARN("%s: No IUCV authorization in CP directory.\n",
919 conn->netdev->name);
920 fsm_newstate(fi, CONN_STATE_CONNERR);
921 break;
922 default:
923 PRINT_WARN("%s: iucv_connect returned error %d\n",
924 conn->netdev->name, rc);
925 fsm_newstate(fi, CONN_STATE_CONNERR);
926 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 }
928 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800929 kfree(conn->path);
930 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931}
932
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800933static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934{
935 struct sk_buff *skb;
936
937 while ((skb = skb_dequeue(q))) {
938 atomic_dec(&skb->users);
939 dev_kfree_skb_any(skb);
940 }
941}
942
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800943static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800945 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 struct iucv_connection *conn = ev->conn;
947 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800948 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949
950 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
951
952 fsm_deltimer(&conn->timer);
953 fsm_newstate(fi, CONN_STATE_STOPPED);
954 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800955 if (conn->path) {
956 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
957 iucv_path_sever(conn->path, iucvMagic);
958 kfree(conn->path);
959 conn->path = NULL;
960 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 netiucv_purge_skb_queue(&conn->commit_queue);
962 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
963}
964
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800965static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800967 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 struct net_device *netdev = conn->netdev;
969
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800970 PRINT_WARN("%s: Cannot connect without username\n", netdev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 IUCV_DBF_TEXT(data, 2, "conn_action_inval called\n");
972}
973
974static const fsm_node conn_fsm[] = {
975 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
976 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
977
978 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
979 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
980 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
981 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
982 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
983 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
984 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
985
986 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
987 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
988 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
989 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
990 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
991
992 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
993 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
994
995 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
996 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
997 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
998
999 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
1000 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1001
1002 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1003 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1004};
1005
1006static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1007
Jeff Garzike82b0f22006-05-26 21:58:38 -04001008
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001009/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001011 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012
1013/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001014 * dev_action_start
1015 * @fi: An instance of an interface statemachine.
1016 * @event: The event, just happened.
1017 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001019 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001021static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001023 struct net_device *dev = arg;
1024 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025
1026 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1027
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001029 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030}
1031
1032/**
1033 * Shutdown connection by sending CONN_EVENT_STOP to it.
1034 *
1035 * @param fi An instance of an interface statemachine.
1036 * @param event The event, just happened.
1037 * @param arg Generic pointer, casted from struct net_device * upon call.
1038 */
1039static void
1040dev_action_stop(fsm_instance *fi, int event, void *arg)
1041{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001042 struct net_device *dev = arg;
1043 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 struct iucv_event ev;
1045
1046 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1047
1048 ev.conn = privptr->conn;
1049
1050 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1051 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1052}
1053
1054/**
1055 * Called from connection statemachine
1056 * when a connection is up and running.
1057 *
1058 * @param fi An instance of an interface statemachine.
1059 * @param event The event, just happened.
1060 * @param arg Generic pointer, casted from struct net_device * upon call.
1061 */
1062static void
1063dev_action_connup(fsm_instance *fi, int event, void *arg)
1064{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001065 struct net_device *dev = arg;
1066 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
1068 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1069
1070 switch (fsm_getstate(fi)) {
1071 case DEV_STATE_STARTWAIT:
1072 fsm_newstate(fi, DEV_STATE_RUNNING);
1073 PRINT_INFO("%s: connected with remote side %s\n",
1074 dev->name, privptr->conn->userid);
1075 IUCV_DBF_TEXT(setup, 3,
1076 "connection is up and running\n");
1077 break;
1078 case DEV_STATE_STOPWAIT:
1079 PRINT_INFO(
1080 "%s: got connection UP event during shutdown!\n",
1081 dev->name);
1082 IUCV_DBF_TEXT(data, 2,
1083 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1084 break;
1085 }
1086}
1087
1088/**
1089 * Called from connection statemachine
1090 * when a connection has been shutdown.
1091 *
1092 * @param fi An instance of an interface statemachine.
1093 * @param event The event, just happened.
1094 * @param arg Generic pointer, casted from struct net_device * upon call.
1095 */
1096static void
1097dev_action_conndown(fsm_instance *fi, int event, void *arg)
1098{
1099 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1100
1101 switch (fsm_getstate(fi)) {
1102 case DEV_STATE_RUNNING:
1103 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1104 break;
1105 case DEV_STATE_STOPWAIT:
1106 fsm_newstate(fi, DEV_STATE_STOPPED);
1107 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1108 break;
1109 }
1110}
1111
1112static const fsm_node dev_fsm[] = {
1113 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1114
1115 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1116 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1117
1118 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1119 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1120
1121 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1122 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
1123 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, fsm_action_nop },
1124};
1125
1126static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1127
1128/**
1129 * Transmit a packet.
1130 * This is a helper function for netiucv_tx().
1131 *
1132 * @param conn Connection to be used for sending.
1133 * @param skb Pointer to struct sk_buff of packet to send.
1134 * The linklevel header has already been set up
1135 * by netiucv_tx().
1136 *
1137 * @return 0 on success, -ERRNO on failure. (Never fails.)
1138 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001139static int netiucv_transmit_skb(struct iucv_connection *conn,
1140 struct sk_buff *skb)
1141{
1142 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001144 struct ll_header header;
1145 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146
1147 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1148 int l = skb->len + NETIUCV_HDRLEN;
1149
1150 spin_lock_irqsave(&conn->collect_lock, saveflags);
1151 if (conn->collect_len + l >
1152 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1153 rc = -EBUSY;
1154 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001155 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 } else {
1157 atomic_inc(&skb->users);
1158 skb_queue_tail(&conn->collect_queue, skb);
1159 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001160 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 }
1162 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1163 } else {
1164 struct sk_buff *nskb = skb;
1165 /**
1166 * Copy the skb to a new allocated skb in lowmem only if the
1167 * data is located above 2G in memory or tailroom is < 2.
1168 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001169 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1170 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 int copied = 0;
1172 if (hi || (skb_tailroom(skb) < 2)) {
1173 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1174 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1175 if (!nskb) {
1176 PRINT_WARN("%s: Could not allocate tx_skb\n",
1177 conn->netdev->name);
1178 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1179 rc = -ENOMEM;
1180 return rc;
1181 } else {
1182 skb_reserve(nskb, NETIUCV_HDRLEN);
1183 memcpy(skb_put(nskb, skb->len),
1184 skb->data, skb->len);
1185 }
1186 copied = 1;
1187 }
1188 /**
1189 * skb now is below 2G and has enough room. Add headers.
1190 */
1191 header.next = nskb->len + NETIUCV_HDRLEN;
1192 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1193 header.next = 0;
1194 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1195
1196 fsm_newstate(conn->fsm, CONN_STATE_TX);
1197 conn->prof.send_stamp = xtime;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001198
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001199 msg.tag = 1;
1200 msg.class = 0;
1201 rc = iucv_message_send(conn->path, &msg, 0, 0,
1202 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 conn->prof.doios_single++;
1204 conn->prof.txlen += skb->len;
1205 conn->prof.tx_pending++;
1206 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1207 conn->prof.tx_max_pending = conn->prof.tx_pending;
1208 if (rc) {
1209 struct netiucv_priv *privptr;
1210 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1211 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001212 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 if (privptr)
1214 privptr->stats.tx_errors++;
1215 if (copied)
1216 dev_kfree_skb(nskb);
1217 else {
1218 /**
1219 * Remove our headers. They get added
1220 * again on retransmit.
1221 */
1222 skb_pull(skb, NETIUCV_HDRLEN);
1223 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1224 }
1225 PRINT_WARN("iucv_send returned %08x\n", rc);
1226 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1227 } else {
1228 if (copied)
1229 dev_kfree_skb(skb);
1230 atomic_inc(&nskb->users);
1231 skb_queue_tail(&conn->commit_queue, nskb);
1232 }
1233 }
1234
1235 return rc;
1236}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001237
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001238/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001240 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241
1242/**
1243 * Open an interface.
1244 * Called from generic network layer when ifconfig up is run.
1245 *
1246 * @param dev Pointer to interface struct.
1247 *
1248 * @return 0 on success, -ERRNO on failure. (Never fails.)
1249 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001250static int netiucv_open(struct net_device *dev)
1251{
1252 struct netiucv_priv *priv = netdev_priv(dev);
1253
1254 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 return 0;
1256}
1257
1258/**
1259 * Close an interface.
1260 * Called from generic network layer when ifconfig down is run.
1261 *
1262 * @param dev Pointer to interface struct.
1263 *
1264 * @return 0 on success, -ERRNO on failure. (Never fails.)
1265 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001266static int netiucv_close(struct net_device *dev)
1267{
1268 struct netiucv_priv *priv = netdev_priv(dev);
1269
1270 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 return 0;
1272}
1273
1274/**
1275 * Start transmission of a packet.
1276 * Called from generic network device layer.
1277 *
1278 * @param skb Pointer to buffer containing the packet.
1279 * @param dev Pointer to interface struct.
1280 *
1281 * @return 0 if packet consumed, !0 if packet rejected.
1282 * Note: If we return !0, then the packet is free'd by
1283 * the generic network layer.
1284 */
1285static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1286{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001287 struct netiucv_priv *privptr = netdev_priv(dev);
1288 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289
1290 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1291 /**
1292 * Some sanity checks ...
1293 */
1294 if (skb == NULL) {
1295 PRINT_WARN("%s: NULL sk_buff passed\n", dev->name);
1296 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1297 privptr->stats.tx_dropped++;
1298 return 0;
1299 }
1300 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
1301 PRINT_WARN("%s: Got sk_buff with head room < %ld bytes\n",
1302 dev->name, NETIUCV_HDRLEN);
1303 IUCV_DBF_TEXT(data, 2,
1304 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1305 dev_kfree_skb(skb);
1306 privptr->stats.tx_dropped++;
1307 return 0;
1308 }
1309
1310 /**
1311 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001312 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 */
1314 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
1315 fsm_event(privptr->fsm, DEV_EVENT_START, dev);
1316 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
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{
1361 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
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
1379 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1380 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
1394 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
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
1450 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1451 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
1462 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1463 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
1514 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1515 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
1525 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1526 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
1536 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1537 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
1546 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1547 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
1558 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1559 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1568 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
1579 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1580 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1589 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
1600 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1601 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1610 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
1621 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1622 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1631 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
1642 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1643 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1652 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
1663 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1664 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
1672 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1673 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
1684 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1685 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
1693 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1694 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 Schwidefskyeebce382007-02-08 13:50:33 -08001729static inline int netiucv_add_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730{
1731 int ret;
1732
1733 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1734 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 Schwidefskyeebce382007-02-08 13:50:33 -08001743static inline void netiucv_remove_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744{
1745 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1746 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
1757 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1758
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{
1793 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1794 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{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
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);
1862 if (conn->path) {
1863 iucv_path_sever(conn->path, iucvMagic);
1864 kfree(conn->path);
1865 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001867 fsm_deltimer(&conn->timer);
1868 kfree_fsm(conn->fsm);
1869 kfree_skb(conn->rx_buff);
1870 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871}
1872
1873/**
1874 * Release everything of a net device.
1875 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001876static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001878 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879
1880 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1881
1882 if (!dev)
1883 return;
1884
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 if (privptr) {
1886 if (privptr->conn)
1887 netiucv_remove_connection(privptr->conn);
1888 if (privptr->fsm)
1889 kfree_fsm(privptr->fsm);
1890 privptr->conn = NULL; privptr->fsm = NULL;
1891 /* privptr gets freed by free_netdev() */
1892 }
1893 free_netdev(dev);
1894}
1895
1896/**
1897 * Initialize a net device. (Called from kernel in alloc_netdev())
1898 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001899static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 dev->mtu = NETIUCV_MTU_DEFAULT;
1902 dev->hard_start_xmit = netiucv_tx;
1903 dev->open = netiucv_open;
1904 dev->stop = netiucv_close;
1905 dev->get_stats = netiucv_stats;
1906 dev->change_mtu = netiucv_change_mtu;
1907 dev->destructor = netiucv_free_netdevice;
1908 dev->hard_header_len = NETIUCV_HDRLEN;
1909 dev->addr_len = 0;
1910 dev->type = ARPHRD_SLIP;
1911 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1912 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
1913 SET_MODULE_OWNER(dev);
1914}
1915
1916/**
1917 * Allocate and initialize everything of a net device.
1918 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001919static struct net_device *netiucv_init_netdevice(char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920{
1921 struct netiucv_priv *privptr;
1922 struct net_device *dev;
1923
1924 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
1925 netiucv_setup_netdevice);
1926 if (!dev)
1927 return NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001928 if (dev_alloc_name(dev, dev->name) < 0)
1929 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001931 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
1933 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
1934 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001935 if (!privptr->fsm)
1936 goto out_netdev;
1937
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938 privptr->conn = netiucv_new_connection(dev, username);
1939 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001941 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 }
1943 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001945
1946out_fsm:
1947 kfree_fsm(privptr->fsm);
1948out_netdev:
1949 free_netdev(dev);
1950 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951}
1952
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001953static ssize_t conn_write(struct device_driver *drv,
1954 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001956 const char *p;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001957 char username[9];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001958 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001960 struct netiucv_priv *priv;
1961 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962
1963 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1964 if (count>9) {
1965 PRINT_WARN("netiucv: username too long (%d)!\n", (int)count);
1966 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1967 return -EINVAL;
1968 }
1969
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001970 for (i = 0, p = buf; i < 8 && *p; i++, p++) {
1971 if (isalnum(*p) || *p == '$') {
1972 username[i] = toupper(*p);
1973 continue;
1974 }
1975 if (*p == '\n')
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976 /* trailing lf, grr */
1977 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001978 PRINT_WARN("netiucv: Invalid character in username!\n");
1979 IUCV_DBF_TEXT_(setup, 2,
1980 "conn_write: invalid character %c\n", *p);
1981 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001983 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001985 username[8] = '\0';
1986
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001987 read_lock_bh(&iucv_connection_rwlock);
1988 list_for_each_entry(cp, &iucv_connection_list, list) {
1989 if (!strncmp(username, cp->userid, 9)) {
1990 read_unlock_bh(&iucv_connection_rwlock);
1991 PRINT_WARN("netiucv: Connection to %s already "
1992 "exists\n", username);
1993 return -EEXIST;
1994 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02001995 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001996 read_unlock_bh(&iucv_connection_rwlock);
1997
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 dev = netiucv_init_netdevice(username);
1999 if (!dev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002000 PRINT_WARN("netiucv: Could not allocate network device "
2001 "structure for user '%s'\n",
2002 netiucv_printname(username));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2004 return -ENODEV;
2005 }
2006
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002007 rc = netiucv_register_device(dev);
2008 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002010 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 goto out_free_ndev;
2012 }
2013
2014 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002015 priv = netdev_priv(dev);
2016 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002018 rc = register_netdev(dev);
2019 if (rc)
2020 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021
2022 PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002023
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 return count;
2025
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002026out_unreg:
2027 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028out_free_ndev:
2029 PRINT_WARN("netiucv: Could not register '%s'\n", dev->name);
2030 IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n");
2031 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002032 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033}
2034
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002035static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002037static ssize_t remove_write (struct device_driver *drv,
2038 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002040 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041 struct net_device *ndev;
2042 struct netiucv_priv *priv;
2043 struct device *dev;
2044 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002045 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 int i;
2047
2048 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
2049
2050 if (count >= IFNAMSIZ)
Frank Pavlic16a83b32006-09-15 16:25:19 +02002051 count = IFNAMSIZ - 1;;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002053 for (i = 0, p = buf; i < count && *p; i++, p++) {
2054 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 /* trailing lf, grr */
2056 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002057 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 }
2059 name[i] = '\0';
2060
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002061 read_lock_bh(&iucv_connection_rwlock);
2062 list_for_each_entry(cp, &iucv_connection_list, list) {
2063 ndev = cp->netdev;
2064 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002066 if (strncmp(name, ndev->name, count))
2067 continue;
2068 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002070 PRINT_WARN("netiucv: net device %s active with peer "
2071 "%s\n", ndev->name, priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 PRINT_WARN("netiucv: %s cannot be removed\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002073 ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
2075 return -EBUSY;
2076 }
2077 unregister_netdev(ndev);
2078 netiucv_unregister_device(dev);
2079 return count;
2080 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002081 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 PRINT_WARN("netiucv: net device %s unknown\n", name);
2083 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2084 return -EINVAL;
2085}
2086
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002087static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002089static struct attribute * netiucv_drv_attrs[] = {
2090 &driver_attr_connection.attr,
2091 &driver_attr_remove.attr,
2092 NULL,
2093};
2094
2095static struct attribute_group netiucv_drv_attr_group = {
2096 .attrs = netiucv_drv_attrs,
2097};
2098
2099static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100{
Heiko Carstense018ba12006-02-01 03:06:31 -08002101 PRINT_INFO("NETIUCV driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102}
2103
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002104static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002106 struct iucv_connection *cp;
2107 struct net_device *ndev;
2108 struct netiucv_priv *priv;
2109 struct device *dev;
2110
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002112 while (!list_empty(&iucv_connection_list)) {
2113 cp = list_entry(iucv_connection_list.next,
2114 struct iucv_connection, list);
2115 list_del(&cp->list);
2116 ndev = cp->netdev;
2117 priv = netdev_priv(ndev);
2118 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119
2120 unregister_netdev(ndev);
2121 netiucv_unregister_device(dev);
2122 }
2123
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002124 sysfs_remove_group(&netiucv_driver.kobj, &netiucv_drv_attr_group);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002126 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127 iucv_unregister_dbf_views();
2128
2129 PRINT_INFO("NETIUCV driver unloaded\n");
2130 return;
2131}
2132
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002133static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002135 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002136
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002137 rc = iucv_register_dbf_views();
2138 if (rc)
2139 goto out;
2140 rc = iucv_register(&netiucv_handler, 1);
2141 if (rc)
2142 goto out_dbf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002144 rc = driver_register(&netiucv_driver);
2145 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 PRINT_ERR("NETIUCV: failed to register driver.\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002147 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2148 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 }
2150
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002151 rc = sysfs_create_group(&netiucv_driver.kobj, &netiucv_drv_attr_group);
2152 if (rc) {
2153 PRINT_ERR("NETIUCV: failed to add driver attributes.\n");
2154 IUCV_DBF_TEXT_(setup, 2,
2155 "ret %d - netiucv_drv_attr_group\n", rc);
2156 goto out_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002158 netiucv_banner();
2159 return rc;
2160
2161out_driver:
2162 driver_unregister(&netiucv_driver);
2163out_iucv:
2164 iucv_unregister(&netiucv_handler, 1);
2165out_dbf:
2166 iucv_unregister_dbf_views();
2167out:
2168 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002170
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171module_init(netiucv_init);
2172module_exit(netiucv_exit);
2173MODULE_LICENSE("GPL");