blob: 260860cf3aa188a9b4dc5f792258723e75f36677 [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
115static struct device_driver netiucv_driver = {
Cornelia Huck22195102008-02-08 13:09:02 +0100116 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 .name = "netiucv",
118 .bus = &iucv_bus,
119};
120
Ursula Braun91e60eb2015-09-18 16:06:52 +0200121static int netiucv_callback_connreq(struct iucv_path *, u8 *, u8 *);
122static void netiucv_callback_connack(struct iucv_path *, u8 *);
123static void netiucv_callback_connrej(struct iucv_path *, u8 *);
124static void netiucv_callback_connsusp(struct iucv_path *, u8 *);
125static void netiucv_callback_connres(struct iucv_path *, u8 *);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800126static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
127static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
128
129static struct iucv_handler netiucv_handler = {
130 .path_pending = netiucv_callback_connreq,
131 .path_complete = netiucv_callback_connack,
132 .path_severed = netiucv_callback_connrej,
133 .path_quiesced = netiucv_callback_connsusp,
134 .path_resumed = netiucv_callback_connres,
135 .message_pending = netiucv_callback_rx,
136 .message_complete = netiucv_callback_txdone
137};
138
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139/**
140 * Per connection profiling data
141 */
142struct connection_profile {
143 unsigned long maxmulti;
144 unsigned long maxcqueue;
145 unsigned long doios_single;
146 unsigned long doios_multi;
147 unsigned long txlen;
148 unsigned long tx_time;
Aya Mahfouzee6edb92015-01-16 14:05:45 +0100149 unsigned long send_stamp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 unsigned long tx_pending;
151 unsigned long tx_max_pending;
152};
153
154/**
155 * Representation of one iucv connection
156 */
157struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800158 struct list_head list;
159 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 struct sk_buff *rx_buff;
161 struct sk_buff *tx_buff;
162 struct sk_buff_head collect_queue;
163 struct sk_buff_head commit_queue;
164 spinlock_t collect_lock;
165 int collect_len;
166 int max_buffsize;
167 fsm_timer timer;
168 fsm_instance *fsm;
169 struct net_device *netdev;
170 struct connection_profile prof;
171 char userid[9];
Ursula Braun08e33562011-12-19 22:56:34 +0000172 char userdata[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173};
174
175/**
176 * Linked list of all connection structs.
177 */
Denis Chengc11ca972008-01-26 14:11:13 +0100178static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200179static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
181/**
182 * Representation of event-data for the
183 * connection state machine.
184 */
185struct iucv_event {
186 struct iucv_connection *conn;
187 void *data;
188};
189
190/**
191 * Private part of the network device structure
192 */
193struct netiucv_priv {
194 struct net_device_stats stats;
195 unsigned long tbusy;
196 fsm_instance *fsm;
197 struct iucv_connection *conn;
198 struct device *dev;
199};
200
201/**
202 * Link level header for a packet.
203 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800204struct ll_header {
205 u16 next;
206};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800208#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Ursula Braun08e33562011-12-19 22:56:34 +0000209#define NETIUCV_BUFSIZE_MAX 65537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
211#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
212#define NETIUCV_MTU_DEFAULT 9216
213#define NETIUCV_QUEUELEN_DEFAULT 50
214#define NETIUCV_TIMEOUT_5SEC 5000
215
216/**
217 * Compatibility macros for busy handling
218 * of network devices.
219 */
Julian Wiedmanncef6ff222017-08-15 17:02:46 +0200220static void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800222 struct netiucv_priv *priv = netdev_priv(dev);
223 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 netif_wake_queue(dev);
225}
226
Julian Wiedmanncef6ff222017-08-15 17:02:46 +0200227static int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800229 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800231 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232}
233
Ursula Braun08e33562011-12-19 22:56:34 +0000234static u8 iucvMagic_ascii[16] = {
235 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
236 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
237};
238
239static u8 iucvMagic_ebcdic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
241 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
242};
243
244/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 * Convert an iucv userId to its printable
246 * form (strip whitespace at end).
247 *
248 * @param An iucv userId
249 *
250 * @returns The printable string (static data!!)
251 */
Ursula Braun08e33562011-12-19 22:56:34 +0000252static char *netiucv_printname(char *name, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253{
Ursula Braun08e33562011-12-19 22:56:34 +0000254 static char tmp[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 char *p = tmp;
Ursula Braun08e33562011-12-19 22:56:34 +0000256 memcpy(tmp, name, len);
257 tmp[len] = '\0';
258 while (*p && ((p - tmp) < len) && (!isspace(*p)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 p++;
260 *p = '\0';
261 return tmp;
262}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400263
Ursula Braun08e33562011-12-19 22:56:34 +0000264static char *netiucv_printuser(struct iucv_connection *conn)
265{
266 static char tmp_uid[9];
267 static char tmp_udat[17];
268 static char buf[100];
269
270 if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) {
271 tmp_uid[8] = '\0';
272 tmp_udat[16] = '\0';
Ursula Braunbaac7892016-10-12 12:38:49 +0200273 memcpy(tmp_uid, netiucv_printname(conn->userid, 8), 8);
Ursula Braun08e33562011-12-19 22:56:34 +0000274 memcpy(tmp_udat, conn->userdata, 16);
275 EBCASC(tmp_udat, 16);
276 memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16);
277 sprintf(buf, "%s.%s", tmp_uid, tmp_udat);
278 return buf;
279 } else
280 return netiucv_printname(conn->userid, 8);
281}
282
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283/**
284 * States of the interface statemachine.
285 */
286enum dev_states {
287 DEV_STATE_STOPPED,
288 DEV_STATE_STARTWAIT,
289 DEV_STATE_STOPWAIT,
290 DEV_STATE_RUNNING,
291 /**
292 * MUST be always the last element!!
293 */
294 NR_DEV_STATES
295};
296
297static const char *dev_state_names[] = {
298 "Stopped",
299 "StartWait",
300 "StopWait",
301 "Running",
302};
303
304/**
305 * Events of the interface statemachine.
306 */
307enum dev_events {
308 DEV_EVENT_START,
309 DEV_EVENT_STOP,
310 DEV_EVENT_CONUP,
311 DEV_EVENT_CONDOWN,
312 /**
313 * MUST be always the last element!!
314 */
315 NR_DEV_EVENTS
316};
317
318static const char *dev_event_names[] = {
319 "Start",
320 "Stop",
321 "Connection up",
322 "Connection down",
323};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325/**
326 * Events of the connection statemachine
327 */
328enum conn_events {
329 /**
330 * Events, representing callbacks from
331 * lowlevel iucv layer)
332 */
333 CONN_EVENT_CONN_REQ,
334 CONN_EVENT_CONN_ACK,
335 CONN_EVENT_CONN_REJ,
336 CONN_EVENT_CONN_SUS,
337 CONN_EVENT_CONN_RES,
338 CONN_EVENT_RX,
339 CONN_EVENT_TXDONE,
340
341 /**
342 * Events, representing errors return codes from
343 * calls to lowlevel iucv layer
344 */
345
346 /**
347 * Event, representing timer expiry.
348 */
349 CONN_EVENT_TIMER,
350
351 /**
352 * Events, representing commands from upper levels.
353 */
354 CONN_EVENT_START,
355 CONN_EVENT_STOP,
356
357 /**
358 * MUST be always the last element!!
359 */
360 NR_CONN_EVENTS,
361};
362
363static const char *conn_event_names[] = {
364 "Remote connection request",
365 "Remote connection acknowledge",
366 "Remote connection reject",
367 "Connection suspended",
368 "Connection resumed",
369 "Data received",
370 "Data sent",
371
372 "Timer",
373
374 "Start",
375 "Stop",
376};
377
378/**
379 * States of the connection statemachine.
380 */
381enum conn_states {
382 /**
383 * Connection not assigned to any device,
384 * initial state, invalid
385 */
386 CONN_STATE_INVALID,
387
388 /**
389 * Userid assigned but not operating
390 */
391 CONN_STATE_STOPPED,
392
393 /**
394 * Connection registered,
395 * no connection request sent yet,
396 * no connection request received
397 */
398 CONN_STATE_STARTWAIT,
399
400 /**
401 * Connection registered and connection request sent,
402 * no acknowledge and no connection request received yet.
403 */
404 CONN_STATE_SETUPWAIT,
405
406 /**
407 * Connection up and running idle
408 */
409 CONN_STATE_IDLE,
410
411 /**
412 * Data sent, awaiting CONN_EVENT_TXDONE
413 */
414 CONN_STATE_TX,
415
416 /**
417 * Error during registration.
418 */
419 CONN_STATE_REGERR,
420
421 /**
422 * Error during registration.
423 */
424 CONN_STATE_CONNERR,
425
426 /**
427 * MUST be always the last element!!
428 */
429 NR_CONN_STATES,
430};
431
432static const char *conn_state_names[] = {
433 "Invalid",
434 "Stopped",
435 "StartWait",
436 "SetupWait",
437 "Idle",
438 "TX",
439 "Terminating",
440 "Registration error",
441 "Connect error",
442};
443
Jeff Garzike82b0f22006-05-26 21:58:38 -0400444
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445/**
446 * Debug Facility Stuff
447 */
448static debug_info_t *iucv_dbf_setup = NULL;
449static debug_info_t *iucv_dbf_data = NULL;
450static debug_info_t *iucv_dbf_trace = NULL;
451
452DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
453
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800454static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455{
Markus Elfringb646c082015-01-16 14:05:46 +0100456 debug_unregister(iucv_dbf_setup);
457 debug_unregister(iucv_dbf_data);
458 debug_unregister(iucv_dbf_trace);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800460static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461{
462 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700463 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 IUCV_DBF_SETUP_NR_AREAS,
465 IUCV_DBF_SETUP_LEN);
466 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700467 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 IUCV_DBF_DATA_NR_AREAS,
469 IUCV_DBF_DATA_LEN);
470 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700471 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 IUCV_DBF_TRACE_NR_AREAS,
473 IUCV_DBF_TRACE_LEN);
474
475 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
476 (iucv_dbf_trace == NULL)) {
477 iucv_unregister_dbf_views();
478 return -ENOMEM;
479 }
480 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
481 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
482
483 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
484 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
485
486 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
487 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
488
489 return 0;
490}
491
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800492/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800494 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800496static void netiucv_callback_rx(struct iucv_path *path,
497 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800499 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 struct iucv_event ev;
501
502 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800503 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
505}
506
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800507static void netiucv_callback_txdone(struct iucv_path *path,
508 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800510 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 struct iucv_event ev;
512
513 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800514 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
516}
517
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800518static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800520 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800522 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523}
524
Ursula Braun91e60eb2015-09-18 16:06:52 +0200525static int netiucv_callback_connreq(struct iucv_path *path, u8 *ipvmid,
526 u8 *ipuser)
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;
Ursula Braun08e33562011-12-19 22:56:34 +0000530 static char tmp_user[9];
531 static char tmp_udat[17];
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800532 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800534 rc = -EINVAL;
Ursula Braun08e33562011-12-19 22:56:34 +0000535 memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8);
536 memcpy(tmp_udat, ipuser, 16);
537 EBCASC(tmp_udat, 16);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800538 read_lock_bh(&iucv_connection_rwlock);
539 list_for_each_entry(conn, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +0000540 if (strncmp(ipvmid, conn->userid, 8) ||
541 strncmp(ipuser, conn->userdata, 16))
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800542 continue;
543 /* Found a matching connection for this path. */
544 conn->path = path;
545 ev.conn = conn;
546 ev.data = path;
547 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
548 rc = 0;
549 }
Ursula Braun08e33562011-12-19 22:56:34 +0000550 IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n",
551 tmp_user, netiucv_printname(tmp_udat, 16));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800552 read_unlock_bh(&iucv_connection_rwlock);
553 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554}
555
Ursula Braun91e60eb2015-09-18 16:06:52 +0200556static void netiucv_callback_connrej(struct iucv_path *path, u8 *ipuser)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800558 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800560 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561}
562
Ursula Braun91e60eb2015-09-18 16:06:52 +0200563static void netiucv_callback_connsusp(struct iucv_path *path, u8 *ipuser)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800565 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800567 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568}
569
Ursula Braun91e60eb2015-09-18 16:06:52 +0200570static void netiucv_callback_connres(struct iucv_path *path, u8 *ipuser)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800572 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800574 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575}
576
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100578 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100580static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581{
582}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400583
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800584/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800586 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
588/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800589 * netiucv_unpack_skb
590 * @conn: The connection where this skb has been received.
591 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800593 * Unpack a just received skb and hand it over to upper layers.
594 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800596static void netiucv_unpack_skb(struct iucv_connection *conn,
597 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598{
599 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800600 struct netiucv_priv *privptr = netdev_priv(dev);
601 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
603 skb_put(pskb, NETIUCV_HDRLEN);
604 pskb->dev = dev;
605 pskb->ip_summed = CHECKSUM_NONE;
Hans Wippel6c37c602017-04-07 09:15:38 +0200606 pskb->protocol = cpu_to_be16(ETH_P_IP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
608 while (1) {
609 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800610 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
612 if (!header->next)
613 break;
614
615 skb_pull(pskb, NETIUCV_HDRLEN);
616 header->next -= offset;
617 offset += header->next;
618 header->next -= NETIUCV_HDRLEN;
619 if (skb_tailroom(pskb) < header->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
621 header->next, skb_tailroom(pskb));
622 return;
623 }
624 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700625 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 skb = dev_alloc_skb(pskb->len);
627 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 IUCV_DBF_TEXT(data, 2,
629 "Out of memory in netiucv_unpack_skb\n");
630 privptr->stats.rx_dropped++;
631 return;
632 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300633 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
634 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700635 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 skb->dev = pskb->dev;
637 skb->protocol = pskb->protocol;
638 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800639 privptr->stats.rx_packets++;
640 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 /*
642 * Since receiving is always initiated from a tasklet (in iucv.c),
643 * we must use netif_rx_ni() instead of netif_rx()
644 */
645 netif_rx_ni(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 skb_pull(pskb, header->next);
647 skb_put(pskb, NETIUCV_HDRLEN);
648 }
649}
650
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800651static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800653 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800655 struct iucv_message *msg = ev->data;
656 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 int rc;
658
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200659 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660
661 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800662 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800664 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 return;
666 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800667 if (msg->length > conn->max_buffsize) {
668 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 privptr->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800671 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 return;
673 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700674 conn->rx_buff->data = conn->rx_buff->head;
675 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800677 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
678 msg->length, NULL);
679 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 privptr->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
682 return;
683 }
684 netiucv_unpack_skb(conn, conn->rx_buff);
685}
686
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800687static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800689 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800691 struct iucv_message *msg = ev->data;
692 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800694 u32 single_flag = msg->tag;
695 u32 txbytes = 0;
696 u32 txpackets = 0;
697 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 struct sk_buff *skb;
699 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800700 struct ll_header header;
701 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200703 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
Ursula Braund239ae32013-12-16 09:44:51 +0100705 if (!conn || !conn->netdev) {
706 IUCV_DBF_TEXT(data, 2,
707 "Send confirmation for unlinked connection\n");
708 return;
709 }
710 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 conn->prof.tx_pending--;
712 if (single_flag) {
713 if ((skb = skb_dequeue(&conn->commit_queue))) {
Reshetova, Elena63354792017-06-30 13:07:58 +0300714 refcount_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 if (privptr) {
716 privptr->stats.tx_packets++;
717 privptr->stats.tx_bytes +=
718 (skb->len - NETIUCV_HDRLEN
Ursula Braun998221c2009-11-12 21:46:30 +0000719 - NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 }
Ursula Braun998221c2009-11-12 21:46:30 +0000721 dev_kfree_skb_any(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 }
723 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700724 conn->tx_buff->data = conn->tx_buff->head;
725 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 conn->tx_buff->len = 0;
727 spin_lock_irqsave(&conn->collect_lock, saveflags);
728 while ((skb = skb_dequeue(&conn->collect_queue))) {
729 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
Johannes Berg59ae1d12017-06-16 14:29:20 +0200730 skb_put_data(conn->tx_buff, &header, NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300731 skb_copy_from_linear_data(skb,
732 skb_put(conn->tx_buff, skb->len),
733 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 txbytes += skb->len;
735 txpackets++;
736 stat_maxcq++;
Reshetova, Elena63354792017-06-30 13:07:58 +0300737 refcount_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 dev_kfree_skb_any(skb);
739 }
740 if (conn->collect_len > conn->prof.maxmulti)
741 conn->prof.maxmulti = conn->collect_len;
742 conn->collect_len = 0;
743 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800744 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800746 return;
747 }
748
749 header.next = 0;
Johannes Berg59ae1d12017-06-16 14:29:20 +0200750 skb_put_data(conn->tx_buff, &header, NETIUCV_HDRLEN);
Aya Mahfouzee6edb92015-01-16 14:05:45 +0100751 conn->prof.send_stamp = jiffies;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800752 txmsg.class = 0;
753 txmsg.tag = 0;
754 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
755 conn->tx_buff->data, conn->tx_buff->len);
756 conn->prof.doios_multi++;
757 conn->prof.txlen += conn->tx_buff->len;
758 conn->prof.tx_pending++;
759 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
760 conn->prof.tx_max_pending = conn->prof.tx_pending;
761 if (rc) {
762 conn->prof.tx_pending--;
763 fsm_newstate(fi, CONN_STATE_IDLE);
764 if (privptr)
765 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800766 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
767 } else {
768 if (privptr) {
769 privptr->stats.tx_packets += txpackets;
770 privptr->stats.tx_bytes += txbytes;
771 }
772 if (stat_maxcq > conn->prof.maxcqueue)
773 conn->prof.maxcqueue = stat_maxcq;
774 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775}
776
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800777static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800779 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800781 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800783 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200786 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800788 conn->path = path;
789 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
790 path->flags = 0;
Ursula Braun08e33562011-12-19 22:56:34 +0000791 rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
794 return;
795 }
796 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800797 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
799}
800
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800801static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800803 struct iucv_event *ev = arg;
804 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200806 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800807 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808}
809
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800810static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800812 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800814 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200816 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 fsm_deltimer(&conn->timer);
818 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800819 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
821}
822
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800823static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800825 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200827 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000829 iucv_path_sever(conn->path, conn->userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 fsm_newstate(fi, CONN_STATE_STARTWAIT);
831}
832
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800833static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800835 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800837 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200839 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
841 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000842 iucv_path_sever(conn->path, conn->userdata);
843 dev_info(privptr->dev, "The peer z/VM guest %s has closed the "
844 "connection\n", netiucv_printuser(conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800846 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 fsm_newstate(fi, CONN_STATE_STARTWAIT);
848 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
849}
850
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800851static void conn_action_start(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;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100854 struct net_device *netdev = conn->netdev;
855 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 int rc;
857
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200858 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800860 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800862 /*
863 * We must set the state before calling iucv_connect because the
864 * callback handler could be called at any point after the connection
865 * request is sent
866 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
868 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800869 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
Ursula Braun08e33562011-12-19 22:56:34 +0000870 IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n",
871 netdev->name, netiucv_printuser(conn));
872
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800873 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
Ursula Braun08e33562011-12-19 22:56:34 +0000874 NULL, conn->userdata, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800876 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100877 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800878 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
879 CONN_EVENT_TIMER, conn);
880 return;
881 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100882 dev_warn(privptr->dev,
883 "The IUCV device failed to connect to z/VM guest %s\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000884 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800885 fsm_newstate(fi, CONN_STATE_STARTWAIT);
886 break;
887 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100888 dev_warn(privptr->dev,
889 "The IUCV device failed to connect to the peer on z/VM"
Ursula Braun08e33562011-12-19 22:56:34 +0000890 " guest %s\n", netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800891 fsm_newstate(fi, CONN_STATE_STARTWAIT);
892 break;
893 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100894 dev_err(privptr->dev,
895 "Connecting the IUCV device would exceed the maximum"
896 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800897 fsm_newstate(fi, CONN_STATE_CONNERR);
898 break;
899 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100900 dev_err(privptr->dev,
901 "z/VM guest %s has too many IUCV connections"
902 " to connect with the IUCV device\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000903 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800904 fsm_newstate(fi, CONN_STATE_CONNERR);
905 break;
906 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100907 dev_err(privptr->dev,
908 "The IUCV device cannot connect to a z/VM guest with no"
909 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800910 fsm_newstate(fi, CONN_STATE_CONNERR);
911 break;
912 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100913 dev_err(privptr->dev,
914 "Connecting the IUCV device failed with error %d\n",
915 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800916 fsm_newstate(fi, CONN_STATE_CONNERR);
917 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 }
919 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800920 kfree(conn->path);
921 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922}
923
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800924static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925{
926 struct sk_buff *skb;
927
928 while ((skb = skb_dequeue(q))) {
Reshetova, Elena63354792017-06-30 13:07:58 +0300929 refcount_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 dev_kfree_skb_any(skb);
931 }
932}
933
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800934static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800936 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 struct iucv_connection *conn = ev->conn;
938 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800939 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200941 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942
943 fsm_deltimer(&conn->timer);
944 fsm_newstate(fi, CONN_STATE_STOPPED);
945 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800946 if (conn->path) {
947 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
Ursula Braun08e33562011-12-19 22:56:34 +0000948 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800949 kfree(conn->path);
950 conn->path = NULL;
951 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 netiucv_purge_skb_queue(&conn->commit_queue);
953 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
954}
955
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800956static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800958 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 struct net_device *netdev = conn->netdev;
960
Ursula Braunf082bca2008-07-14 09:59:30 +0200961 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
962 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963}
964
965static const fsm_node conn_fsm[] = {
966 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
967 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
968
969 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
970 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
971 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
972 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
973 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
974 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
975 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
976
977 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
978 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
979 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
980 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
981 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
982
983 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
984 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
985
986 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
987 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
988 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
989
990 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
991 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
992
993 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
994 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
995};
996
997static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
998
Jeff Garzike82b0f22006-05-26 21:58:38 -0400999
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001000/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001002 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
1004/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001005 * dev_action_start
1006 * @fi: An instance of an interface statemachine.
1007 * @event: The event, just happened.
1008 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001010 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001012static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001014 struct net_device *dev = arg;
1015 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001017 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001020 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021}
1022
1023/**
1024 * Shutdown connection by sending CONN_EVENT_STOP to it.
1025 *
1026 * @param fi An instance of an interface statemachine.
1027 * @param event The event, just happened.
1028 * @param arg Generic pointer, casted from struct net_device * upon call.
1029 */
1030static void
1031dev_action_stop(fsm_instance *fi, int event, void *arg)
1032{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001033 struct net_device *dev = arg;
1034 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 struct iucv_event ev;
1036
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001037 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038
1039 ev.conn = privptr->conn;
1040
1041 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1042 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1043}
1044
1045/**
1046 * Called from connection statemachine
1047 * when a connection is up and running.
1048 *
1049 * @param fi An instance of an interface statemachine.
1050 * @param event The event, just happened.
1051 * @param arg Generic pointer, casted from struct net_device * upon call.
1052 */
1053static void
1054dev_action_connup(fsm_instance *fi, int event, void *arg)
1055{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001056 struct net_device *dev = arg;
1057 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001059 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060
1061 switch (fsm_getstate(fi)) {
1062 case DEV_STATE_STARTWAIT:
1063 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001064 dev_info(privptr->dev,
1065 "The IUCV device has been connected"
Ursula Braun08e33562011-12-19 22:56:34 +00001066 " successfully to %s\n",
1067 netiucv_printuser(privptr->conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 IUCV_DBF_TEXT(setup, 3,
1069 "connection is up and running\n");
1070 break;
1071 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 IUCV_DBF_TEXT(data, 2,
1073 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1074 break;
1075 }
1076}
1077
1078/**
1079 * Called from connection statemachine
1080 * when a connection has been shutdown.
1081 *
1082 * @param fi An instance of an interface statemachine.
1083 * @param event The event, just happened.
1084 * @param arg Generic pointer, casted from struct net_device * upon call.
1085 */
1086static void
1087dev_action_conndown(fsm_instance *fi, int event, void *arg)
1088{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001089 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090
1091 switch (fsm_getstate(fi)) {
1092 case DEV_STATE_RUNNING:
1093 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1094 break;
1095 case DEV_STATE_STOPWAIT:
1096 fsm_newstate(fi, DEV_STATE_STOPPED);
1097 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1098 break;
1099 }
1100}
1101
1102static const fsm_node dev_fsm[] = {
1103 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1104
1105 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1106 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1107
1108 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1109 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1110
1111 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1112 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001113 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114};
1115
1116static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1117
1118/**
1119 * Transmit a packet.
1120 * This is a helper function for netiucv_tx().
1121 *
1122 * @param conn Connection to be used for sending.
1123 * @param skb Pointer to struct sk_buff of packet to send.
1124 * The linklevel header has already been set up
1125 * by netiucv_tx().
1126 *
1127 * @return 0 on success, -ERRNO on failure. (Never fails.)
1128 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001129static int netiucv_transmit_skb(struct iucv_connection *conn,
1130 struct sk_buff *skb)
1131{
1132 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001134 struct ll_header header;
1135 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136
1137 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1138 int l = skb->len + NETIUCV_HDRLEN;
1139
1140 spin_lock_irqsave(&conn->collect_lock, saveflags);
1141 if (conn->collect_len + l >
1142 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1143 rc = -EBUSY;
1144 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001145 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 } else {
Reshetova, Elena63354792017-06-30 13:07:58 +03001147 refcount_inc(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 skb_queue_tail(&conn->collect_queue, skb);
1149 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001150 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 }
1152 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1153 } else {
1154 struct sk_buff *nskb = skb;
1155 /**
1156 * Copy the skb to a new allocated skb in lowmem only if the
1157 * data is located above 2G in memory or tailroom is < 2.
1158 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001159 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1160 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 int copied = 0;
1162 if (hi || (skb_tailroom(skb) < 2)) {
1163 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1164 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1165 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1167 rc = -ENOMEM;
1168 return rc;
1169 } else {
1170 skb_reserve(nskb, NETIUCV_HDRLEN);
Johannes Berg59ae1d12017-06-16 14:29:20 +02001171 skb_put_data(nskb, skb->data, skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 }
1173 copied = 1;
1174 }
1175 /**
1176 * skb now is below 2G and has enough room. Add headers.
1177 */
1178 header.next = nskb->len + NETIUCV_HDRLEN;
1179 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1180 header.next = 0;
Johannes Berg59ae1d12017-06-16 14:29:20 +02001181 skb_put_data(nskb, &header, NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182
1183 fsm_newstate(conn->fsm, CONN_STATE_TX);
Aya Mahfouzee6edb92015-01-16 14:05:45 +01001184 conn->prof.send_stamp = jiffies;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001185
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001186 msg.tag = 1;
1187 msg.class = 0;
1188 rc = iucv_message_send(conn->path, &msg, 0, 0,
1189 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 conn->prof.doios_single++;
1191 conn->prof.txlen += skb->len;
1192 conn->prof.tx_pending++;
1193 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1194 conn->prof.tx_max_pending = conn->prof.tx_pending;
1195 if (rc) {
1196 struct netiucv_priv *privptr;
1197 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1198 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001199 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 if (privptr)
1201 privptr->stats.tx_errors++;
1202 if (copied)
1203 dev_kfree_skb(nskb);
1204 else {
1205 /**
1206 * Remove our headers. They get added
1207 * again on retransmit.
1208 */
1209 skb_pull(skb, NETIUCV_HDRLEN);
1210 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1211 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1213 } else {
1214 if (copied)
1215 dev_kfree_skb(skb);
Reshetova, Elena63354792017-06-30 13:07:58 +03001216 refcount_inc(&nskb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 skb_queue_tail(&conn->commit_queue, nskb);
1218 }
1219 }
1220
1221 return rc;
1222}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001223
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001224/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001226 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227
1228/**
1229 * Open an interface.
1230 * Called from generic network layer when ifconfig up is run.
1231 *
1232 * @param dev Pointer to interface struct.
1233 *
1234 * @return 0 on success, -ERRNO on failure. (Never fails.)
1235 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001236static int netiucv_open(struct net_device *dev)
1237{
1238 struct netiucv_priv *priv = netdev_priv(dev);
1239
1240 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 return 0;
1242}
1243
1244/**
1245 * Close an interface.
1246 * Called from generic network layer when ifconfig down is run.
1247 *
1248 * @param dev Pointer to interface struct.
1249 *
1250 * @return 0 on success, -ERRNO on failure. (Never fails.)
1251 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001252static int netiucv_close(struct net_device *dev)
1253{
1254 struct netiucv_priv *priv = netdev_priv(dev);
1255
1256 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 return 0;
1258}
1259
1260/**
1261 * Start transmission of a packet.
1262 * Called from generic network device layer.
1263 *
1264 * @param skb Pointer to buffer containing the packet.
1265 * @param dev Pointer to interface struct.
1266 *
1267 * @return 0 if packet consumed, !0 if packet rejected.
1268 * Note: If we return !0, then the packet is free'd by
1269 * the generic network layer.
1270 */
1271static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1272{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001273 struct netiucv_priv *privptr = netdev_priv(dev);
1274 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001276 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 /**
1278 * Some sanity checks ...
1279 */
1280 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1282 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001283 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 }
1285 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 IUCV_DBF_TEXT(data, 2,
1287 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1288 dev_kfree_skb(skb);
1289 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001290 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 }
1292
1293 /**
1294 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001295 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 */
1297 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 dev_kfree_skb(skb);
1299 privptr->stats.tx_dropped++;
1300 privptr->stats.tx_errors++;
1301 privptr->stats.tx_carrier_errors++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001302 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 }
1304
1305 if (netiucv_test_and_set_busy(dev)) {
1306 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001307 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 }
Florian Westphal860e9532016-05-03 16:33:13 +02001309 netif_trans_update(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001310 rc = netiucv_transmit_skb(privptr->conn, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 netiucv_clear_busy(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001312 return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313}
1314
1315/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001316 * netiucv_stats
1317 * @dev: Pointer to interface struct.
1318 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 * Returns interface statistics of a device.
1320 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001321 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001323static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001325 struct netiucv_priv *priv = netdev_priv(dev);
1326
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001327 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001328 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329}
1330
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001331/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001333 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001335static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1336 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001338 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001340 IUCV_DBF_TEXT(trace, 5, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001341 return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));
1342}
1343
1344static int netiucv_check_user(const char *buf, size_t count, char *username,
1345 char *userdata)
1346{
1347 const char *p;
1348 int i;
1349
1350 p = strchr(buf, '.');
1351 if ((p && ((count > 26) ||
1352 ((p - buf) > 8) ||
1353 (buf + count - p > 18))) ||
1354 (!p && (count > 9))) {
1355 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1356 return -EINVAL;
1357 }
1358
1359 for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) {
1360 if (isalnum(*p) || *p == '$') {
1361 username[i] = toupper(*p);
1362 continue;
1363 }
1364 if (*p == '\n')
1365 /* trailing lf, grr */
1366 break;
1367 IUCV_DBF_TEXT_(setup, 2,
1368 "conn_write: invalid character %02x\n", *p);
1369 return -EINVAL;
1370 }
1371 while (i < 8)
1372 username[i++] = ' ';
1373 username[8] = '\0';
1374
1375 if (*p == '.') {
1376 p++;
1377 for (i = 0; i < 16 && *p; i++, p++) {
1378 if (*p == '\n')
1379 break;
1380 userdata[i] = toupper(*p);
1381 }
1382 while (i > 0 && i < 16)
1383 userdata[i++] = ' ';
1384 } else
1385 memcpy(userdata, iucvMagic_ascii, 16);
1386 userdata[16] = '\0';
1387 ASCEBC(userdata, 16);
1388
1389 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390}
1391
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001392static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1393 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001395 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 struct net_device *ndev = priv->conn->netdev;
Ursula Braun08e33562011-12-19 22:56:34 +00001397 char username[9];
1398 char userdata[17];
1399 int rc;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001400 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001402 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001403 rc = netiucv_check_user(buf, count, username, userdata);
1404 if (rc)
1405 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001407 if (memcmp(username, priv->conn->userid, 9) &&
1408 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1409 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001410 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001411 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001412 }
1413 read_lock_bh(&iucv_connection_rwlock);
1414 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00001415 if (!strncmp(username, cp->userid, 9) &&
1416 !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001417 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00001418 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s "
1419 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001420 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 }
1422 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001423 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 memcpy(priv->conn->userid, username, 9);
Ursula Braun08e33562011-12-19 22:56:34 +00001425 memcpy(priv->conn->userdata, userdata, 17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427}
1428
1429static DEVICE_ATTR(user, 0644, user_show, user_write);
1430
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001431static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1432 char *buf)
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001433{
1434 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001436 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1438}
1439
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001440static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1441 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001443 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 struct net_device *ndev = priv->conn->netdev;
Ursula Braun9edebf12016-10-12 12:38:50 +02001445 unsigned int bs1;
1446 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001448 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 if (count >= 39)
1450 return -EINVAL;
1451
Ursula Braun9edebf12016-10-12 12:38:50 +02001452 rc = kstrtouint(buf, 0, &bs1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453
Ursula Braun9edebf12016-10-12 12:38:50 +02001454 if (rc == -EINVAL) {
1455 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %s\n",
1456 buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 return -EINVAL;
1458 }
Ursula Braun9edebf12016-10-12 12:38:50 +02001459 if ((rc == -ERANGE) || (bs1 > NETIUCV_BUFSIZE_MAX)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 IUCV_DBF_TEXT_(setup, 2,
1461 "buffer_write: buffer size %d too large\n",
1462 bs1);
1463 return -EINVAL;
1464 }
1465 if ((ndev->flags & IFF_RUNNING) &&
1466 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 IUCV_DBF_TEXT_(setup, 2,
1468 "buffer_write: buffer size %d too small\n",
1469 bs1);
1470 return -EINVAL;
1471 }
1472 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 IUCV_DBF_TEXT_(setup, 2,
1474 "buffer_write: buffer size %d too small\n",
1475 bs1);
1476 return -EINVAL;
1477 }
1478
1479 priv->conn->max_buffsize = bs1;
1480 if (!(ndev->flags & IFF_RUNNING))
1481 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1482
1483 return count;
1484
1485}
1486
1487static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1488
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001489static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1490 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001492 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001494 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1496}
1497
1498static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1499
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001500static ssize_t conn_fsm_show (struct device *dev,
1501 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001503 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001505 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1507}
1508
1509static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1510
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001511static ssize_t maxmulti_show (struct device *dev,
1512 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001514 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001516 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1518}
1519
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001520static ssize_t maxmulti_write (struct device *dev,
1521 struct device_attribute *attr,
1522 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001524 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001526 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 priv->conn->prof.maxmulti = 0;
1528 return count;
1529}
1530
1531static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1532
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001533static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1534 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001536 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001538 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1540}
1541
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001542static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1543 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001545 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001546
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001547 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 priv->conn->prof.maxcqueue = 0;
1549 return count;
1550}
1551
1552static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1553
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001554static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1555 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001557 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001559 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1561}
1562
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001563static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1564 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001566 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001567
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001568 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 priv->conn->prof.doios_single = 0;
1570 return count;
1571}
1572
1573static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1574
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001575static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1576 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001578 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001580 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1582}
1583
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001584static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1585 const char *buf, size_t count)
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);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001588
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001589 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 priv->conn->prof.doios_multi = 0;
1591 return count;
1592}
1593
1594static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1595
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001596static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1597 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001599 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001601 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1603}
1604
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001605static ssize_t txlen_write (struct device *dev, 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);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001609
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.txlen = 0;
1612 return count;
1613}
1614
1615static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1616
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001617static ssize_t txtime_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.tx_time);
1624}
1625
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001626static ssize_t txtime_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.tx_time = 0;
1633 return count;
1634}
1635
1636static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1637
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001638static ssize_t txpend_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.tx_pending);
1645}
1646
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001647static ssize_t txpend_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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001652 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 priv->conn->prof.tx_pending = 0;
1654 return count;
1655}
1656
1657static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1658
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001659static ssize_t txmpnd_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.tx_max_pending);
1666}
1667
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001668static ssize_t txmpnd_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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001673 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 priv->conn->prof.tx_max_pending = 0;
1675 return count;
1676}
1677
1678static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1679
1680static struct attribute *netiucv_attrs[] = {
1681 &dev_attr_buffer.attr,
1682 &dev_attr_user.attr,
1683 NULL,
1684};
1685
1686static struct attribute_group netiucv_attr_group = {
1687 .attrs = netiucv_attrs,
1688};
1689
1690static struct attribute *netiucv_stat_attrs[] = {
1691 &dev_attr_device_fsm_state.attr,
1692 &dev_attr_connection_fsm_state.attr,
1693 &dev_attr_max_tx_buffer_used.attr,
1694 &dev_attr_max_chained_skbs.attr,
1695 &dev_attr_tx_single_write_ops.attr,
1696 &dev_attr_tx_multi_write_ops.attr,
1697 &dev_attr_netto_bytes.attr,
1698 &dev_attr_max_tx_io_time.attr,
1699 &dev_attr_tx_pending.attr,
1700 &dev_attr_tx_max_pending.attr,
1701 NULL,
1702};
1703
1704static struct attribute_group netiucv_stat_attr_group = {
1705 .name = "stats",
1706 .attrs = netiucv_stat_attrs,
1707};
1708
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001709static const struct attribute_group *netiucv_attr_groups[] = {
1710 &netiucv_stat_attr_group,
1711 &netiucv_attr_group,
1712 NULL,
1713};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001715static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001717 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001718 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719 int ret;
1720
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001721 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722
1723 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001724 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 dev->bus = &iucv_bus;
1726 dev->parent = iucv_root;
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001727 dev->groups = netiucv_attr_groups;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 /*
1729 * The release function could be called after the
1730 * module has been unloaded. It's _only_ task is to
1731 * free the struct. Therefore, we specify kfree()
1732 * directly here. (Probably a little bit obfuscating
1733 * but legitime ...).
1734 */
1735 dev->release = (void (*)(struct device *))kfree;
1736 dev->driver = &netiucv_driver;
1737 } else
1738 return -ENOMEM;
1739
1740 ret = device_register(dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02001741 if (ret) {
1742 put_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 return ret;
Sebastian Ottc6304932009-09-11 10:28:38 +02001744 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745 priv->dev = dev;
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001746 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748}
1749
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001750static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001752 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 device_unregister(dev);
1754}
1755
1756/**
1757 * Allocate and initialize a new connection structure.
1758 * Add it to the list of netiucv connections;
1759 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001760static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
Ursula Braun08e33562011-12-19 22:56:34 +00001761 char *username,
1762 char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001764 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001766 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1767 if (!conn)
1768 goto out;
1769 skb_queue_head_init(&conn->collect_queue);
1770 skb_queue_head_init(&conn->commit_queue);
1771 spin_lock_init(&conn->collect_lock);
1772 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1773 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001775 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1776 if (!conn->rx_buff)
1777 goto out_conn;
1778 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1779 if (!conn->tx_buff)
1780 goto out_rx;
1781 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1782 conn_event_names, NR_CONN_STATES,
1783 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1784 GFP_KERNEL);
1785 if (!conn->fsm)
1786 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001788 fsm_settimer(conn->fsm, &conn->timer);
1789 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790
Ursula Braun08e33562011-12-19 22:56:34 +00001791 if (userdata)
1792 memcpy(conn->userdata, userdata, 17);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001793 if (username) {
1794 memcpy(conn->userid, username, 9);
1795 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001797
1798 write_lock_bh(&iucv_connection_rwlock);
1799 list_add_tail(&conn->list, &iucv_connection_list);
1800 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001802
1803out_tx:
1804 kfree_skb(conn->tx_buff);
1805out_rx:
1806 kfree_skb(conn->rx_buff);
1807out_conn:
1808 kfree(conn);
1809out:
1810 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811}
1812
1813/**
1814 * Release a connection structure and remove it from the
1815 * list of netiucv connections.
1816 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001817static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818{
Ursula Braun08e33562011-12-19 22:56:34 +00001819
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001820 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001821 write_lock_bh(&iucv_connection_rwlock);
1822 list_del_init(&conn->list);
1823 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001824 fsm_deltimer(&conn->timer);
1825 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001826 if (conn->path) {
Ursula Braun08e33562011-12-19 22:56:34 +00001827 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001828 kfree(conn->path);
1829 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001831 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001832 kfree_fsm(conn->fsm);
1833 kfree_skb(conn->rx_buff);
1834 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835}
1836
1837/**
1838 * Release everything of a net device.
1839 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001840static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001842 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001844 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845
1846 if (!dev)
1847 return;
1848
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849 if (privptr) {
1850 if (privptr->conn)
1851 netiucv_remove_connection(privptr->conn);
1852 if (privptr->fsm)
1853 kfree_fsm(privptr->fsm);
1854 privptr->conn = NULL; privptr->fsm = NULL;
1855 /* privptr gets freed by free_netdev() */
1856 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857}
1858
1859/**
1860 * Initialize a net device. (Called from kernel in alloc_netdev())
1861 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001862static const struct net_device_ops netiucv_netdev_ops = {
1863 .ndo_open = netiucv_open,
1864 .ndo_stop = netiucv_close,
1865 .ndo_get_stats = netiucv_stats,
1866 .ndo_start_xmit = netiucv_tx,
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001867};
1868
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001869static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871 dev->mtu = NETIUCV_MTU_DEFAULT;
Jarod Wilson46b3ef42016-10-20 13:55:23 -04001872 dev->min_mtu = 576;
1873 dev->max_mtu = NETIUCV_MTU_MAX;
Stephen Rothwellcd1997f2017-06-08 19:06:29 +10001874 dev->needs_free_netdev = true;
1875 dev->priv_destructor = netiucv_free_netdevice;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 dev->hard_header_len = NETIUCV_HDRLEN;
1877 dev->addr_len = 0;
1878 dev->type = ARPHRD_SLIP;
1879 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1880 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001881 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882}
1883
1884/**
1885 * Allocate and initialize everything of a net device.
1886 */
Ursula Braun08e33562011-12-19 22:56:34 +00001887static struct net_device *netiucv_init_netdevice(char *username, char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888{
1889 struct netiucv_priv *privptr;
1890 struct net_device *dev;
1891
1892 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
Tom Gundersenc835a672014-07-14 16:37:24 +02001893 NET_NAME_UNKNOWN, netiucv_setup_netdevice);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 if (!dev)
1895 return NULL;
Benjamin Poirieraaf95222013-06-13 09:09:47 -04001896 rtnl_lock();
Ursula Braun1d503562011-11-15 02:31:14 +00001897 if (dev_alloc_name(dev, dev->name) < 0)
1898 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001900 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
1902 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
1903 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001904 if (!privptr->fsm)
1905 goto out_netdev;
1906
Ursula Braun08e33562011-12-19 22:56:34 +00001907 privptr->conn = netiucv_new_connection(dev, username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001910 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 }
1912 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001914
1915out_fsm:
1916 kfree_fsm(privptr->fsm);
1917out_netdev:
Benjamin Poirieraaf95222013-06-13 09:09:47 -04001918 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001919 free_netdev(dev);
1920 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921}
1922
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02001923static ssize_t connection_store(struct device_driver *drv, const char *buf,
1924 size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925{
Frank Pavlic16a83b32006-09-15 16:25:19 +02001926 char username[9];
Ursula Braun08e33562011-12-19 22:56:34 +00001927 char userdata[17];
1928 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001930 struct netiucv_priv *priv;
1931 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001933 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001934 rc = netiucv_check_user(buf, count, username, userdata);
1935 if (rc)
1936 return rc;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001937
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001938 read_lock_bh(&iucv_connection_rwlock);
1939 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00001940 if (!strncmp(username, cp->userid, 9) &&
1941 !strncmp(userdata, cp->userdata, 17)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001942 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00001943 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s "
1944 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001945 return -EEXIST;
1946 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02001947 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001948 read_unlock_bh(&iucv_connection_rwlock);
1949
Ursula Braun08e33562011-12-19 22:56:34 +00001950 dev = netiucv_init_netdevice(username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
1953 return -ENODEV;
1954 }
1955
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001956 rc = netiucv_register_device(dev);
1957 if (rc) {
Benjamin Poirieraaf95222013-06-13 09:09:47 -04001958 rtnl_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001960 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 goto out_free_ndev;
1962 }
1963
1964 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001965 priv = netdev_priv(dev);
1966 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967
Benjamin Poirieraaf95222013-06-13 09:09:47 -04001968 rc = register_netdevice(dev);
1969 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001970 if (rc)
1971 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972
Ursula Braun08e33562011-12-19 22:56:34 +00001973 dev_info(priv->dev, "The IUCV interface to %s has been established "
1974 "successfully\n",
1975 netiucv_printuser(priv->conn));
Jeff Garzike82b0f22006-05-26 21:58:38 -04001976
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 return count;
1978
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001979out_unreg:
1980 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001983 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984}
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02001985static DRIVER_ATTR_WO(connection);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02001987static ssize_t remove_store(struct device_driver *drv, const char *buf,
1988 size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001990 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 struct net_device *ndev;
1992 struct netiucv_priv *priv;
1993 struct device *dev;
1994 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001995 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 int i;
1997
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001998 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999
2000 if (count >= IFNAMSIZ)
Joe Perchesa419aef2009-08-18 11:18:35 -07002001 count = IFNAMSIZ - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002003 for (i = 0, p = buf; i < count && *p; i++, p++) {
2004 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 /* trailing lf, grr */
2006 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002007 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 }
2009 name[i] = '\0';
2010
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002011 read_lock_bh(&iucv_connection_rwlock);
2012 list_for_each_entry(cp, &iucv_connection_list, list) {
2013 ndev = cp->netdev;
2014 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002016 if (strncmp(name, ndev->name, count))
2017 continue;
2018 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002020 dev_warn(dev, "The IUCV device is connected"
2021 " to %s and cannot be removed\n",
2022 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002024 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 }
2026 unregister_netdev(ndev);
2027 netiucv_unregister_device(dev);
2028 return count;
2029 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002030 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2032 return -EINVAL;
2033}
Greg Kroah-Hartman36369562017-06-09 11:03:13 +02002034static DRIVER_ATTR_WO(remove);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002036static struct attribute * netiucv_drv_attrs[] = {
2037 &driver_attr_connection.attr,
2038 &driver_attr_remove.attr,
2039 NULL,
2040};
2041
2042static struct attribute_group netiucv_drv_attr_group = {
2043 .attrs = netiucv_drv_attrs,
2044};
2045
David Brownella4dbd672009-06-24 10:06:31 -07002046static const struct attribute_group *netiucv_drv_attr_groups[] = {
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002047 &netiucv_drv_attr_group,
2048 NULL,
2049};
2050
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002051static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002053 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054}
2055
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002056static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002058 struct iucv_connection *cp;
2059 struct net_device *ndev;
2060 struct netiucv_priv *priv;
2061 struct device *dev;
2062
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002063 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002064 while (!list_empty(&iucv_connection_list)) {
2065 cp = list_entry(iucv_connection_list.next,
2066 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002067 ndev = cp->netdev;
2068 priv = netdev_priv(ndev);
2069 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070
2071 unregister_netdev(ndev);
2072 netiucv_unregister_device(dev);
2073 }
2074
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002076 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 iucv_unregister_dbf_views();
2078
Ursula Braun8f7c5022008-12-25 13:39:47 +01002079 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 return;
2081}
2082
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002083static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002085 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002086
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002087 rc = iucv_register_dbf_views();
2088 if (rc)
2089 goto out;
2090 rc = iucv_register(&netiucv_handler, 1);
2091 if (rc)
2092 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002093 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002094 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002095 rc = driver_register(&netiucv_driver);
2096 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002097 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2098 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 }
Julian Wiedmann97ccf6f2020-05-19 21:09:04 +02002100
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002101 netiucv_banner();
2102 return rc;
2103
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002104out_iucv:
2105 iucv_unregister(&netiucv_handler, 1);
2106out_dbf:
2107 iucv_unregister_dbf_views();
2108out:
2109 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002111
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112module_init(netiucv_init);
2113module_exit(netiucv_exit);
2114MODULE_LICENSE("GPL");