blob: ce16d1bdb20a2fb24b3cc557a62c7aba25a8ca58 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * IUCV network driver
3 *
Ursula Braun1175b252009-06-16 10:30:43 +02004 * Copyright IBM Corp. 2001, 2009
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 *
Ursula Braun1175b252009-06-16 10:30:43 +02006 * Author(s):
7 * Original netiucv driver:
8 * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
9 * Sysfs integration and all bugs therein:
10 * Cornelia Huck (cornelia.huck@de.ibm.com)
11 * PM functions:
12 * Ursula Braun (ursula.braun@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -070013 *
14 * Documentation used:
15 * the source of the original IUCV driver by:
16 * Stefan Hegewald <hegewald@de.ibm.com>
17 * Hartmut Penner <hpenner@de.ibm.com>
18 * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
19 * Martin Schwidefsky (schwidefsky@de.ibm.com)
20 * Alan Altmark (Alan_Altmark@us.ibm.com) Sept. 2000
21 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2, or (at your option)
25 * any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 */
Jeff Garzike82b0f22006-05-26 21:58:38 -040037
Ursula Braun8f7c5022008-12-25 13:39:47 +010038#define KMSG_COMPONENT "netiucv"
39#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
40
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#undef DEBUG
42
43#include <linux/module.h>
44#include <linux/init.h>
45#include <linux/kernel.h>
46#include <linux/slab.h>
47#include <linux/errno.h>
48#include <linux/types.h>
49#include <linux/interrupt.h>
50#include <linux/timer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#include <linux/bitops.h>
52
53#include <linux/signal.h>
54#include <linux/string.h>
55#include <linux/device.h>
56
57#include <linux/ip.h>
58#include <linux/if_arp.h>
59#include <linux/tcp.h>
60#include <linux/skbuff.h>
61#include <linux/ctype.h>
62#include <net/dst.h>
63
64#include <asm/io.h>
65#include <asm/uaccess.h>
Ursula Braun08e33562011-12-19 22:56:34 +000066#include <asm/ebcdic.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
Martin Schwidefskyeebce382007-02-08 13:50:33 -080068#include <net/iucv/iucv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070069#include "fsm.h"
70
71MODULE_AUTHOR
72 ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)");
73MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
74
Martin Schwidefskyeebce382007-02-08 13:50:33 -080075/**
76 * Debug Facility stuff
77 */
78#define IUCV_DBF_SETUP_NAME "iucv_setup"
Ursula Braun08e33562011-12-19 22:56:34 +000079#define IUCV_DBF_SETUP_LEN 64
Martin Schwidefskyeebce382007-02-08 13:50:33 -080080#define IUCV_DBF_SETUP_PAGES 2
81#define IUCV_DBF_SETUP_NR_AREAS 1
82#define IUCV_DBF_SETUP_LEVEL 3
83
84#define IUCV_DBF_DATA_NAME "iucv_data"
85#define IUCV_DBF_DATA_LEN 128
86#define IUCV_DBF_DATA_PAGES 2
87#define IUCV_DBF_DATA_NR_AREAS 1
88#define IUCV_DBF_DATA_LEVEL 2
89
90#define IUCV_DBF_TRACE_NAME "iucv_trace"
91#define IUCV_DBF_TRACE_LEN 16
92#define IUCV_DBF_TRACE_PAGES 4
93#define IUCV_DBF_TRACE_NR_AREAS 1
94#define IUCV_DBF_TRACE_LEVEL 3
95
96#define IUCV_DBF_TEXT(name,level,text) \
97 do { \
98 debug_text_event(iucv_dbf_##name,level,text); \
99 } while (0)
100
101#define IUCV_DBF_HEX(name,level,addr,len) \
102 do { \
103 debug_event(iucv_dbf_##name,level,(void*)(addr),len); \
104 } while (0)
105
106DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf);
107
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100108#define IUCV_DBF_TEXT_(name, level, text...) \
109 do { \
Hendrik Brueckner8e6a8282013-09-18 17:21:34 +0200110 if (debug_level_enabled(iucv_dbf_##name, level)) { \
Tejun Heo390dfd92009-10-29 22:34:14 +0900111 char* __buf = get_cpu_var(iucv_dbf_txt_buf); \
112 sprintf(__buf, text); \
113 debug_text_event(iucv_dbf_##name, level, __buf); \
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100114 put_cpu_var(iucv_dbf_txt_buf); \
115 } \
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800116 } while (0)
117
118#define IUCV_DBF_SPRINTF(name,level,text...) \
119 do { \
120 debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
121 debug_sprintf_event(iucv_dbf_trace, level, text ); \
122 } while (0)
123
124/**
125 * some more debug stuff
126 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127#define PRINTK_HEADER " iucv: " /* for debugging */
128
Ursula Braun1175b252009-06-16 10:30:43 +0200129/* dummy device to make sure netiucv_pm functions are called */
130static struct device *netiucv_dev;
131
132static int netiucv_pm_prepare(struct device *);
133static void netiucv_pm_complete(struct device *);
134static int netiucv_pm_freeze(struct device *);
135static int netiucv_pm_restore_thaw(struct device *);
136
Alexey Dobriyan47145212009-12-14 18:00:08 -0800137static const struct dev_pm_ops netiucv_pm_ops = {
Ursula Braun1175b252009-06-16 10:30:43 +0200138 .prepare = netiucv_pm_prepare,
139 .complete = netiucv_pm_complete,
140 .freeze = netiucv_pm_freeze,
141 .thaw = netiucv_pm_restore_thaw,
142 .restore = netiucv_pm_restore_thaw,
143};
144
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145static struct device_driver netiucv_driver = {
Cornelia Huck22195102008-02-08 13:09:02 +0100146 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 .name = "netiucv",
148 .bus = &iucv_bus,
Ursula Braun1175b252009-06-16 10:30:43 +0200149 .pm = &netiucv_pm_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150};
151
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800152static int netiucv_callback_connreq(struct iucv_path *,
153 u8 ipvmid[8], u8 ipuser[16]);
154static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
155static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
156static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
157static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
158static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
159static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
160
161static struct iucv_handler netiucv_handler = {
162 .path_pending = netiucv_callback_connreq,
163 .path_complete = netiucv_callback_connack,
164 .path_severed = netiucv_callback_connrej,
165 .path_quiesced = netiucv_callback_connsusp,
166 .path_resumed = netiucv_callback_connres,
167 .message_pending = netiucv_callback_rx,
168 .message_complete = netiucv_callback_txdone
169};
170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171/**
172 * Per connection profiling data
173 */
174struct connection_profile {
175 unsigned long maxmulti;
176 unsigned long maxcqueue;
177 unsigned long doios_single;
178 unsigned long doios_multi;
179 unsigned long txlen;
180 unsigned long tx_time;
181 struct timespec send_stamp;
182 unsigned long tx_pending;
183 unsigned long tx_max_pending;
184};
185
186/**
187 * Representation of one iucv connection
188 */
189struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800190 struct list_head list;
191 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 struct sk_buff *rx_buff;
193 struct sk_buff *tx_buff;
194 struct sk_buff_head collect_queue;
195 struct sk_buff_head commit_queue;
196 spinlock_t collect_lock;
197 int collect_len;
198 int max_buffsize;
199 fsm_timer timer;
200 fsm_instance *fsm;
201 struct net_device *netdev;
202 struct connection_profile prof;
203 char userid[9];
Ursula Braun08e33562011-12-19 22:56:34 +0000204 char userdata[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205};
206
207/**
208 * Linked list of all connection structs.
209 */
Denis Chengc11ca972008-01-26 14:11:13 +0100210static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200211static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
213/**
214 * Representation of event-data for the
215 * connection state machine.
216 */
217struct iucv_event {
218 struct iucv_connection *conn;
219 void *data;
220};
221
222/**
223 * Private part of the network device structure
224 */
225struct netiucv_priv {
226 struct net_device_stats stats;
227 unsigned long tbusy;
228 fsm_instance *fsm;
229 struct iucv_connection *conn;
230 struct device *dev;
Ursula Braun1175b252009-06-16 10:30:43 +0200231 int pm_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232};
233
234/**
235 * Link level header for a packet.
236 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800237struct ll_header {
238 u16 next;
239};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800241#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Ursula Braun08e33562011-12-19 22:56:34 +0000242#define NETIUCV_BUFSIZE_MAX 65537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
244#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
245#define NETIUCV_MTU_DEFAULT 9216
246#define NETIUCV_QUEUELEN_DEFAULT 50
247#define NETIUCV_TIMEOUT_5SEC 5000
248
249/**
250 * Compatibility macros for busy handling
251 * of network devices.
252 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800253static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800255 struct netiucv_priv *priv = netdev_priv(dev);
256 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 netif_wake_queue(dev);
258}
259
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800260static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800262 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800264 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265}
266
Ursula Braun08e33562011-12-19 22:56:34 +0000267static u8 iucvMagic_ascii[16] = {
268 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
269 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
270};
271
272static u8 iucvMagic_ebcdic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
274 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
275};
276
277/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 * Convert an iucv userId to its printable
279 * form (strip whitespace at end).
280 *
281 * @param An iucv userId
282 *
283 * @returns The printable string (static data!!)
284 */
Ursula Braun08e33562011-12-19 22:56:34 +0000285static char *netiucv_printname(char *name, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
Ursula Braun08e33562011-12-19 22:56:34 +0000287 static char tmp[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 char *p = tmp;
Ursula Braun08e33562011-12-19 22:56:34 +0000289 memcpy(tmp, name, len);
290 tmp[len] = '\0';
291 while (*p && ((p - tmp) < len) && (!isspace(*p)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 p++;
293 *p = '\0';
294 return tmp;
295}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400296
Ursula Braun08e33562011-12-19 22:56:34 +0000297static char *netiucv_printuser(struct iucv_connection *conn)
298{
299 static char tmp_uid[9];
300 static char tmp_udat[17];
301 static char buf[100];
302
303 if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) {
304 tmp_uid[8] = '\0';
305 tmp_udat[16] = '\0';
306 memcpy(tmp_uid, conn->userid, 8);
307 memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8);
308 memcpy(tmp_udat, conn->userdata, 16);
309 EBCASC(tmp_udat, 16);
310 memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16);
311 sprintf(buf, "%s.%s", tmp_uid, tmp_udat);
312 return buf;
313 } else
314 return netiucv_printname(conn->userid, 8);
315}
316
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317/**
318 * States of the interface statemachine.
319 */
320enum dev_states {
321 DEV_STATE_STOPPED,
322 DEV_STATE_STARTWAIT,
323 DEV_STATE_STOPWAIT,
324 DEV_STATE_RUNNING,
325 /**
326 * MUST be always the last element!!
327 */
328 NR_DEV_STATES
329};
330
331static const char *dev_state_names[] = {
332 "Stopped",
333 "StartWait",
334 "StopWait",
335 "Running",
336};
337
338/**
339 * Events of the interface statemachine.
340 */
341enum dev_events {
342 DEV_EVENT_START,
343 DEV_EVENT_STOP,
344 DEV_EVENT_CONUP,
345 DEV_EVENT_CONDOWN,
346 /**
347 * MUST be always the last element!!
348 */
349 NR_DEV_EVENTS
350};
351
352static const char *dev_event_names[] = {
353 "Start",
354 "Stop",
355 "Connection up",
356 "Connection down",
357};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359/**
360 * Events of the connection statemachine
361 */
362enum conn_events {
363 /**
364 * Events, representing callbacks from
365 * lowlevel iucv layer)
366 */
367 CONN_EVENT_CONN_REQ,
368 CONN_EVENT_CONN_ACK,
369 CONN_EVENT_CONN_REJ,
370 CONN_EVENT_CONN_SUS,
371 CONN_EVENT_CONN_RES,
372 CONN_EVENT_RX,
373 CONN_EVENT_TXDONE,
374
375 /**
376 * Events, representing errors return codes from
377 * calls to lowlevel iucv layer
378 */
379
380 /**
381 * Event, representing timer expiry.
382 */
383 CONN_EVENT_TIMER,
384
385 /**
386 * Events, representing commands from upper levels.
387 */
388 CONN_EVENT_START,
389 CONN_EVENT_STOP,
390
391 /**
392 * MUST be always the last element!!
393 */
394 NR_CONN_EVENTS,
395};
396
397static const char *conn_event_names[] = {
398 "Remote connection request",
399 "Remote connection acknowledge",
400 "Remote connection reject",
401 "Connection suspended",
402 "Connection resumed",
403 "Data received",
404 "Data sent",
405
406 "Timer",
407
408 "Start",
409 "Stop",
410};
411
412/**
413 * States of the connection statemachine.
414 */
415enum conn_states {
416 /**
417 * Connection not assigned to any device,
418 * initial state, invalid
419 */
420 CONN_STATE_INVALID,
421
422 /**
423 * Userid assigned but not operating
424 */
425 CONN_STATE_STOPPED,
426
427 /**
428 * Connection registered,
429 * no connection request sent yet,
430 * no connection request received
431 */
432 CONN_STATE_STARTWAIT,
433
434 /**
435 * Connection registered and connection request sent,
436 * no acknowledge and no connection request received yet.
437 */
438 CONN_STATE_SETUPWAIT,
439
440 /**
441 * Connection up and running idle
442 */
443 CONN_STATE_IDLE,
444
445 /**
446 * Data sent, awaiting CONN_EVENT_TXDONE
447 */
448 CONN_STATE_TX,
449
450 /**
451 * Error during registration.
452 */
453 CONN_STATE_REGERR,
454
455 /**
456 * Error during registration.
457 */
458 CONN_STATE_CONNERR,
459
460 /**
461 * MUST be always the last element!!
462 */
463 NR_CONN_STATES,
464};
465
466static const char *conn_state_names[] = {
467 "Invalid",
468 "Stopped",
469 "StartWait",
470 "SetupWait",
471 "Idle",
472 "TX",
473 "Terminating",
474 "Registration error",
475 "Connect error",
476};
477
Jeff Garzike82b0f22006-05-26 21:58:38 -0400478
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479/**
480 * Debug Facility Stuff
481 */
482static debug_info_t *iucv_dbf_setup = NULL;
483static debug_info_t *iucv_dbf_data = NULL;
484static debug_info_t *iucv_dbf_trace = NULL;
485
486DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
487
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800488static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489{
490 if (iucv_dbf_setup)
491 debug_unregister(iucv_dbf_setup);
492 if (iucv_dbf_data)
493 debug_unregister(iucv_dbf_data);
494 if (iucv_dbf_trace)
495 debug_unregister(iucv_dbf_trace);
496}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800497static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498{
499 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700500 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 IUCV_DBF_SETUP_NR_AREAS,
502 IUCV_DBF_SETUP_LEN);
503 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700504 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 IUCV_DBF_DATA_NR_AREAS,
506 IUCV_DBF_DATA_LEN);
507 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700508 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 IUCV_DBF_TRACE_NR_AREAS,
510 IUCV_DBF_TRACE_LEN);
511
512 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
513 (iucv_dbf_trace == NULL)) {
514 iucv_unregister_dbf_views();
515 return -ENOMEM;
516 }
517 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
518 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
519
520 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
521 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
522
523 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
524 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
525
526 return 0;
527}
528
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800529/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800531 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800533static void netiucv_callback_rx(struct iucv_path *path,
534 struct iucv_message *msg)
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 struct iucv_event ev;
538
539 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800540 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
542}
543
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800544static void netiucv_callback_txdone(struct iucv_path *path,
545 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800547 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 struct iucv_event ev;
549
550 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800551 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
553}
554
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800555static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800557 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800559 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560}
561
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800562static int netiucv_callback_connreq(struct iucv_path *path,
563 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800565 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 struct iucv_event ev;
Ursula Braun08e33562011-12-19 22:56:34 +0000567 static char tmp_user[9];
568 static char tmp_udat[17];
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800569 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800571 rc = -EINVAL;
Ursula Braun08e33562011-12-19 22:56:34 +0000572 memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8);
573 memcpy(tmp_udat, ipuser, 16);
574 EBCASC(tmp_udat, 16);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800575 read_lock_bh(&iucv_connection_rwlock);
576 list_for_each_entry(conn, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +0000577 if (strncmp(ipvmid, conn->userid, 8) ||
578 strncmp(ipuser, conn->userdata, 16))
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800579 continue;
580 /* Found a matching connection for this path. */
581 conn->path = path;
582 ev.conn = conn;
583 ev.data = path;
584 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
585 rc = 0;
586 }
Ursula Braun08e33562011-12-19 22:56:34 +0000587 IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n",
588 tmp_user, netiucv_printname(tmp_udat, 16));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800589 read_unlock_bh(&iucv_connection_rwlock);
590 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591}
592
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800593static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800595 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800597 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598}
599
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800600static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800602 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800604 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605}
606
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800607static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800609 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800611 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612}
613
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100615 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100617static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618{
619}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400620
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800621/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800623 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800626 * netiucv_unpack_skb
627 * @conn: The connection where this skb has been received.
628 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800630 * Unpack a just received skb and hand it over to upper layers.
631 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800633static void netiucv_unpack_skb(struct iucv_connection *conn,
634 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635{
636 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800637 struct netiucv_priv *privptr = netdev_priv(dev);
638 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
640 skb_put(pskb, NETIUCV_HDRLEN);
641 pskb->dev = dev;
642 pskb->ip_summed = CHECKSUM_NONE;
643 pskb->protocol = ntohs(ETH_P_IP);
644
645 while (1) {
646 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800647 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
649 if (!header->next)
650 break;
651
652 skb_pull(pskb, NETIUCV_HDRLEN);
653 header->next -= offset;
654 offset += header->next;
655 header->next -= NETIUCV_HDRLEN;
656 if (skb_tailroom(pskb) < header->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
658 header->next, skb_tailroom(pskb));
659 return;
660 }
661 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700662 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 skb = dev_alloc_skb(pskb->len);
664 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 IUCV_DBF_TEXT(data, 2,
666 "Out of memory in netiucv_unpack_skb\n");
667 privptr->stats.rx_dropped++;
668 return;
669 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300670 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
671 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700672 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 skb->dev = pskb->dev;
674 skb->protocol = pskb->protocol;
675 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800676 privptr->stats.rx_packets++;
677 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 /*
679 * Since receiving is always initiated from a tasklet (in iucv.c),
680 * we must use netif_rx_ni() instead of netif_rx()
681 */
682 netif_rx_ni(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 skb_pull(pskb, header->next);
684 skb_put(pskb, NETIUCV_HDRLEN);
685 }
686}
687
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800688static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800690 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800692 struct iucv_message *msg = ev->data;
693 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 int rc;
695
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200696 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
698 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800699 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800701 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 return;
703 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800704 if (msg->length > conn->max_buffsize) {
705 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 privptr->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800708 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 return;
710 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700711 conn->rx_buff->data = conn->rx_buff->head;
712 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800714 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
715 msg->length, NULL);
716 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 privptr->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
719 return;
720 }
721 netiucv_unpack_skb(conn, conn->rx_buff);
722}
723
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800724static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800726 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800728 struct iucv_message *msg = ev->data;
729 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800731 u32 single_flag = msg->tag;
732 u32 txbytes = 0;
733 u32 txpackets = 0;
734 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 struct sk_buff *skb;
736 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800737 struct ll_header header;
738 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200740 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Ursula Braund239ae32013-12-16 09:44:51 +0100742 if (!conn || !conn->netdev) {
743 IUCV_DBF_TEXT(data, 2,
744 "Send confirmation for unlinked connection\n");
745 return;
746 }
747 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 conn->prof.tx_pending--;
749 if (single_flag) {
750 if ((skb = skb_dequeue(&conn->commit_queue))) {
751 atomic_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 if (privptr) {
753 privptr->stats.tx_packets++;
754 privptr->stats.tx_bytes +=
755 (skb->len - NETIUCV_HDRLEN
Ursula Braun998221c2009-11-12 21:46:30 +0000756 - NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 }
Ursula Braun998221c2009-11-12 21:46:30 +0000758 dev_kfree_skb_any(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 }
760 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700761 conn->tx_buff->data = conn->tx_buff->head;
762 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 conn->tx_buff->len = 0;
764 spin_lock_irqsave(&conn->collect_lock, saveflags);
765 while ((skb = skb_dequeue(&conn->collect_queue))) {
766 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
767 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
768 NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300769 skb_copy_from_linear_data(skb,
770 skb_put(conn->tx_buff, skb->len),
771 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 txbytes += skb->len;
773 txpackets++;
774 stat_maxcq++;
775 atomic_dec(&skb->users);
776 dev_kfree_skb_any(skb);
777 }
778 if (conn->collect_len > conn->prof.maxmulti)
779 conn->prof.maxmulti = conn->collect_len;
780 conn->collect_len = 0;
781 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800782 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800784 return;
785 }
786
787 header.next = 0;
788 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
john stultz2c6b47d2007-07-24 17:47:43 -0700789 conn->prof.send_stamp = current_kernel_time();
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800790 txmsg.class = 0;
791 txmsg.tag = 0;
792 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
793 conn->tx_buff->data, conn->tx_buff->len);
794 conn->prof.doios_multi++;
795 conn->prof.txlen += conn->tx_buff->len;
796 conn->prof.tx_pending++;
797 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
798 conn->prof.tx_max_pending = conn->prof.tx_pending;
799 if (rc) {
800 conn->prof.tx_pending--;
801 fsm_newstate(fi, CONN_STATE_IDLE);
802 if (privptr)
803 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800804 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
805 } else {
806 if (privptr) {
807 privptr->stats.tx_packets += txpackets;
808 privptr->stats.tx_bytes += txbytes;
809 }
810 if (stat_maxcq > conn->prof.maxcqueue)
811 conn->prof.maxcqueue = stat_maxcq;
812 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813}
814
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800815static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800817 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800819 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800821 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200824 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800826 conn->path = path;
827 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
828 path->flags = 0;
Ursula Braun08e33562011-12-19 22:56:34 +0000829 rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
832 return;
833 }
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_connreject(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_event *ev = arg;
842 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200844 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800845 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846}
847
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800848static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800850 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800852 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200854 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 fsm_deltimer(&conn->timer);
856 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800857 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
859}
860
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800861static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800863 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200865 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000867 iucv_path_sever(conn->path, conn->userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 fsm_newstate(fi, CONN_STATE_STARTWAIT);
869}
870
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800871static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800873 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800875 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200877 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
879 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000880 iucv_path_sever(conn->path, conn->userdata);
881 dev_info(privptr->dev, "The peer z/VM guest %s has closed the "
882 "connection\n", netiucv_printuser(conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800884 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 fsm_newstate(fi, CONN_STATE_STARTWAIT);
886 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
887}
888
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800889static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800891 struct iucv_connection *conn = arg;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100892 struct net_device *netdev = conn->netdev;
893 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 int rc;
895
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200896 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800898 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800900 /*
901 * We must set the state before calling iucv_connect because the
902 * callback handler could be called at any point after the connection
903 * request is sent
904 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905
906 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800907 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
Ursula Braun08e33562011-12-19 22:56:34 +0000908 IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n",
909 netdev->name, netiucv_printuser(conn));
910
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800911 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
Ursula Braun08e33562011-12-19 22:56:34 +0000912 NULL, conn->userdata, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800914 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100915 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800916 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
917 CONN_EVENT_TIMER, conn);
918 return;
919 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100920 dev_warn(privptr->dev,
921 "The IUCV device failed to connect to z/VM guest %s\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000922 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800923 fsm_newstate(fi, CONN_STATE_STARTWAIT);
924 break;
925 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100926 dev_warn(privptr->dev,
927 "The IUCV device failed to connect to the peer on z/VM"
Ursula Braun08e33562011-12-19 22:56:34 +0000928 " guest %s\n", netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800929 fsm_newstate(fi, CONN_STATE_STARTWAIT);
930 break;
931 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100932 dev_err(privptr->dev,
933 "Connecting the IUCV device would exceed the maximum"
934 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800935 fsm_newstate(fi, CONN_STATE_CONNERR);
936 break;
937 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100938 dev_err(privptr->dev,
939 "z/VM guest %s has too many IUCV connections"
940 " to connect with the IUCV device\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000941 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800942 fsm_newstate(fi, CONN_STATE_CONNERR);
943 break;
944 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100945 dev_err(privptr->dev,
946 "The IUCV device cannot connect to a z/VM guest with no"
947 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800948 fsm_newstate(fi, CONN_STATE_CONNERR);
949 break;
950 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100951 dev_err(privptr->dev,
952 "Connecting the IUCV device failed with error %d\n",
953 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800954 fsm_newstate(fi, CONN_STATE_CONNERR);
955 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 }
957 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800958 kfree(conn->path);
959 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960}
961
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800962static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963{
964 struct sk_buff *skb;
965
966 while ((skb = skb_dequeue(q))) {
967 atomic_dec(&skb->users);
968 dev_kfree_skb_any(skb);
969 }
970}
971
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800972static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800974 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 struct iucv_connection *conn = ev->conn;
976 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800977 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200979 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980
981 fsm_deltimer(&conn->timer);
982 fsm_newstate(fi, CONN_STATE_STOPPED);
983 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800984 if (conn->path) {
985 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
Ursula Braun08e33562011-12-19 22:56:34 +0000986 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800987 kfree(conn->path);
988 conn->path = NULL;
989 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 netiucv_purge_skb_queue(&conn->commit_queue);
991 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
992}
993
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800994static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800996 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 struct net_device *netdev = conn->netdev;
998
Ursula Braunf082bca2008-07-14 09:59:30 +0200999 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
1000 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001}
1002
1003static const fsm_node conn_fsm[] = {
1004 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
1005 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
1006
1007 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
1008 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
1009 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
1010 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
1011 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
1012 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
1013 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
1014
1015 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
1016 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1017 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1018 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
1019 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
1020
1021 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
1022 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
1023
1024 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
1025 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
1026 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
1027
1028 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
1029 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1030
1031 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1032 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1033};
1034
1035static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1036
Jeff Garzike82b0f22006-05-26 21:58:38 -04001037
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001038/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001040 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041
1042/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001043 * dev_action_start
1044 * @fi: An instance of an interface statemachine.
1045 * @event: The event, just happened.
1046 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001048 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001050static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001052 struct net_device *dev = arg;
1053 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001055 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001058 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059}
1060
1061/**
1062 * Shutdown connection by sending CONN_EVENT_STOP to it.
1063 *
1064 * @param fi An instance of an interface statemachine.
1065 * @param event The event, just happened.
1066 * @param arg Generic pointer, casted from struct net_device * upon call.
1067 */
1068static void
1069dev_action_stop(fsm_instance *fi, int event, void *arg)
1070{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001071 struct net_device *dev = arg;
1072 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 struct iucv_event ev;
1074
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001075 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
1077 ev.conn = privptr->conn;
1078
1079 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1080 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1081}
1082
1083/**
1084 * Called from connection statemachine
1085 * when a connection is up and running.
1086 *
1087 * @param fi An instance of an interface statemachine.
1088 * @param event The event, just happened.
1089 * @param arg Generic pointer, casted from struct net_device * upon call.
1090 */
1091static void
1092dev_action_connup(fsm_instance *fi, int event, void *arg)
1093{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001094 struct net_device *dev = arg;
1095 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001097 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098
1099 switch (fsm_getstate(fi)) {
1100 case DEV_STATE_STARTWAIT:
1101 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001102 dev_info(privptr->dev,
1103 "The IUCV device has been connected"
Ursula Braun08e33562011-12-19 22:56:34 +00001104 " successfully to %s\n",
1105 netiucv_printuser(privptr->conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 IUCV_DBF_TEXT(setup, 3,
1107 "connection is up and running\n");
1108 break;
1109 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 IUCV_DBF_TEXT(data, 2,
1111 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1112 break;
1113 }
1114}
1115
1116/**
1117 * Called from connection statemachine
1118 * when a connection has been shutdown.
1119 *
1120 * @param fi An instance of an interface statemachine.
1121 * @param event The event, just happened.
1122 * @param arg Generic pointer, casted from struct net_device * upon call.
1123 */
1124static void
1125dev_action_conndown(fsm_instance *fi, int event, void *arg)
1126{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001127 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128
1129 switch (fsm_getstate(fi)) {
1130 case DEV_STATE_RUNNING:
1131 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1132 break;
1133 case DEV_STATE_STOPWAIT:
1134 fsm_newstate(fi, DEV_STATE_STOPPED);
1135 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1136 break;
1137 }
1138}
1139
1140static const fsm_node dev_fsm[] = {
1141 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1142
1143 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1144 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1145
1146 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1147 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1148
1149 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1150 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001151 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152};
1153
1154static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1155
1156/**
1157 * Transmit a packet.
1158 * This is a helper function for netiucv_tx().
1159 *
1160 * @param conn Connection to be used for sending.
1161 * @param skb Pointer to struct sk_buff of packet to send.
1162 * The linklevel header has already been set up
1163 * by netiucv_tx().
1164 *
1165 * @return 0 on success, -ERRNO on failure. (Never fails.)
1166 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001167static int netiucv_transmit_skb(struct iucv_connection *conn,
1168 struct sk_buff *skb)
1169{
1170 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001172 struct ll_header header;
1173 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174
1175 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1176 int l = skb->len + NETIUCV_HDRLEN;
1177
1178 spin_lock_irqsave(&conn->collect_lock, saveflags);
1179 if (conn->collect_len + l >
1180 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1181 rc = -EBUSY;
1182 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001183 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 } else {
1185 atomic_inc(&skb->users);
1186 skb_queue_tail(&conn->collect_queue, skb);
1187 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001188 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 }
1190 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1191 } else {
1192 struct sk_buff *nskb = skb;
1193 /**
1194 * Copy the skb to a new allocated skb in lowmem only if the
1195 * data is located above 2G in memory or tailroom is < 2.
1196 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001197 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1198 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 int copied = 0;
1200 if (hi || (skb_tailroom(skb) < 2)) {
1201 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1202 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1203 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1205 rc = -ENOMEM;
1206 return rc;
1207 } else {
1208 skb_reserve(nskb, NETIUCV_HDRLEN);
1209 memcpy(skb_put(nskb, skb->len),
1210 skb->data, skb->len);
1211 }
1212 copied = 1;
1213 }
1214 /**
1215 * skb now is below 2G and has enough room. Add headers.
1216 */
1217 header.next = nskb->len + NETIUCV_HDRLEN;
1218 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1219 header.next = 0;
1220 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1221
1222 fsm_newstate(conn->fsm, CONN_STATE_TX);
john stultz2c6b47d2007-07-24 17:47:43 -07001223 conn->prof.send_stamp = current_kernel_time();
Jeff Garzike82b0f22006-05-26 21:58:38 -04001224
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001225 msg.tag = 1;
1226 msg.class = 0;
1227 rc = iucv_message_send(conn->path, &msg, 0, 0,
1228 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 conn->prof.doios_single++;
1230 conn->prof.txlen += skb->len;
1231 conn->prof.tx_pending++;
1232 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1233 conn->prof.tx_max_pending = conn->prof.tx_pending;
1234 if (rc) {
1235 struct netiucv_priv *privptr;
1236 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1237 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001238 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 if (privptr)
1240 privptr->stats.tx_errors++;
1241 if (copied)
1242 dev_kfree_skb(nskb);
1243 else {
1244 /**
1245 * Remove our headers. They get added
1246 * again on retransmit.
1247 */
1248 skb_pull(skb, NETIUCV_HDRLEN);
1249 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1250 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1252 } else {
1253 if (copied)
1254 dev_kfree_skb(skb);
1255 atomic_inc(&nskb->users);
1256 skb_queue_tail(&conn->commit_queue, nskb);
1257 }
1258 }
1259
1260 return rc;
1261}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001262
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001263/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001265 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266
1267/**
1268 * Open an interface.
1269 * Called from generic network layer when ifconfig up is run.
1270 *
1271 * @param dev Pointer to interface struct.
1272 *
1273 * @return 0 on success, -ERRNO on failure. (Never fails.)
1274 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001275static int netiucv_open(struct net_device *dev)
1276{
1277 struct netiucv_priv *priv = netdev_priv(dev);
1278
1279 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 return 0;
1281}
1282
1283/**
1284 * Close an interface.
1285 * Called from generic network layer when ifconfig down is run.
1286 *
1287 * @param dev Pointer to interface struct.
1288 *
1289 * @return 0 on success, -ERRNO on failure. (Never fails.)
1290 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001291static int netiucv_close(struct net_device *dev)
1292{
1293 struct netiucv_priv *priv = netdev_priv(dev);
1294
1295 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 return 0;
1297}
1298
Ursula Braun1175b252009-06-16 10:30:43 +02001299static int netiucv_pm_prepare(struct device *dev)
1300{
1301 IUCV_DBF_TEXT(trace, 3, __func__);
1302 return 0;
1303}
1304
1305static void netiucv_pm_complete(struct device *dev)
1306{
1307 IUCV_DBF_TEXT(trace, 3, __func__);
1308 return;
1309}
1310
1311/**
1312 * netiucv_pm_freeze() - Freeze PM callback
1313 * @dev: netiucv device
1314 *
1315 * close open netiucv interfaces
1316 */
1317static int netiucv_pm_freeze(struct device *dev)
1318{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001319 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001320 struct net_device *ndev = NULL;
1321 int rc = 0;
1322
1323 IUCV_DBF_TEXT(trace, 3, __func__);
1324 if (priv && priv->conn)
1325 ndev = priv->conn->netdev;
1326 if (!ndev)
1327 goto out;
1328 netif_device_detach(ndev);
1329 priv->pm_state = fsm_getstate(priv->fsm);
1330 rc = netiucv_close(ndev);
1331out:
1332 return rc;
1333}
1334
1335/**
1336 * netiucv_pm_restore_thaw() - Thaw and restore PM callback
1337 * @dev: netiucv device
1338 *
1339 * re-open netiucv interfaces closed during freeze
1340 */
1341static int netiucv_pm_restore_thaw(struct device *dev)
1342{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001343 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001344 struct net_device *ndev = NULL;
1345 int rc = 0;
1346
1347 IUCV_DBF_TEXT(trace, 3, __func__);
1348 if (priv && priv->conn)
1349 ndev = priv->conn->netdev;
1350 if (!ndev)
1351 goto out;
1352 switch (priv->pm_state) {
1353 case DEV_STATE_RUNNING:
1354 case DEV_STATE_STARTWAIT:
1355 rc = netiucv_open(ndev);
1356 break;
1357 default:
1358 break;
1359 }
1360 netif_device_attach(ndev);
1361out:
1362 return rc;
1363}
1364
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365/**
1366 * Start transmission of a packet.
1367 * Called from generic network device layer.
1368 *
1369 * @param skb Pointer to buffer containing the packet.
1370 * @param dev Pointer to interface struct.
1371 *
1372 * @return 0 if packet consumed, !0 if packet rejected.
1373 * Note: If we return !0, then the packet is free'd by
1374 * the generic network layer.
1375 */
1376static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1377{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001378 struct netiucv_priv *privptr = netdev_priv(dev);
1379 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001381 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382 /**
1383 * Some sanity checks ...
1384 */
1385 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1387 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001388 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 }
1390 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 IUCV_DBF_TEXT(data, 2,
1392 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1393 dev_kfree_skb(skb);
1394 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001395 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 }
1397
1398 /**
1399 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001400 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 */
1402 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 dev_kfree_skb(skb);
1404 privptr->stats.tx_dropped++;
1405 privptr->stats.tx_errors++;
1406 privptr->stats.tx_carrier_errors++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001407 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 }
1409
1410 if (netiucv_test_and_set_busy(dev)) {
1411 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001412 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 }
1414 dev->trans_start = jiffies;
Patrick McHardy5b548142009-06-12 06:22:29 +00001415 rc = netiucv_transmit_skb(privptr->conn, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 netiucv_clear_busy(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001417 return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418}
1419
1420/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001421 * netiucv_stats
1422 * @dev: Pointer to interface struct.
1423 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 * Returns interface statistics of a device.
1425 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001426 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001428static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001430 struct netiucv_priv *priv = netdev_priv(dev);
1431
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001432 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001433 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434}
1435
1436/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001437 * netiucv_change_mtu
1438 * @dev: Pointer to interface struct.
1439 * @new_mtu: The new MTU to use for this interface.
1440 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 * Sets MTU of an interface.
1442 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001443 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 * (valid range is 576 .. NETIUCV_MTU_MAX).
1445 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001446static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001448 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001449 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1451 return -EINVAL;
1452 }
1453 dev->mtu = new_mtu;
1454 return 0;
1455}
1456
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001457/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001459 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001461static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1462 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001464 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001466 IUCV_DBF_TEXT(trace, 5, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001467 return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));
1468}
1469
1470static int netiucv_check_user(const char *buf, size_t count, char *username,
1471 char *userdata)
1472{
1473 const char *p;
1474 int i;
1475
1476 p = strchr(buf, '.');
1477 if ((p && ((count > 26) ||
1478 ((p - buf) > 8) ||
1479 (buf + count - p > 18))) ||
1480 (!p && (count > 9))) {
1481 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1482 return -EINVAL;
1483 }
1484
1485 for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) {
1486 if (isalnum(*p) || *p == '$') {
1487 username[i] = toupper(*p);
1488 continue;
1489 }
1490 if (*p == '\n')
1491 /* trailing lf, grr */
1492 break;
1493 IUCV_DBF_TEXT_(setup, 2,
1494 "conn_write: invalid character %02x\n", *p);
1495 return -EINVAL;
1496 }
1497 while (i < 8)
1498 username[i++] = ' ';
1499 username[8] = '\0';
1500
1501 if (*p == '.') {
1502 p++;
1503 for (i = 0; i < 16 && *p; i++, p++) {
1504 if (*p == '\n')
1505 break;
1506 userdata[i] = toupper(*p);
1507 }
1508 while (i > 0 && i < 16)
1509 userdata[i++] = ' ';
1510 } else
1511 memcpy(userdata, iucvMagic_ascii, 16);
1512 userdata[16] = '\0';
1513 ASCEBC(userdata, 16);
1514
1515 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516}
1517
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001518static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1519 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001521 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 struct net_device *ndev = priv->conn->netdev;
Ursula Braun08e33562011-12-19 22:56:34 +00001523 char username[9];
1524 char userdata[17];
1525 int rc;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001526 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001528 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001529 rc = netiucv_check_user(buf, count, username, userdata);
1530 if (rc)
1531 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001533 if (memcmp(username, priv->conn->userid, 9) &&
1534 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1535 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001536 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001537 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001538 }
1539 read_lock_bh(&iucv_connection_rwlock);
1540 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00001541 if (!strncmp(username, cp->userid, 9) &&
1542 !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001543 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00001544 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s "
1545 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001546 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 }
1548 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001549 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 memcpy(priv->conn->userid, username, 9);
Ursula Braun08e33562011-12-19 22:56:34 +00001551 memcpy(priv->conn->userdata, userdata, 17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553}
1554
1555static DEVICE_ATTR(user, 0644, user_show, user_write);
1556
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001557static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1558 char *buf)
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001559{
1560 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001562 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1564}
1565
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001566static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1567 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001569 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 struct net_device *ndev = priv->conn->netdev;
1571 char *e;
1572 int bs1;
1573
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001574 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575 if (count >= 39)
1576 return -EINVAL;
1577
1578 bs1 = simple_strtoul(buf, &e, 0);
1579
1580 if (e && (!isspace(*e))) {
Ursula Braun08e33562011-12-19 22:56:34 +00001581 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n",
1582 *e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 return -EINVAL;
1584 }
1585 if (bs1 > NETIUCV_BUFSIZE_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586 IUCV_DBF_TEXT_(setup, 2,
1587 "buffer_write: buffer size %d too large\n",
1588 bs1);
1589 return -EINVAL;
1590 }
1591 if ((ndev->flags & IFF_RUNNING) &&
1592 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 IUCV_DBF_TEXT_(setup, 2,
1594 "buffer_write: buffer size %d too small\n",
1595 bs1);
1596 return -EINVAL;
1597 }
1598 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 IUCV_DBF_TEXT_(setup, 2,
1600 "buffer_write: buffer size %d too small\n",
1601 bs1);
1602 return -EINVAL;
1603 }
1604
1605 priv->conn->max_buffsize = bs1;
1606 if (!(ndev->flags & IFF_RUNNING))
1607 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1608
1609 return count;
1610
1611}
1612
1613static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1614
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001615static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1616 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001618 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001620 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1622}
1623
1624static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1625
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001626static ssize_t conn_fsm_show (struct device *dev,
1627 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001629 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001631 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1633}
1634
1635static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1636
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001637static ssize_t maxmulti_show (struct device *dev,
1638 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001640 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001642 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1644}
1645
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001646static ssize_t maxmulti_write (struct device *dev,
1647 struct device_attribute *attr,
1648 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001650 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001652 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 priv->conn->prof.maxmulti = 0;
1654 return count;
1655}
1656
1657static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1658
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001659static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1660 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001662 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001664 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1666}
1667
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001668static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1669 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001671 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001672
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001673 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 priv->conn->prof.maxcqueue = 0;
1675 return count;
1676}
1677
1678static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1679
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001680static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1681 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001683 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001685 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1687}
1688
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001689static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1690 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001692 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001693
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001694 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 priv->conn->prof.doios_single = 0;
1696 return count;
1697}
1698
1699static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1700
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001701static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1702 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001704 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001706 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1708}
1709
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001710static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1711 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001713 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001714
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001715 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 priv->conn->prof.doios_multi = 0;
1717 return count;
1718}
1719
1720static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1721
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001722static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1723 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001725 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001727 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1729}
1730
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001731static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1732 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001734 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001735
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001736 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 priv->conn->prof.txlen = 0;
1738 return count;
1739}
1740
1741static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1742
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001743static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1744 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001746 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001748 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1750}
1751
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001752static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1753 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001755 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001756
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001757 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758 priv->conn->prof.tx_time = 0;
1759 return count;
1760}
1761
1762static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1763
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001764static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1765 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001767 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001769 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1771}
1772
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001773static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1774 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001776 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001778 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779 priv->conn->prof.tx_pending = 0;
1780 return count;
1781}
1782
1783static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1784
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001785static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1786 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001788 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001790 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1792}
1793
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001794static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1795 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001797 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001799 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 priv->conn->prof.tx_max_pending = 0;
1801 return count;
1802}
1803
1804static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1805
1806static struct attribute *netiucv_attrs[] = {
1807 &dev_attr_buffer.attr,
1808 &dev_attr_user.attr,
1809 NULL,
1810};
1811
1812static struct attribute_group netiucv_attr_group = {
1813 .attrs = netiucv_attrs,
1814};
1815
1816static struct attribute *netiucv_stat_attrs[] = {
1817 &dev_attr_device_fsm_state.attr,
1818 &dev_attr_connection_fsm_state.attr,
1819 &dev_attr_max_tx_buffer_used.attr,
1820 &dev_attr_max_chained_skbs.attr,
1821 &dev_attr_tx_single_write_ops.attr,
1822 &dev_attr_tx_multi_write_ops.attr,
1823 &dev_attr_netto_bytes.attr,
1824 &dev_attr_max_tx_io_time.attr,
1825 &dev_attr_tx_pending.attr,
1826 &dev_attr_tx_max_pending.attr,
1827 NULL,
1828};
1829
1830static struct attribute_group netiucv_stat_attr_group = {
1831 .name = "stats",
1832 .attrs = netiucv_stat_attrs,
1833};
1834
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001835static const struct attribute_group *netiucv_attr_groups[] = {
1836 &netiucv_stat_attr_group,
1837 &netiucv_attr_group,
1838 NULL,
1839};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001841static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001843 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001844 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 int ret;
1846
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001847 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848
1849 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001850 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851 dev->bus = &iucv_bus;
1852 dev->parent = iucv_root;
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001853 dev->groups = netiucv_attr_groups;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854 /*
1855 * The release function could be called after the
1856 * module has been unloaded. It's _only_ task is to
1857 * free the struct. Therefore, we specify kfree()
1858 * directly here. (Probably a little bit obfuscating
1859 * but legitime ...).
1860 */
1861 dev->release = (void (*)(struct device *))kfree;
1862 dev->driver = &netiucv_driver;
1863 } else
1864 return -ENOMEM;
1865
1866 ret = device_register(dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02001867 if (ret) {
1868 put_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869 return ret;
Sebastian Ottc6304932009-09-11 10:28:38 +02001870 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871 priv->dev = dev;
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001872 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874}
1875
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001876static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001878 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 device_unregister(dev);
1880}
1881
1882/**
1883 * Allocate and initialize a new connection structure.
1884 * Add it to the list of netiucv connections;
1885 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001886static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
Ursula Braun08e33562011-12-19 22:56:34 +00001887 char *username,
1888 char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001890 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001892 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1893 if (!conn)
1894 goto out;
1895 skb_queue_head_init(&conn->collect_queue);
1896 skb_queue_head_init(&conn->commit_queue);
1897 spin_lock_init(&conn->collect_lock);
1898 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1899 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001901 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1902 if (!conn->rx_buff)
1903 goto out_conn;
1904 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1905 if (!conn->tx_buff)
1906 goto out_rx;
1907 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1908 conn_event_names, NR_CONN_STATES,
1909 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1910 GFP_KERNEL);
1911 if (!conn->fsm)
1912 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001914 fsm_settimer(conn->fsm, &conn->timer);
1915 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916
Ursula Braun08e33562011-12-19 22:56:34 +00001917 if (userdata)
1918 memcpy(conn->userdata, userdata, 17);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001919 if (username) {
1920 memcpy(conn->userid, username, 9);
1921 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001923
1924 write_lock_bh(&iucv_connection_rwlock);
1925 list_add_tail(&conn->list, &iucv_connection_list);
1926 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001928
1929out_tx:
1930 kfree_skb(conn->tx_buff);
1931out_rx:
1932 kfree_skb(conn->rx_buff);
1933out_conn:
1934 kfree(conn);
1935out:
1936 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937}
1938
1939/**
1940 * Release a connection structure and remove it from the
1941 * list of netiucv connections.
1942 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001943static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944{
Ursula Braun08e33562011-12-19 22:56:34 +00001945
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001946 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001947 write_lock_bh(&iucv_connection_rwlock);
1948 list_del_init(&conn->list);
1949 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001950 fsm_deltimer(&conn->timer);
1951 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001952 if (conn->path) {
Ursula Braun08e33562011-12-19 22:56:34 +00001953 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001954 kfree(conn->path);
1955 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001957 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001958 kfree_fsm(conn->fsm);
1959 kfree_skb(conn->rx_buff);
1960 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961}
1962
1963/**
1964 * Release everything of a net device.
1965 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001966static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001968 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001970 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971
1972 if (!dev)
1973 return;
1974
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 if (privptr) {
1976 if (privptr->conn)
1977 netiucv_remove_connection(privptr->conn);
1978 if (privptr->fsm)
1979 kfree_fsm(privptr->fsm);
1980 privptr->conn = NULL; privptr->fsm = NULL;
1981 /* privptr gets freed by free_netdev() */
1982 }
1983 free_netdev(dev);
1984}
1985
1986/**
1987 * Initialize a net device. (Called from kernel in alloc_netdev())
1988 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001989static const struct net_device_ops netiucv_netdev_ops = {
1990 .ndo_open = netiucv_open,
1991 .ndo_stop = netiucv_close,
1992 .ndo_get_stats = netiucv_stats,
1993 .ndo_start_xmit = netiucv_tx,
1994 .ndo_change_mtu = netiucv_change_mtu,
1995};
1996
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001997static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 dev->mtu = NETIUCV_MTU_DEFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 dev->destructor = netiucv_free_netdevice;
2001 dev->hard_header_len = NETIUCV_HDRLEN;
2002 dev->addr_len = 0;
2003 dev->type = ARPHRD_SLIP;
2004 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
2005 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00002006 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007}
2008
2009/**
2010 * Allocate and initialize everything of a net device.
2011 */
Ursula Braun08e33562011-12-19 22:56:34 +00002012static struct net_device *netiucv_init_netdevice(char *username, char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013{
2014 struct netiucv_priv *privptr;
2015 struct net_device *dev;
2016
2017 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
2018 netiucv_setup_netdevice);
2019 if (!dev)
2020 return NULL;
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002021 rtnl_lock();
Ursula Braun1d503562011-11-15 02:31:14 +00002022 if (dev_alloc_name(dev, dev->name) < 0)
2023 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002025 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
2027 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
2028 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002029 if (!privptr->fsm)
2030 goto out_netdev;
2031
Ursula Braun08e33562011-12-19 22:56:34 +00002032 privptr->conn = netiucv_new_connection(dev, username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002035 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 }
2037 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002039
2040out_fsm:
2041 kfree_fsm(privptr->fsm);
2042out_netdev:
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002043 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002044 free_netdev(dev);
2045 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046}
2047
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002048static ssize_t conn_write(struct device_driver *drv,
2049 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050{
Frank Pavlic16a83b32006-09-15 16:25:19 +02002051 char username[9];
Ursula Braun08e33562011-12-19 22:56:34 +00002052 char userdata[17];
2053 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002055 struct netiucv_priv *priv;
2056 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002058 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00002059 rc = netiucv_check_user(buf, count, username, userdata);
2060 if (rc)
2061 return rc;
Frank Pavlic16a83b32006-09-15 16:25:19 +02002062
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002063 read_lock_bh(&iucv_connection_rwlock);
2064 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00002065 if (!strncmp(username, cp->userid, 9) &&
2066 !strncmp(userdata, cp->userdata, 17)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002067 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00002068 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s "
2069 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002070 return -EEXIST;
2071 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02002072 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002073 read_unlock_bh(&iucv_connection_rwlock);
2074
Ursula Braun08e33562011-12-19 22:56:34 +00002075 dev = netiucv_init_netdevice(username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2078 return -ENODEV;
2079 }
2080
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002081 rc = netiucv_register_device(dev);
2082 if (rc) {
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002083 rtnl_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002085 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 goto out_free_ndev;
2087 }
2088
2089 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002090 priv = netdev_priv(dev);
2091 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002093 rc = register_netdevice(dev);
2094 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002095 if (rc)
2096 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097
Ursula Braun08e33562011-12-19 22:56:34 +00002098 dev_info(priv->dev, "The IUCV interface to %s has been established "
2099 "successfully\n",
2100 netiucv_printuser(priv->conn));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002101
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 return count;
2103
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002104out_unreg:
2105 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002108 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109}
2110
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002111static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002113static ssize_t remove_write (struct device_driver *drv,
2114 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002116 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 struct net_device *ndev;
2118 struct netiucv_priv *priv;
2119 struct device *dev;
2120 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002121 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 int i;
2123
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002124 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125
2126 if (count >= IFNAMSIZ)
Joe Perchesa419aef2009-08-18 11:18:35 -07002127 count = IFNAMSIZ - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002129 for (i = 0, p = buf; i < count && *p; i++, p++) {
2130 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 /* trailing lf, grr */
2132 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002133 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134 }
2135 name[i] = '\0';
2136
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002137 read_lock_bh(&iucv_connection_rwlock);
2138 list_for_each_entry(cp, &iucv_connection_list, list) {
2139 ndev = cp->netdev;
2140 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002142 if (strncmp(name, ndev->name, count))
2143 continue;
2144 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002146 dev_warn(dev, "The IUCV device is connected"
2147 " to %s and cannot be removed\n",
2148 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002150 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 }
2152 unregister_netdev(ndev);
2153 netiucv_unregister_device(dev);
2154 return count;
2155 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002156 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2158 return -EINVAL;
2159}
2160
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002161static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002163static struct attribute * netiucv_drv_attrs[] = {
2164 &driver_attr_connection.attr,
2165 &driver_attr_remove.attr,
2166 NULL,
2167};
2168
2169static struct attribute_group netiucv_drv_attr_group = {
2170 .attrs = netiucv_drv_attrs,
2171};
2172
David Brownella4dbd672009-06-24 10:06:31 -07002173static const struct attribute_group *netiucv_drv_attr_groups[] = {
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002174 &netiucv_drv_attr_group,
2175 NULL,
2176};
2177
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002178static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002180 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181}
2182
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002183static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002185 struct iucv_connection *cp;
2186 struct net_device *ndev;
2187 struct netiucv_priv *priv;
2188 struct device *dev;
2189
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002190 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002191 while (!list_empty(&iucv_connection_list)) {
2192 cp = list_entry(iucv_connection_list.next,
2193 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002194 ndev = cp->netdev;
2195 priv = netdev_priv(ndev);
2196 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197
2198 unregister_netdev(ndev);
2199 netiucv_unregister_device(dev);
2200 }
2201
Ursula Braun1175b252009-06-16 10:30:43 +02002202 device_unregister(netiucv_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002204 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 iucv_unregister_dbf_views();
2206
Ursula Braun8f7c5022008-12-25 13:39:47 +01002207 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208 return;
2209}
2210
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002211static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002213 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002214
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002215 rc = iucv_register_dbf_views();
2216 if (rc)
2217 goto out;
2218 rc = iucv_register(&netiucv_handler, 1);
2219 if (rc)
2220 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002221 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002222 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002223 rc = driver_register(&netiucv_driver);
2224 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002225 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2226 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227 }
Ursula Braun1175b252009-06-16 10:30:43 +02002228 /* establish dummy device */
2229 netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
2230 if (!netiucv_dev) {
2231 rc = -ENOMEM;
2232 goto out_driver;
2233 }
2234 dev_set_name(netiucv_dev, "netiucv");
2235 netiucv_dev->bus = &iucv_bus;
2236 netiucv_dev->parent = iucv_root;
2237 netiucv_dev->release = (void (*)(struct device *))kfree;
2238 netiucv_dev->driver = &netiucv_driver;
2239 rc = device_register(netiucv_dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02002240 if (rc) {
2241 put_device(netiucv_dev);
Ursula Braun1175b252009-06-16 10:30:43 +02002242 goto out_driver;
Sebastian Ottc6304932009-09-11 10:28:38 +02002243 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002244 netiucv_banner();
2245 return rc;
2246
Ursula Braun1175b252009-06-16 10:30:43 +02002247out_driver:
2248 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002249out_iucv:
2250 iucv_unregister(&netiucv_handler, 1);
2251out_dbf:
2252 iucv_unregister_dbf_views();
2253out:
2254 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002256
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257module_init(netiucv_init);
2258module_exit(netiucv_exit);
2259MODULE_LICENSE("GPL");