blob: fa732bd8672961ce89c911f5e60212db1e2da386 [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>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080065#include <linux/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
Ursula Braun91e60eb2015-09-18 16:06:52 +0200152static int netiucv_callback_connreq(struct iucv_path *, u8 *, u8 *);
153static void netiucv_callback_connack(struct iucv_path *, u8 *);
154static void netiucv_callback_connrej(struct iucv_path *, u8 *);
155static void netiucv_callback_connsusp(struct iucv_path *, u8 *);
156static void netiucv_callback_connres(struct iucv_path *, u8 *);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800157static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
158static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
159
160static struct iucv_handler netiucv_handler = {
161 .path_pending = netiucv_callback_connreq,
162 .path_complete = netiucv_callback_connack,
163 .path_severed = netiucv_callback_connrej,
164 .path_quiesced = netiucv_callback_connsusp,
165 .path_resumed = netiucv_callback_connres,
166 .message_pending = netiucv_callback_rx,
167 .message_complete = netiucv_callback_txdone
168};
169
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170/**
171 * Per connection profiling data
172 */
173struct connection_profile {
174 unsigned long maxmulti;
175 unsigned long maxcqueue;
176 unsigned long doios_single;
177 unsigned long doios_multi;
178 unsigned long txlen;
179 unsigned long tx_time;
Aya Mahfouzee6edb92015-01-16 14:05:45 +0100180 unsigned long send_stamp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 unsigned long tx_pending;
182 unsigned long tx_max_pending;
183};
184
185/**
186 * Representation of one iucv connection
187 */
188struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800189 struct list_head list;
190 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 struct sk_buff *rx_buff;
192 struct sk_buff *tx_buff;
193 struct sk_buff_head collect_queue;
194 struct sk_buff_head commit_queue;
195 spinlock_t collect_lock;
196 int collect_len;
197 int max_buffsize;
198 fsm_timer timer;
199 fsm_instance *fsm;
200 struct net_device *netdev;
201 struct connection_profile prof;
202 char userid[9];
Ursula Braun08e33562011-12-19 22:56:34 +0000203 char userdata[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204};
205
206/**
207 * Linked list of all connection structs.
208 */
Denis Chengc11ca972008-01-26 14:11:13 +0100209static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200210static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211
212/**
213 * Representation of event-data for the
214 * connection state machine.
215 */
216struct iucv_event {
217 struct iucv_connection *conn;
218 void *data;
219};
220
221/**
222 * Private part of the network device structure
223 */
224struct netiucv_priv {
225 struct net_device_stats stats;
226 unsigned long tbusy;
227 fsm_instance *fsm;
228 struct iucv_connection *conn;
229 struct device *dev;
Ursula Braun1175b252009-06-16 10:30:43 +0200230 int pm_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231};
232
233/**
234 * Link level header for a packet.
235 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800236struct ll_header {
237 u16 next;
238};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800240#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Ursula Braun08e33562011-12-19 22:56:34 +0000241#define NETIUCV_BUFSIZE_MAX 65537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
243#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
244#define NETIUCV_MTU_DEFAULT 9216
245#define NETIUCV_QUEUELEN_DEFAULT 50
246#define NETIUCV_TIMEOUT_5SEC 5000
247
248/**
249 * Compatibility macros for busy handling
250 * of network devices.
251 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800252static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800254 struct netiucv_priv *priv = netdev_priv(dev);
255 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 netif_wake_queue(dev);
257}
258
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800259static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800261 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800263 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264}
265
Ursula Braun08e33562011-12-19 22:56:34 +0000266static u8 iucvMagic_ascii[16] = {
267 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
268 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
269};
270
271static u8 iucvMagic_ebcdic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
273 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
274};
275
276/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 * Convert an iucv userId to its printable
278 * form (strip whitespace at end).
279 *
280 * @param An iucv userId
281 *
282 * @returns The printable string (static data!!)
283 */
Ursula Braun08e33562011-12-19 22:56:34 +0000284static char *netiucv_printname(char *name, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285{
Ursula Braun08e33562011-12-19 22:56:34 +0000286 static char tmp[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 char *p = tmp;
Ursula Braun08e33562011-12-19 22:56:34 +0000288 memcpy(tmp, name, len);
289 tmp[len] = '\0';
290 while (*p && ((p - tmp) < len) && (!isspace(*p)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 p++;
292 *p = '\0';
293 return tmp;
294}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400295
Ursula Braun08e33562011-12-19 22:56:34 +0000296static char *netiucv_printuser(struct iucv_connection *conn)
297{
298 static char tmp_uid[9];
299 static char tmp_udat[17];
300 static char buf[100];
301
302 if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) {
303 tmp_uid[8] = '\0';
304 tmp_udat[16] = '\0';
Ursula Braunbaac7892016-10-12 12:38:49 +0200305 memcpy(tmp_uid, netiucv_printname(conn->userid, 8), 8);
Ursula Braun08e33562011-12-19 22:56:34 +0000306 memcpy(tmp_udat, conn->userdata, 16);
307 EBCASC(tmp_udat, 16);
308 memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16);
309 sprintf(buf, "%s.%s", tmp_uid, tmp_udat);
310 return buf;
311 } else
312 return netiucv_printname(conn->userid, 8);
313}
314
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315/**
316 * States of the interface statemachine.
317 */
318enum dev_states {
319 DEV_STATE_STOPPED,
320 DEV_STATE_STARTWAIT,
321 DEV_STATE_STOPWAIT,
322 DEV_STATE_RUNNING,
323 /**
324 * MUST be always the last element!!
325 */
326 NR_DEV_STATES
327};
328
329static const char *dev_state_names[] = {
330 "Stopped",
331 "StartWait",
332 "StopWait",
333 "Running",
334};
335
336/**
337 * Events of the interface statemachine.
338 */
339enum dev_events {
340 DEV_EVENT_START,
341 DEV_EVENT_STOP,
342 DEV_EVENT_CONUP,
343 DEV_EVENT_CONDOWN,
344 /**
345 * MUST be always the last element!!
346 */
347 NR_DEV_EVENTS
348};
349
350static const char *dev_event_names[] = {
351 "Start",
352 "Stop",
353 "Connection up",
354 "Connection down",
355};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400356
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357/**
358 * Events of the connection statemachine
359 */
360enum conn_events {
361 /**
362 * Events, representing callbacks from
363 * lowlevel iucv layer)
364 */
365 CONN_EVENT_CONN_REQ,
366 CONN_EVENT_CONN_ACK,
367 CONN_EVENT_CONN_REJ,
368 CONN_EVENT_CONN_SUS,
369 CONN_EVENT_CONN_RES,
370 CONN_EVENT_RX,
371 CONN_EVENT_TXDONE,
372
373 /**
374 * Events, representing errors return codes from
375 * calls to lowlevel iucv layer
376 */
377
378 /**
379 * Event, representing timer expiry.
380 */
381 CONN_EVENT_TIMER,
382
383 /**
384 * Events, representing commands from upper levels.
385 */
386 CONN_EVENT_START,
387 CONN_EVENT_STOP,
388
389 /**
390 * MUST be always the last element!!
391 */
392 NR_CONN_EVENTS,
393};
394
395static const char *conn_event_names[] = {
396 "Remote connection request",
397 "Remote connection acknowledge",
398 "Remote connection reject",
399 "Connection suspended",
400 "Connection resumed",
401 "Data received",
402 "Data sent",
403
404 "Timer",
405
406 "Start",
407 "Stop",
408};
409
410/**
411 * States of the connection statemachine.
412 */
413enum conn_states {
414 /**
415 * Connection not assigned to any device,
416 * initial state, invalid
417 */
418 CONN_STATE_INVALID,
419
420 /**
421 * Userid assigned but not operating
422 */
423 CONN_STATE_STOPPED,
424
425 /**
426 * Connection registered,
427 * no connection request sent yet,
428 * no connection request received
429 */
430 CONN_STATE_STARTWAIT,
431
432 /**
433 * Connection registered and connection request sent,
434 * no acknowledge and no connection request received yet.
435 */
436 CONN_STATE_SETUPWAIT,
437
438 /**
439 * Connection up and running idle
440 */
441 CONN_STATE_IDLE,
442
443 /**
444 * Data sent, awaiting CONN_EVENT_TXDONE
445 */
446 CONN_STATE_TX,
447
448 /**
449 * Error during registration.
450 */
451 CONN_STATE_REGERR,
452
453 /**
454 * Error during registration.
455 */
456 CONN_STATE_CONNERR,
457
458 /**
459 * MUST be always the last element!!
460 */
461 NR_CONN_STATES,
462};
463
464static const char *conn_state_names[] = {
465 "Invalid",
466 "Stopped",
467 "StartWait",
468 "SetupWait",
469 "Idle",
470 "TX",
471 "Terminating",
472 "Registration error",
473 "Connect error",
474};
475
Jeff Garzike82b0f22006-05-26 21:58:38 -0400476
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477/**
478 * Debug Facility Stuff
479 */
480static debug_info_t *iucv_dbf_setup = NULL;
481static debug_info_t *iucv_dbf_data = NULL;
482static debug_info_t *iucv_dbf_trace = NULL;
483
484DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
485
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800486static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
Markus Elfringb646c082015-01-16 14:05:46 +0100488 debug_unregister(iucv_dbf_setup);
489 debug_unregister(iucv_dbf_data);
490 debug_unregister(iucv_dbf_trace);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800492static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493{
494 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700495 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 IUCV_DBF_SETUP_NR_AREAS,
497 IUCV_DBF_SETUP_LEN);
498 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700499 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 IUCV_DBF_DATA_NR_AREAS,
501 IUCV_DBF_DATA_LEN);
502 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700503 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 IUCV_DBF_TRACE_NR_AREAS,
505 IUCV_DBF_TRACE_LEN);
506
507 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
508 (iucv_dbf_trace == NULL)) {
509 iucv_unregister_dbf_views();
510 return -ENOMEM;
511 }
512 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
513 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
514
515 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
516 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
517
518 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
519 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
520
521 return 0;
522}
523
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800524/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800526 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800528static void netiucv_callback_rx(struct iucv_path *path,
529 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800531 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 struct iucv_event ev;
533
534 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800535 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
537}
538
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800539static void netiucv_callback_txdone(struct iucv_path *path,
540 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800542 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 struct iucv_event ev;
544
545 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800546 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
548}
549
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800550static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800552 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800554 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555}
556
Ursula Braun91e60eb2015-09-18 16:06:52 +0200557static int netiucv_callback_connreq(struct iucv_path *path, u8 *ipvmid,
558 u8 *ipuser)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800560 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 struct iucv_event ev;
Ursula Braun08e33562011-12-19 22:56:34 +0000562 static char tmp_user[9];
563 static char tmp_udat[17];
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800564 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800566 rc = -EINVAL;
Ursula Braun08e33562011-12-19 22:56:34 +0000567 memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8);
568 memcpy(tmp_udat, ipuser, 16);
569 EBCASC(tmp_udat, 16);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800570 read_lock_bh(&iucv_connection_rwlock);
571 list_for_each_entry(conn, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +0000572 if (strncmp(ipvmid, conn->userid, 8) ||
573 strncmp(ipuser, conn->userdata, 16))
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800574 continue;
575 /* Found a matching connection for this path. */
576 conn->path = path;
577 ev.conn = conn;
578 ev.data = path;
579 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
580 rc = 0;
581 }
Ursula Braun08e33562011-12-19 22:56:34 +0000582 IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n",
583 tmp_user, netiucv_printname(tmp_udat, 16));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800584 read_unlock_bh(&iucv_connection_rwlock);
585 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586}
587
Ursula Braun91e60eb2015-09-18 16:06:52 +0200588static void netiucv_callback_connrej(struct iucv_path *path, u8 *ipuser)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800590 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800592 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593}
594
Ursula Braun91e60eb2015-09-18 16:06:52 +0200595static void netiucv_callback_connsusp(struct iucv_path *path, u8 *ipuser)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800597 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800599 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600}
601
Ursula Braun91e60eb2015-09-18 16:06:52 +0200602static void netiucv_callback_connres(struct iucv_path *path, u8 *ipuser)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800604 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800606 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607}
608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100610 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100612static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613{
614}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400615
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800616/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800618 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619
620/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800621 * netiucv_unpack_skb
622 * @conn: The connection where this skb has been received.
623 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800625 * Unpack a just received skb and hand it over to upper layers.
626 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800628static void netiucv_unpack_skb(struct iucv_connection *conn,
629 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630{
631 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800632 struct netiucv_priv *privptr = netdev_priv(dev);
633 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
635 skb_put(pskb, NETIUCV_HDRLEN);
636 pskb->dev = dev;
637 pskb->ip_summed = CHECKSUM_NONE;
Hans Wippel6c37c602017-04-07 09:15:38 +0200638 pskb->protocol = cpu_to_be16(ETH_P_IP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
640 while (1) {
641 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800642 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643
644 if (!header->next)
645 break;
646
647 skb_pull(pskb, NETIUCV_HDRLEN);
648 header->next -= offset;
649 offset += header->next;
650 header->next -= NETIUCV_HDRLEN;
651 if (skb_tailroom(pskb) < header->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
653 header->next, skb_tailroom(pskb));
654 return;
655 }
656 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700657 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 skb = dev_alloc_skb(pskb->len);
659 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 IUCV_DBF_TEXT(data, 2,
661 "Out of memory in netiucv_unpack_skb\n");
662 privptr->stats.rx_dropped++;
663 return;
664 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300665 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
666 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700667 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 skb->dev = pskb->dev;
669 skb->protocol = pskb->protocol;
670 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800671 privptr->stats.rx_packets++;
672 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 /*
674 * Since receiving is always initiated from a tasklet (in iucv.c),
675 * we must use netif_rx_ni() instead of netif_rx()
676 */
677 netif_rx_ni(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 skb_pull(pskb, header->next);
679 skb_put(pskb, NETIUCV_HDRLEN);
680 }
681}
682
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800683static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800685 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800687 struct iucv_message *msg = ev->data;
688 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 int rc;
690
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200691 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
693 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800694 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800696 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 return;
698 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800699 if (msg->length > conn->max_buffsize) {
700 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 privptr->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800703 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 return;
705 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700706 conn->rx_buff->data = conn->rx_buff->head;
707 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800709 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
710 msg->length, NULL);
711 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 privptr->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
714 return;
715 }
716 netiucv_unpack_skb(conn, conn->rx_buff);
717}
718
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800719static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800721 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800723 struct iucv_message *msg = ev->data;
724 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800726 u32 single_flag = msg->tag;
727 u32 txbytes = 0;
728 u32 txpackets = 0;
729 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 struct sk_buff *skb;
731 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800732 struct ll_header header;
733 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200735 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736
Ursula Braund239ae32013-12-16 09:44:51 +0100737 if (!conn || !conn->netdev) {
738 IUCV_DBF_TEXT(data, 2,
739 "Send confirmation for unlinked connection\n");
740 return;
741 }
742 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 conn->prof.tx_pending--;
744 if (single_flag) {
745 if ((skb = skb_dequeue(&conn->commit_queue))) {
746 atomic_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 if (privptr) {
748 privptr->stats.tx_packets++;
749 privptr->stats.tx_bytes +=
750 (skb->len - NETIUCV_HDRLEN
Ursula Braun998221c2009-11-12 21:46:30 +0000751 - NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 }
Ursula Braun998221c2009-11-12 21:46:30 +0000753 dev_kfree_skb_any(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 }
755 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700756 conn->tx_buff->data = conn->tx_buff->head;
757 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 conn->tx_buff->len = 0;
759 spin_lock_irqsave(&conn->collect_lock, saveflags);
760 while ((skb = skb_dequeue(&conn->collect_queue))) {
761 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
762 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
763 NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300764 skb_copy_from_linear_data(skb,
765 skb_put(conn->tx_buff, skb->len),
766 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 txbytes += skb->len;
768 txpackets++;
769 stat_maxcq++;
770 atomic_dec(&skb->users);
771 dev_kfree_skb_any(skb);
772 }
773 if (conn->collect_len > conn->prof.maxmulti)
774 conn->prof.maxmulti = conn->collect_len;
775 conn->collect_len = 0;
776 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800777 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800779 return;
780 }
781
782 header.next = 0;
783 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
Aya Mahfouzee6edb92015-01-16 14:05:45 +0100784 conn->prof.send_stamp = jiffies;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800785 txmsg.class = 0;
786 txmsg.tag = 0;
787 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
788 conn->tx_buff->data, conn->tx_buff->len);
789 conn->prof.doios_multi++;
790 conn->prof.txlen += conn->tx_buff->len;
791 conn->prof.tx_pending++;
792 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
793 conn->prof.tx_max_pending = conn->prof.tx_pending;
794 if (rc) {
795 conn->prof.tx_pending--;
796 fsm_newstate(fi, CONN_STATE_IDLE);
797 if (privptr)
798 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800799 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
800 } else {
801 if (privptr) {
802 privptr->stats.tx_packets += txpackets;
803 privptr->stats.tx_bytes += txbytes;
804 }
805 if (stat_maxcq > conn->prof.maxcqueue)
806 conn->prof.maxcqueue = stat_maxcq;
807 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808}
809
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800810static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800812 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800814 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800816 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200819 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800821 conn->path = path;
822 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
823 path->flags = 0;
Ursula Braun08e33562011-12-19 22:56:34 +0000824 rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
827 return;
828 }
829 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800830 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
832}
833
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800834static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800836 struct iucv_event *ev = arg;
837 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200839 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800840 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841}
842
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800843static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800845 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800847 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200849 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 fsm_deltimer(&conn->timer);
851 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800852 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
854}
855
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800856static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800858 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200860 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000862 iucv_path_sever(conn->path, conn->userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 fsm_newstate(fi, CONN_STATE_STARTWAIT);
864}
865
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800866static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800868 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800870 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200872 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873
874 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000875 iucv_path_sever(conn->path, conn->userdata);
876 dev_info(privptr->dev, "The peer z/VM guest %s has closed the "
877 "connection\n", netiucv_printuser(conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800879 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 fsm_newstate(fi, CONN_STATE_STARTWAIT);
881 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
882}
883
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800884static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800886 struct iucv_connection *conn = arg;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100887 struct net_device *netdev = conn->netdev;
888 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 int rc;
890
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200891 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800893 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800895 /*
896 * We must set the state before calling iucv_connect because the
897 * callback handler could be called at any point after the connection
898 * request is sent
899 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
901 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800902 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
Ursula Braun08e33562011-12-19 22:56:34 +0000903 IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n",
904 netdev->name, netiucv_printuser(conn));
905
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800906 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
Ursula Braun08e33562011-12-19 22:56:34 +0000907 NULL, conn->userdata, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800909 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100910 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800911 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
912 CONN_EVENT_TIMER, conn);
913 return;
914 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100915 dev_warn(privptr->dev,
916 "The IUCV device failed to connect to z/VM guest %s\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000917 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800918 fsm_newstate(fi, CONN_STATE_STARTWAIT);
919 break;
920 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100921 dev_warn(privptr->dev,
922 "The IUCV device failed to connect to the peer on z/VM"
Ursula Braun08e33562011-12-19 22:56:34 +0000923 " guest %s\n", netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800924 fsm_newstate(fi, CONN_STATE_STARTWAIT);
925 break;
926 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100927 dev_err(privptr->dev,
928 "Connecting the IUCV device would exceed the maximum"
929 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800930 fsm_newstate(fi, CONN_STATE_CONNERR);
931 break;
932 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100933 dev_err(privptr->dev,
934 "z/VM guest %s has too many IUCV connections"
935 " to connect with the IUCV device\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000936 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800937 fsm_newstate(fi, CONN_STATE_CONNERR);
938 break;
939 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100940 dev_err(privptr->dev,
941 "The IUCV device cannot connect to a z/VM guest with no"
942 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800943 fsm_newstate(fi, CONN_STATE_CONNERR);
944 break;
945 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100946 dev_err(privptr->dev,
947 "Connecting the IUCV device failed with error %d\n",
948 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800949 fsm_newstate(fi, CONN_STATE_CONNERR);
950 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 }
952 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800953 kfree(conn->path);
954 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955}
956
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800957static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958{
959 struct sk_buff *skb;
960
961 while ((skb = skb_dequeue(q))) {
962 atomic_dec(&skb->users);
963 dev_kfree_skb_any(skb);
964 }
965}
966
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800967static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800969 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 struct iucv_connection *conn = ev->conn;
971 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800972 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200974 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
976 fsm_deltimer(&conn->timer);
977 fsm_newstate(fi, CONN_STATE_STOPPED);
978 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800979 if (conn->path) {
980 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
Ursula Braun08e33562011-12-19 22:56:34 +0000981 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800982 kfree(conn->path);
983 conn->path = NULL;
984 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 netiucv_purge_skb_queue(&conn->commit_queue);
986 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
987}
988
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800989static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800991 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 struct net_device *netdev = conn->netdev;
993
Ursula Braunf082bca2008-07-14 09:59:30 +0200994 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
995 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996}
997
998static const fsm_node conn_fsm[] = {
999 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
1000 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
1001
1002 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
1003 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
1004 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
1005 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
1006 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
1007 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
1008 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
1009
1010 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
1011 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1012 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1013 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
1014 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
1015
1016 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
1017 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
1018
1019 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
1020 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
1021 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
1022
1023 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
1024 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1025
1026 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1027 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1028};
1029
1030static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1031
Jeff Garzike82b0f22006-05-26 21:58:38 -04001032
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001033/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001035 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
1037/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001038 * dev_action_start
1039 * @fi: An instance of an interface statemachine.
1040 * @event: The event, just happened.
1041 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001043 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001045static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001047 struct net_device *dev = arg;
1048 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001050 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001053 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054}
1055
1056/**
1057 * Shutdown connection by sending CONN_EVENT_STOP to it.
1058 *
1059 * @param fi An instance of an interface statemachine.
1060 * @param event The event, just happened.
1061 * @param arg Generic pointer, casted from struct net_device * upon call.
1062 */
1063static void
1064dev_action_stop(fsm_instance *fi, int event, void *arg)
1065{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001066 struct net_device *dev = arg;
1067 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 struct iucv_event ev;
1069
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001070 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
1072 ev.conn = privptr->conn;
1073
1074 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1075 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1076}
1077
1078/**
1079 * Called from connection statemachine
1080 * when a connection is up and running.
1081 *
1082 * @param fi An instance of an interface statemachine.
1083 * @param event The event, just happened.
1084 * @param arg Generic pointer, casted from struct net_device * upon call.
1085 */
1086static void
1087dev_action_connup(fsm_instance *fi, int event, void *arg)
1088{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001089 struct net_device *dev = arg;
1090 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001092 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
1094 switch (fsm_getstate(fi)) {
1095 case DEV_STATE_STARTWAIT:
1096 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001097 dev_info(privptr->dev,
1098 "The IUCV device has been connected"
Ursula Braun08e33562011-12-19 22:56:34 +00001099 " successfully to %s\n",
1100 netiucv_printuser(privptr->conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 IUCV_DBF_TEXT(setup, 3,
1102 "connection is up and running\n");
1103 break;
1104 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 IUCV_DBF_TEXT(data, 2,
1106 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1107 break;
1108 }
1109}
1110
1111/**
1112 * Called from connection statemachine
1113 * when a connection has been shutdown.
1114 *
1115 * @param fi An instance of an interface statemachine.
1116 * @param event The event, just happened.
1117 * @param arg Generic pointer, casted from struct net_device * upon call.
1118 */
1119static void
1120dev_action_conndown(fsm_instance *fi, int event, void *arg)
1121{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001122 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123
1124 switch (fsm_getstate(fi)) {
1125 case DEV_STATE_RUNNING:
1126 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1127 break;
1128 case DEV_STATE_STOPWAIT:
1129 fsm_newstate(fi, DEV_STATE_STOPPED);
1130 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1131 break;
1132 }
1133}
1134
1135static const fsm_node dev_fsm[] = {
1136 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1137
1138 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1139 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1140
1141 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1142 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1143
1144 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1145 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001146 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147};
1148
1149static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1150
1151/**
1152 * Transmit a packet.
1153 * This is a helper function for netiucv_tx().
1154 *
1155 * @param conn Connection to be used for sending.
1156 * @param skb Pointer to struct sk_buff of packet to send.
1157 * The linklevel header has already been set up
1158 * by netiucv_tx().
1159 *
1160 * @return 0 on success, -ERRNO on failure. (Never fails.)
1161 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001162static int netiucv_transmit_skb(struct iucv_connection *conn,
1163 struct sk_buff *skb)
1164{
1165 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001167 struct ll_header header;
1168 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169
1170 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1171 int l = skb->len + NETIUCV_HDRLEN;
1172
1173 spin_lock_irqsave(&conn->collect_lock, saveflags);
1174 if (conn->collect_len + l >
1175 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1176 rc = -EBUSY;
1177 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001178 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 } else {
1180 atomic_inc(&skb->users);
1181 skb_queue_tail(&conn->collect_queue, skb);
1182 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001183 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 }
1185 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1186 } else {
1187 struct sk_buff *nskb = skb;
1188 /**
1189 * Copy the skb to a new allocated skb in lowmem only if the
1190 * data is located above 2G in memory or tailroom is < 2.
1191 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001192 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1193 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 int copied = 0;
1195 if (hi || (skb_tailroom(skb) < 2)) {
1196 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1197 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1198 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1200 rc = -ENOMEM;
1201 return rc;
1202 } else {
1203 skb_reserve(nskb, NETIUCV_HDRLEN);
1204 memcpy(skb_put(nskb, skb->len),
1205 skb->data, skb->len);
1206 }
1207 copied = 1;
1208 }
1209 /**
1210 * skb now is below 2G and has enough room. Add headers.
1211 */
1212 header.next = nskb->len + NETIUCV_HDRLEN;
1213 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1214 header.next = 0;
1215 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1216
1217 fsm_newstate(conn->fsm, CONN_STATE_TX);
Aya Mahfouzee6edb92015-01-16 14:05:45 +01001218 conn->prof.send_stamp = jiffies;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001219
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001220 msg.tag = 1;
1221 msg.class = 0;
1222 rc = iucv_message_send(conn->path, &msg, 0, 0,
1223 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 conn->prof.doios_single++;
1225 conn->prof.txlen += skb->len;
1226 conn->prof.tx_pending++;
1227 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1228 conn->prof.tx_max_pending = conn->prof.tx_pending;
1229 if (rc) {
1230 struct netiucv_priv *privptr;
1231 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1232 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001233 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 if (privptr)
1235 privptr->stats.tx_errors++;
1236 if (copied)
1237 dev_kfree_skb(nskb);
1238 else {
1239 /**
1240 * Remove our headers. They get added
1241 * again on retransmit.
1242 */
1243 skb_pull(skb, NETIUCV_HDRLEN);
1244 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1245 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1247 } else {
1248 if (copied)
1249 dev_kfree_skb(skb);
1250 atomic_inc(&nskb->users);
1251 skb_queue_tail(&conn->commit_queue, nskb);
1252 }
1253 }
1254
1255 return rc;
1256}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001257
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001258/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001260 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261
1262/**
1263 * Open an interface.
1264 * Called from generic network layer when ifconfig up is run.
1265 *
1266 * @param dev Pointer to interface struct.
1267 *
1268 * @return 0 on success, -ERRNO on failure. (Never fails.)
1269 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001270static int netiucv_open(struct net_device *dev)
1271{
1272 struct netiucv_priv *priv = netdev_priv(dev);
1273
1274 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 return 0;
1276}
1277
1278/**
1279 * Close an interface.
1280 * Called from generic network layer when ifconfig down is run.
1281 *
1282 * @param dev Pointer to interface struct.
1283 *
1284 * @return 0 on success, -ERRNO on failure. (Never fails.)
1285 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001286static int netiucv_close(struct net_device *dev)
1287{
1288 struct netiucv_priv *priv = netdev_priv(dev);
1289
1290 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 return 0;
1292}
1293
Ursula Braun1175b252009-06-16 10:30:43 +02001294static int netiucv_pm_prepare(struct device *dev)
1295{
1296 IUCV_DBF_TEXT(trace, 3, __func__);
1297 return 0;
1298}
1299
1300static void netiucv_pm_complete(struct device *dev)
1301{
1302 IUCV_DBF_TEXT(trace, 3, __func__);
1303 return;
1304}
1305
1306/**
1307 * netiucv_pm_freeze() - Freeze PM callback
1308 * @dev: netiucv device
1309 *
1310 * close open netiucv interfaces
1311 */
1312static int netiucv_pm_freeze(struct device *dev)
1313{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001314 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001315 struct net_device *ndev = NULL;
1316 int rc = 0;
1317
1318 IUCV_DBF_TEXT(trace, 3, __func__);
1319 if (priv && priv->conn)
1320 ndev = priv->conn->netdev;
1321 if (!ndev)
1322 goto out;
1323 netif_device_detach(ndev);
1324 priv->pm_state = fsm_getstate(priv->fsm);
1325 rc = netiucv_close(ndev);
1326out:
1327 return rc;
1328}
1329
1330/**
1331 * netiucv_pm_restore_thaw() - Thaw and restore PM callback
1332 * @dev: netiucv device
1333 *
1334 * re-open netiucv interfaces closed during freeze
1335 */
1336static int netiucv_pm_restore_thaw(struct device *dev)
1337{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001338 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001339 struct net_device *ndev = NULL;
1340 int rc = 0;
1341
1342 IUCV_DBF_TEXT(trace, 3, __func__);
1343 if (priv && priv->conn)
1344 ndev = priv->conn->netdev;
1345 if (!ndev)
1346 goto out;
1347 switch (priv->pm_state) {
1348 case DEV_STATE_RUNNING:
1349 case DEV_STATE_STARTWAIT:
1350 rc = netiucv_open(ndev);
1351 break;
1352 default:
1353 break;
1354 }
1355 netif_device_attach(ndev);
1356out:
1357 return rc;
1358}
1359
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360/**
1361 * Start transmission of a packet.
1362 * Called from generic network device layer.
1363 *
1364 * @param skb Pointer to buffer containing the packet.
1365 * @param dev Pointer to interface struct.
1366 *
1367 * @return 0 if packet consumed, !0 if packet rejected.
1368 * Note: If we return !0, then the packet is free'd by
1369 * the generic network layer.
1370 */
1371static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1372{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001373 struct netiucv_priv *privptr = netdev_priv(dev);
1374 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001376 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 /**
1378 * Some sanity checks ...
1379 */
1380 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1382 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001383 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 }
1385 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 IUCV_DBF_TEXT(data, 2,
1387 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1388 dev_kfree_skb(skb);
1389 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001390 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 }
1392
1393 /**
1394 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001395 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 */
1397 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 dev_kfree_skb(skb);
1399 privptr->stats.tx_dropped++;
1400 privptr->stats.tx_errors++;
1401 privptr->stats.tx_carrier_errors++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001402 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 }
1404
1405 if (netiucv_test_and_set_busy(dev)) {
1406 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001407 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 }
Florian Westphal860e9532016-05-03 16:33:13 +02001409 netif_trans_update(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001410 rc = netiucv_transmit_skb(privptr->conn, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 netiucv_clear_busy(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001412 return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413}
1414
1415/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001416 * netiucv_stats
1417 * @dev: Pointer to interface struct.
1418 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 * Returns interface statistics of a device.
1420 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001421 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001423static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001425 struct netiucv_priv *priv = netdev_priv(dev);
1426
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001427 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001428 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429}
1430
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001431/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001433 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001435static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1436 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001438 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001440 IUCV_DBF_TEXT(trace, 5, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001441 return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));
1442}
1443
1444static int netiucv_check_user(const char *buf, size_t count, char *username,
1445 char *userdata)
1446{
1447 const char *p;
1448 int i;
1449
1450 p = strchr(buf, '.');
1451 if ((p && ((count > 26) ||
1452 ((p - buf) > 8) ||
1453 (buf + count - p > 18))) ||
1454 (!p && (count > 9))) {
1455 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1456 return -EINVAL;
1457 }
1458
1459 for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) {
1460 if (isalnum(*p) || *p == '$') {
1461 username[i] = toupper(*p);
1462 continue;
1463 }
1464 if (*p == '\n')
1465 /* trailing lf, grr */
1466 break;
1467 IUCV_DBF_TEXT_(setup, 2,
1468 "conn_write: invalid character %02x\n", *p);
1469 return -EINVAL;
1470 }
1471 while (i < 8)
1472 username[i++] = ' ';
1473 username[8] = '\0';
1474
1475 if (*p == '.') {
1476 p++;
1477 for (i = 0; i < 16 && *p; i++, p++) {
1478 if (*p == '\n')
1479 break;
1480 userdata[i] = toupper(*p);
1481 }
1482 while (i > 0 && i < 16)
1483 userdata[i++] = ' ';
1484 } else
1485 memcpy(userdata, iucvMagic_ascii, 16);
1486 userdata[16] = '\0';
1487 ASCEBC(userdata, 16);
1488
1489 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490}
1491
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001492static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1493 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001495 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 struct net_device *ndev = priv->conn->netdev;
Ursula Braun08e33562011-12-19 22:56:34 +00001497 char username[9];
1498 char userdata[17];
1499 int rc;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001500 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001502 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001503 rc = netiucv_check_user(buf, count, username, userdata);
1504 if (rc)
1505 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001507 if (memcmp(username, priv->conn->userid, 9) &&
1508 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1509 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001510 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001511 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001512 }
1513 read_lock_bh(&iucv_connection_rwlock);
1514 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00001515 if (!strncmp(username, cp->userid, 9) &&
1516 !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001517 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00001518 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s "
1519 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001520 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 }
1522 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001523 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 memcpy(priv->conn->userid, username, 9);
Ursula Braun08e33562011-12-19 22:56:34 +00001525 memcpy(priv->conn->userdata, userdata, 17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527}
1528
1529static DEVICE_ATTR(user, 0644, user_show, user_write);
1530
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001531static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1532 char *buf)
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001533{
1534 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001536 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1538}
1539
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001540static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1541 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001543 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 struct net_device *ndev = priv->conn->netdev;
Ursula Braun9edebf12016-10-12 12:38:50 +02001545 unsigned int bs1;
1546 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001548 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 if (count >= 39)
1550 return -EINVAL;
1551
Ursula Braun9edebf12016-10-12 12:38:50 +02001552 rc = kstrtouint(buf, 0, &bs1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553
Ursula Braun9edebf12016-10-12 12:38:50 +02001554 if (rc == -EINVAL) {
1555 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %s\n",
1556 buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 return -EINVAL;
1558 }
Ursula Braun9edebf12016-10-12 12:38:50 +02001559 if ((rc == -ERANGE) || (bs1 > NETIUCV_BUFSIZE_MAX)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 IUCV_DBF_TEXT_(setup, 2,
1561 "buffer_write: buffer size %d too large\n",
1562 bs1);
1563 return -EINVAL;
1564 }
1565 if ((ndev->flags & IFF_RUNNING) &&
1566 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 IUCV_DBF_TEXT_(setup, 2,
1568 "buffer_write: buffer size %d too small\n",
1569 bs1);
1570 return -EINVAL;
1571 }
1572 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 IUCV_DBF_TEXT_(setup, 2,
1574 "buffer_write: buffer size %d too small\n",
1575 bs1);
1576 return -EINVAL;
1577 }
1578
1579 priv->conn->max_buffsize = bs1;
1580 if (!(ndev->flags & IFF_RUNNING))
1581 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1582
1583 return count;
1584
1585}
1586
1587static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1588
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001589static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1590 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001592 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001594 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1596}
1597
1598static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1599
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001600static ssize_t conn_fsm_show (struct device *dev,
1601 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001603 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001605 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1607}
1608
1609static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1610
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001611static ssize_t maxmulti_show (struct device *dev,
1612 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001614 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001616 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1618}
1619
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001620static ssize_t maxmulti_write (struct device *dev,
1621 struct device_attribute *attr,
1622 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001624 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001626 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 priv->conn->prof.maxmulti = 0;
1628 return count;
1629}
1630
1631static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1632
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001633static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1634 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001636 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001638 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1640}
1641
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001642static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1643 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001645 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001646
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001647 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 priv->conn->prof.maxcqueue = 0;
1649 return count;
1650}
1651
1652static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1653
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001654static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1655 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001657 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001659 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1661}
1662
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001663static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1664 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001666 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001667
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001668 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 priv->conn->prof.doios_single = 0;
1670 return count;
1671}
1672
1673static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1674
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001675static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1676 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001678 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001680 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1682}
1683
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001684static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1685 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001687 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001688
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001689 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 priv->conn->prof.doios_multi = 0;
1691 return count;
1692}
1693
1694static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1695
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001696static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1697 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001699 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001701 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1703}
1704
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001705static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1706 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001708 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001709
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001710 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 priv->conn->prof.txlen = 0;
1712 return count;
1713}
1714
1715static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1716
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001717static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1718 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001720 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001722 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1724}
1725
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001726static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1727 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001729 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001730
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001731 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 priv->conn->prof.tx_time = 0;
1733 return count;
1734}
1735
1736static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1737
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001738static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1739 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001741 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001743 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1745}
1746
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001747static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1748 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001750 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001752 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 priv->conn->prof.tx_pending = 0;
1754 return count;
1755}
1756
1757static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1758
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001759static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1760 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001762 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001764 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1766}
1767
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001768static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1769 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001771 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001773 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774 priv->conn->prof.tx_max_pending = 0;
1775 return count;
1776}
1777
1778static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1779
1780static struct attribute *netiucv_attrs[] = {
1781 &dev_attr_buffer.attr,
1782 &dev_attr_user.attr,
1783 NULL,
1784};
1785
1786static struct attribute_group netiucv_attr_group = {
1787 .attrs = netiucv_attrs,
1788};
1789
1790static struct attribute *netiucv_stat_attrs[] = {
1791 &dev_attr_device_fsm_state.attr,
1792 &dev_attr_connection_fsm_state.attr,
1793 &dev_attr_max_tx_buffer_used.attr,
1794 &dev_attr_max_chained_skbs.attr,
1795 &dev_attr_tx_single_write_ops.attr,
1796 &dev_attr_tx_multi_write_ops.attr,
1797 &dev_attr_netto_bytes.attr,
1798 &dev_attr_max_tx_io_time.attr,
1799 &dev_attr_tx_pending.attr,
1800 &dev_attr_tx_max_pending.attr,
1801 NULL,
1802};
1803
1804static struct attribute_group netiucv_stat_attr_group = {
1805 .name = "stats",
1806 .attrs = netiucv_stat_attrs,
1807};
1808
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001809static const struct attribute_group *netiucv_attr_groups[] = {
1810 &netiucv_stat_attr_group,
1811 &netiucv_attr_group,
1812 NULL,
1813};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001815static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001817 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001818 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 int ret;
1820
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001821 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822
1823 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001824 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825 dev->bus = &iucv_bus;
1826 dev->parent = iucv_root;
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001827 dev->groups = netiucv_attr_groups;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828 /*
1829 * The release function could be called after the
1830 * module has been unloaded. It's _only_ task is to
1831 * free the struct. Therefore, we specify kfree()
1832 * directly here. (Probably a little bit obfuscating
1833 * but legitime ...).
1834 */
1835 dev->release = (void (*)(struct device *))kfree;
1836 dev->driver = &netiucv_driver;
1837 } else
1838 return -ENOMEM;
1839
1840 ret = device_register(dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02001841 if (ret) {
1842 put_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 return ret;
Sebastian Ottc6304932009-09-11 10:28:38 +02001844 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 priv->dev = dev;
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001846 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848}
1849
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001850static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001852 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853 device_unregister(dev);
1854}
1855
1856/**
1857 * Allocate and initialize a new connection structure.
1858 * Add it to the list of netiucv connections;
1859 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001860static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
Ursula Braun08e33562011-12-19 22:56:34 +00001861 char *username,
1862 char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001864 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001866 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1867 if (!conn)
1868 goto out;
1869 skb_queue_head_init(&conn->collect_queue);
1870 skb_queue_head_init(&conn->commit_queue);
1871 spin_lock_init(&conn->collect_lock);
1872 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1873 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001875 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1876 if (!conn->rx_buff)
1877 goto out_conn;
1878 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1879 if (!conn->tx_buff)
1880 goto out_rx;
1881 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1882 conn_event_names, NR_CONN_STATES,
1883 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1884 GFP_KERNEL);
1885 if (!conn->fsm)
1886 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001888 fsm_settimer(conn->fsm, &conn->timer);
1889 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890
Ursula Braun08e33562011-12-19 22:56:34 +00001891 if (userdata)
1892 memcpy(conn->userdata, userdata, 17);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001893 if (username) {
1894 memcpy(conn->userid, username, 9);
1895 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001897
1898 write_lock_bh(&iucv_connection_rwlock);
1899 list_add_tail(&conn->list, &iucv_connection_list);
1900 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001902
1903out_tx:
1904 kfree_skb(conn->tx_buff);
1905out_rx:
1906 kfree_skb(conn->rx_buff);
1907out_conn:
1908 kfree(conn);
1909out:
1910 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911}
1912
1913/**
1914 * Release a connection structure and remove it from the
1915 * list of netiucv connections.
1916 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001917static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918{
Ursula Braun08e33562011-12-19 22:56:34 +00001919
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001920 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001921 write_lock_bh(&iucv_connection_rwlock);
1922 list_del_init(&conn->list);
1923 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001924 fsm_deltimer(&conn->timer);
1925 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001926 if (conn->path) {
Ursula Braun08e33562011-12-19 22:56:34 +00001927 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001928 kfree(conn->path);
1929 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001931 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001932 kfree_fsm(conn->fsm);
1933 kfree_skb(conn->rx_buff);
1934 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935}
1936
1937/**
1938 * Release everything of a net device.
1939 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001940static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001942 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001944 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945
1946 if (!dev)
1947 return;
1948
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 if (privptr) {
1950 if (privptr->conn)
1951 netiucv_remove_connection(privptr->conn);
1952 if (privptr->fsm)
1953 kfree_fsm(privptr->fsm);
1954 privptr->conn = NULL; privptr->fsm = NULL;
1955 /* privptr gets freed by free_netdev() */
1956 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957}
1958
1959/**
1960 * Initialize a net device. (Called from kernel in alloc_netdev())
1961 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001962static const struct net_device_ops netiucv_netdev_ops = {
1963 .ndo_open = netiucv_open,
1964 .ndo_stop = netiucv_close,
1965 .ndo_get_stats = netiucv_stats,
1966 .ndo_start_xmit = netiucv_tx,
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001967};
1968
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001969static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971 dev->mtu = NETIUCV_MTU_DEFAULT;
Jarod Wilson46b3ef42016-10-20 13:55:23 -04001972 dev->min_mtu = 576;
1973 dev->max_mtu = NETIUCV_MTU_MAX;
Stephen Rothwellcd1997f2017-06-08 19:06:29 +10001974 dev->needs_free_netdev = true;
1975 dev->priv_destructor = netiucv_free_netdevice;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976 dev->hard_header_len = NETIUCV_HDRLEN;
1977 dev->addr_len = 0;
1978 dev->type = ARPHRD_SLIP;
1979 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1980 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001981 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982}
1983
1984/**
1985 * Allocate and initialize everything of a net device.
1986 */
Ursula Braun08e33562011-12-19 22:56:34 +00001987static struct net_device *netiucv_init_netdevice(char *username, char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988{
1989 struct netiucv_priv *privptr;
1990 struct net_device *dev;
1991
1992 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
Tom Gundersenc835a672014-07-14 16:37:24 +02001993 NET_NAME_UNKNOWN, netiucv_setup_netdevice);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 if (!dev)
1995 return NULL;
Benjamin Poirieraaf95222013-06-13 09:09:47 -04001996 rtnl_lock();
Ursula Braun1d503562011-11-15 02:31:14 +00001997 if (dev_alloc_name(dev, dev->name) < 0)
1998 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002000 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
2002 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
2003 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002004 if (!privptr->fsm)
2005 goto out_netdev;
2006
Ursula Braun08e33562011-12-19 22:56:34 +00002007 privptr->conn = netiucv_new_connection(dev, username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002010 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 }
2012 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002014
2015out_fsm:
2016 kfree_fsm(privptr->fsm);
2017out_netdev:
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002018 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002019 free_netdev(dev);
2020 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021}
2022
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002023static ssize_t conn_write(struct device_driver *drv,
2024 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025{
Frank Pavlic16a83b32006-09-15 16:25:19 +02002026 char username[9];
Ursula Braun08e33562011-12-19 22:56:34 +00002027 char userdata[17];
2028 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002030 struct netiucv_priv *priv;
2031 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002033 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00002034 rc = netiucv_check_user(buf, count, username, userdata);
2035 if (rc)
2036 return rc;
Frank Pavlic16a83b32006-09-15 16:25:19 +02002037
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002038 read_lock_bh(&iucv_connection_rwlock);
2039 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00002040 if (!strncmp(username, cp->userid, 9) &&
2041 !strncmp(userdata, cp->userdata, 17)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002042 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00002043 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s "
2044 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002045 return -EEXIST;
2046 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02002047 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002048 read_unlock_bh(&iucv_connection_rwlock);
2049
Ursula Braun08e33562011-12-19 22:56:34 +00002050 dev = netiucv_init_netdevice(username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2053 return -ENODEV;
2054 }
2055
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002056 rc = netiucv_register_device(dev);
2057 if (rc) {
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002058 rtnl_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002060 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 goto out_free_ndev;
2062 }
2063
2064 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002065 priv = netdev_priv(dev);
2066 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002068 rc = register_netdevice(dev);
2069 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002070 if (rc)
2071 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072
Ursula Braun08e33562011-12-19 22:56:34 +00002073 dev_info(priv->dev, "The IUCV interface to %s has been established "
2074 "successfully\n",
2075 netiucv_printuser(priv->conn));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002076
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 return count;
2078
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002079out_unreg:
2080 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002083 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084}
2085
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002086static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002088static ssize_t remove_write (struct device_driver *drv,
2089 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002091 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 struct net_device *ndev;
2093 struct netiucv_priv *priv;
2094 struct device *dev;
2095 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002096 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 int i;
2098
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002099 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100
2101 if (count >= IFNAMSIZ)
Joe Perchesa419aef2009-08-18 11:18:35 -07002102 count = IFNAMSIZ - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002104 for (i = 0, p = buf; i < count && *p; i++, p++) {
2105 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 /* trailing lf, grr */
2107 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002108 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 }
2110 name[i] = '\0';
2111
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002112 read_lock_bh(&iucv_connection_rwlock);
2113 list_for_each_entry(cp, &iucv_connection_list, list) {
2114 ndev = cp->netdev;
2115 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002117 if (strncmp(name, ndev->name, count))
2118 continue;
2119 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002121 dev_warn(dev, "The IUCV device is connected"
2122 " to %s and cannot be removed\n",
2123 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002125 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 }
2127 unregister_netdev(ndev);
2128 netiucv_unregister_device(dev);
2129 return count;
2130 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002131 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2133 return -EINVAL;
2134}
2135
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002136static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002138static struct attribute * netiucv_drv_attrs[] = {
2139 &driver_attr_connection.attr,
2140 &driver_attr_remove.attr,
2141 NULL,
2142};
2143
2144static struct attribute_group netiucv_drv_attr_group = {
2145 .attrs = netiucv_drv_attrs,
2146};
2147
David Brownella4dbd672009-06-24 10:06:31 -07002148static const struct attribute_group *netiucv_drv_attr_groups[] = {
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002149 &netiucv_drv_attr_group,
2150 NULL,
2151};
2152
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002153static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002155 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156}
2157
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002158static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002160 struct iucv_connection *cp;
2161 struct net_device *ndev;
2162 struct netiucv_priv *priv;
2163 struct device *dev;
2164
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002165 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002166 while (!list_empty(&iucv_connection_list)) {
2167 cp = list_entry(iucv_connection_list.next,
2168 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002169 ndev = cp->netdev;
2170 priv = netdev_priv(ndev);
2171 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172
2173 unregister_netdev(ndev);
2174 netiucv_unregister_device(dev);
2175 }
2176
Ursula Braun1175b252009-06-16 10:30:43 +02002177 device_unregister(netiucv_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002179 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180 iucv_unregister_dbf_views();
2181
Ursula Braun8f7c5022008-12-25 13:39:47 +01002182 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 return;
2184}
2185
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002186static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002188 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002189
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002190 rc = iucv_register_dbf_views();
2191 if (rc)
2192 goto out;
2193 rc = iucv_register(&netiucv_handler, 1);
2194 if (rc)
2195 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002196 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002197 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002198 rc = driver_register(&netiucv_driver);
2199 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002200 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2201 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202 }
Ursula Braun1175b252009-06-16 10:30:43 +02002203 /* establish dummy device */
2204 netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
2205 if (!netiucv_dev) {
2206 rc = -ENOMEM;
2207 goto out_driver;
2208 }
2209 dev_set_name(netiucv_dev, "netiucv");
2210 netiucv_dev->bus = &iucv_bus;
2211 netiucv_dev->parent = iucv_root;
2212 netiucv_dev->release = (void (*)(struct device *))kfree;
2213 netiucv_dev->driver = &netiucv_driver;
2214 rc = device_register(netiucv_dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02002215 if (rc) {
2216 put_device(netiucv_dev);
Ursula Braun1175b252009-06-16 10:30:43 +02002217 goto out_driver;
Sebastian Ottc6304932009-09-11 10:28:38 +02002218 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002219 netiucv_banner();
2220 return rc;
2221
Ursula Braun1175b252009-06-16 10:30:43 +02002222out_driver:
2223 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002224out_iucv:
2225 iucv_unregister(&netiucv_handler, 1);
2226out_dbf:
2227 iucv_unregister_dbf_views();
2228out:
2229 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002231
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232module_init(netiucv_init);
2233module_exit(netiucv_exit);
2234MODULE_LICENSE("GPL");