blob: 5ce2424ca7290397e43b55c66581071424da99b9 [file] [log] [blame]
Greg Kroah-Hartmanab9953f2017-11-14 18:38:04 +01001// SPDX-License-Identifier: GPL-2.0+
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * IUCV network driver
4 *
Ursula Braun1175b252009-06-16 10:30:43 +02005 * Copyright IBM Corp. 2001, 2009
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
Ursula Braun1175b252009-06-16 10:30:43 +02007 * Author(s):
8 * Original netiucv driver:
9 * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
10 * Sysfs integration and all bugs therein:
11 * Cornelia Huck (cornelia.huck@de.ibm.com)
12 * PM functions:
13 * Ursula Braun (ursula.braun@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 *
15 * Documentation used:
16 * the source of the original IUCV driver by:
17 * Stefan Hegewald <hegewald@de.ibm.com>
18 * Hartmut Penner <hpenner@de.ibm.com>
19 * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
20 * Martin Schwidefsky (schwidefsky@de.ibm.com)
21 * Alan Altmark (Alan_Altmark@us.ibm.com) Sept. 2000
Linus Torvalds1da177e2005-04-16 15:20:36 -070022 */
Jeff Garzike82b0f22006-05-26 21:58:38 -040023
Ursula Braun8f7c5022008-12-25 13:39:47 +010024#define KMSG_COMPONENT "netiucv"
25#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
26
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#undef DEBUG
28
29#include <linux/module.h>
30#include <linux/init.h>
31#include <linux/kernel.h>
32#include <linux/slab.h>
33#include <linux/errno.h>
34#include <linux/types.h>
35#include <linux/interrupt.h>
36#include <linux/timer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/bitops.h>
38
39#include <linux/signal.h>
40#include <linux/string.h>
41#include <linux/device.h>
42
43#include <linux/ip.h>
44#include <linux/if_arp.h>
45#include <linux/tcp.h>
46#include <linux/skbuff.h>
47#include <linux/ctype.h>
48#include <net/dst.h>
49
50#include <asm/io.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080051#include <linux/uaccess.h>
Ursula Braun08e33562011-12-19 22:56:34 +000052#include <asm/ebcdic.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
Martin Schwidefskyeebce382007-02-08 13:50:33 -080054#include <net/iucv/iucv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#include "fsm.h"
56
57MODULE_AUTHOR
58 ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)");
59MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
60
Martin Schwidefskyeebce382007-02-08 13:50:33 -080061/**
62 * Debug Facility stuff
63 */
64#define IUCV_DBF_SETUP_NAME "iucv_setup"
Ursula Braun08e33562011-12-19 22:56:34 +000065#define IUCV_DBF_SETUP_LEN 64
Martin Schwidefskyeebce382007-02-08 13:50:33 -080066#define IUCV_DBF_SETUP_PAGES 2
67#define IUCV_DBF_SETUP_NR_AREAS 1
68#define IUCV_DBF_SETUP_LEVEL 3
69
70#define IUCV_DBF_DATA_NAME "iucv_data"
71#define IUCV_DBF_DATA_LEN 128
72#define IUCV_DBF_DATA_PAGES 2
73#define IUCV_DBF_DATA_NR_AREAS 1
74#define IUCV_DBF_DATA_LEVEL 2
75
76#define IUCV_DBF_TRACE_NAME "iucv_trace"
77#define IUCV_DBF_TRACE_LEN 16
78#define IUCV_DBF_TRACE_PAGES 4
79#define IUCV_DBF_TRACE_NR_AREAS 1
80#define IUCV_DBF_TRACE_LEVEL 3
81
82#define IUCV_DBF_TEXT(name,level,text) \
83 do { \
84 debug_text_event(iucv_dbf_##name,level,text); \
85 } while (0)
86
87#define IUCV_DBF_HEX(name,level,addr,len) \
88 do { \
89 debug_event(iucv_dbf_##name,level,(void*)(addr),len); \
90 } while (0)
91
92DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf);
93
Peter Tiedemannf33780d2008-02-08 13:09:05 +010094#define IUCV_DBF_TEXT_(name, level, text...) \
95 do { \
Hendrik Brueckner8e6a8282013-09-18 17:21:34 +020096 if (debug_level_enabled(iucv_dbf_##name, level)) { \
Tejun Heo390dfd92009-10-29 22:34:14 +090097 char* __buf = get_cpu_var(iucv_dbf_txt_buf); \
98 sprintf(__buf, text); \
99 debug_text_event(iucv_dbf_##name, level, __buf); \
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100100 put_cpu_var(iucv_dbf_txt_buf); \
101 } \
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800102 } while (0)
103
104#define IUCV_DBF_SPRINTF(name,level,text...) \
105 do { \
106 debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
107 debug_sprintf_event(iucv_dbf_trace, level, text ); \
108 } while (0)
109
110/**
111 * some more debug stuff
112 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113#define PRINTK_HEADER " iucv: " /* for debugging */
114
Ursula Braun1175b252009-06-16 10:30:43 +0200115/* dummy device to make sure netiucv_pm functions are called */
116static struct device *netiucv_dev;
117
118static int netiucv_pm_prepare(struct device *);
119static void netiucv_pm_complete(struct device *);
120static int netiucv_pm_freeze(struct device *);
121static int netiucv_pm_restore_thaw(struct device *);
122
Alexey Dobriyan47145212009-12-14 18:00:08 -0800123static const struct dev_pm_ops netiucv_pm_ops = {
Ursula Braun1175b252009-06-16 10:30:43 +0200124 .prepare = netiucv_pm_prepare,
125 .complete = netiucv_pm_complete,
126 .freeze = netiucv_pm_freeze,
127 .thaw = netiucv_pm_restore_thaw,
128 .restore = netiucv_pm_restore_thaw,
129};
130
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131static struct device_driver netiucv_driver = {
Cornelia Huck22195102008-02-08 13:09:02 +0100132 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 .name = "netiucv",
134 .bus = &iucv_bus,
Ursula Braun1175b252009-06-16 10:30:43 +0200135 .pm = &netiucv_pm_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136};
137
Ursula Braun91e60eb2015-09-18 16:06:52 +0200138static int netiucv_callback_connreq(struct iucv_path *, u8 *, u8 *);
139static void netiucv_callback_connack(struct iucv_path *, u8 *);
140static void netiucv_callback_connrej(struct iucv_path *, u8 *);
141static void netiucv_callback_connsusp(struct iucv_path *, u8 *);
142static void netiucv_callback_connres(struct iucv_path *, u8 *);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800143static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
144static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
145
146static struct iucv_handler netiucv_handler = {
147 .path_pending = netiucv_callback_connreq,
148 .path_complete = netiucv_callback_connack,
149 .path_severed = netiucv_callback_connrej,
150 .path_quiesced = netiucv_callback_connsusp,
151 .path_resumed = netiucv_callback_connres,
152 .message_pending = netiucv_callback_rx,
153 .message_complete = netiucv_callback_txdone
154};
155
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156/**
157 * Per connection profiling data
158 */
159struct connection_profile {
160 unsigned long maxmulti;
161 unsigned long maxcqueue;
162 unsigned long doios_single;
163 unsigned long doios_multi;
164 unsigned long txlen;
165 unsigned long tx_time;
Aya Mahfouzee6edb92015-01-16 14:05:45 +0100166 unsigned long send_stamp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 unsigned long tx_pending;
168 unsigned long tx_max_pending;
169};
170
171/**
172 * Representation of one iucv connection
173 */
174struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800175 struct list_head list;
176 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 struct sk_buff *rx_buff;
178 struct sk_buff *tx_buff;
179 struct sk_buff_head collect_queue;
180 struct sk_buff_head commit_queue;
181 spinlock_t collect_lock;
182 int collect_len;
183 int max_buffsize;
184 fsm_timer timer;
185 fsm_instance *fsm;
186 struct net_device *netdev;
187 struct connection_profile prof;
188 char userid[9];
Ursula Braun08e33562011-12-19 22:56:34 +0000189 char userdata[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190};
191
192/**
193 * Linked list of all connection structs.
194 */
Denis Chengc11ca972008-01-26 14:11:13 +0100195static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200196static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
198/**
199 * Representation of event-data for the
200 * connection state machine.
201 */
202struct iucv_event {
203 struct iucv_connection *conn;
204 void *data;
205};
206
207/**
208 * Private part of the network device structure
209 */
210struct netiucv_priv {
211 struct net_device_stats stats;
212 unsigned long tbusy;
213 fsm_instance *fsm;
214 struct iucv_connection *conn;
215 struct device *dev;
Ursula Braun1175b252009-06-16 10:30:43 +0200216 int pm_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217};
218
219/**
220 * Link level header for a packet.
221 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800222struct ll_header {
223 u16 next;
224};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800226#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Ursula Braun08e33562011-12-19 22:56:34 +0000227#define NETIUCV_BUFSIZE_MAX 65537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
229#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
230#define NETIUCV_MTU_DEFAULT 9216
231#define NETIUCV_QUEUELEN_DEFAULT 50
232#define NETIUCV_TIMEOUT_5SEC 5000
233
234/**
235 * Compatibility macros for busy handling
236 * of network devices.
237 */
Julian Wiedmanncef6ff222017-08-15 17:02:46 +0200238static void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800240 struct netiucv_priv *priv = netdev_priv(dev);
241 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 netif_wake_queue(dev);
243}
244
Julian Wiedmanncef6ff222017-08-15 17:02:46 +0200245static int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800247 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800249 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250}
251
Ursula Braun08e33562011-12-19 22:56:34 +0000252static u8 iucvMagic_ascii[16] = {
253 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
254 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
255};
256
257static u8 iucvMagic_ebcdic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
259 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
260};
261
262/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 * Convert an iucv userId to its printable
264 * form (strip whitespace at end).
265 *
266 * @param An iucv userId
267 *
268 * @returns The printable string (static data!!)
269 */
Ursula Braun08e33562011-12-19 22:56:34 +0000270static char *netiucv_printname(char *name, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
Ursula Braun08e33562011-12-19 22:56:34 +0000272 static char tmp[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 char *p = tmp;
Ursula Braun08e33562011-12-19 22:56:34 +0000274 memcpy(tmp, name, len);
275 tmp[len] = '\0';
276 while (*p && ((p - tmp) < len) && (!isspace(*p)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 p++;
278 *p = '\0';
279 return tmp;
280}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400281
Ursula Braun08e33562011-12-19 22:56:34 +0000282static char *netiucv_printuser(struct iucv_connection *conn)
283{
284 static char tmp_uid[9];
285 static char tmp_udat[17];
286 static char buf[100];
287
288 if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) {
289 tmp_uid[8] = '\0';
290 tmp_udat[16] = '\0';
Ursula Braunbaac7892016-10-12 12:38:49 +0200291 memcpy(tmp_uid, netiucv_printname(conn->userid, 8), 8);
Ursula Braun08e33562011-12-19 22:56:34 +0000292 memcpy(tmp_udat, conn->userdata, 16);
293 EBCASC(tmp_udat, 16);
294 memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16);
295 sprintf(buf, "%s.%s", tmp_uid, tmp_udat);
296 return buf;
297 } else
298 return netiucv_printname(conn->userid, 8);
299}
300
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301/**
302 * States of the interface statemachine.
303 */
304enum dev_states {
305 DEV_STATE_STOPPED,
306 DEV_STATE_STARTWAIT,
307 DEV_STATE_STOPWAIT,
308 DEV_STATE_RUNNING,
309 /**
310 * MUST be always the last element!!
311 */
312 NR_DEV_STATES
313};
314
315static const char *dev_state_names[] = {
316 "Stopped",
317 "StartWait",
318 "StopWait",
319 "Running",
320};
321
322/**
323 * Events of the interface statemachine.
324 */
325enum dev_events {
326 DEV_EVENT_START,
327 DEV_EVENT_STOP,
328 DEV_EVENT_CONUP,
329 DEV_EVENT_CONDOWN,
330 /**
331 * MUST be always the last element!!
332 */
333 NR_DEV_EVENTS
334};
335
336static const char *dev_event_names[] = {
337 "Start",
338 "Stop",
339 "Connection up",
340 "Connection down",
341};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400342
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343/**
344 * Events of the connection statemachine
345 */
346enum conn_events {
347 /**
348 * Events, representing callbacks from
349 * lowlevel iucv layer)
350 */
351 CONN_EVENT_CONN_REQ,
352 CONN_EVENT_CONN_ACK,
353 CONN_EVENT_CONN_REJ,
354 CONN_EVENT_CONN_SUS,
355 CONN_EVENT_CONN_RES,
356 CONN_EVENT_RX,
357 CONN_EVENT_TXDONE,
358
359 /**
360 * Events, representing errors return codes from
361 * calls to lowlevel iucv layer
362 */
363
364 /**
365 * Event, representing timer expiry.
366 */
367 CONN_EVENT_TIMER,
368
369 /**
370 * Events, representing commands from upper levels.
371 */
372 CONN_EVENT_START,
373 CONN_EVENT_STOP,
374
375 /**
376 * MUST be always the last element!!
377 */
378 NR_CONN_EVENTS,
379};
380
381static const char *conn_event_names[] = {
382 "Remote connection request",
383 "Remote connection acknowledge",
384 "Remote connection reject",
385 "Connection suspended",
386 "Connection resumed",
387 "Data received",
388 "Data sent",
389
390 "Timer",
391
392 "Start",
393 "Stop",
394};
395
396/**
397 * States of the connection statemachine.
398 */
399enum conn_states {
400 /**
401 * Connection not assigned to any device,
402 * initial state, invalid
403 */
404 CONN_STATE_INVALID,
405
406 /**
407 * Userid assigned but not operating
408 */
409 CONN_STATE_STOPPED,
410
411 /**
412 * Connection registered,
413 * no connection request sent yet,
414 * no connection request received
415 */
416 CONN_STATE_STARTWAIT,
417
418 /**
419 * Connection registered and connection request sent,
420 * no acknowledge and no connection request received yet.
421 */
422 CONN_STATE_SETUPWAIT,
423
424 /**
425 * Connection up and running idle
426 */
427 CONN_STATE_IDLE,
428
429 /**
430 * Data sent, awaiting CONN_EVENT_TXDONE
431 */
432 CONN_STATE_TX,
433
434 /**
435 * Error during registration.
436 */
437 CONN_STATE_REGERR,
438
439 /**
440 * Error during registration.
441 */
442 CONN_STATE_CONNERR,
443
444 /**
445 * MUST be always the last element!!
446 */
447 NR_CONN_STATES,
448};
449
450static const char *conn_state_names[] = {
451 "Invalid",
452 "Stopped",
453 "StartWait",
454 "SetupWait",
455 "Idle",
456 "TX",
457 "Terminating",
458 "Registration error",
459 "Connect error",
460};
461
Jeff Garzike82b0f22006-05-26 21:58:38 -0400462
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463/**
464 * Debug Facility Stuff
465 */
466static debug_info_t *iucv_dbf_setup = NULL;
467static debug_info_t *iucv_dbf_data = NULL;
468static debug_info_t *iucv_dbf_trace = NULL;
469
470DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
471
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800472static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473{
Markus Elfringb646c082015-01-16 14:05:46 +0100474 debug_unregister(iucv_dbf_setup);
475 debug_unregister(iucv_dbf_data);
476 debug_unregister(iucv_dbf_trace);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800478static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479{
480 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700481 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 IUCV_DBF_SETUP_NR_AREAS,
483 IUCV_DBF_SETUP_LEN);
484 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700485 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 IUCV_DBF_DATA_NR_AREAS,
487 IUCV_DBF_DATA_LEN);
488 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700489 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 IUCV_DBF_TRACE_NR_AREAS,
491 IUCV_DBF_TRACE_LEN);
492
493 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
494 (iucv_dbf_trace == NULL)) {
495 iucv_unregister_dbf_views();
496 return -ENOMEM;
497 }
498 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
499 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
500
501 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
502 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
503
504 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
505 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
506
507 return 0;
508}
509
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800510/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800512 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800514static void netiucv_callback_rx(struct iucv_path *path,
515 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800517 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 struct iucv_event ev;
519
520 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800521 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
523}
524
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800525static void netiucv_callback_txdone(struct iucv_path *path,
526 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800528 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 struct iucv_event ev;
530
531 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800532 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
534}
535
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800536static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800538 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800540 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541}
542
Ursula Braun91e60eb2015-09-18 16:06:52 +0200543static int netiucv_callback_connreq(struct iucv_path *path, u8 *ipvmid,
544 u8 *ipuser)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800546 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 struct iucv_event ev;
Ursula Braun08e33562011-12-19 22:56:34 +0000548 static char tmp_user[9];
549 static char tmp_udat[17];
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800550 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800552 rc = -EINVAL;
Ursula Braun08e33562011-12-19 22:56:34 +0000553 memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8);
554 memcpy(tmp_udat, ipuser, 16);
555 EBCASC(tmp_udat, 16);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800556 read_lock_bh(&iucv_connection_rwlock);
557 list_for_each_entry(conn, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +0000558 if (strncmp(ipvmid, conn->userid, 8) ||
559 strncmp(ipuser, conn->userdata, 16))
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800560 continue;
561 /* Found a matching connection for this path. */
562 conn->path = path;
563 ev.conn = conn;
564 ev.data = path;
565 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
566 rc = 0;
567 }
Ursula Braun08e33562011-12-19 22:56:34 +0000568 IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n",
569 tmp_user, netiucv_printname(tmp_udat, 16));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800570 read_unlock_bh(&iucv_connection_rwlock);
571 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
573
Ursula Braun91e60eb2015-09-18 16:06:52 +0200574static void netiucv_callback_connrej(struct iucv_path *path, u8 *ipuser)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800576 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800578 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579}
580
Ursula Braun91e60eb2015-09-18 16:06:52 +0200581static void netiucv_callback_connsusp(struct iucv_path *path, u8 *ipuser)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800583 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800585 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586}
587
Ursula Braun91e60eb2015-09-18 16:06:52 +0200588static void netiucv_callback_connres(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_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593}
594
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100596 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100598static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599{
600}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400601
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800602/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800604 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
606/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800607 * netiucv_unpack_skb
608 * @conn: The connection where this skb has been received.
609 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800611 * Unpack a just received skb and hand it over to upper layers.
612 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800614static void netiucv_unpack_skb(struct iucv_connection *conn,
615 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616{
617 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800618 struct netiucv_priv *privptr = netdev_priv(dev);
619 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620
621 skb_put(pskb, NETIUCV_HDRLEN);
622 pskb->dev = dev;
623 pskb->ip_summed = CHECKSUM_NONE;
Hans Wippel6c37c602017-04-07 09:15:38 +0200624 pskb->protocol = cpu_to_be16(ETH_P_IP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
626 while (1) {
627 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800628 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
630 if (!header->next)
631 break;
632
633 skb_pull(pskb, NETIUCV_HDRLEN);
634 header->next -= offset;
635 offset += header->next;
636 header->next -= NETIUCV_HDRLEN;
637 if (skb_tailroom(pskb) < header->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
639 header->next, skb_tailroom(pskb));
640 return;
641 }
642 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700643 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 skb = dev_alloc_skb(pskb->len);
645 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 IUCV_DBF_TEXT(data, 2,
647 "Out of memory in netiucv_unpack_skb\n");
648 privptr->stats.rx_dropped++;
649 return;
650 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300651 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
652 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700653 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 skb->dev = pskb->dev;
655 skb->protocol = pskb->protocol;
656 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800657 privptr->stats.rx_packets++;
658 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 /*
660 * Since receiving is always initiated from a tasklet (in iucv.c),
661 * we must use netif_rx_ni() instead of netif_rx()
662 */
663 netif_rx_ni(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 skb_pull(pskb, header->next);
665 skb_put(pskb, NETIUCV_HDRLEN);
666 }
667}
668
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800669static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800671 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800673 struct iucv_message *msg = ev->data;
674 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 int rc;
676
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200677 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
679 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800680 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800682 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 return;
684 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800685 if (msg->length > conn->max_buffsize) {
686 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 privptr->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800689 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 return;
691 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700692 conn->rx_buff->data = conn->rx_buff->head;
693 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800695 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
696 msg->length, NULL);
697 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 privptr->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
700 return;
701 }
702 netiucv_unpack_skb(conn, conn->rx_buff);
703}
704
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800705static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800707 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800709 struct iucv_message *msg = ev->data;
710 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800712 u32 single_flag = msg->tag;
713 u32 txbytes = 0;
714 u32 txpackets = 0;
715 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 struct sk_buff *skb;
717 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800718 struct ll_header header;
719 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200721 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
Ursula Braund239ae32013-12-16 09:44:51 +0100723 if (!conn || !conn->netdev) {
724 IUCV_DBF_TEXT(data, 2,
725 "Send confirmation for unlinked connection\n");
726 return;
727 }
728 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 conn->prof.tx_pending--;
730 if (single_flag) {
731 if ((skb = skb_dequeue(&conn->commit_queue))) {
Reshetova, Elena63354792017-06-30 13:07:58 +0300732 refcount_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 if (privptr) {
734 privptr->stats.tx_packets++;
735 privptr->stats.tx_bytes +=
736 (skb->len - NETIUCV_HDRLEN
Ursula Braun998221c2009-11-12 21:46:30 +0000737 - NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 }
Ursula Braun998221c2009-11-12 21:46:30 +0000739 dev_kfree_skb_any(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 }
741 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700742 conn->tx_buff->data = conn->tx_buff->head;
743 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 conn->tx_buff->len = 0;
745 spin_lock_irqsave(&conn->collect_lock, saveflags);
746 while ((skb = skb_dequeue(&conn->collect_queue))) {
747 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
Johannes Berg59ae1d12017-06-16 14:29:20 +0200748 skb_put_data(conn->tx_buff, &header, NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300749 skb_copy_from_linear_data(skb,
750 skb_put(conn->tx_buff, skb->len),
751 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 txbytes += skb->len;
753 txpackets++;
754 stat_maxcq++;
Reshetova, Elena63354792017-06-30 13:07:58 +0300755 refcount_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 dev_kfree_skb_any(skb);
757 }
758 if (conn->collect_len > conn->prof.maxmulti)
759 conn->prof.maxmulti = conn->collect_len;
760 conn->collect_len = 0;
761 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800762 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800764 return;
765 }
766
767 header.next = 0;
Johannes Berg59ae1d12017-06-16 14:29:20 +0200768 skb_put_data(conn->tx_buff, &header, NETIUCV_HDRLEN);
Aya Mahfouzee6edb92015-01-16 14:05:45 +0100769 conn->prof.send_stamp = jiffies;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800770 txmsg.class = 0;
771 txmsg.tag = 0;
772 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
773 conn->tx_buff->data, conn->tx_buff->len);
774 conn->prof.doios_multi++;
775 conn->prof.txlen += conn->tx_buff->len;
776 conn->prof.tx_pending++;
777 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
778 conn->prof.tx_max_pending = conn->prof.tx_pending;
779 if (rc) {
780 conn->prof.tx_pending--;
781 fsm_newstate(fi, CONN_STATE_IDLE);
782 if (privptr)
783 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800784 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
785 } else {
786 if (privptr) {
787 privptr->stats.tx_packets += txpackets;
788 privptr->stats.tx_bytes += txbytes;
789 }
790 if (stat_maxcq > conn->prof.maxcqueue)
791 conn->prof.maxcqueue = stat_maxcq;
792 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793}
794
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800795static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800797 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800799 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800801 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200804 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800806 conn->path = path;
807 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
808 path->flags = 0;
Ursula Braun08e33562011-12-19 22:56:34 +0000809 rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
812 return;
813 }
814 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800815 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
817}
818
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800819static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800821 struct iucv_event *ev = arg;
822 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200824 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800825 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826}
827
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800828static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800830 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800832 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200834 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 fsm_deltimer(&conn->timer);
836 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800837 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
839}
840
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800841static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800843 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200845 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000847 iucv_path_sever(conn->path, conn->userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 fsm_newstate(fi, CONN_STATE_STARTWAIT);
849}
850
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800851static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800853 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800855 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200857 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
859 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000860 iucv_path_sever(conn->path, conn->userdata);
861 dev_info(privptr->dev, "The peer z/VM guest %s has closed the "
862 "connection\n", netiucv_printuser(conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800864 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 fsm_newstate(fi, CONN_STATE_STARTWAIT);
866 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
867}
868
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800869static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800871 struct iucv_connection *conn = arg;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100872 struct net_device *netdev = conn->netdev;
873 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 int rc;
875
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200876 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800878 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800880 /*
881 * We must set the state before calling iucv_connect because the
882 * callback handler could be called at any point after the connection
883 * request is sent
884 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
886 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800887 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
Ursula Braun08e33562011-12-19 22:56:34 +0000888 IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n",
889 netdev->name, netiucv_printuser(conn));
890
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800891 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
Ursula Braun08e33562011-12-19 22:56:34 +0000892 NULL, conn->userdata, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800894 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100895 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800896 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
897 CONN_EVENT_TIMER, conn);
898 return;
899 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100900 dev_warn(privptr->dev,
901 "The IUCV device failed to connect to z/VM guest %s\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000902 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800903 fsm_newstate(fi, CONN_STATE_STARTWAIT);
904 break;
905 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100906 dev_warn(privptr->dev,
907 "The IUCV device failed to connect to the peer on z/VM"
Ursula Braun08e33562011-12-19 22:56:34 +0000908 " guest %s\n", netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800909 fsm_newstate(fi, CONN_STATE_STARTWAIT);
910 break;
911 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100912 dev_err(privptr->dev,
913 "Connecting the IUCV device would exceed the maximum"
914 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800915 fsm_newstate(fi, CONN_STATE_CONNERR);
916 break;
917 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100918 dev_err(privptr->dev,
919 "z/VM guest %s has too many IUCV connections"
920 " to connect with the IUCV device\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000921 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800922 fsm_newstate(fi, CONN_STATE_CONNERR);
923 break;
924 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100925 dev_err(privptr->dev,
926 "The IUCV device cannot connect to a z/VM guest with no"
927 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800928 fsm_newstate(fi, CONN_STATE_CONNERR);
929 break;
930 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100931 dev_err(privptr->dev,
932 "Connecting the IUCV device failed with error %d\n",
933 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800934 fsm_newstate(fi, CONN_STATE_CONNERR);
935 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 }
937 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800938 kfree(conn->path);
939 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940}
941
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800942static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943{
944 struct sk_buff *skb;
945
946 while ((skb = skb_dequeue(q))) {
Reshetova, Elena63354792017-06-30 13:07:58 +0300947 refcount_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 dev_kfree_skb_any(skb);
949 }
950}
951
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800952static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800954 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 struct iucv_connection *conn = ev->conn;
956 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800957 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200959 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
961 fsm_deltimer(&conn->timer);
962 fsm_newstate(fi, CONN_STATE_STOPPED);
963 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800964 if (conn->path) {
965 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
Ursula Braun08e33562011-12-19 22:56:34 +0000966 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800967 kfree(conn->path);
968 conn->path = NULL;
969 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 netiucv_purge_skb_queue(&conn->commit_queue);
971 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
972}
973
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800974static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800976 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 struct net_device *netdev = conn->netdev;
978
Ursula Braunf082bca2008-07-14 09:59:30 +0200979 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
980 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981}
982
983static const fsm_node conn_fsm[] = {
984 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
985 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
986
987 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
988 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
989 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
990 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
991 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
992 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
993 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
994
995 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
996 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
997 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
998 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
999 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
1000
1001 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
1002 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
1003
1004 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
1005 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
1006 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
1007
1008 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
1009 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1010
1011 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1012 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1013};
1014
1015static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1016
Jeff Garzike82b0f22006-05-26 21:58:38 -04001017
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001018/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001020 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021
1022/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001023 * dev_action_start
1024 * @fi: An instance of an interface statemachine.
1025 * @event: The event, just happened.
1026 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001028 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001030static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001032 struct net_device *dev = arg;
1033 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001035 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001038 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039}
1040
1041/**
1042 * Shutdown connection by sending CONN_EVENT_STOP to it.
1043 *
1044 * @param fi An instance of an interface statemachine.
1045 * @param event The event, just happened.
1046 * @param arg Generic pointer, casted from struct net_device * upon call.
1047 */
1048static void
1049dev_action_stop(fsm_instance *fi, int event, void *arg)
1050{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001051 struct net_device *dev = arg;
1052 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 struct iucv_event ev;
1054
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001055 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
1057 ev.conn = privptr->conn;
1058
1059 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1060 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1061}
1062
1063/**
1064 * Called from connection statemachine
1065 * when a connection is up and running.
1066 *
1067 * @param fi An instance of an interface statemachine.
1068 * @param event The event, just happened.
1069 * @param arg Generic pointer, casted from struct net_device * upon call.
1070 */
1071static void
1072dev_action_connup(fsm_instance *fi, int event, void *arg)
1073{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001074 struct net_device *dev = arg;
1075 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001077 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078
1079 switch (fsm_getstate(fi)) {
1080 case DEV_STATE_STARTWAIT:
1081 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001082 dev_info(privptr->dev,
1083 "The IUCV device has been connected"
Ursula Braun08e33562011-12-19 22:56:34 +00001084 " successfully to %s\n",
1085 netiucv_printuser(privptr->conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 IUCV_DBF_TEXT(setup, 3,
1087 "connection is up and running\n");
1088 break;
1089 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 IUCV_DBF_TEXT(data, 2,
1091 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1092 break;
1093 }
1094}
1095
1096/**
1097 * Called from connection statemachine
1098 * when a connection has been shutdown.
1099 *
1100 * @param fi An instance of an interface statemachine.
1101 * @param event The event, just happened.
1102 * @param arg Generic pointer, casted from struct net_device * upon call.
1103 */
1104static void
1105dev_action_conndown(fsm_instance *fi, int event, void *arg)
1106{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001107 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108
1109 switch (fsm_getstate(fi)) {
1110 case DEV_STATE_RUNNING:
1111 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1112 break;
1113 case DEV_STATE_STOPWAIT:
1114 fsm_newstate(fi, DEV_STATE_STOPPED);
1115 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1116 break;
1117 }
1118}
1119
1120static const fsm_node dev_fsm[] = {
1121 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1122
1123 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1124 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1125
1126 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1127 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1128
1129 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1130 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001131 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132};
1133
1134static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1135
1136/**
1137 * Transmit a packet.
1138 * This is a helper function for netiucv_tx().
1139 *
1140 * @param conn Connection to be used for sending.
1141 * @param skb Pointer to struct sk_buff of packet to send.
1142 * The linklevel header has already been set up
1143 * by netiucv_tx().
1144 *
1145 * @return 0 on success, -ERRNO on failure. (Never fails.)
1146 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001147static int netiucv_transmit_skb(struct iucv_connection *conn,
1148 struct sk_buff *skb)
1149{
1150 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001152 struct ll_header header;
1153 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154
1155 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1156 int l = skb->len + NETIUCV_HDRLEN;
1157
1158 spin_lock_irqsave(&conn->collect_lock, saveflags);
1159 if (conn->collect_len + l >
1160 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1161 rc = -EBUSY;
1162 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001163 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 } else {
Reshetova, Elena63354792017-06-30 13:07:58 +03001165 refcount_inc(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 skb_queue_tail(&conn->collect_queue, skb);
1167 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001168 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 }
1170 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1171 } else {
1172 struct sk_buff *nskb = skb;
1173 /**
1174 * Copy the skb to a new allocated skb in lowmem only if the
1175 * data is located above 2G in memory or tailroom is < 2.
1176 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001177 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1178 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 int copied = 0;
1180 if (hi || (skb_tailroom(skb) < 2)) {
1181 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1182 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1183 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1185 rc = -ENOMEM;
1186 return rc;
1187 } else {
1188 skb_reserve(nskb, NETIUCV_HDRLEN);
Johannes Berg59ae1d12017-06-16 14:29:20 +02001189 skb_put_data(nskb, skb->data, skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 }
1191 copied = 1;
1192 }
1193 /**
1194 * skb now is below 2G and has enough room. Add headers.
1195 */
1196 header.next = nskb->len + NETIUCV_HDRLEN;
1197 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1198 header.next = 0;
Johannes Berg59ae1d12017-06-16 14:29:20 +02001199 skb_put_data(nskb, &header, NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200
1201 fsm_newstate(conn->fsm, CONN_STATE_TX);
Aya Mahfouzee6edb92015-01-16 14:05:45 +01001202 conn->prof.send_stamp = jiffies;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001203
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001204 msg.tag = 1;
1205 msg.class = 0;
1206 rc = iucv_message_send(conn->path, &msg, 0, 0,
1207 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 conn->prof.doios_single++;
1209 conn->prof.txlen += skb->len;
1210 conn->prof.tx_pending++;
1211 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1212 conn->prof.tx_max_pending = conn->prof.tx_pending;
1213 if (rc) {
1214 struct netiucv_priv *privptr;
1215 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1216 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001217 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 if (privptr)
1219 privptr->stats.tx_errors++;
1220 if (copied)
1221 dev_kfree_skb(nskb);
1222 else {
1223 /**
1224 * Remove our headers. They get added
1225 * again on retransmit.
1226 */
1227 skb_pull(skb, NETIUCV_HDRLEN);
1228 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1229 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1231 } else {
1232 if (copied)
1233 dev_kfree_skb(skb);
Reshetova, Elena63354792017-06-30 13:07:58 +03001234 refcount_inc(&nskb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 skb_queue_tail(&conn->commit_queue, nskb);
1236 }
1237 }
1238
1239 return rc;
1240}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001241
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001242/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001244 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245
1246/**
1247 * Open an interface.
1248 * Called from generic network layer when ifconfig up is run.
1249 *
1250 * @param dev Pointer to interface struct.
1251 *
1252 * @return 0 on success, -ERRNO on failure. (Never fails.)
1253 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001254static int netiucv_open(struct net_device *dev)
1255{
1256 struct netiucv_priv *priv = netdev_priv(dev);
1257
1258 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 return 0;
1260}
1261
1262/**
1263 * Close an interface.
1264 * Called from generic network layer when ifconfig down is run.
1265 *
1266 * @param dev Pointer to interface struct.
1267 *
1268 * @return 0 on success, -ERRNO on failure. (Never fails.)
1269 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001270static int netiucv_close(struct net_device *dev)
1271{
1272 struct netiucv_priv *priv = netdev_priv(dev);
1273
1274 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 return 0;
1276}
1277
Ursula Braun1175b252009-06-16 10:30:43 +02001278static int netiucv_pm_prepare(struct device *dev)
1279{
1280 IUCV_DBF_TEXT(trace, 3, __func__);
1281 return 0;
1282}
1283
1284static void netiucv_pm_complete(struct device *dev)
1285{
1286 IUCV_DBF_TEXT(trace, 3, __func__);
1287 return;
1288}
1289
1290/**
1291 * netiucv_pm_freeze() - Freeze PM callback
1292 * @dev: netiucv device
1293 *
1294 * close open netiucv interfaces
1295 */
1296static int netiucv_pm_freeze(struct device *dev)
1297{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001298 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001299 struct net_device *ndev = NULL;
1300 int rc = 0;
1301
1302 IUCV_DBF_TEXT(trace, 3, __func__);
1303 if (priv && priv->conn)
1304 ndev = priv->conn->netdev;
1305 if (!ndev)
1306 goto out;
1307 netif_device_detach(ndev);
1308 priv->pm_state = fsm_getstate(priv->fsm);
1309 rc = netiucv_close(ndev);
1310out:
1311 return rc;
1312}
1313
1314/**
1315 * netiucv_pm_restore_thaw() - Thaw and restore PM callback
1316 * @dev: netiucv device
1317 *
1318 * re-open netiucv interfaces closed during freeze
1319 */
1320static int netiucv_pm_restore_thaw(struct device *dev)
1321{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001322 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001323 struct net_device *ndev = NULL;
1324 int rc = 0;
1325
1326 IUCV_DBF_TEXT(trace, 3, __func__);
1327 if (priv && priv->conn)
1328 ndev = priv->conn->netdev;
1329 if (!ndev)
1330 goto out;
1331 switch (priv->pm_state) {
1332 case DEV_STATE_RUNNING:
1333 case DEV_STATE_STARTWAIT:
1334 rc = netiucv_open(ndev);
1335 break;
1336 default:
1337 break;
1338 }
1339 netif_device_attach(ndev);
1340out:
1341 return rc;
1342}
1343
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344/**
1345 * Start transmission of a packet.
1346 * Called from generic network device layer.
1347 *
1348 * @param skb Pointer to buffer containing the packet.
1349 * @param dev Pointer to interface struct.
1350 *
1351 * @return 0 if packet consumed, !0 if packet rejected.
1352 * Note: If we return !0, then the packet is free'd by
1353 * the generic network layer.
1354 */
1355static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1356{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001357 struct netiucv_priv *privptr = netdev_priv(dev);
1358 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001360 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 /**
1362 * Some sanity checks ...
1363 */
1364 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1366 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001367 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 }
1369 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 IUCV_DBF_TEXT(data, 2,
1371 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1372 dev_kfree_skb(skb);
1373 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001374 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 }
1376
1377 /**
1378 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001379 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 */
1381 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382 dev_kfree_skb(skb);
1383 privptr->stats.tx_dropped++;
1384 privptr->stats.tx_errors++;
1385 privptr->stats.tx_carrier_errors++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001386 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 }
1388
1389 if (netiucv_test_and_set_busy(dev)) {
1390 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001391 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 }
Florian Westphal860e9532016-05-03 16:33:13 +02001393 netif_trans_update(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001394 rc = netiucv_transmit_skb(privptr->conn, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 netiucv_clear_busy(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001396 return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397}
1398
1399/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001400 * netiucv_stats
1401 * @dev: Pointer to interface struct.
1402 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 * Returns interface statistics of a device.
1404 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001405 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001407static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001409 struct netiucv_priv *priv = netdev_priv(dev);
1410
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001411 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001412 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413}
1414
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001415/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001417 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001419static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1420 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001422 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001424 IUCV_DBF_TEXT(trace, 5, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001425 return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));
1426}
1427
1428static int netiucv_check_user(const char *buf, size_t count, char *username,
1429 char *userdata)
1430{
1431 const char *p;
1432 int i;
1433
1434 p = strchr(buf, '.');
1435 if ((p && ((count > 26) ||
1436 ((p - buf) > 8) ||
1437 (buf + count - p > 18))) ||
1438 (!p && (count > 9))) {
1439 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1440 return -EINVAL;
1441 }
1442
1443 for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) {
1444 if (isalnum(*p) || *p == '$') {
1445 username[i] = toupper(*p);
1446 continue;
1447 }
1448 if (*p == '\n')
1449 /* trailing lf, grr */
1450 break;
1451 IUCV_DBF_TEXT_(setup, 2,
1452 "conn_write: invalid character %02x\n", *p);
1453 return -EINVAL;
1454 }
1455 while (i < 8)
1456 username[i++] = ' ';
1457 username[8] = '\0';
1458
1459 if (*p == '.') {
1460 p++;
1461 for (i = 0; i < 16 && *p; i++, p++) {
1462 if (*p == '\n')
1463 break;
1464 userdata[i] = toupper(*p);
1465 }
1466 while (i > 0 && i < 16)
1467 userdata[i++] = ' ';
1468 } else
1469 memcpy(userdata, iucvMagic_ascii, 16);
1470 userdata[16] = '\0';
1471 ASCEBC(userdata, 16);
1472
1473 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474}
1475
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001476static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1477 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001479 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 struct net_device *ndev = priv->conn->netdev;
Ursula Braun08e33562011-12-19 22:56:34 +00001481 char username[9];
1482 char userdata[17];
1483 int rc;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001484 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001486 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001487 rc = netiucv_check_user(buf, count, username, userdata);
1488 if (rc)
1489 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001491 if (memcmp(username, priv->conn->userid, 9) &&
1492 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1493 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001494 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001495 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001496 }
1497 read_lock_bh(&iucv_connection_rwlock);
1498 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00001499 if (!strncmp(username, cp->userid, 9) &&
1500 !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001501 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00001502 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s "
1503 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001504 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 }
1506 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001507 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 memcpy(priv->conn->userid, username, 9);
Ursula Braun08e33562011-12-19 22:56:34 +00001509 memcpy(priv->conn->userdata, userdata, 17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511}
1512
1513static DEVICE_ATTR(user, 0644, user_show, user_write);
1514
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001515static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1516 char *buf)
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001517{
1518 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001520 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1522}
1523
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001524static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1525 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001527 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 struct net_device *ndev = priv->conn->netdev;
Ursula Braun9edebf12016-10-12 12:38:50 +02001529 unsigned int bs1;
1530 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001532 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 if (count >= 39)
1534 return -EINVAL;
1535
Ursula Braun9edebf12016-10-12 12:38:50 +02001536 rc = kstrtouint(buf, 0, &bs1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537
Ursula Braun9edebf12016-10-12 12:38:50 +02001538 if (rc == -EINVAL) {
1539 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %s\n",
1540 buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 return -EINVAL;
1542 }
Ursula Braun9edebf12016-10-12 12:38:50 +02001543 if ((rc == -ERANGE) || (bs1 > NETIUCV_BUFSIZE_MAX)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 IUCV_DBF_TEXT_(setup, 2,
1545 "buffer_write: buffer size %d too large\n",
1546 bs1);
1547 return -EINVAL;
1548 }
1549 if ((ndev->flags & IFF_RUNNING) &&
1550 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 IUCV_DBF_TEXT_(setup, 2,
1552 "buffer_write: buffer size %d too small\n",
1553 bs1);
1554 return -EINVAL;
1555 }
1556 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 IUCV_DBF_TEXT_(setup, 2,
1558 "buffer_write: buffer size %d too small\n",
1559 bs1);
1560 return -EINVAL;
1561 }
1562
1563 priv->conn->max_buffsize = bs1;
1564 if (!(ndev->flags & IFF_RUNNING))
1565 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1566
1567 return count;
1568
1569}
1570
1571static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1572
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001573static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1574 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001576 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001578 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1580}
1581
1582static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1583
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001584static ssize_t conn_fsm_show (struct device *dev,
1585 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001587 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001589 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1591}
1592
1593static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1594
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001595static ssize_t maxmulti_show (struct device *dev,
1596 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001598 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001600 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1602}
1603
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001604static ssize_t maxmulti_write (struct device *dev,
1605 struct device_attribute *attr,
1606 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001608 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001610 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 priv->conn->prof.maxmulti = 0;
1612 return count;
1613}
1614
1615static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1616
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001617static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1618 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001620 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001622 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1624}
1625
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001626static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1627 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001629 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001630
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001631 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 priv->conn->prof.maxcqueue = 0;
1633 return count;
1634}
1635
1636static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1637
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001638static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1639 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001641 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001643 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1645}
1646
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001647static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1648 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001650 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001651
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001652 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 priv->conn->prof.doios_single = 0;
1654 return count;
1655}
1656
1657static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1658
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001659static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1660 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001662 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001664 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1666}
1667
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001668static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1669 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001671 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001672
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001673 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 priv->conn->prof.doios_multi = 0;
1675 return count;
1676}
1677
1678static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1679
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001680static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1681 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001683 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001685 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1687}
1688
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001689static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1690 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001692 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001693
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001694 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 priv->conn->prof.txlen = 0;
1696 return count;
1697}
1698
1699static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1700
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001701static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1702 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001704 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001706 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1708}
1709
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001710static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1711 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001713 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001714
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001715 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 priv->conn->prof.tx_time = 0;
1717 return count;
1718}
1719
1720static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1721
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001722static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1723 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001725 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001727 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1729}
1730
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001731static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1732 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001734 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001736 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 priv->conn->prof.tx_pending = 0;
1738 return count;
1739}
1740
1741static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1742
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001743static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1744 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001746 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001748 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1750}
1751
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001752static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1753 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001755 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001757 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758 priv->conn->prof.tx_max_pending = 0;
1759 return count;
1760}
1761
1762static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1763
1764static struct attribute *netiucv_attrs[] = {
1765 &dev_attr_buffer.attr,
1766 &dev_attr_user.attr,
1767 NULL,
1768};
1769
1770static struct attribute_group netiucv_attr_group = {
1771 .attrs = netiucv_attrs,
1772};
1773
1774static struct attribute *netiucv_stat_attrs[] = {
1775 &dev_attr_device_fsm_state.attr,
1776 &dev_attr_connection_fsm_state.attr,
1777 &dev_attr_max_tx_buffer_used.attr,
1778 &dev_attr_max_chained_skbs.attr,
1779 &dev_attr_tx_single_write_ops.attr,
1780 &dev_attr_tx_multi_write_ops.attr,
1781 &dev_attr_netto_bytes.attr,
1782 &dev_attr_max_tx_io_time.attr,
1783 &dev_attr_tx_pending.attr,
1784 &dev_attr_tx_max_pending.attr,
1785 NULL,
1786};
1787
1788static struct attribute_group netiucv_stat_attr_group = {
1789 .name = "stats",
1790 .attrs = netiucv_stat_attrs,
1791};
1792
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001793static const struct attribute_group *netiucv_attr_groups[] = {
1794 &netiucv_stat_attr_group,
1795 &netiucv_attr_group,
1796 NULL,
1797};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001799static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001801 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001802 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803 int ret;
1804
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001805 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806
1807 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001808 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 dev->bus = &iucv_bus;
1810 dev->parent = iucv_root;
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001811 dev->groups = netiucv_attr_groups;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812 /*
1813 * The release function could be called after the
1814 * module has been unloaded. It's _only_ task is to
1815 * free the struct. Therefore, we specify kfree()
1816 * directly here. (Probably a little bit obfuscating
1817 * but legitime ...).
1818 */
1819 dev->release = (void (*)(struct device *))kfree;
1820 dev->driver = &netiucv_driver;
1821 } else
1822 return -ENOMEM;
1823
1824 ret = device_register(dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02001825 if (ret) {
1826 put_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827 return ret;
Sebastian Ottc6304932009-09-11 10:28:38 +02001828 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829 priv->dev = dev;
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001830 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832}
1833
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001834static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001836 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 device_unregister(dev);
1838}
1839
1840/**
1841 * Allocate and initialize a new connection structure.
1842 * Add it to the list of netiucv connections;
1843 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001844static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
Ursula Braun08e33562011-12-19 22:56:34 +00001845 char *username,
1846 char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001848 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001850 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1851 if (!conn)
1852 goto out;
1853 skb_queue_head_init(&conn->collect_queue);
1854 skb_queue_head_init(&conn->commit_queue);
1855 spin_lock_init(&conn->collect_lock);
1856 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1857 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001859 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1860 if (!conn->rx_buff)
1861 goto out_conn;
1862 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1863 if (!conn->tx_buff)
1864 goto out_rx;
1865 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1866 conn_event_names, NR_CONN_STATES,
1867 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1868 GFP_KERNEL);
1869 if (!conn->fsm)
1870 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001872 fsm_settimer(conn->fsm, &conn->timer);
1873 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874
Ursula Braun08e33562011-12-19 22:56:34 +00001875 if (userdata)
1876 memcpy(conn->userdata, userdata, 17);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001877 if (username) {
1878 memcpy(conn->userid, username, 9);
1879 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001881
1882 write_lock_bh(&iucv_connection_rwlock);
1883 list_add_tail(&conn->list, &iucv_connection_list);
1884 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001886
1887out_tx:
1888 kfree_skb(conn->tx_buff);
1889out_rx:
1890 kfree_skb(conn->rx_buff);
1891out_conn:
1892 kfree(conn);
1893out:
1894 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895}
1896
1897/**
1898 * Release a connection structure and remove it from the
1899 * list of netiucv connections.
1900 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001901static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902{
Ursula Braun08e33562011-12-19 22:56:34 +00001903
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001904 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001905 write_lock_bh(&iucv_connection_rwlock);
1906 list_del_init(&conn->list);
1907 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001908 fsm_deltimer(&conn->timer);
1909 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001910 if (conn->path) {
Ursula Braun08e33562011-12-19 22:56:34 +00001911 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001912 kfree(conn->path);
1913 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001915 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001916 kfree_fsm(conn->fsm);
1917 kfree_skb(conn->rx_buff);
1918 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919}
1920
1921/**
1922 * Release everything of a net device.
1923 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001924static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001926 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001928 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929
1930 if (!dev)
1931 return;
1932
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 if (privptr) {
1934 if (privptr->conn)
1935 netiucv_remove_connection(privptr->conn);
1936 if (privptr->fsm)
1937 kfree_fsm(privptr->fsm);
1938 privptr->conn = NULL; privptr->fsm = NULL;
1939 /* privptr gets freed by free_netdev() */
1940 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941}
1942
1943/**
1944 * Initialize a net device. (Called from kernel in alloc_netdev())
1945 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001946static const struct net_device_ops netiucv_netdev_ops = {
1947 .ndo_open = netiucv_open,
1948 .ndo_stop = netiucv_close,
1949 .ndo_get_stats = netiucv_stats,
1950 .ndo_start_xmit = netiucv_tx,
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001951};
1952
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001953static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955 dev->mtu = NETIUCV_MTU_DEFAULT;
Jarod Wilson46b3ef42016-10-20 13:55:23 -04001956 dev->min_mtu = 576;
1957 dev->max_mtu = NETIUCV_MTU_MAX;
Stephen Rothwellcd1997f2017-06-08 19:06:29 +10001958 dev->needs_free_netdev = true;
1959 dev->priv_destructor = netiucv_free_netdevice;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 dev->hard_header_len = NETIUCV_HDRLEN;
1961 dev->addr_len = 0;
1962 dev->type = ARPHRD_SLIP;
1963 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1964 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001965 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966}
1967
1968/**
1969 * Allocate and initialize everything of a net device.
1970 */
Ursula Braun08e33562011-12-19 22:56:34 +00001971static struct net_device *netiucv_init_netdevice(char *username, char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972{
1973 struct netiucv_priv *privptr;
1974 struct net_device *dev;
1975
1976 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
Tom Gundersenc835a672014-07-14 16:37:24 +02001977 NET_NAME_UNKNOWN, netiucv_setup_netdevice);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 if (!dev)
1979 return NULL;
Benjamin Poirieraaf95222013-06-13 09:09:47 -04001980 rtnl_lock();
Ursula Braun1d503562011-11-15 02:31:14 +00001981 if (dev_alloc_name(dev, dev->name) < 0)
1982 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001984 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
1986 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
1987 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001988 if (!privptr->fsm)
1989 goto out_netdev;
1990
Ursula Braun08e33562011-12-19 22:56:34 +00001991 privptr->conn = netiucv_new_connection(dev, username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001994 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 }
1996 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001998
1999out_fsm:
2000 kfree_fsm(privptr->fsm);
2001out_netdev:
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002002 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002003 free_netdev(dev);
2004 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005}
2006
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02002007static ssize_t connection_store(struct device_driver *drv, const char *buf,
2008 size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009{
Frank Pavlic16a83b32006-09-15 16:25:19 +02002010 char username[9];
Ursula Braun08e33562011-12-19 22:56:34 +00002011 char userdata[17];
2012 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002014 struct netiucv_priv *priv;
2015 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002017 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00002018 rc = netiucv_check_user(buf, count, username, userdata);
2019 if (rc)
2020 return rc;
Frank Pavlic16a83b32006-09-15 16:25:19 +02002021
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002022 read_lock_bh(&iucv_connection_rwlock);
2023 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00002024 if (!strncmp(username, cp->userid, 9) &&
2025 !strncmp(userdata, cp->userdata, 17)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002026 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00002027 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s "
2028 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002029 return -EEXIST;
2030 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02002031 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002032 read_unlock_bh(&iucv_connection_rwlock);
2033
Ursula Braun08e33562011-12-19 22:56:34 +00002034 dev = netiucv_init_netdevice(username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2037 return -ENODEV;
2038 }
2039
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002040 rc = netiucv_register_device(dev);
2041 if (rc) {
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002042 rtnl_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002044 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045 goto out_free_ndev;
2046 }
2047
2048 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002049 priv = netdev_priv(dev);
2050 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002052 rc = register_netdevice(dev);
2053 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002054 if (rc)
2055 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056
Ursula Braun08e33562011-12-19 22:56:34 +00002057 dev_info(priv->dev, "The IUCV interface to %s has been established "
2058 "successfully\n",
2059 netiucv_printuser(priv->conn));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002060
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 return count;
2062
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002063out_unreg:
2064 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002067 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068}
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02002069static DRIVER_ATTR_WO(connection);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02002071static ssize_t remove_store(struct device_driver *drv, const char *buf,
2072 size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002074 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 struct net_device *ndev;
2076 struct netiucv_priv *priv;
2077 struct device *dev;
2078 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002079 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 int i;
2081
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002082 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083
2084 if (count >= IFNAMSIZ)
Joe Perchesa419aef2009-08-18 11:18:35 -07002085 count = IFNAMSIZ - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002087 for (i = 0, p = buf; i < count && *p; i++, p++) {
2088 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 /* trailing lf, grr */
2090 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002091 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 }
2093 name[i] = '\0';
2094
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002095 read_lock_bh(&iucv_connection_rwlock);
2096 list_for_each_entry(cp, &iucv_connection_list, list) {
2097 ndev = cp->netdev;
2098 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002100 if (strncmp(name, ndev->name, count))
2101 continue;
2102 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002104 dev_warn(dev, "The IUCV device is connected"
2105 " to %s and cannot be removed\n",
2106 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002108 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 }
2110 unregister_netdev(ndev);
2111 netiucv_unregister_device(dev);
2112 return count;
2113 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002114 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2116 return -EINVAL;
2117}
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02002118static DRIVER_ATTR_WO(remove);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002120static struct attribute * netiucv_drv_attrs[] = {
2121 &driver_attr_connection.attr,
2122 &driver_attr_remove.attr,
2123 NULL,
2124};
2125
2126static struct attribute_group netiucv_drv_attr_group = {
2127 .attrs = netiucv_drv_attrs,
2128};
2129
David Brownella4dbd672009-06-24 10:06:31 -07002130static const struct attribute_group *netiucv_drv_attr_groups[] = {
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002131 &netiucv_drv_attr_group,
2132 NULL,
2133};
2134
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002135static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002137 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138}
2139
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002140static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002142 struct iucv_connection *cp;
2143 struct net_device *ndev;
2144 struct netiucv_priv *priv;
2145 struct device *dev;
2146
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002147 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002148 while (!list_empty(&iucv_connection_list)) {
2149 cp = list_entry(iucv_connection_list.next,
2150 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002151 ndev = cp->netdev;
2152 priv = netdev_priv(ndev);
2153 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154
2155 unregister_netdev(ndev);
2156 netiucv_unregister_device(dev);
2157 }
2158
Ursula Braun1175b252009-06-16 10:30:43 +02002159 device_unregister(netiucv_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002161 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 iucv_unregister_dbf_views();
2163
Ursula Braun8f7c5022008-12-25 13:39:47 +01002164 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165 return;
2166}
2167
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002168static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002170 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002171
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002172 rc = iucv_register_dbf_views();
2173 if (rc)
2174 goto out;
2175 rc = iucv_register(&netiucv_handler, 1);
2176 if (rc)
2177 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002178 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002179 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002180 rc = driver_register(&netiucv_driver);
2181 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002182 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2183 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184 }
Ursula Braun1175b252009-06-16 10:30:43 +02002185 /* establish dummy device */
2186 netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
2187 if (!netiucv_dev) {
2188 rc = -ENOMEM;
2189 goto out_driver;
2190 }
2191 dev_set_name(netiucv_dev, "netiucv");
2192 netiucv_dev->bus = &iucv_bus;
2193 netiucv_dev->parent = iucv_root;
2194 netiucv_dev->release = (void (*)(struct device *))kfree;
2195 netiucv_dev->driver = &netiucv_driver;
2196 rc = device_register(netiucv_dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02002197 if (rc) {
2198 put_device(netiucv_dev);
Ursula Braun1175b252009-06-16 10:30:43 +02002199 goto out_driver;
Sebastian Ottc6304932009-09-11 10:28:38 +02002200 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002201 netiucv_banner();
2202 return rc;
2203
Ursula Braun1175b252009-06-16 10:30:43 +02002204out_driver:
2205 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002206out_iucv:
2207 iucv_unregister(&netiucv_handler, 1);
2208out_dbf:
2209 iucv_unregister_dbf_views();
2210out:
2211 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002213
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214module_init(netiucv_init);
2215module_exit(netiucv_exit);
2216MODULE_LICENSE("GPL");