blob: 6387b483f2bfe32fb7c7875fa28fe5b742222e67 [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>
44#include <linux/sched.h>
45#include <linux/bitops.h>
46
47#include <linux/signal.h>
48#include <linux/string.h>
49#include <linux/device.h>
50
51#include <linux/ip.h>
52#include <linux/if_arp.h>
53#include <linux/tcp.h>
54#include <linux/skbuff.h>
55#include <linux/ctype.h>
56#include <net/dst.h>
57
58#include <asm/io.h>
59#include <asm/uaccess.h>
60
Martin Schwidefskyeebce382007-02-08 13:50:33 -080061#include <net/iucv/iucv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#include "fsm.h"
63
64MODULE_AUTHOR
65 ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)");
66MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
67
Martin Schwidefskyeebce382007-02-08 13:50:33 -080068/**
69 * Debug Facility stuff
70 */
71#define IUCV_DBF_SETUP_NAME "iucv_setup"
72#define IUCV_DBF_SETUP_LEN 32
73#define IUCV_DBF_SETUP_PAGES 2
74#define IUCV_DBF_SETUP_NR_AREAS 1
75#define IUCV_DBF_SETUP_LEVEL 3
76
77#define IUCV_DBF_DATA_NAME "iucv_data"
78#define IUCV_DBF_DATA_LEN 128
79#define IUCV_DBF_DATA_PAGES 2
80#define IUCV_DBF_DATA_NR_AREAS 1
81#define IUCV_DBF_DATA_LEVEL 2
82
83#define IUCV_DBF_TRACE_NAME "iucv_trace"
84#define IUCV_DBF_TRACE_LEN 16
85#define IUCV_DBF_TRACE_PAGES 4
86#define IUCV_DBF_TRACE_NR_AREAS 1
87#define IUCV_DBF_TRACE_LEVEL 3
88
89#define IUCV_DBF_TEXT(name,level,text) \
90 do { \
91 debug_text_event(iucv_dbf_##name,level,text); \
92 } while (0)
93
94#define IUCV_DBF_HEX(name,level,addr,len) \
95 do { \
96 debug_event(iucv_dbf_##name,level,(void*)(addr),len); \
97 } while (0)
98
99DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf);
100
101#define IUCV_DBF_TEXT_(name,level,text...) \
102 do { \
103 char* iucv_dbf_txt_buf = get_cpu_var(iucv_dbf_txt_buf); \
104 sprintf(iucv_dbf_txt_buf, text); \
105 debug_text_event(iucv_dbf_##name,level,iucv_dbf_txt_buf); \
106 put_cpu_var(iucv_dbf_txt_buf); \
107 } while (0)
108
109#define IUCV_DBF_SPRINTF(name,level,text...) \
110 do { \
111 debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
112 debug_sprintf_event(iucv_dbf_trace, level, text ); \
113 } while (0)
114
115/**
116 * some more debug stuff
117 */
118#define IUCV_HEXDUMP16(importance,header,ptr) \
119PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
120 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
121 *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
122 *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
123 *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
124 *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
125 *(((char*)ptr)+12),*(((char*)ptr)+13), \
126 *(((char*)ptr)+14),*(((char*)ptr)+15)); \
127PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
128 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
129 *(((char*)ptr)+16),*(((char*)ptr)+17), \
130 *(((char*)ptr)+18),*(((char*)ptr)+19), \
131 *(((char*)ptr)+20),*(((char*)ptr)+21), \
132 *(((char*)ptr)+22),*(((char*)ptr)+23), \
133 *(((char*)ptr)+24),*(((char*)ptr)+25), \
134 *(((char*)ptr)+26),*(((char*)ptr)+27), \
135 *(((char*)ptr)+28),*(((char*)ptr)+29), \
136 *(((char*)ptr)+30),*(((char*)ptr)+31));
137
138static inline void iucv_hex_dump(unsigned char *buf, size_t len)
139{
140 size_t i;
141
142 for (i = 0; i < len; i++) {
143 if (i && !(i % 16))
144 printk("\n");
145 printk("%02x ", *(buf + i));
146 }
147 printk("\n");
148}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400149
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150#define PRINTK_HEADER " iucv: " /* for debugging */
151
152static struct device_driver netiucv_driver = {
153 .name = "netiucv",
154 .bus = &iucv_bus,
155};
156
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800157static int netiucv_callback_connreq(struct iucv_path *,
158 u8 ipvmid[8], u8 ipuser[16]);
159static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
160static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
161static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
162static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
163static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
164static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
165
166static struct iucv_handler netiucv_handler = {
167 .path_pending = netiucv_callback_connreq,
168 .path_complete = netiucv_callback_connack,
169 .path_severed = netiucv_callback_connrej,
170 .path_quiesced = netiucv_callback_connsusp,
171 .path_resumed = netiucv_callback_connres,
172 .message_pending = netiucv_callback_rx,
173 .message_complete = netiucv_callback_txdone
174};
175
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176/**
177 * Per connection profiling data
178 */
179struct connection_profile {
180 unsigned long maxmulti;
181 unsigned long maxcqueue;
182 unsigned long doios_single;
183 unsigned long doios_multi;
184 unsigned long txlen;
185 unsigned long tx_time;
186 struct timespec send_stamp;
187 unsigned long tx_pending;
188 unsigned long tx_max_pending;
189};
190
191/**
192 * Representation of one iucv connection
193 */
194struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800195 struct list_head list;
196 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 struct sk_buff *rx_buff;
198 struct sk_buff *tx_buff;
199 struct sk_buff_head collect_queue;
200 struct sk_buff_head commit_queue;
201 spinlock_t collect_lock;
202 int collect_len;
203 int max_buffsize;
204 fsm_timer timer;
205 fsm_instance *fsm;
206 struct net_device *netdev;
207 struct connection_profile prof;
208 char userid[9];
209};
210
211/**
212 * Linked list of all connection structs.
213 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800214static struct list_head iucv_connection_list =
215 LIST_HEAD_INIT(iucv_connection_list);
216static rwlock_t iucv_connection_rwlock = RW_LOCK_UNLOCKED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217
218/**
219 * Representation of event-data for the
220 * connection state machine.
221 */
222struct iucv_event {
223 struct iucv_connection *conn;
224 void *data;
225};
226
227/**
228 * Private part of the network device structure
229 */
230struct netiucv_priv {
231 struct net_device_stats stats;
232 unsigned long tbusy;
233 fsm_instance *fsm;
234 struct iucv_connection *conn;
235 struct device *dev;
236};
237
238/**
239 * Link level header for a packet.
240 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800241struct ll_header {
242 u16 next;
243};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800245#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246#define NETIUCV_BUFSIZE_MAX 32768
247#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
248#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
249#define NETIUCV_MTU_DEFAULT 9216
250#define NETIUCV_QUEUELEN_DEFAULT 50
251#define NETIUCV_TIMEOUT_5SEC 5000
252
253/**
254 * Compatibility macros for busy handling
255 * of network devices.
256 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800257static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800259 struct netiucv_priv *priv = netdev_priv(dev);
260 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 netif_wake_queue(dev);
262}
263
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800264static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800266 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800268 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269}
270
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800271static u8 iucvMagic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
273 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
274};
275
276/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 * Convert an iucv userId to its printable
278 * form (strip whitespace at end).
279 *
280 * @param An iucv userId
281 *
282 * @returns The printable string (static data!!)
283 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800284static inline char *netiucv_printname(char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285{
286 static char tmp[9];
287 char *p = tmp;
288 memcpy(tmp, name, 8);
289 tmp[8] = '\0';
290 while (*p && (!isspace(*p)))
291 p++;
292 *p = '\0';
293 return tmp;
294}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296/**
297 * States of the interface statemachine.
298 */
299enum dev_states {
300 DEV_STATE_STOPPED,
301 DEV_STATE_STARTWAIT,
302 DEV_STATE_STOPWAIT,
303 DEV_STATE_RUNNING,
304 /**
305 * MUST be always the last element!!
306 */
307 NR_DEV_STATES
308};
309
310static const char *dev_state_names[] = {
311 "Stopped",
312 "StartWait",
313 "StopWait",
314 "Running",
315};
316
317/**
318 * Events of the interface statemachine.
319 */
320enum dev_events {
321 DEV_EVENT_START,
322 DEV_EVENT_STOP,
323 DEV_EVENT_CONUP,
324 DEV_EVENT_CONDOWN,
325 /**
326 * MUST be always the last element!!
327 */
328 NR_DEV_EVENTS
329};
330
331static const char *dev_event_names[] = {
332 "Start",
333 "Stop",
334 "Connection up",
335 "Connection down",
336};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400337
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338/**
339 * Events of the connection statemachine
340 */
341enum conn_events {
342 /**
343 * Events, representing callbacks from
344 * lowlevel iucv layer)
345 */
346 CONN_EVENT_CONN_REQ,
347 CONN_EVENT_CONN_ACK,
348 CONN_EVENT_CONN_REJ,
349 CONN_EVENT_CONN_SUS,
350 CONN_EVENT_CONN_RES,
351 CONN_EVENT_RX,
352 CONN_EVENT_TXDONE,
353
354 /**
355 * Events, representing errors return codes from
356 * calls to lowlevel iucv layer
357 */
358
359 /**
360 * Event, representing timer expiry.
361 */
362 CONN_EVENT_TIMER,
363
364 /**
365 * Events, representing commands from upper levels.
366 */
367 CONN_EVENT_START,
368 CONN_EVENT_STOP,
369
370 /**
371 * MUST be always the last element!!
372 */
373 NR_CONN_EVENTS,
374};
375
376static const char *conn_event_names[] = {
377 "Remote connection request",
378 "Remote connection acknowledge",
379 "Remote connection reject",
380 "Connection suspended",
381 "Connection resumed",
382 "Data received",
383 "Data sent",
384
385 "Timer",
386
387 "Start",
388 "Stop",
389};
390
391/**
392 * States of the connection statemachine.
393 */
394enum conn_states {
395 /**
396 * Connection not assigned to any device,
397 * initial state, invalid
398 */
399 CONN_STATE_INVALID,
400
401 /**
402 * Userid assigned but not operating
403 */
404 CONN_STATE_STOPPED,
405
406 /**
407 * Connection registered,
408 * no connection request sent yet,
409 * no connection request received
410 */
411 CONN_STATE_STARTWAIT,
412
413 /**
414 * Connection registered and connection request sent,
415 * no acknowledge and no connection request received yet.
416 */
417 CONN_STATE_SETUPWAIT,
418
419 /**
420 * Connection up and running idle
421 */
422 CONN_STATE_IDLE,
423
424 /**
425 * Data sent, awaiting CONN_EVENT_TXDONE
426 */
427 CONN_STATE_TX,
428
429 /**
430 * Error during registration.
431 */
432 CONN_STATE_REGERR,
433
434 /**
435 * Error during registration.
436 */
437 CONN_STATE_CONNERR,
438
439 /**
440 * MUST be always the last element!!
441 */
442 NR_CONN_STATES,
443};
444
445static const char *conn_state_names[] = {
446 "Invalid",
447 "Stopped",
448 "StartWait",
449 "SetupWait",
450 "Idle",
451 "TX",
452 "Terminating",
453 "Registration error",
454 "Connect error",
455};
456
Jeff Garzike82b0f22006-05-26 21:58:38 -0400457
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458/**
459 * Debug Facility Stuff
460 */
461static debug_info_t *iucv_dbf_setup = NULL;
462static debug_info_t *iucv_dbf_data = NULL;
463static debug_info_t *iucv_dbf_trace = NULL;
464
465DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
466
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800467static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468{
469 if (iucv_dbf_setup)
470 debug_unregister(iucv_dbf_setup);
471 if (iucv_dbf_data)
472 debug_unregister(iucv_dbf_data);
473 if (iucv_dbf_trace)
474 debug_unregister(iucv_dbf_trace);
475}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800476static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
478 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700479 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 IUCV_DBF_SETUP_NR_AREAS,
481 IUCV_DBF_SETUP_LEN);
482 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700483 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 IUCV_DBF_DATA_NR_AREAS,
485 IUCV_DBF_DATA_LEN);
486 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700487 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 IUCV_DBF_TRACE_NR_AREAS,
489 IUCV_DBF_TRACE_LEN);
490
491 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
492 (iucv_dbf_trace == NULL)) {
493 iucv_unregister_dbf_views();
494 return -ENOMEM;
495 }
496 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
497 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
498
499 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
500 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
501
502 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
503 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
504
505 return 0;
506}
507
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800508/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800510 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800512static void netiucv_callback_rx(struct iucv_path *path,
513 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800515 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 struct iucv_event ev;
517
518 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800519 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
521}
522
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800523static void netiucv_callback_txdone(struct iucv_path *path,
524 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800526 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 struct iucv_event ev;
528
529 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800530 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
532}
533
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800534static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800536 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800538 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539}
540
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800541static int netiucv_callback_connreq(struct iucv_path *path,
542 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800544 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 struct iucv_event ev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800546 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800548 if (memcmp(iucvMagic, ipuser, sizeof(ipuser)))
549 /* ipuser must match iucvMagic. */
550 return -EINVAL;
551 rc = -EINVAL;
552 read_lock_bh(&iucv_connection_rwlock);
553 list_for_each_entry(conn, &iucv_connection_list, list) {
554 if (strncmp(ipvmid, conn->userid, 8))
555 continue;
556 /* Found a matching connection for this path. */
557 conn->path = path;
558 ev.conn = conn;
559 ev.data = path;
560 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
561 rc = 0;
562 }
563 read_unlock_bh(&iucv_connection_rwlock);
564 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800567static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800569 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800571 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
573
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800574static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800576 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800578 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579}
580
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800581static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800583 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800585 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586}
587
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588/**
589 * Dummy NOP action for all statemachines
590 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800591static void fsm_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
593}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400594
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800595/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800597 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
599/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800600 * netiucv_unpack_skb
601 * @conn: The connection where this skb has been received.
602 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800604 * Unpack a just received skb and hand it over to upper layers.
605 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800607static void netiucv_unpack_skb(struct iucv_connection *conn,
608 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609{
610 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800611 struct netiucv_priv *privptr = netdev_priv(dev);
612 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
614 skb_put(pskb, NETIUCV_HDRLEN);
615 pskb->dev = dev;
616 pskb->ip_summed = CHECKSUM_NONE;
617 pskb->protocol = ntohs(ETH_P_IP);
618
619 while (1) {
620 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800621 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
623 if (!header->next)
624 break;
625
626 skb_pull(pskb, NETIUCV_HDRLEN);
627 header->next -= offset;
628 offset += header->next;
629 header->next -= NETIUCV_HDRLEN;
630 if (skb_tailroom(pskb) < header->next) {
631 PRINT_WARN("%s: Illegal next field in iucv header: "
632 "%d > %d\n",
633 dev->name, header->next, skb_tailroom(pskb));
634 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
635 header->next, skb_tailroom(pskb));
636 return;
637 }
638 skb_put(pskb, header->next);
639 pskb->mac.raw = pskb->data;
640 skb = dev_alloc_skb(pskb->len);
641 if (!skb) {
642 PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n",
643 dev->name);
644 IUCV_DBF_TEXT(data, 2,
645 "Out of memory in netiucv_unpack_skb\n");
646 privptr->stats.rx_dropped++;
647 return;
648 }
649 memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len);
650 skb->mac.raw = skb->data;
651 skb->dev = pskb->dev;
652 skb->protocol = pskb->protocol;
653 pskb->ip_summed = CHECKSUM_UNNECESSARY;
654 /*
655 * Since receiving is always initiated from a tasklet (in iucv.c),
656 * we must use netif_rx_ni() instead of netif_rx()
657 */
658 netif_rx_ni(skb);
659 dev->last_rx = jiffies;
660 privptr->stats.rx_packets++;
661 privptr->stats.rx_bytes += skb->len;
662 skb_pull(pskb, header->next);
663 skb_put(pskb, NETIUCV_HDRLEN);
664 }
665}
666
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800667static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800669 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800671 struct iucv_message *msg = ev->data;
672 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 int rc;
674
675 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
676
677 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800678 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 PRINT_WARN("Received data for unlinked connection\n");
680 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800681 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 return;
683 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800684 if (msg->length > conn->max_buffsize) {
685 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 privptr->stats.rx_dropped++;
687 PRINT_WARN("msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800688 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800690 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 return;
692 }
693 conn->rx_buff->data = conn->rx_buff->tail = conn->rx_buff->head;
694 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 }
739 conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head;
740 conn->tx_buff->len = 0;
741 spin_lock_irqsave(&conn->collect_lock, saveflags);
742 while ((skb = skb_dequeue(&conn->collect_queue))) {
743 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
744 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
745 NETIUCV_HDRLEN);
746 memcpy(skb_put(conn->tx_buff, skb->len), skb->data, skb->len);
747 txbytes += skb->len;
748 txpackets++;
749 stat_maxcq++;
750 atomic_dec(&skb->users);
751 dev_kfree_skb_any(skb);
752 }
753 if (conn->collect_len > conn->prof.maxmulti)
754 conn->prof.maxmulti = conn->collect_len;
755 conn->collect_len = 0;
756 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800757 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800759 return;
760 }
761
762 header.next = 0;
763 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
764 conn->prof.send_stamp = xtime;
765 txmsg.class = 0;
766 txmsg.tag = 0;
767 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
768 conn->tx_buff->data, conn->tx_buff->len);
769 conn->prof.doios_multi++;
770 conn->prof.txlen += conn->tx_buff->len;
771 conn->prof.tx_pending++;
772 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
773 conn->prof.tx_max_pending = conn->prof.tx_pending;
774 if (rc) {
775 conn->prof.tx_pending--;
776 fsm_newstate(fi, CONN_STATE_IDLE);
777 if (privptr)
778 privptr->stats.tx_errors += txpackets;
779 PRINT_WARN("iucv_send returned %08x\n", rc);
780 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
781 } else {
782 if (privptr) {
783 privptr->stats.tx_packets += txpackets;
784 privptr->stats.tx_bytes += txbytes;
785 }
786 if (stat_maxcq > conn->prof.maxcqueue)
787 conn->prof.maxcqueue = stat_maxcq;
788 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789}
790
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800791static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800793 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800795 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800797 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
801
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800802 conn->path = path;
803 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
804 path->flags = 0;
805 rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 if (rc) {
807 PRINT_WARN("%s: IUCV accept failed with error %d\n",
808 netdev->name, rc);
809 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
810 return;
811 }
812 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800813 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
815}
816
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800817static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800819 struct iucv_event *ev = arg;
820 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
822 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800823 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824}
825
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800826static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800828 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800830 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
832 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 fsm_deltimer(&conn->timer);
834 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800835 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
837}
838
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800839static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800841 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
843 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800845 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 fsm_newstate(fi, CONN_STATE_STARTWAIT);
847}
848
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800849static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800851 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800853 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
855 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
856
857 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800858 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 PRINT_INFO("%s: Remote dropped connection\n", netdev->name);
860 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800861 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 fsm_newstate(fi, CONN_STATE_STARTWAIT);
863 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
864}
865
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800866static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800868 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 int rc;
870
871 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
872
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800873 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 PRINT_DEBUG("%s('%s'): connecting ...\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800875 conn->netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800877 /*
878 * We must set the state before calling iucv_connect because the
879 * callback handler could be called at any point after the connection
880 * request is sent
881 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882
883 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800884 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
885 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
886 NULL, iucvMagic, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800888 case 0:
889 conn->netdev->tx_queue_len = conn->path->msglim;
890 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
891 CONN_EVENT_TIMER, conn);
892 return;
893 case 11:
894 PRINT_INFO("%s: User %s is currently not available.\n",
895 conn->netdev->name,
896 netiucv_printname(conn->userid));
897 fsm_newstate(fi, CONN_STATE_STARTWAIT);
898 break;
899 case 12:
900 PRINT_INFO("%s: User %s is currently not ready.\n",
901 conn->netdev->name,
902 netiucv_printname(conn->userid));
903 fsm_newstate(fi, CONN_STATE_STARTWAIT);
904 break;
905 case 13:
906 PRINT_WARN("%s: Too many IUCV connections.\n",
907 conn->netdev->name);
908 fsm_newstate(fi, CONN_STATE_CONNERR);
909 break;
910 case 14:
911 PRINT_WARN("%s: User %s has too many IUCV connections.\n",
912 conn->netdev->name,
913 netiucv_printname(conn->userid));
914 fsm_newstate(fi, CONN_STATE_CONNERR);
915 break;
916 case 15:
917 PRINT_WARN("%s: No IUCV authorization in CP directory.\n",
918 conn->netdev->name);
919 fsm_newstate(fi, CONN_STATE_CONNERR);
920 break;
921 default:
922 PRINT_WARN("%s: iucv_connect returned error %d\n",
923 conn->netdev->name, rc);
924 fsm_newstate(fi, CONN_STATE_CONNERR);
925 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 }
927 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800928 kfree(conn->path);
929 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930}
931
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800932static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933{
934 struct sk_buff *skb;
935
936 while ((skb = skb_dequeue(q))) {
937 atomic_dec(&skb->users);
938 dev_kfree_skb_any(skb);
939 }
940}
941
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800942static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800944 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 struct iucv_connection *conn = ev->conn;
946 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800947 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
949 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
950
951 fsm_deltimer(&conn->timer);
952 fsm_newstate(fi, CONN_STATE_STOPPED);
953 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800954 if (conn->path) {
955 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
956 iucv_path_sever(conn->path, iucvMagic);
957 kfree(conn->path);
958 conn->path = NULL;
959 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 netiucv_purge_skb_queue(&conn->commit_queue);
961 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
962}
963
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800964static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800966 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 struct net_device *netdev = conn->netdev;
968
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800969 PRINT_WARN("%s: Cannot connect without username\n", netdev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 IUCV_DBF_TEXT(data, 2, "conn_action_inval called\n");
971}
972
973static const fsm_node conn_fsm[] = {
974 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
975 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
976
977 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
978 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
979 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
980 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
981 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
982 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
983 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
984
985 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
986 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
987 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
988 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
989 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
990
991 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
992 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
993
994 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
995 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
996 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
997
998 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
999 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1000
1001 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1002 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1003};
1004
1005static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1006
Jeff Garzike82b0f22006-05-26 21:58:38 -04001007
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001008/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001010 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011
1012/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001013 * dev_action_start
1014 * @fi: An instance of an interface statemachine.
1015 * @event: The event, just happened.
1016 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001018 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001020static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001022 struct net_device *dev = arg;
1023 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024
1025 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1026
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001028 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029}
1030
1031/**
1032 * Shutdown connection by sending CONN_EVENT_STOP to it.
1033 *
1034 * @param fi An instance of an interface statemachine.
1035 * @param event The event, just happened.
1036 * @param arg Generic pointer, casted from struct net_device * upon call.
1037 */
1038static void
1039dev_action_stop(fsm_instance *fi, int event, void *arg)
1040{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001041 struct net_device *dev = arg;
1042 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 struct iucv_event ev;
1044
1045 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1046
1047 ev.conn = privptr->conn;
1048
1049 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1050 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1051}
1052
1053/**
1054 * Called from connection statemachine
1055 * when a connection is up and running.
1056 *
1057 * @param fi An instance of an interface statemachine.
1058 * @param event The event, just happened.
1059 * @param arg Generic pointer, casted from struct net_device * upon call.
1060 */
1061static void
1062dev_action_connup(fsm_instance *fi, int event, void *arg)
1063{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001064 struct net_device *dev = arg;
1065 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066
1067 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1068
1069 switch (fsm_getstate(fi)) {
1070 case DEV_STATE_STARTWAIT:
1071 fsm_newstate(fi, DEV_STATE_RUNNING);
1072 PRINT_INFO("%s: connected with remote side %s\n",
1073 dev->name, privptr->conn->userid);
1074 IUCV_DBF_TEXT(setup, 3,
1075 "connection is up and running\n");
1076 break;
1077 case DEV_STATE_STOPWAIT:
1078 PRINT_INFO(
1079 "%s: got connection UP event during shutdown!\n",
1080 dev->name);
1081 IUCV_DBF_TEXT(data, 2,
1082 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1083 break;
1084 }
1085}
1086
1087/**
1088 * Called from connection statemachine
1089 * when a connection has been shutdown.
1090 *
1091 * @param fi An instance of an interface statemachine.
1092 * @param event The event, just happened.
1093 * @param arg Generic pointer, casted from struct net_device * upon call.
1094 */
1095static void
1096dev_action_conndown(fsm_instance *fi, int event, void *arg)
1097{
1098 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1099
1100 switch (fsm_getstate(fi)) {
1101 case DEV_STATE_RUNNING:
1102 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1103 break;
1104 case DEV_STATE_STOPWAIT:
1105 fsm_newstate(fi, DEV_STATE_STOPPED);
1106 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1107 break;
1108 }
1109}
1110
1111static const fsm_node dev_fsm[] = {
1112 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1113
1114 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1115 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1116
1117 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1118 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1119
1120 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1121 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
1122 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, fsm_action_nop },
1123};
1124
1125static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1126
1127/**
1128 * Transmit a packet.
1129 * This is a helper function for netiucv_tx().
1130 *
1131 * @param conn Connection to be used for sending.
1132 * @param skb Pointer to struct sk_buff of packet to send.
1133 * The linklevel header has already been set up
1134 * by netiucv_tx().
1135 *
1136 * @return 0 on success, -ERRNO on failure. (Never fails.)
1137 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001138static int netiucv_transmit_skb(struct iucv_connection *conn,
1139 struct sk_buff *skb)
1140{
1141 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001143 struct ll_header header;
1144 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145
1146 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1147 int l = skb->len + NETIUCV_HDRLEN;
1148
1149 spin_lock_irqsave(&conn->collect_lock, saveflags);
1150 if (conn->collect_len + l >
1151 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1152 rc = -EBUSY;
1153 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001154 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 } else {
1156 atomic_inc(&skb->users);
1157 skb_queue_tail(&conn->collect_queue, skb);
1158 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001159 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 }
1161 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1162 } else {
1163 struct sk_buff *nskb = skb;
1164 /**
1165 * Copy the skb to a new allocated skb in lowmem only if the
1166 * data is located above 2G in memory or tailroom is < 2.
1167 */
1168 unsigned long hi =
1169 ((unsigned long)(skb->tail + NETIUCV_HDRLEN)) >> 31;
1170 int copied = 0;
1171 if (hi || (skb_tailroom(skb) < 2)) {
1172 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1173 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1174 if (!nskb) {
1175 PRINT_WARN("%s: Could not allocate tx_skb\n",
1176 conn->netdev->name);
1177 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1178 rc = -ENOMEM;
1179 return rc;
1180 } else {
1181 skb_reserve(nskb, NETIUCV_HDRLEN);
1182 memcpy(skb_put(nskb, skb->len),
1183 skb->data, skb->len);
1184 }
1185 copied = 1;
1186 }
1187 /**
1188 * skb now is below 2G and has enough room. Add headers.
1189 */
1190 header.next = nskb->len + NETIUCV_HDRLEN;
1191 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1192 header.next = 0;
1193 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1194
1195 fsm_newstate(conn->fsm, CONN_STATE_TX);
1196 conn->prof.send_stamp = xtime;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001197
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001198 msg.tag = 1;
1199 msg.class = 0;
1200 rc = iucv_message_send(conn->path, &msg, 0, 0,
1201 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 conn->prof.doios_single++;
1203 conn->prof.txlen += skb->len;
1204 conn->prof.tx_pending++;
1205 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1206 conn->prof.tx_max_pending = conn->prof.tx_pending;
1207 if (rc) {
1208 struct netiucv_priv *privptr;
1209 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1210 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001211 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 if (privptr)
1213 privptr->stats.tx_errors++;
1214 if (copied)
1215 dev_kfree_skb(nskb);
1216 else {
1217 /**
1218 * Remove our headers. They get added
1219 * again on retransmit.
1220 */
1221 skb_pull(skb, NETIUCV_HDRLEN);
1222 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1223 }
1224 PRINT_WARN("iucv_send returned %08x\n", rc);
1225 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1226 } else {
1227 if (copied)
1228 dev_kfree_skb(skb);
1229 atomic_inc(&nskb->users);
1230 skb_queue_tail(&conn->commit_queue, nskb);
1231 }
1232 }
1233
1234 return rc;
1235}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001236
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001237/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001239 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240
1241/**
1242 * Open an interface.
1243 * Called from generic network layer when ifconfig up is run.
1244 *
1245 * @param dev Pointer to interface struct.
1246 *
1247 * @return 0 on success, -ERRNO on failure. (Never fails.)
1248 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001249static int netiucv_open(struct net_device *dev)
1250{
1251 struct netiucv_priv *priv = netdev_priv(dev);
1252
1253 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 return 0;
1255}
1256
1257/**
1258 * Close an interface.
1259 * Called from generic network layer when ifconfig down is run.
1260 *
1261 * @param dev Pointer to interface struct.
1262 *
1263 * @return 0 on success, -ERRNO on failure. (Never fails.)
1264 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001265static int netiucv_close(struct net_device *dev)
1266{
1267 struct netiucv_priv *priv = netdev_priv(dev);
1268
1269 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 return 0;
1271}
1272
1273/**
1274 * Start transmission of a packet.
1275 * Called from generic network device layer.
1276 *
1277 * @param skb Pointer to buffer containing the packet.
1278 * @param dev Pointer to interface struct.
1279 *
1280 * @return 0 if packet consumed, !0 if packet rejected.
1281 * Note: If we return !0, then the packet is free'd by
1282 * the generic network layer.
1283 */
1284static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1285{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001286 struct netiucv_priv *privptr = netdev_priv(dev);
1287 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
1289 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1290 /**
1291 * Some sanity checks ...
1292 */
1293 if (skb == NULL) {
1294 PRINT_WARN("%s: NULL sk_buff passed\n", dev->name);
1295 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1296 privptr->stats.tx_dropped++;
1297 return 0;
1298 }
1299 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
1300 PRINT_WARN("%s: Got sk_buff with head room < %ld bytes\n",
1301 dev->name, NETIUCV_HDRLEN);
1302 IUCV_DBF_TEXT(data, 2,
1303 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1304 dev_kfree_skb(skb);
1305 privptr->stats.tx_dropped++;
1306 return 0;
1307 }
1308
1309 /**
1310 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001311 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 */
1313 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
1314 fsm_event(privptr->fsm, DEV_EVENT_START, dev);
1315 dev_kfree_skb(skb);
1316 privptr->stats.tx_dropped++;
1317 privptr->stats.tx_errors++;
1318 privptr->stats.tx_carrier_errors++;
1319 return 0;
1320 }
1321
1322 if (netiucv_test_and_set_busy(dev)) {
1323 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
1324 return -EBUSY;
1325 }
1326 dev->trans_start = jiffies;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001327 rc = netiucv_transmit_skb(privptr->conn, skb) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 netiucv_clear_busy(dev);
1329 return rc;
1330}
1331
1332/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001333 * netiucv_stats
1334 * @dev: Pointer to interface struct.
1335 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 * Returns interface statistics of a device.
1337 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001338 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001340static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001342 struct netiucv_priv *priv = netdev_priv(dev);
1343
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001345 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346}
1347
1348/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001349 * netiucv_change_mtu
1350 * @dev: Pointer to interface struct.
1351 * @new_mtu: The new MTU to use for this interface.
1352 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 * Sets MTU of an interface.
1354 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001355 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 * (valid range is 576 .. NETIUCV_MTU_MAX).
1357 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001358static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359{
1360 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001361 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1363 return -EINVAL;
1364 }
1365 dev->mtu = new_mtu;
1366 return 0;
1367}
1368
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001369/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001371 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001373static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1374 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375{
1376 struct netiucv_priv *priv = dev->driver_data;
1377
1378 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1379 return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));
1380}
1381
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001382static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1383 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384{
1385 struct netiucv_priv *priv = dev->driver_data;
1386 struct net_device *ndev = priv->conn->netdev;
1387 char *p;
1388 char *tmp;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001389 char username[9];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 int i;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001391 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392
1393 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001394 if (count > 9) {
1395 PRINT_WARN("netiucv: username too long (%d)!\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001397 "%d is length of username\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 return -EINVAL;
1399 }
1400
1401 tmp = strsep((char **) &buf, "\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001402 for (i = 0, p = tmp; i < 8 && *p; i++, p++) {
1403 if (isalnum(*p) || (*p == '$')) {
Frank Pavlic16a83b32006-09-15 16:25:19 +02001404 username[i]= toupper(*p);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001405 continue;
1406 }
1407 if (*p == '\n') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 /* trailing lf, grr */
1409 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001411 PRINT_WARN("netiucv: Invalid char %c in username!\n", *p);
1412 IUCV_DBF_TEXT_(setup, 2,
1413 "username: invalid character %c\n", *p);
1414 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001416 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001418 username[8] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001420 if (memcmp(username, priv->conn->userid, 9) &&
1421 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1422 /* username changed while the interface is active. */
1423 PRINT_WARN("netiucv: device %s active, connected to %s\n",
1424 dev->bus_id, priv->conn->userid);
1425 PRINT_WARN("netiucv: user cannot be updated\n");
1426 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
1427 return -EBUSY;
1428 }
1429 read_lock_bh(&iucv_connection_rwlock);
1430 list_for_each_entry(cp, &iucv_connection_list, list) {
1431 if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
1432 read_unlock_bh(&iucv_connection_rwlock);
1433 PRINT_WARN("netiucv: Connection to %s already "
1434 "exists\n", username);
1435 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 }
1437 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001438 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 memcpy(priv->conn->userid, username, 9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441}
1442
1443static DEVICE_ATTR(user, 0644, user_show, user_write);
1444
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001445static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1446 char *buf)
1447{ struct netiucv_priv *priv = dev->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448
1449 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1450 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1451}
1452
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001453static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1454 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455{
1456 struct netiucv_priv *priv = dev->driver_data;
1457 struct net_device *ndev = priv->conn->netdev;
1458 char *e;
1459 int bs1;
1460
1461 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1462 if (count >= 39)
1463 return -EINVAL;
1464
1465 bs1 = simple_strtoul(buf, &e, 0);
1466
1467 if (e && (!isspace(*e))) {
1468 PRINT_WARN("netiucv: Invalid character in buffer!\n");
1469 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
1470 return -EINVAL;
1471 }
1472 if (bs1 > NETIUCV_BUFSIZE_MAX) {
1473 PRINT_WARN("netiucv: Given buffer size %d too large.\n",
1474 bs1);
1475 IUCV_DBF_TEXT_(setup, 2,
1476 "buffer_write: buffer size %d too large\n",
1477 bs1);
1478 return -EINVAL;
1479 }
1480 if ((ndev->flags & IFF_RUNNING) &&
1481 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
1482 PRINT_WARN("netiucv: Given buffer size %d too small.\n",
1483 bs1);
1484 IUCV_DBF_TEXT_(setup, 2,
1485 "buffer_write: buffer size %d too small\n",
1486 bs1);
1487 return -EINVAL;
1488 }
1489 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
1490 PRINT_WARN("netiucv: Given buffer size %d too small.\n",
1491 bs1);
1492 IUCV_DBF_TEXT_(setup, 2,
1493 "buffer_write: buffer size %d too small\n",
1494 bs1);
1495 return -EINVAL;
1496 }
1497
1498 priv->conn->max_buffsize = bs1;
1499 if (!(ndev->flags & IFF_RUNNING))
1500 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1501
1502 return count;
1503
1504}
1505
1506static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1507
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001508static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1509 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510{
1511 struct netiucv_priv *priv = dev->driver_data;
1512
1513 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1514 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1515}
1516
1517static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1518
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001519static ssize_t conn_fsm_show (struct device *dev,
1520 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521{
1522 struct netiucv_priv *priv = dev->driver_data;
1523
1524 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1525 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1526}
1527
1528static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1529
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001530static ssize_t maxmulti_show (struct device *dev,
1531 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532{
1533 struct netiucv_priv *priv = dev->driver_data;
1534
1535 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1536 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1537}
1538
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001539static ssize_t maxmulti_write (struct device *dev,
1540 struct device_attribute *attr,
1541 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542{
1543 struct netiucv_priv *priv = dev->driver_data;
1544
1545 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1546 priv->conn->prof.maxmulti = 0;
1547 return count;
1548}
1549
1550static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1551
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001552static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1553 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554{
1555 struct netiucv_priv *priv = dev->driver_data;
1556
1557 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1558 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1559}
1560
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001561static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1562 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563{
1564 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001565
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1567 priv->conn->prof.maxcqueue = 0;
1568 return count;
1569}
1570
1571static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1572
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001573static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1574 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575{
1576 struct netiucv_priv *priv = dev->driver_data;
1577
1578 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1579 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1580}
1581
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001582static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1583 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584{
1585 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001586
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1588 priv->conn->prof.doios_single = 0;
1589 return count;
1590}
1591
1592static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1593
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001594static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1595 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596{
1597 struct netiucv_priv *priv = dev->driver_data;
1598
1599 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1600 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1601}
1602
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001603static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1604 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605{
1606 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001607
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1609 priv->conn->prof.doios_multi = 0;
1610 return count;
1611}
1612
1613static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1614
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001615static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1616 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617{
1618 struct netiucv_priv *priv = dev->driver_data;
1619
1620 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1621 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1622}
1623
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001624static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1625 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626{
1627 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001628
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1630 priv->conn->prof.txlen = 0;
1631 return count;
1632}
1633
1634static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1635
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001636static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1637 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638{
1639 struct netiucv_priv *priv = dev->driver_data;
1640
1641 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1642 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1643}
1644
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001645static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1646 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647{
1648 struct netiucv_priv *priv = dev->driver_data;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001649
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1651 priv->conn->prof.tx_time = 0;
1652 return count;
1653}
1654
1655static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1656
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001657static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1658 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659{
1660 struct netiucv_priv *priv = dev->driver_data;
1661
1662 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1663 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1664}
1665
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001666static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1667 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668{
1669 struct netiucv_priv *priv = dev->driver_data;
1670
1671 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1672 priv->conn->prof.tx_pending = 0;
1673 return count;
1674}
1675
1676static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1677
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001678static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1679 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680{
1681 struct netiucv_priv *priv = dev->driver_data;
1682
1683 IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
1684 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1685}
1686
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001687static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1688 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689{
1690 struct netiucv_priv *priv = dev->driver_data;
1691
1692 IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
1693 priv->conn->prof.tx_max_pending = 0;
1694 return count;
1695}
1696
1697static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1698
1699static struct attribute *netiucv_attrs[] = {
1700 &dev_attr_buffer.attr,
1701 &dev_attr_user.attr,
1702 NULL,
1703};
1704
1705static struct attribute_group netiucv_attr_group = {
1706 .attrs = netiucv_attrs,
1707};
1708
1709static struct attribute *netiucv_stat_attrs[] = {
1710 &dev_attr_device_fsm_state.attr,
1711 &dev_attr_connection_fsm_state.attr,
1712 &dev_attr_max_tx_buffer_used.attr,
1713 &dev_attr_max_chained_skbs.attr,
1714 &dev_attr_tx_single_write_ops.attr,
1715 &dev_attr_tx_multi_write_ops.attr,
1716 &dev_attr_netto_bytes.attr,
1717 &dev_attr_max_tx_io_time.attr,
1718 &dev_attr_tx_pending.attr,
1719 &dev_attr_tx_max_pending.attr,
1720 NULL,
1721};
1722
1723static struct attribute_group netiucv_stat_attr_group = {
1724 .name = "stats",
1725 .attrs = netiucv_stat_attrs,
1726};
1727
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001728static inline int netiucv_add_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729{
1730 int ret;
1731
1732 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1733 ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group);
1734 if (ret)
1735 return ret;
1736 ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group);
1737 if (ret)
1738 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1739 return ret;
1740}
1741
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001742static inline void netiucv_remove_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743{
1744 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1745 sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group);
1746 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1747}
1748
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001749static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001751 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001752 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 int ret;
1754
1755
1756 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1757
1758 if (dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759 snprintf(dev->bus_id, BUS_ID_SIZE, "net%s", ndev->name);
1760 dev->bus = &iucv_bus;
1761 dev->parent = iucv_root;
1762 /*
1763 * The release function could be called after the
1764 * module has been unloaded. It's _only_ task is to
1765 * free the struct. Therefore, we specify kfree()
1766 * directly here. (Probably a little bit obfuscating
1767 * but legitime ...).
1768 */
1769 dev->release = (void (*)(struct device *))kfree;
1770 dev->driver = &netiucv_driver;
1771 } else
1772 return -ENOMEM;
1773
1774 ret = device_register(dev);
1775
1776 if (ret)
1777 return ret;
1778 ret = netiucv_add_files(dev);
1779 if (ret)
1780 goto out_unreg;
1781 priv->dev = dev;
1782 dev->driver_data = priv;
1783 return 0;
1784
1785out_unreg:
1786 device_unregister(dev);
1787 return ret;
1788}
1789
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001790static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791{
1792 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1793 netiucv_remove_files(dev);
1794 device_unregister(dev);
1795}
1796
1797/**
1798 * Allocate and initialize a new connection structure.
1799 * Add it to the list of netiucv connections;
1800 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001801static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
1802 char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001804 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001806 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1807 if (!conn)
1808 goto out;
1809 skb_queue_head_init(&conn->collect_queue);
1810 skb_queue_head_init(&conn->commit_queue);
1811 spin_lock_init(&conn->collect_lock);
1812 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1813 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001815 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1816 if (!conn->rx_buff)
1817 goto out_conn;
1818 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1819 if (!conn->tx_buff)
1820 goto out_rx;
1821 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1822 conn_event_names, NR_CONN_STATES,
1823 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1824 GFP_KERNEL);
1825 if (!conn->fsm)
1826 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001828 fsm_settimer(conn->fsm, &conn->timer);
1829 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001831 if (username) {
1832 memcpy(conn->userid, username, 9);
1833 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001835
1836 write_lock_bh(&iucv_connection_rwlock);
1837 list_add_tail(&conn->list, &iucv_connection_list);
1838 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001840
1841out_tx:
1842 kfree_skb(conn->tx_buff);
1843out_rx:
1844 kfree_skb(conn->rx_buff);
1845out_conn:
1846 kfree(conn);
1847out:
1848 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849}
1850
1851/**
1852 * Release a connection structure and remove it from the
1853 * list of netiucv connections.
1854 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001855static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001858 write_lock_bh(&iucv_connection_rwlock);
1859 list_del_init(&conn->list);
1860 write_unlock_bh(&iucv_connection_rwlock);
1861 if (conn->path) {
1862 iucv_path_sever(conn->path, iucvMagic);
1863 kfree(conn->path);
1864 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001866 fsm_deltimer(&conn->timer);
1867 kfree_fsm(conn->fsm);
1868 kfree_skb(conn->rx_buff);
1869 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870}
1871
1872/**
1873 * Release everything of a net device.
1874 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001875static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001877 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878
1879 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1880
1881 if (!dev)
1882 return;
1883
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 if (privptr) {
1885 if (privptr->conn)
1886 netiucv_remove_connection(privptr->conn);
1887 if (privptr->fsm)
1888 kfree_fsm(privptr->fsm);
1889 privptr->conn = NULL; privptr->fsm = NULL;
1890 /* privptr gets freed by free_netdev() */
1891 }
1892 free_netdev(dev);
1893}
1894
1895/**
1896 * Initialize a net device. (Called from kernel in alloc_netdev())
1897 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001898static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 dev->mtu = NETIUCV_MTU_DEFAULT;
1901 dev->hard_start_xmit = netiucv_tx;
1902 dev->open = netiucv_open;
1903 dev->stop = netiucv_close;
1904 dev->get_stats = netiucv_stats;
1905 dev->change_mtu = netiucv_change_mtu;
1906 dev->destructor = netiucv_free_netdevice;
1907 dev->hard_header_len = NETIUCV_HDRLEN;
1908 dev->addr_len = 0;
1909 dev->type = ARPHRD_SLIP;
1910 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1911 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
1912 SET_MODULE_OWNER(dev);
1913}
1914
1915/**
1916 * Allocate and initialize everything of a net device.
1917 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001918static struct net_device *netiucv_init_netdevice(char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919{
1920 struct netiucv_priv *privptr;
1921 struct net_device *dev;
1922
1923 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
1924 netiucv_setup_netdevice);
1925 if (!dev)
1926 return NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001927 if (dev_alloc_name(dev, dev->name) < 0)
1928 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001930 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
1932 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
1933 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001934 if (!privptr->fsm)
1935 goto out_netdev;
1936
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 privptr->conn = netiucv_new_connection(dev, username);
1938 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001940 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 }
1942 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001944
1945out_fsm:
1946 kfree_fsm(privptr->fsm);
1947out_netdev:
1948 free_netdev(dev);
1949 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950}
1951
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001952static ssize_t conn_write(struct device_driver *drv,
1953 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001955 const char *p;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001956 char username[9];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001957 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001959 struct netiucv_priv *priv;
1960 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961
1962 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
1963 if (count>9) {
1964 PRINT_WARN("netiucv: username too long (%d)!\n", (int)count);
1965 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1966 return -EINVAL;
1967 }
1968
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001969 for (i = 0, p = buf; i < 8 && *p; i++, p++) {
1970 if (isalnum(*p) || *p == '$') {
1971 username[i] = toupper(*p);
1972 continue;
1973 }
1974 if (*p == '\n')
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 /* trailing lf, grr */
1976 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001977 PRINT_WARN("netiucv: Invalid character in username!\n");
1978 IUCV_DBF_TEXT_(setup, 2,
1979 "conn_write: invalid character %c\n", *p);
1980 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001982 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001984 username[8] = '\0';
1985
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001986 read_lock_bh(&iucv_connection_rwlock);
1987 list_for_each_entry(cp, &iucv_connection_list, list) {
1988 if (!strncmp(username, cp->userid, 9)) {
1989 read_unlock_bh(&iucv_connection_rwlock);
1990 PRINT_WARN("netiucv: Connection to %s already "
1991 "exists\n", username);
1992 return -EEXIST;
1993 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02001994 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001995 read_unlock_bh(&iucv_connection_rwlock);
1996
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 dev = netiucv_init_netdevice(username);
1998 if (!dev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001999 PRINT_WARN("netiucv: Could not allocate network device "
2000 "structure for user '%s'\n",
2001 netiucv_printname(username));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2003 return -ENODEV;
2004 }
2005
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002006 rc = netiucv_register_device(dev);
2007 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002009 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 goto out_free_ndev;
2011 }
2012
2013 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002014 priv = netdev_priv(dev);
2015 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002017 rc = register_netdev(dev);
2018 if (rc)
2019 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020
2021 PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002022
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 return count;
2024
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002025out_unreg:
2026 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027out_free_ndev:
2028 PRINT_WARN("netiucv: Could not register '%s'\n", dev->name);
2029 IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n");
2030 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002031 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032}
2033
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002034static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002036static ssize_t remove_write (struct device_driver *drv,
2037 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002039 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 struct net_device *ndev;
2041 struct netiucv_priv *priv;
2042 struct device *dev;
2043 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002044 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045 int i;
2046
2047 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
2048
2049 if (count >= IFNAMSIZ)
Frank Pavlic16a83b32006-09-15 16:25:19 +02002050 count = IFNAMSIZ - 1;;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002052 for (i = 0, p = buf; i < count && *p; i++, p++) {
2053 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 /* trailing lf, grr */
2055 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002056 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057 }
2058 name[i] = '\0';
2059
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002060 read_lock_bh(&iucv_connection_rwlock);
2061 list_for_each_entry(cp, &iucv_connection_list, list) {
2062 ndev = cp->netdev;
2063 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002065 if (strncmp(name, ndev->name, count))
2066 continue;
2067 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002069 PRINT_WARN("netiucv: net device %s active with peer "
2070 "%s\n", ndev->name, priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 PRINT_WARN("netiucv: %s cannot be removed\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002072 ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
2074 return -EBUSY;
2075 }
2076 unregister_netdev(ndev);
2077 netiucv_unregister_device(dev);
2078 return count;
2079 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002080 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 PRINT_WARN("netiucv: net device %s unknown\n", name);
2082 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2083 return -EINVAL;
2084}
2085
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002086static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002088static struct attribute * netiucv_drv_attrs[] = {
2089 &driver_attr_connection.attr,
2090 &driver_attr_remove.attr,
2091 NULL,
2092};
2093
2094static struct attribute_group netiucv_drv_attr_group = {
2095 .attrs = netiucv_drv_attrs,
2096};
2097
2098static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099{
Heiko Carstense018ba12006-02-01 03:06:31 -08002100 PRINT_INFO("NETIUCV driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101}
2102
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002103static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002105 struct iucv_connection *cp;
2106 struct net_device *ndev;
2107 struct netiucv_priv *priv;
2108 struct device *dev;
2109
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002111 while (!list_empty(&iucv_connection_list)) {
2112 cp = list_entry(iucv_connection_list.next,
2113 struct iucv_connection, list);
2114 list_del(&cp->list);
2115 ndev = cp->netdev;
2116 priv = netdev_priv(ndev);
2117 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118
2119 unregister_netdev(ndev);
2120 netiucv_unregister_device(dev);
2121 }
2122
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002123 sysfs_remove_group(&netiucv_driver.kobj, &netiucv_drv_attr_group);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002125 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 iucv_unregister_dbf_views();
2127
2128 PRINT_INFO("NETIUCV driver unloaded\n");
2129 return;
2130}
2131
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002132static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002134 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002135
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002136 rc = iucv_register_dbf_views();
2137 if (rc)
2138 goto out;
2139 rc = iucv_register(&netiucv_handler, 1);
2140 if (rc)
2141 goto out_dbf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002143 rc = driver_register(&netiucv_driver);
2144 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 PRINT_ERR("NETIUCV: failed to register driver.\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002146 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2147 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148 }
2149
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002150 rc = sysfs_create_group(&netiucv_driver.kobj, &netiucv_drv_attr_group);
2151 if (rc) {
2152 PRINT_ERR("NETIUCV: failed to add driver attributes.\n");
2153 IUCV_DBF_TEXT_(setup, 2,
2154 "ret %d - netiucv_drv_attr_group\n", rc);
2155 goto out_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002157 netiucv_banner();
2158 return rc;
2159
2160out_driver:
2161 driver_unregister(&netiucv_driver);
2162out_iucv:
2163 iucv_unregister(&netiucv_handler, 1);
2164out_dbf:
2165 iucv_unregister_dbf_views();
2166out:
2167 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002169
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170module_init(netiucv_init);
2171module_exit(netiucv_exit);
2172MODULE_LICENSE("GPL");