blob: b9c7c1e61da296f743f7bbd6f5d30e43d5940117 [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 */
Julian Wiedmanncef6ff222017-08-15 17:02:46 +0200252static 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
Julian Wiedmanncef6ff222017-08-15 17:02:46 +0200259static 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))) {
Reshetova, Elena63354792017-06-30 13:07:58 +0300746 refcount_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;
Johannes Berg59ae1d12017-06-16 14:29:20 +0200762 skb_put_data(conn->tx_buff, &header, NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300763 skb_copy_from_linear_data(skb,
764 skb_put(conn->tx_buff, skb->len),
765 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 txbytes += skb->len;
767 txpackets++;
768 stat_maxcq++;
Reshetova, Elena63354792017-06-30 13:07:58 +0300769 refcount_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 dev_kfree_skb_any(skb);
771 }
772 if (conn->collect_len > conn->prof.maxmulti)
773 conn->prof.maxmulti = conn->collect_len;
774 conn->collect_len = 0;
775 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800776 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800778 return;
779 }
780
781 header.next = 0;
Johannes Berg59ae1d12017-06-16 14:29:20 +0200782 skb_put_data(conn->tx_buff, &header, NETIUCV_HDRLEN);
Aya Mahfouzee6edb92015-01-16 14:05:45 +0100783 conn->prof.send_stamp = jiffies;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800784 txmsg.class = 0;
785 txmsg.tag = 0;
786 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
787 conn->tx_buff->data, conn->tx_buff->len);
788 conn->prof.doios_multi++;
789 conn->prof.txlen += conn->tx_buff->len;
790 conn->prof.tx_pending++;
791 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
792 conn->prof.tx_max_pending = conn->prof.tx_pending;
793 if (rc) {
794 conn->prof.tx_pending--;
795 fsm_newstate(fi, CONN_STATE_IDLE);
796 if (privptr)
797 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800798 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
799 } else {
800 if (privptr) {
801 privptr->stats.tx_packets += txpackets;
802 privptr->stats.tx_bytes += txbytes;
803 }
804 if (stat_maxcq > conn->prof.maxcqueue)
805 conn->prof.maxcqueue = stat_maxcq;
806 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807}
808
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800809static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800811 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800813 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800815 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200818 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800820 conn->path = path;
821 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
822 path->flags = 0;
Ursula Braun08e33562011-12-19 22:56:34 +0000823 rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
826 return;
827 }
828 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800829 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
831}
832
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800833static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800835 struct iucv_event *ev = arg;
836 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200838 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800839 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840}
841
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800842static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800844 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800846 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200848 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 fsm_deltimer(&conn->timer);
850 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800851 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
853}
854
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800855static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800857 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200859 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000861 iucv_path_sever(conn->path, conn->userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 fsm_newstate(fi, CONN_STATE_STARTWAIT);
863}
864
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800865static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800867 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800869 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200871 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
873 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000874 iucv_path_sever(conn->path, conn->userdata);
875 dev_info(privptr->dev, "The peer z/VM guest %s has closed the "
876 "connection\n", netiucv_printuser(conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800878 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 fsm_newstate(fi, CONN_STATE_STARTWAIT);
880 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
881}
882
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800883static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800885 struct iucv_connection *conn = arg;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100886 struct net_device *netdev = conn->netdev;
887 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 int rc;
889
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200890 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800892 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800894 /*
895 * We must set the state before calling iucv_connect because the
896 * callback handler could be called at any point after the connection
897 * request is sent
898 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
900 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800901 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
Ursula Braun08e33562011-12-19 22:56:34 +0000902 IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n",
903 netdev->name, netiucv_printuser(conn));
904
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800905 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
Ursula Braun08e33562011-12-19 22:56:34 +0000906 NULL, conn->userdata, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800908 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100909 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800910 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
911 CONN_EVENT_TIMER, conn);
912 return;
913 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100914 dev_warn(privptr->dev,
915 "The IUCV device failed to connect to z/VM guest %s\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000916 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800917 fsm_newstate(fi, CONN_STATE_STARTWAIT);
918 break;
919 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100920 dev_warn(privptr->dev,
921 "The IUCV device failed to connect to the peer on z/VM"
Ursula Braun08e33562011-12-19 22:56:34 +0000922 " guest %s\n", netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800923 fsm_newstate(fi, CONN_STATE_STARTWAIT);
924 break;
925 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100926 dev_err(privptr->dev,
927 "Connecting the IUCV device would exceed the maximum"
928 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800929 fsm_newstate(fi, CONN_STATE_CONNERR);
930 break;
931 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100932 dev_err(privptr->dev,
933 "z/VM guest %s has too many IUCV connections"
934 " to connect with the IUCV device\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000935 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800936 fsm_newstate(fi, CONN_STATE_CONNERR);
937 break;
938 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100939 dev_err(privptr->dev,
940 "The IUCV device cannot connect to a z/VM guest with no"
941 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800942 fsm_newstate(fi, CONN_STATE_CONNERR);
943 break;
944 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100945 dev_err(privptr->dev,
946 "Connecting the IUCV device failed with error %d\n",
947 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800948 fsm_newstate(fi, CONN_STATE_CONNERR);
949 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 }
951 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800952 kfree(conn->path);
953 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954}
955
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800956static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957{
958 struct sk_buff *skb;
959
960 while ((skb = skb_dequeue(q))) {
Reshetova, Elena63354792017-06-30 13:07:58 +0300961 refcount_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 dev_kfree_skb_any(skb);
963 }
964}
965
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800966static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800968 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 struct iucv_connection *conn = ev->conn;
970 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800971 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200973 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974
975 fsm_deltimer(&conn->timer);
976 fsm_newstate(fi, CONN_STATE_STOPPED);
977 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800978 if (conn->path) {
979 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
Ursula Braun08e33562011-12-19 22:56:34 +0000980 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800981 kfree(conn->path);
982 conn->path = NULL;
983 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 netiucv_purge_skb_queue(&conn->commit_queue);
985 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
986}
987
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800988static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800990 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 struct net_device *netdev = conn->netdev;
992
Ursula Braunf082bca2008-07-14 09:59:30 +0200993 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
994 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995}
996
997static const fsm_node conn_fsm[] = {
998 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
999 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
1000
1001 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
1002 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
1003 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
1004 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
1005 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
1006 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
1007 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
1008
1009 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
1010 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1011 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1012 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
1013 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
1014
1015 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
1016 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
1017
1018 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
1019 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
1020 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
1021
1022 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
1023 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1024
1025 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1026 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1027};
1028
1029static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1030
Jeff Garzike82b0f22006-05-26 21:58:38 -04001031
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001032/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001034 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035
1036/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001037 * dev_action_start
1038 * @fi: An instance of an interface statemachine.
1039 * @event: The event, just happened.
1040 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001042 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001044static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001046 struct net_device *dev = arg;
1047 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001049 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001052 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053}
1054
1055/**
1056 * Shutdown connection by sending CONN_EVENT_STOP to it.
1057 *
1058 * @param fi An instance of an interface statemachine.
1059 * @param event The event, just happened.
1060 * @param arg Generic pointer, casted from struct net_device * upon call.
1061 */
1062static void
1063dev_action_stop(fsm_instance *fi, int event, void *arg)
1064{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001065 struct net_device *dev = arg;
1066 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 struct iucv_event ev;
1068
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001069 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070
1071 ev.conn = privptr->conn;
1072
1073 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1074 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1075}
1076
1077/**
1078 * Called from connection statemachine
1079 * when a connection is up and running.
1080 *
1081 * @param fi An instance of an interface statemachine.
1082 * @param event The event, just happened.
1083 * @param arg Generic pointer, casted from struct net_device * upon call.
1084 */
1085static void
1086dev_action_connup(fsm_instance *fi, int event, void *arg)
1087{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001088 struct net_device *dev = arg;
1089 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001091 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092
1093 switch (fsm_getstate(fi)) {
1094 case DEV_STATE_STARTWAIT:
1095 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001096 dev_info(privptr->dev,
1097 "The IUCV device has been connected"
Ursula Braun08e33562011-12-19 22:56:34 +00001098 " successfully to %s\n",
1099 netiucv_printuser(privptr->conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 IUCV_DBF_TEXT(setup, 3,
1101 "connection is up and running\n");
1102 break;
1103 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 IUCV_DBF_TEXT(data, 2,
1105 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1106 break;
1107 }
1108}
1109
1110/**
1111 * Called from connection statemachine
1112 * when a connection has been shutdown.
1113 *
1114 * @param fi An instance of an interface statemachine.
1115 * @param event The event, just happened.
1116 * @param arg Generic pointer, casted from struct net_device * upon call.
1117 */
1118static void
1119dev_action_conndown(fsm_instance *fi, int event, void *arg)
1120{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001121 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122
1123 switch (fsm_getstate(fi)) {
1124 case DEV_STATE_RUNNING:
1125 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1126 break;
1127 case DEV_STATE_STOPWAIT:
1128 fsm_newstate(fi, DEV_STATE_STOPPED);
1129 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1130 break;
1131 }
1132}
1133
1134static const fsm_node dev_fsm[] = {
1135 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1136
1137 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1138 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1139
1140 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1141 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1142
1143 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1144 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001145 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146};
1147
1148static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1149
1150/**
1151 * Transmit a packet.
1152 * This is a helper function for netiucv_tx().
1153 *
1154 * @param conn Connection to be used for sending.
1155 * @param skb Pointer to struct sk_buff of packet to send.
1156 * The linklevel header has already been set up
1157 * by netiucv_tx().
1158 *
1159 * @return 0 on success, -ERRNO on failure. (Never fails.)
1160 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001161static int netiucv_transmit_skb(struct iucv_connection *conn,
1162 struct sk_buff *skb)
1163{
1164 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001166 struct ll_header header;
1167 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168
1169 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1170 int l = skb->len + NETIUCV_HDRLEN;
1171
1172 spin_lock_irqsave(&conn->collect_lock, saveflags);
1173 if (conn->collect_len + l >
1174 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1175 rc = -EBUSY;
1176 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001177 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 } else {
Reshetova, Elena63354792017-06-30 13:07:58 +03001179 refcount_inc(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 skb_queue_tail(&conn->collect_queue, skb);
1181 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001182 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 }
1184 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1185 } else {
1186 struct sk_buff *nskb = skb;
1187 /**
1188 * Copy the skb to a new allocated skb in lowmem only if the
1189 * data is located above 2G in memory or tailroom is < 2.
1190 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001191 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1192 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 int copied = 0;
1194 if (hi || (skb_tailroom(skb) < 2)) {
1195 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1196 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1197 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1199 rc = -ENOMEM;
1200 return rc;
1201 } else {
1202 skb_reserve(nskb, NETIUCV_HDRLEN);
Johannes Berg59ae1d12017-06-16 14:29:20 +02001203 skb_put_data(nskb, skb->data, skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 }
1205 copied = 1;
1206 }
1207 /**
1208 * skb now is below 2G and has enough room. Add headers.
1209 */
1210 header.next = nskb->len + NETIUCV_HDRLEN;
1211 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1212 header.next = 0;
Johannes Berg59ae1d12017-06-16 14:29:20 +02001213 skb_put_data(nskb, &header, NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214
1215 fsm_newstate(conn->fsm, CONN_STATE_TX);
Aya Mahfouzee6edb92015-01-16 14:05:45 +01001216 conn->prof.send_stamp = jiffies;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001217
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001218 msg.tag = 1;
1219 msg.class = 0;
1220 rc = iucv_message_send(conn->path, &msg, 0, 0,
1221 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 conn->prof.doios_single++;
1223 conn->prof.txlen += skb->len;
1224 conn->prof.tx_pending++;
1225 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1226 conn->prof.tx_max_pending = conn->prof.tx_pending;
1227 if (rc) {
1228 struct netiucv_priv *privptr;
1229 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1230 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001231 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 if (privptr)
1233 privptr->stats.tx_errors++;
1234 if (copied)
1235 dev_kfree_skb(nskb);
1236 else {
1237 /**
1238 * Remove our headers. They get added
1239 * again on retransmit.
1240 */
1241 skb_pull(skb, NETIUCV_HDRLEN);
1242 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1243 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1245 } else {
1246 if (copied)
1247 dev_kfree_skb(skb);
Reshetova, Elena63354792017-06-30 13:07:58 +03001248 refcount_inc(&nskb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 skb_queue_tail(&conn->commit_queue, nskb);
1250 }
1251 }
1252
1253 return rc;
1254}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001255
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001256/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001258 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259
1260/**
1261 * Open an interface.
1262 * Called from generic network layer when ifconfig up is run.
1263 *
1264 * @param dev Pointer to interface struct.
1265 *
1266 * @return 0 on success, -ERRNO on failure. (Never fails.)
1267 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001268static int netiucv_open(struct net_device *dev)
1269{
1270 struct netiucv_priv *priv = netdev_priv(dev);
1271
1272 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 return 0;
1274}
1275
1276/**
1277 * Close an interface.
1278 * Called from generic network layer when ifconfig down is run.
1279 *
1280 * @param dev Pointer to interface struct.
1281 *
1282 * @return 0 on success, -ERRNO on failure. (Never fails.)
1283 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001284static int netiucv_close(struct net_device *dev)
1285{
1286 struct netiucv_priv *priv = netdev_priv(dev);
1287
1288 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 return 0;
1290}
1291
Ursula Braun1175b252009-06-16 10:30:43 +02001292static int netiucv_pm_prepare(struct device *dev)
1293{
1294 IUCV_DBF_TEXT(trace, 3, __func__);
1295 return 0;
1296}
1297
1298static void netiucv_pm_complete(struct device *dev)
1299{
1300 IUCV_DBF_TEXT(trace, 3, __func__);
1301 return;
1302}
1303
1304/**
1305 * netiucv_pm_freeze() - Freeze PM callback
1306 * @dev: netiucv device
1307 *
1308 * close open netiucv interfaces
1309 */
1310static int netiucv_pm_freeze(struct device *dev)
1311{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001312 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001313 struct net_device *ndev = NULL;
1314 int rc = 0;
1315
1316 IUCV_DBF_TEXT(trace, 3, __func__);
1317 if (priv && priv->conn)
1318 ndev = priv->conn->netdev;
1319 if (!ndev)
1320 goto out;
1321 netif_device_detach(ndev);
1322 priv->pm_state = fsm_getstate(priv->fsm);
1323 rc = netiucv_close(ndev);
1324out:
1325 return rc;
1326}
1327
1328/**
1329 * netiucv_pm_restore_thaw() - Thaw and restore PM callback
1330 * @dev: netiucv device
1331 *
1332 * re-open netiucv interfaces closed during freeze
1333 */
1334static int netiucv_pm_restore_thaw(struct device *dev)
1335{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001336 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001337 struct net_device *ndev = NULL;
1338 int rc = 0;
1339
1340 IUCV_DBF_TEXT(trace, 3, __func__);
1341 if (priv && priv->conn)
1342 ndev = priv->conn->netdev;
1343 if (!ndev)
1344 goto out;
1345 switch (priv->pm_state) {
1346 case DEV_STATE_RUNNING:
1347 case DEV_STATE_STARTWAIT:
1348 rc = netiucv_open(ndev);
1349 break;
1350 default:
1351 break;
1352 }
1353 netif_device_attach(ndev);
1354out:
1355 return rc;
1356}
1357
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358/**
1359 * Start transmission of a packet.
1360 * Called from generic network device layer.
1361 *
1362 * @param skb Pointer to buffer containing the packet.
1363 * @param dev Pointer to interface struct.
1364 *
1365 * @return 0 if packet consumed, !0 if packet rejected.
1366 * Note: If we return !0, then the packet is free'd by
1367 * the generic network layer.
1368 */
1369static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1370{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001371 struct netiucv_priv *privptr = netdev_priv(dev);
1372 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001374 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 /**
1376 * Some sanity checks ...
1377 */
1378 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1380 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001381 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382 }
1383 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 IUCV_DBF_TEXT(data, 2,
1385 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1386 dev_kfree_skb(skb);
1387 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001388 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 }
1390
1391 /**
1392 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001393 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 */
1395 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 dev_kfree_skb(skb);
1397 privptr->stats.tx_dropped++;
1398 privptr->stats.tx_errors++;
1399 privptr->stats.tx_carrier_errors++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001400 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 }
1402
1403 if (netiucv_test_and_set_busy(dev)) {
1404 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001405 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 }
Florian Westphal860e9532016-05-03 16:33:13 +02001407 netif_trans_update(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001408 rc = netiucv_transmit_skb(privptr->conn, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 netiucv_clear_busy(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001410 return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411}
1412
1413/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001414 * netiucv_stats
1415 * @dev: Pointer to interface struct.
1416 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 * Returns interface statistics of a device.
1418 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001419 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001421static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001423 struct netiucv_priv *priv = netdev_priv(dev);
1424
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001425 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001426 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427}
1428
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001429/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001431 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001433static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1434 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001436 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001438 IUCV_DBF_TEXT(trace, 5, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001439 return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));
1440}
1441
1442static int netiucv_check_user(const char *buf, size_t count, char *username,
1443 char *userdata)
1444{
1445 const char *p;
1446 int i;
1447
1448 p = strchr(buf, '.');
1449 if ((p && ((count > 26) ||
1450 ((p - buf) > 8) ||
1451 (buf + count - p > 18))) ||
1452 (!p && (count > 9))) {
1453 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1454 return -EINVAL;
1455 }
1456
1457 for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) {
1458 if (isalnum(*p) || *p == '$') {
1459 username[i] = toupper(*p);
1460 continue;
1461 }
1462 if (*p == '\n')
1463 /* trailing lf, grr */
1464 break;
1465 IUCV_DBF_TEXT_(setup, 2,
1466 "conn_write: invalid character %02x\n", *p);
1467 return -EINVAL;
1468 }
1469 while (i < 8)
1470 username[i++] = ' ';
1471 username[8] = '\0';
1472
1473 if (*p == '.') {
1474 p++;
1475 for (i = 0; i < 16 && *p; i++, p++) {
1476 if (*p == '\n')
1477 break;
1478 userdata[i] = toupper(*p);
1479 }
1480 while (i > 0 && i < 16)
1481 userdata[i++] = ' ';
1482 } else
1483 memcpy(userdata, iucvMagic_ascii, 16);
1484 userdata[16] = '\0';
1485 ASCEBC(userdata, 16);
1486
1487 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488}
1489
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001490static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1491 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001493 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 struct net_device *ndev = priv->conn->netdev;
Ursula Braun08e33562011-12-19 22:56:34 +00001495 char username[9];
1496 char userdata[17];
1497 int rc;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001498 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001500 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001501 rc = netiucv_check_user(buf, count, username, userdata);
1502 if (rc)
1503 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001505 if (memcmp(username, priv->conn->userid, 9) &&
1506 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1507 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001508 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001509 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001510 }
1511 read_lock_bh(&iucv_connection_rwlock);
1512 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00001513 if (!strncmp(username, cp->userid, 9) &&
1514 !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001515 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00001516 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s "
1517 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001518 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 }
1520 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001521 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 memcpy(priv->conn->userid, username, 9);
Ursula Braun08e33562011-12-19 22:56:34 +00001523 memcpy(priv->conn->userdata, userdata, 17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525}
1526
1527static DEVICE_ATTR(user, 0644, user_show, user_write);
1528
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001529static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1530 char *buf)
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001531{
1532 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001534 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1536}
1537
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001538static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1539 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001541 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 struct net_device *ndev = priv->conn->netdev;
Ursula Braun9edebf12016-10-12 12:38:50 +02001543 unsigned int bs1;
1544 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001546 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 if (count >= 39)
1548 return -EINVAL;
1549
Ursula Braun9edebf12016-10-12 12:38:50 +02001550 rc = kstrtouint(buf, 0, &bs1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551
Ursula Braun9edebf12016-10-12 12:38:50 +02001552 if (rc == -EINVAL) {
1553 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %s\n",
1554 buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 return -EINVAL;
1556 }
Ursula Braun9edebf12016-10-12 12:38:50 +02001557 if ((rc == -ERANGE) || (bs1 > NETIUCV_BUFSIZE_MAX)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 IUCV_DBF_TEXT_(setup, 2,
1559 "buffer_write: buffer size %d too large\n",
1560 bs1);
1561 return -EINVAL;
1562 }
1563 if ((ndev->flags & IFF_RUNNING) &&
1564 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 IUCV_DBF_TEXT_(setup, 2,
1566 "buffer_write: buffer size %d too small\n",
1567 bs1);
1568 return -EINVAL;
1569 }
1570 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 IUCV_DBF_TEXT_(setup, 2,
1572 "buffer_write: buffer size %d too small\n",
1573 bs1);
1574 return -EINVAL;
1575 }
1576
1577 priv->conn->max_buffsize = bs1;
1578 if (!(ndev->flags & IFF_RUNNING))
1579 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1580
1581 return count;
1582
1583}
1584
1585static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1586
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001587static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1588 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001590 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001592 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1594}
1595
1596static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1597
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001598static ssize_t conn_fsm_show (struct device *dev,
1599 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001601 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001603 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1605}
1606
1607static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1608
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001609static ssize_t maxmulti_show (struct device *dev,
1610 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001612 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001614 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1616}
1617
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001618static ssize_t maxmulti_write (struct device *dev,
1619 struct device_attribute *attr,
1620 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001622 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001624 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 priv->conn->prof.maxmulti = 0;
1626 return count;
1627}
1628
1629static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1630
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001631static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1632 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001634 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001636 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1638}
1639
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001640static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1641 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001643 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001644
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001645 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 priv->conn->prof.maxcqueue = 0;
1647 return count;
1648}
1649
1650static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1651
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001652static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1653 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001655 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001657 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1659}
1660
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001661static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1662 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001664 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001665
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001666 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 priv->conn->prof.doios_single = 0;
1668 return count;
1669}
1670
1671static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1672
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001673static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1674 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001676 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001678 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1680}
1681
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001682static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1683 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001685 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001686
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001687 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 priv->conn->prof.doios_multi = 0;
1689 return count;
1690}
1691
1692static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1693
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001694static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1695 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001697 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001699 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1701}
1702
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001703static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1704 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001706 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001707
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001708 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 priv->conn->prof.txlen = 0;
1710 return count;
1711}
1712
1713static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1714
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001715static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1716 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001718 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001720 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1722}
1723
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001724static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1725 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001727 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001728
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001729 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 priv->conn->prof.tx_time = 0;
1731 return count;
1732}
1733
1734static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1735
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001736static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1737 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001739 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001741 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1743}
1744
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001745static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1746 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001748 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001750 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 priv->conn->prof.tx_pending = 0;
1752 return count;
1753}
1754
1755static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1756
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001757static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1758 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001760 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001762 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1764}
1765
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001766static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1767 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001769 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001771 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 priv->conn->prof.tx_max_pending = 0;
1773 return count;
1774}
1775
1776static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1777
1778static struct attribute *netiucv_attrs[] = {
1779 &dev_attr_buffer.attr,
1780 &dev_attr_user.attr,
1781 NULL,
1782};
1783
1784static struct attribute_group netiucv_attr_group = {
1785 .attrs = netiucv_attrs,
1786};
1787
1788static struct attribute *netiucv_stat_attrs[] = {
1789 &dev_attr_device_fsm_state.attr,
1790 &dev_attr_connection_fsm_state.attr,
1791 &dev_attr_max_tx_buffer_used.attr,
1792 &dev_attr_max_chained_skbs.attr,
1793 &dev_attr_tx_single_write_ops.attr,
1794 &dev_attr_tx_multi_write_ops.attr,
1795 &dev_attr_netto_bytes.attr,
1796 &dev_attr_max_tx_io_time.attr,
1797 &dev_attr_tx_pending.attr,
1798 &dev_attr_tx_max_pending.attr,
1799 NULL,
1800};
1801
1802static struct attribute_group netiucv_stat_attr_group = {
1803 .name = "stats",
1804 .attrs = netiucv_stat_attrs,
1805};
1806
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001807static const struct attribute_group *netiucv_attr_groups[] = {
1808 &netiucv_stat_attr_group,
1809 &netiucv_attr_group,
1810 NULL,
1811};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001813static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001815 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001816 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817 int ret;
1818
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001819 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820
1821 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001822 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 dev->bus = &iucv_bus;
1824 dev->parent = iucv_root;
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001825 dev->groups = netiucv_attr_groups;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001826 /*
1827 * The release function could be called after the
1828 * module has been unloaded. It's _only_ task is to
1829 * free the struct. Therefore, we specify kfree()
1830 * directly here. (Probably a little bit obfuscating
1831 * but legitime ...).
1832 */
1833 dev->release = (void (*)(struct device *))kfree;
1834 dev->driver = &netiucv_driver;
1835 } else
1836 return -ENOMEM;
1837
1838 ret = device_register(dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02001839 if (ret) {
1840 put_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841 return ret;
Sebastian Ottc6304932009-09-11 10:28:38 +02001842 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 priv->dev = dev;
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001844 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846}
1847
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001848static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001850 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851 device_unregister(dev);
1852}
1853
1854/**
1855 * Allocate and initialize a new connection structure.
1856 * Add it to the list of netiucv connections;
1857 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001858static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
Ursula Braun08e33562011-12-19 22:56:34 +00001859 char *username,
1860 char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001862 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001864 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1865 if (!conn)
1866 goto out;
1867 skb_queue_head_init(&conn->collect_queue);
1868 skb_queue_head_init(&conn->commit_queue);
1869 spin_lock_init(&conn->collect_lock);
1870 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1871 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001873 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1874 if (!conn->rx_buff)
1875 goto out_conn;
1876 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1877 if (!conn->tx_buff)
1878 goto out_rx;
1879 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1880 conn_event_names, NR_CONN_STATES,
1881 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1882 GFP_KERNEL);
1883 if (!conn->fsm)
1884 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001886 fsm_settimer(conn->fsm, &conn->timer);
1887 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888
Ursula Braun08e33562011-12-19 22:56:34 +00001889 if (userdata)
1890 memcpy(conn->userdata, userdata, 17);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001891 if (username) {
1892 memcpy(conn->userid, username, 9);
1893 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001895
1896 write_lock_bh(&iucv_connection_rwlock);
1897 list_add_tail(&conn->list, &iucv_connection_list);
1898 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001900
1901out_tx:
1902 kfree_skb(conn->tx_buff);
1903out_rx:
1904 kfree_skb(conn->rx_buff);
1905out_conn:
1906 kfree(conn);
1907out:
1908 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909}
1910
1911/**
1912 * Release a connection structure and remove it from the
1913 * list of netiucv connections.
1914 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001915static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916{
Ursula Braun08e33562011-12-19 22:56:34 +00001917
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001918 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001919 write_lock_bh(&iucv_connection_rwlock);
1920 list_del_init(&conn->list);
1921 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001922 fsm_deltimer(&conn->timer);
1923 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001924 if (conn->path) {
Ursula Braun08e33562011-12-19 22:56:34 +00001925 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001926 kfree(conn->path);
1927 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001929 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001930 kfree_fsm(conn->fsm);
1931 kfree_skb(conn->rx_buff);
1932 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933}
1934
1935/**
1936 * Release everything of a net device.
1937 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001938static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001940 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001942 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943
1944 if (!dev)
1945 return;
1946
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 if (privptr) {
1948 if (privptr->conn)
1949 netiucv_remove_connection(privptr->conn);
1950 if (privptr->fsm)
1951 kfree_fsm(privptr->fsm);
1952 privptr->conn = NULL; privptr->fsm = NULL;
1953 /* privptr gets freed by free_netdev() */
1954 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955}
1956
1957/**
1958 * Initialize a net device. (Called from kernel in alloc_netdev())
1959 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001960static const struct net_device_ops netiucv_netdev_ops = {
1961 .ndo_open = netiucv_open,
1962 .ndo_stop = netiucv_close,
1963 .ndo_get_stats = netiucv_stats,
1964 .ndo_start_xmit = netiucv_tx,
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001965};
1966
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001967static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969 dev->mtu = NETIUCV_MTU_DEFAULT;
Jarod Wilson46b3ef42016-10-20 13:55:23 -04001970 dev->min_mtu = 576;
1971 dev->max_mtu = NETIUCV_MTU_MAX;
Stephen Rothwellcd1997f2017-06-08 19:06:29 +10001972 dev->needs_free_netdev = true;
1973 dev->priv_destructor = netiucv_free_netdevice;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 dev->hard_header_len = NETIUCV_HDRLEN;
1975 dev->addr_len = 0;
1976 dev->type = ARPHRD_SLIP;
1977 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1978 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001979 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980}
1981
1982/**
1983 * Allocate and initialize everything of a net device.
1984 */
Ursula Braun08e33562011-12-19 22:56:34 +00001985static struct net_device *netiucv_init_netdevice(char *username, char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986{
1987 struct netiucv_priv *privptr;
1988 struct net_device *dev;
1989
1990 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
Tom Gundersenc835a672014-07-14 16:37:24 +02001991 NET_NAME_UNKNOWN, netiucv_setup_netdevice);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 if (!dev)
1993 return NULL;
Benjamin Poirieraaf95222013-06-13 09:09:47 -04001994 rtnl_lock();
Ursula Braun1d503562011-11-15 02:31:14 +00001995 if (dev_alloc_name(dev, dev->name) < 0)
1996 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001998 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
2000 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
2001 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002002 if (!privptr->fsm)
2003 goto out_netdev;
2004
Ursula Braun08e33562011-12-19 22:56:34 +00002005 privptr->conn = netiucv_new_connection(dev, username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002008 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 }
2010 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002012
2013out_fsm:
2014 kfree_fsm(privptr->fsm);
2015out_netdev:
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002016 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002017 free_netdev(dev);
2018 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019}
2020
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02002021static ssize_t connection_store(struct device_driver *drv, const char *buf,
2022 size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023{
Frank Pavlic16a83b32006-09-15 16:25:19 +02002024 char username[9];
Ursula Braun08e33562011-12-19 22:56:34 +00002025 char userdata[17];
2026 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002028 struct netiucv_priv *priv;
2029 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002031 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00002032 rc = netiucv_check_user(buf, count, username, userdata);
2033 if (rc)
2034 return rc;
Frank Pavlic16a83b32006-09-15 16:25:19 +02002035
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002036 read_lock_bh(&iucv_connection_rwlock);
2037 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00002038 if (!strncmp(username, cp->userid, 9) &&
2039 !strncmp(userdata, cp->userdata, 17)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002040 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00002041 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s "
2042 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002043 return -EEXIST;
2044 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02002045 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002046 read_unlock_bh(&iucv_connection_rwlock);
2047
Ursula Braun08e33562011-12-19 22:56:34 +00002048 dev = netiucv_init_netdevice(username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2051 return -ENODEV;
2052 }
2053
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002054 rc = netiucv_register_device(dev);
2055 if (rc) {
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002056 rtnl_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002058 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 goto out_free_ndev;
2060 }
2061
2062 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002063 priv = netdev_priv(dev);
2064 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002066 rc = register_netdevice(dev);
2067 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002068 if (rc)
2069 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070
Ursula Braun08e33562011-12-19 22:56:34 +00002071 dev_info(priv->dev, "The IUCV interface to %s has been established "
2072 "successfully\n",
2073 netiucv_printuser(priv->conn));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002074
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 return count;
2076
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002077out_unreg:
2078 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002081 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082}
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02002083static DRIVER_ATTR_WO(connection);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02002085static ssize_t remove_store(struct device_driver *drv, const char *buf,
2086 size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002088 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 struct net_device *ndev;
2090 struct netiucv_priv *priv;
2091 struct device *dev;
2092 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002093 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 int i;
2095
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002096 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097
2098 if (count >= IFNAMSIZ)
Joe Perchesa419aef2009-08-18 11:18:35 -07002099 count = IFNAMSIZ - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002101 for (i = 0, p = buf; i < count && *p; i++, p++) {
2102 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 /* trailing lf, grr */
2104 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002105 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 }
2107 name[i] = '\0';
2108
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002109 read_lock_bh(&iucv_connection_rwlock);
2110 list_for_each_entry(cp, &iucv_connection_list, list) {
2111 ndev = cp->netdev;
2112 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002114 if (strncmp(name, ndev->name, count))
2115 continue;
2116 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002118 dev_warn(dev, "The IUCV device is connected"
2119 " to %s and cannot be removed\n",
2120 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002122 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123 }
2124 unregister_netdev(ndev);
2125 netiucv_unregister_device(dev);
2126 return count;
2127 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002128 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2130 return -EINVAL;
2131}
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02002132static DRIVER_ATTR_WO(remove);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002134static struct attribute * netiucv_drv_attrs[] = {
2135 &driver_attr_connection.attr,
2136 &driver_attr_remove.attr,
2137 NULL,
2138};
2139
2140static struct attribute_group netiucv_drv_attr_group = {
2141 .attrs = netiucv_drv_attrs,
2142};
2143
David Brownella4dbd672009-06-24 10:06:31 -07002144static const struct attribute_group *netiucv_drv_attr_groups[] = {
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002145 &netiucv_drv_attr_group,
2146 NULL,
2147};
2148
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002149static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002151 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152}
2153
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002154static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002156 struct iucv_connection *cp;
2157 struct net_device *ndev;
2158 struct netiucv_priv *priv;
2159 struct device *dev;
2160
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002161 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002162 while (!list_empty(&iucv_connection_list)) {
2163 cp = list_entry(iucv_connection_list.next,
2164 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002165 ndev = cp->netdev;
2166 priv = netdev_priv(ndev);
2167 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168
2169 unregister_netdev(ndev);
2170 netiucv_unregister_device(dev);
2171 }
2172
Ursula Braun1175b252009-06-16 10:30:43 +02002173 device_unregister(netiucv_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002175 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 iucv_unregister_dbf_views();
2177
Ursula Braun8f7c5022008-12-25 13:39:47 +01002178 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 return;
2180}
2181
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002182static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002184 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002185
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002186 rc = iucv_register_dbf_views();
2187 if (rc)
2188 goto out;
2189 rc = iucv_register(&netiucv_handler, 1);
2190 if (rc)
2191 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002192 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002193 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002194 rc = driver_register(&netiucv_driver);
2195 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002196 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2197 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198 }
Ursula Braun1175b252009-06-16 10:30:43 +02002199 /* establish dummy device */
2200 netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
2201 if (!netiucv_dev) {
2202 rc = -ENOMEM;
2203 goto out_driver;
2204 }
2205 dev_set_name(netiucv_dev, "netiucv");
2206 netiucv_dev->bus = &iucv_bus;
2207 netiucv_dev->parent = iucv_root;
2208 netiucv_dev->release = (void (*)(struct device *))kfree;
2209 netiucv_dev->driver = &netiucv_driver;
2210 rc = device_register(netiucv_dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02002211 if (rc) {
2212 put_device(netiucv_dev);
Ursula Braun1175b252009-06-16 10:30:43 +02002213 goto out_driver;
Sebastian Ottc6304932009-09-11 10:28:38 +02002214 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002215 netiucv_banner();
2216 return rc;
2217
Ursula Braun1175b252009-06-16 10:30:43 +02002218out_driver:
2219 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002220out_iucv:
2221 iucv_unregister(&netiucv_handler, 1);
2222out_dbf:
2223 iucv_unregister_dbf_views();
2224out:
2225 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002227
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228module_init(netiucv_init);
2229module_exit(netiucv_exit);
2230MODULE_LICENSE("GPL");