blob: 33f7040d711d6f66aaf8dbc96987664bd197c134 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * IUCV network driver
3 *
Ursula Braun1175b252009-06-16 10:30:43 +02004 * Copyright IBM Corp. 2001, 2009
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 *
Ursula Braun1175b252009-06-16 10:30:43 +02006 * Author(s):
7 * Original netiucv driver:
8 * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
9 * Sysfs integration and all bugs therein:
10 * Cornelia Huck (cornelia.huck@de.ibm.com)
11 * PM functions:
12 * Ursula Braun (ursula.braun@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -070013 *
14 * Documentation used:
15 * the source of the original IUCV driver by:
16 * Stefan Hegewald <hegewald@de.ibm.com>
17 * Hartmut Penner <hpenner@de.ibm.com>
18 * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
19 * Martin Schwidefsky (schwidefsky@de.ibm.com)
20 * Alan Altmark (Alan_Altmark@us.ibm.com) Sept. 2000
21 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2, or (at your option)
25 * any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 */
Jeff Garzike82b0f22006-05-26 21:58:38 -040037
Ursula Braun8f7c5022008-12-25 13:39:47 +010038#define KMSG_COMPONENT "netiucv"
39#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
40
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#undef DEBUG
42
43#include <linux/module.h>
44#include <linux/init.h>
45#include <linux/kernel.h>
46#include <linux/slab.h>
47#include <linux/errno.h>
48#include <linux/types.h>
49#include <linux/interrupt.h>
50#include <linux/timer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#include <linux/bitops.h>
52
53#include <linux/signal.h>
54#include <linux/string.h>
55#include <linux/device.h>
56
57#include <linux/ip.h>
58#include <linux/if_arp.h>
59#include <linux/tcp.h>
60#include <linux/skbuff.h>
61#include <linux/ctype.h>
62#include <net/dst.h>
63
64#include <asm/io.h>
65#include <asm/uaccess.h>
Ursula Braun08e33562011-12-19 22:56:34 +000066#include <asm/ebcdic.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
Martin Schwidefskyeebce382007-02-08 13:50:33 -080068#include <net/iucv/iucv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070069#include "fsm.h"
70
71MODULE_AUTHOR
72 ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)");
73MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
74
Martin Schwidefskyeebce382007-02-08 13:50:33 -080075/**
76 * Debug Facility stuff
77 */
78#define IUCV_DBF_SETUP_NAME "iucv_setup"
Ursula Braun08e33562011-12-19 22:56:34 +000079#define IUCV_DBF_SETUP_LEN 64
Martin Schwidefskyeebce382007-02-08 13:50:33 -080080#define IUCV_DBF_SETUP_PAGES 2
81#define IUCV_DBF_SETUP_NR_AREAS 1
82#define IUCV_DBF_SETUP_LEVEL 3
83
84#define IUCV_DBF_DATA_NAME "iucv_data"
85#define IUCV_DBF_DATA_LEN 128
86#define IUCV_DBF_DATA_PAGES 2
87#define IUCV_DBF_DATA_NR_AREAS 1
88#define IUCV_DBF_DATA_LEVEL 2
89
90#define IUCV_DBF_TRACE_NAME "iucv_trace"
91#define IUCV_DBF_TRACE_LEN 16
92#define IUCV_DBF_TRACE_PAGES 4
93#define IUCV_DBF_TRACE_NR_AREAS 1
94#define IUCV_DBF_TRACE_LEVEL 3
95
96#define IUCV_DBF_TEXT(name,level,text) \
97 do { \
98 debug_text_event(iucv_dbf_##name,level,text); \
99 } while (0)
100
101#define IUCV_DBF_HEX(name,level,addr,len) \
102 do { \
103 debug_event(iucv_dbf_##name,level,(void*)(addr),len); \
104 } while (0)
105
106DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf);
107
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100108#define IUCV_DBF_TEXT_(name, level, text...) \
109 do { \
Hendrik Brueckner8e6a8282013-09-18 17:21:34 +0200110 if (debug_level_enabled(iucv_dbf_##name, level)) { \
Tejun Heo390dfd92009-10-29 22:34:14 +0900111 char* __buf = get_cpu_var(iucv_dbf_txt_buf); \
112 sprintf(__buf, text); \
113 debug_text_event(iucv_dbf_##name, level, __buf); \
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100114 put_cpu_var(iucv_dbf_txt_buf); \
115 } \
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800116 } while (0)
117
118#define IUCV_DBF_SPRINTF(name,level,text...) \
119 do { \
120 debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
121 debug_sprintf_event(iucv_dbf_trace, level, text ); \
122 } while (0)
123
124/**
125 * some more debug stuff
126 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127#define PRINTK_HEADER " iucv: " /* for debugging */
128
Ursula Braun1175b252009-06-16 10:30:43 +0200129/* dummy device to make sure netiucv_pm functions are called */
130static struct device *netiucv_dev;
131
132static int netiucv_pm_prepare(struct device *);
133static void netiucv_pm_complete(struct device *);
134static int netiucv_pm_freeze(struct device *);
135static int netiucv_pm_restore_thaw(struct device *);
136
Alexey Dobriyan47145212009-12-14 18:00:08 -0800137static const struct dev_pm_ops netiucv_pm_ops = {
Ursula Braun1175b252009-06-16 10:30:43 +0200138 .prepare = netiucv_pm_prepare,
139 .complete = netiucv_pm_complete,
140 .freeze = netiucv_pm_freeze,
141 .thaw = netiucv_pm_restore_thaw,
142 .restore = netiucv_pm_restore_thaw,
143};
144
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145static struct device_driver netiucv_driver = {
Cornelia Huck22195102008-02-08 13:09:02 +0100146 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 .name = "netiucv",
148 .bus = &iucv_bus,
Ursula Braun1175b252009-06-16 10:30:43 +0200149 .pm = &netiucv_pm_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150};
151
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800152static int netiucv_callback_connreq(struct iucv_path *,
153 u8 ipvmid[8], u8 ipuser[16]);
154static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
155static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
156static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
157static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
158static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
159static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
160
161static struct iucv_handler netiucv_handler = {
162 .path_pending = netiucv_callback_connreq,
163 .path_complete = netiucv_callback_connack,
164 .path_severed = netiucv_callback_connrej,
165 .path_quiesced = netiucv_callback_connsusp,
166 .path_resumed = netiucv_callback_connres,
167 .message_pending = netiucv_callback_rx,
168 .message_complete = netiucv_callback_txdone
169};
170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171/**
172 * Per connection profiling data
173 */
174struct connection_profile {
175 unsigned long maxmulti;
176 unsigned long maxcqueue;
177 unsigned long doios_single;
178 unsigned long doios_multi;
179 unsigned long txlen;
180 unsigned long tx_time;
Aya Mahfouzee6edb92015-01-16 14:05:45 +0100181 unsigned long send_stamp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 unsigned long tx_pending;
183 unsigned long tx_max_pending;
184};
185
186/**
187 * Representation of one iucv connection
188 */
189struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800190 struct list_head list;
191 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 struct sk_buff *rx_buff;
193 struct sk_buff *tx_buff;
194 struct sk_buff_head collect_queue;
195 struct sk_buff_head commit_queue;
196 spinlock_t collect_lock;
197 int collect_len;
198 int max_buffsize;
199 fsm_timer timer;
200 fsm_instance *fsm;
201 struct net_device *netdev;
202 struct connection_profile prof;
203 char userid[9];
Ursula Braun08e33562011-12-19 22:56:34 +0000204 char userdata[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205};
206
207/**
208 * Linked list of all connection structs.
209 */
Denis Chengc11ca972008-01-26 14:11:13 +0100210static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200211static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
213/**
214 * Representation of event-data for the
215 * connection state machine.
216 */
217struct iucv_event {
218 struct iucv_connection *conn;
219 void *data;
220};
221
222/**
223 * Private part of the network device structure
224 */
225struct netiucv_priv {
226 struct net_device_stats stats;
227 unsigned long tbusy;
228 fsm_instance *fsm;
229 struct iucv_connection *conn;
230 struct device *dev;
Ursula Braun1175b252009-06-16 10:30:43 +0200231 int pm_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232};
233
234/**
235 * Link level header for a packet.
236 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800237struct ll_header {
238 u16 next;
239};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800241#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Ursula Braun08e33562011-12-19 22:56:34 +0000242#define NETIUCV_BUFSIZE_MAX 65537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
244#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
245#define NETIUCV_MTU_DEFAULT 9216
246#define NETIUCV_QUEUELEN_DEFAULT 50
247#define NETIUCV_TIMEOUT_5SEC 5000
248
249/**
250 * Compatibility macros for busy handling
251 * of network devices.
252 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800253static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800255 struct netiucv_priv *priv = netdev_priv(dev);
256 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 netif_wake_queue(dev);
258}
259
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800260static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800262 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800264 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265}
266
Ursula Braun08e33562011-12-19 22:56:34 +0000267static u8 iucvMagic_ascii[16] = {
268 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
269 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
270};
271
272static u8 iucvMagic_ebcdic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
274 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
275};
276
277/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 * Convert an iucv userId to its printable
279 * form (strip whitespace at end).
280 *
281 * @param An iucv userId
282 *
283 * @returns The printable string (static data!!)
284 */
Ursula Braun08e33562011-12-19 22:56:34 +0000285static char *netiucv_printname(char *name, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
Ursula Braun08e33562011-12-19 22:56:34 +0000287 static char tmp[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 char *p = tmp;
Ursula Braun08e33562011-12-19 22:56:34 +0000289 memcpy(tmp, name, len);
290 tmp[len] = '\0';
291 while (*p && ((p - tmp) < len) && (!isspace(*p)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 p++;
293 *p = '\0';
294 return tmp;
295}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400296
Ursula Braun08e33562011-12-19 22:56:34 +0000297static char *netiucv_printuser(struct iucv_connection *conn)
298{
299 static char tmp_uid[9];
300 static char tmp_udat[17];
301 static char buf[100];
302
303 if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) {
304 tmp_uid[8] = '\0';
305 tmp_udat[16] = '\0';
306 memcpy(tmp_uid, conn->userid, 8);
307 memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8);
308 memcpy(tmp_udat, conn->userdata, 16);
309 EBCASC(tmp_udat, 16);
310 memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16);
311 sprintf(buf, "%s.%s", tmp_uid, tmp_udat);
312 return buf;
313 } else
314 return netiucv_printname(conn->userid, 8);
315}
316
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317/**
318 * States of the interface statemachine.
319 */
320enum dev_states {
321 DEV_STATE_STOPPED,
322 DEV_STATE_STARTWAIT,
323 DEV_STATE_STOPWAIT,
324 DEV_STATE_RUNNING,
325 /**
326 * MUST be always the last element!!
327 */
328 NR_DEV_STATES
329};
330
331static const char *dev_state_names[] = {
332 "Stopped",
333 "StartWait",
334 "StopWait",
335 "Running",
336};
337
338/**
339 * Events of the interface statemachine.
340 */
341enum dev_events {
342 DEV_EVENT_START,
343 DEV_EVENT_STOP,
344 DEV_EVENT_CONUP,
345 DEV_EVENT_CONDOWN,
346 /**
347 * MUST be always the last element!!
348 */
349 NR_DEV_EVENTS
350};
351
352static const char *dev_event_names[] = {
353 "Start",
354 "Stop",
355 "Connection up",
356 "Connection down",
357};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359/**
360 * Events of the connection statemachine
361 */
362enum conn_events {
363 /**
364 * Events, representing callbacks from
365 * lowlevel iucv layer)
366 */
367 CONN_EVENT_CONN_REQ,
368 CONN_EVENT_CONN_ACK,
369 CONN_EVENT_CONN_REJ,
370 CONN_EVENT_CONN_SUS,
371 CONN_EVENT_CONN_RES,
372 CONN_EVENT_RX,
373 CONN_EVENT_TXDONE,
374
375 /**
376 * Events, representing errors return codes from
377 * calls to lowlevel iucv layer
378 */
379
380 /**
381 * Event, representing timer expiry.
382 */
383 CONN_EVENT_TIMER,
384
385 /**
386 * Events, representing commands from upper levels.
387 */
388 CONN_EVENT_START,
389 CONN_EVENT_STOP,
390
391 /**
392 * MUST be always the last element!!
393 */
394 NR_CONN_EVENTS,
395};
396
397static const char *conn_event_names[] = {
398 "Remote connection request",
399 "Remote connection acknowledge",
400 "Remote connection reject",
401 "Connection suspended",
402 "Connection resumed",
403 "Data received",
404 "Data sent",
405
406 "Timer",
407
408 "Start",
409 "Stop",
410};
411
412/**
413 * States of the connection statemachine.
414 */
415enum conn_states {
416 /**
417 * Connection not assigned to any device,
418 * initial state, invalid
419 */
420 CONN_STATE_INVALID,
421
422 /**
423 * Userid assigned but not operating
424 */
425 CONN_STATE_STOPPED,
426
427 /**
428 * Connection registered,
429 * no connection request sent yet,
430 * no connection request received
431 */
432 CONN_STATE_STARTWAIT,
433
434 /**
435 * Connection registered and connection request sent,
436 * no acknowledge and no connection request received yet.
437 */
438 CONN_STATE_SETUPWAIT,
439
440 /**
441 * Connection up and running idle
442 */
443 CONN_STATE_IDLE,
444
445 /**
446 * Data sent, awaiting CONN_EVENT_TXDONE
447 */
448 CONN_STATE_TX,
449
450 /**
451 * Error during registration.
452 */
453 CONN_STATE_REGERR,
454
455 /**
456 * Error during registration.
457 */
458 CONN_STATE_CONNERR,
459
460 /**
461 * MUST be always the last element!!
462 */
463 NR_CONN_STATES,
464};
465
466static const char *conn_state_names[] = {
467 "Invalid",
468 "Stopped",
469 "StartWait",
470 "SetupWait",
471 "Idle",
472 "TX",
473 "Terminating",
474 "Registration error",
475 "Connect error",
476};
477
Jeff Garzike82b0f22006-05-26 21:58:38 -0400478
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479/**
480 * Debug Facility Stuff
481 */
482static debug_info_t *iucv_dbf_setup = NULL;
483static debug_info_t *iucv_dbf_data = NULL;
484static debug_info_t *iucv_dbf_trace = NULL;
485
486DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
487
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800488static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489{
Markus Elfringb646c082015-01-16 14:05:46 +0100490 debug_unregister(iucv_dbf_setup);
491 debug_unregister(iucv_dbf_data);
492 debug_unregister(iucv_dbf_trace);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800494static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495{
496 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700497 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 IUCV_DBF_SETUP_NR_AREAS,
499 IUCV_DBF_SETUP_LEN);
500 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700501 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 IUCV_DBF_DATA_NR_AREAS,
503 IUCV_DBF_DATA_LEN);
504 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700505 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 IUCV_DBF_TRACE_NR_AREAS,
507 IUCV_DBF_TRACE_LEN);
508
509 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
510 (iucv_dbf_trace == NULL)) {
511 iucv_unregister_dbf_views();
512 return -ENOMEM;
513 }
514 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
515 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
516
517 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
518 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
519
520 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
521 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
522
523 return 0;
524}
525
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800526/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800528 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800530static void netiucv_callback_rx(struct iucv_path *path,
531 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800533 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 struct iucv_event ev;
535
536 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800537 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
539}
540
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800541static void netiucv_callback_txdone(struct iucv_path *path,
542 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800544 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 struct iucv_event ev;
546
547 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800548 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
550}
551
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800552static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800554 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800556 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557}
558
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800559static int netiucv_callback_connreq(struct iucv_path *path,
560 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800562 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 struct iucv_event ev;
Ursula Braun08e33562011-12-19 22:56:34 +0000564 static char tmp_user[9];
565 static char tmp_udat[17];
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800566 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800568 rc = -EINVAL;
Ursula Braun08e33562011-12-19 22:56:34 +0000569 memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8);
570 memcpy(tmp_udat, ipuser, 16);
571 EBCASC(tmp_udat, 16);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800572 read_lock_bh(&iucv_connection_rwlock);
573 list_for_each_entry(conn, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +0000574 if (strncmp(ipvmid, conn->userid, 8) ||
575 strncmp(ipuser, conn->userdata, 16))
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800576 continue;
577 /* Found a matching connection for this path. */
578 conn->path = path;
579 ev.conn = conn;
580 ev.data = path;
581 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
582 rc = 0;
583 }
Ursula Braun08e33562011-12-19 22:56:34 +0000584 IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n",
585 tmp_user, netiucv_printname(tmp_udat, 16));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800586 read_unlock_bh(&iucv_connection_rwlock);
587 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588}
589
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800590static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800592 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800594 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595}
596
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800597static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800599 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800601 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602}
603
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800604static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800606 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800608 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609}
610
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100612 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100614static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615{
616}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400617
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800618/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800620 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
622/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800623 * netiucv_unpack_skb
624 * @conn: The connection where this skb has been received.
625 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800627 * Unpack a just received skb and hand it over to upper layers.
628 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800630static void netiucv_unpack_skb(struct iucv_connection *conn,
631 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632{
633 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800634 struct netiucv_priv *privptr = netdev_priv(dev);
635 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637 skb_put(pskb, NETIUCV_HDRLEN);
638 pskb->dev = dev;
639 pskb->ip_summed = CHECKSUM_NONE;
640 pskb->protocol = ntohs(ETH_P_IP);
641
642 while (1) {
643 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800644 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
646 if (!header->next)
647 break;
648
649 skb_pull(pskb, NETIUCV_HDRLEN);
650 header->next -= offset;
651 offset += header->next;
652 header->next -= NETIUCV_HDRLEN;
653 if (skb_tailroom(pskb) < header->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
655 header->next, skb_tailroom(pskb));
656 return;
657 }
658 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700659 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 skb = dev_alloc_skb(pskb->len);
661 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 IUCV_DBF_TEXT(data, 2,
663 "Out of memory in netiucv_unpack_skb\n");
664 privptr->stats.rx_dropped++;
665 return;
666 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300667 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
668 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700669 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 skb->dev = pskb->dev;
671 skb->protocol = pskb->protocol;
672 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800673 privptr->stats.rx_packets++;
674 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 /*
676 * Since receiving is always initiated from a tasklet (in iucv.c),
677 * we must use netif_rx_ni() instead of netif_rx()
678 */
679 netif_rx_ni(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 skb_pull(pskb, header->next);
681 skb_put(pskb, NETIUCV_HDRLEN);
682 }
683}
684
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800685static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800687 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800689 struct iucv_message *msg = ev->data;
690 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 int rc;
692
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200693 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
695 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800696 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800698 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 return;
700 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800701 if (msg->length > conn->max_buffsize) {
702 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 privptr->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800705 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 return;
707 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700708 conn->rx_buff->data = conn->rx_buff->head;
709 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800711 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
712 msg->length, NULL);
713 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 privptr->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
716 return;
717 }
718 netiucv_unpack_skb(conn, conn->rx_buff);
719}
720
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800721static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800723 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800725 struct iucv_message *msg = ev->data;
726 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800728 u32 single_flag = msg->tag;
729 u32 txbytes = 0;
730 u32 txpackets = 0;
731 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 struct sk_buff *skb;
733 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800734 struct ll_header header;
735 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200737 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Ursula Braund239ae32013-12-16 09:44:51 +0100739 if (!conn || !conn->netdev) {
740 IUCV_DBF_TEXT(data, 2,
741 "Send confirmation for unlinked connection\n");
742 return;
743 }
744 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 conn->prof.tx_pending--;
746 if (single_flag) {
747 if ((skb = skb_dequeue(&conn->commit_queue))) {
748 atomic_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 if (privptr) {
750 privptr->stats.tx_packets++;
751 privptr->stats.tx_bytes +=
752 (skb->len - NETIUCV_HDRLEN
Ursula Braun998221c2009-11-12 21:46:30 +0000753 - NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 }
Ursula Braun998221c2009-11-12 21:46:30 +0000755 dev_kfree_skb_any(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 }
757 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700758 conn->tx_buff->data = conn->tx_buff->head;
759 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 conn->tx_buff->len = 0;
761 spin_lock_irqsave(&conn->collect_lock, saveflags);
762 while ((skb = skb_dequeue(&conn->collect_queue))) {
763 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
764 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
765 NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300766 skb_copy_from_linear_data(skb,
767 skb_put(conn->tx_buff, skb->len),
768 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 txbytes += skb->len;
770 txpackets++;
771 stat_maxcq++;
772 atomic_dec(&skb->users);
773 dev_kfree_skb_any(skb);
774 }
775 if (conn->collect_len > conn->prof.maxmulti)
776 conn->prof.maxmulti = conn->collect_len;
777 conn->collect_len = 0;
778 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800779 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800781 return;
782 }
783
784 header.next = 0;
785 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
Aya Mahfouzee6edb92015-01-16 14:05:45 +0100786 conn->prof.send_stamp = jiffies;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800787 txmsg.class = 0;
788 txmsg.tag = 0;
789 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
790 conn->tx_buff->data, conn->tx_buff->len);
791 conn->prof.doios_multi++;
792 conn->prof.txlen += conn->tx_buff->len;
793 conn->prof.tx_pending++;
794 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
795 conn->prof.tx_max_pending = conn->prof.tx_pending;
796 if (rc) {
797 conn->prof.tx_pending--;
798 fsm_newstate(fi, CONN_STATE_IDLE);
799 if (privptr)
800 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800801 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
802 } else {
803 if (privptr) {
804 privptr->stats.tx_packets += txpackets;
805 privptr->stats.tx_bytes += txbytes;
806 }
807 if (stat_maxcq > conn->prof.maxcqueue)
808 conn->prof.maxcqueue = stat_maxcq;
809 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810}
811
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800812static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800814 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800816 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800818 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200821 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800823 conn->path = path;
824 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
825 path->flags = 0;
Ursula Braun08e33562011-12-19 22:56:34 +0000826 rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
829 return;
830 }
831 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800832 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
834}
835
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800836static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800838 struct iucv_event *ev = arg;
839 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200841 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800842 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843}
844
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800845static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800847 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800849 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200851 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 fsm_deltimer(&conn->timer);
853 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800854 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
856}
857
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800858static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800860 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200862 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000864 iucv_path_sever(conn->path, conn->userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 fsm_newstate(fi, CONN_STATE_STARTWAIT);
866}
867
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800868static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800870 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800872 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200874 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
876 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000877 iucv_path_sever(conn->path, conn->userdata);
878 dev_info(privptr->dev, "The peer z/VM guest %s has closed the "
879 "connection\n", netiucv_printuser(conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800881 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 fsm_newstate(fi, CONN_STATE_STARTWAIT);
883 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
884}
885
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800886static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800888 struct iucv_connection *conn = arg;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100889 struct net_device *netdev = conn->netdev;
890 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 int rc;
892
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200893 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800895 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800897 /*
898 * We must set the state before calling iucv_connect because the
899 * callback handler could be called at any point after the connection
900 * request is sent
901 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
903 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800904 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
Ursula Braun08e33562011-12-19 22:56:34 +0000905 IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n",
906 netdev->name, netiucv_printuser(conn));
907
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800908 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
Ursula Braun08e33562011-12-19 22:56:34 +0000909 NULL, conn->userdata, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800911 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100912 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800913 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
914 CONN_EVENT_TIMER, conn);
915 return;
916 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100917 dev_warn(privptr->dev,
918 "The IUCV device failed to connect to z/VM guest %s\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000919 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800920 fsm_newstate(fi, CONN_STATE_STARTWAIT);
921 break;
922 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100923 dev_warn(privptr->dev,
924 "The IUCV device failed to connect to the peer on z/VM"
Ursula Braun08e33562011-12-19 22:56:34 +0000925 " guest %s\n", netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800926 fsm_newstate(fi, CONN_STATE_STARTWAIT);
927 break;
928 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100929 dev_err(privptr->dev,
930 "Connecting the IUCV device would exceed the maximum"
931 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800932 fsm_newstate(fi, CONN_STATE_CONNERR);
933 break;
934 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100935 dev_err(privptr->dev,
936 "z/VM guest %s has too many IUCV connections"
937 " to connect with the IUCV device\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000938 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800939 fsm_newstate(fi, CONN_STATE_CONNERR);
940 break;
941 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100942 dev_err(privptr->dev,
943 "The IUCV device cannot connect to a z/VM guest with no"
944 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800945 fsm_newstate(fi, CONN_STATE_CONNERR);
946 break;
947 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100948 dev_err(privptr->dev,
949 "Connecting the IUCV device failed with error %d\n",
950 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800951 fsm_newstate(fi, CONN_STATE_CONNERR);
952 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 }
954 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800955 kfree(conn->path);
956 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957}
958
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800959static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960{
961 struct sk_buff *skb;
962
963 while ((skb = skb_dequeue(q))) {
964 atomic_dec(&skb->users);
965 dev_kfree_skb_any(skb);
966 }
967}
968
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800969static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800971 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 struct iucv_connection *conn = ev->conn;
973 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800974 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200976 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
978 fsm_deltimer(&conn->timer);
979 fsm_newstate(fi, CONN_STATE_STOPPED);
980 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800981 if (conn->path) {
982 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
Ursula Braun08e33562011-12-19 22:56:34 +0000983 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800984 kfree(conn->path);
985 conn->path = NULL;
986 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 netiucv_purge_skb_queue(&conn->commit_queue);
988 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
989}
990
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800991static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800993 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 struct net_device *netdev = conn->netdev;
995
Ursula Braunf082bca2008-07-14 09:59:30 +0200996 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
997 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998}
999
1000static const fsm_node conn_fsm[] = {
1001 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
1002 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
1003
1004 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
1005 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
1006 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
1007 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
1008 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
1009 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
1010 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
1011
1012 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
1013 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1014 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1015 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
1016 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
1017
1018 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
1019 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
1020
1021 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
1022 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
1023 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
1024
1025 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
1026 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1027
1028 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1029 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1030};
1031
1032static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1033
Jeff Garzike82b0f22006-05-26 21:58:38 -04001034
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001035/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001037 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038
1039/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001040 * dev_action_start
1041 * @fi: An instance of an interface statemachine.
1042 * @event: The event, just happened.
1043 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001045 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001047static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001049 struct net_device *dev = arg;
1050 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001052 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001055 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056}
1057
1058/**
1059 * Shutdown connection by sending CONN_EVENT_STOP to it.
1060 *
1061 * @param fi An instance of an interface statemachine.
1062 * @param event The event, just happened.
1063 * @param arg Generic pointer, casted from struct net_device * upon call.
1064 */
1065static void
1066dev_action_stop(fsm_instance *fi, int event, void *arg)
1067{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001068 struct net_device *dev = arg;
1069 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 struct iucv_event ev;
1071
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001072 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
1074 ev.conn = privptr->conn;
1075
1076 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1077 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1078}
1079
1080/**
1081 * Called from connection statemachine
1082 * when a connection is up and running.
1083 *
1084 * @param fi An instance of an interface statemachine.
1085 * @param event The event, just happened.
1086 * @param arg Generic pointer, casted from struct net_device * upon call.
1087 */
1088static void
1089dev_action_connup(fsm_instance *fi, int event, void *arg)
1090{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001091 struct net_device *dev = arg;
1092 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001094 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095
1096 switch (fsm_getstate(fi)) {
1097 case DEV_STATE_STARTWAIT:
1098 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001099 dev_info(privptr->dev,
1100 "The IUCV device has been connected"
Ursula Braun08e33562011-12-19 22:56:34 +00001101 " successfully to %s\n",
1102 netiucv_printuser(privptr->conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 IUCV_DBF_TEXT(setup, 3,
1104 "connection is up and running\n");
1105 break;
1106 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 IUCV_DBF_TEXT(data, 2,
1108 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1109 break;
1110 }
1111}
1112
1113/**
1114 * Called from connection statemachine
1115 * when a connection has been shutdown.
1116 *
1117 * @param fi An instance of an interface statemachine.
1118 * @param event The event, just happened.
1119 * @param arg Generic pointer, casted from struct net_device * upon call.
1120 */
1121static void
1122dev_action_conndown(fsm_instance *fi, int event, void *arg)
1123{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001124 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
1126 switch (fsm_getstate(fi)) {
1127 case DEV_STATE_RUNNING:
1128 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1129 break;
1130 case DEV_STATE_STOPWAIT:
1131 fsm_newstate(fi, DEV_STATE_STOPPED);
1132 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1133 break;
1134 }
1135}
1136
1137static const fsm_node dev_fsm[] = {
1138 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1139
1140 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1141 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1142
1143 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1144 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1145
1146 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1147 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001148 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149};
1150
1151static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1152
1153/**
1154 * Transmit a packet.
1155 * This is a helper function for netiucv_tx().
1156 *
1157 * @param conn Connection to be used for sending.
1158 * @param skb Pointer to struct sk_buff of packet to send.
1159 * The linklevel header has already been set up
1160 * by netiucv_tx().
1161 *
1162 * @return 0 on success, -ERRNO on failure. (Never fails.)
1163 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001164static int netiucv_transmit_skb(struct iucv_connection *conn,
1165 struct sk_buff *skb)
1166{
1167 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001169 struct ll_header header;
1170 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171
1172 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1173 int l = skb->len + NETIUCV_HDRLEN;
1174
1175 spin_lock_irqsave(&conn->collect_lock, saveflags);
1176 if (conn->collect_len + l >
1177 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1178 rc = -EBUSY;
1179 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001180 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 } else {
1182 atomic_inc(&skb->users);
1183 skb_queue_tail(&conn->collect_queue, skb);
1184 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001185 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 }
1187 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1188 } else {
1189 struct sk_buff *nskb = skb;
1190 /**
1191 * Copy the skb to a new allocated skb in lowmem only if the
1192 * data is located above 2G in memory or tailroom is < 2.
1193 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001194 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1195 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 int copied = 0;
1197 if (hi || (skb_tailroom(skb) < 2)) {
1198 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1199 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1200 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1202 rc = -ENOMEM;
1203 return rc;
1204 } else {
1205 skb_reserve(nskb, NETIUCV_HDRLEN);
1206 memcpy(skb_put(nskb, skb->len),
1207 skb->data, skb->len);
1208 }
1209 copied = 1;
1210 }
1211 /**
1212 * skb now is below 2G and has enough room. Add headers.
1213 */
1214 header.next = nskb->len + NETIUCV_HDRLEN;
1215 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1216 header.next = 0;
1217 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1218
1219 fsm_newstate(conn->fsm, CONN_STATE_TX);
Aya Mahfouzee6edb92015-01-16 14:05:45 +01001220 conn->prof.send_stamp = jiffies;
Jeff Garzike82b0f22006-05-26 21:58:38 -04001221
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001222 msg.tag = 1;
1223 msg.class = 0;
1224 rc = iucv_message_send(conn->path, &msg, 0, 0,
1225 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 conn->prof.doios_single++;
1227 conn->prof.txlen += skb->len;
1228 conn->prof.tx_pending++;
1229 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1230 conn->prof.tx_max_pending = conn->prof.tx_pending;
1231 if (rc) {
1232 struct netiucv_priv *privptr;
1233 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1234 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001235 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 if (privptr)
1237 privptr->stats.tx_errors++;
1238 if (copied)
1239 dev_kfree_skb(nskb);
1240 else {
1241 /**
1242 * Remove our headers. They get added
1243 * again on retransmit.
1244 */
1245 skb_pull(skb, NETIUCV_HDRLEN);
1246 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1247 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1249 } else {
1250 if (copied)
1251 dev_kfree_skb(skb);
1252 atomic_inc(&nskb->users);
1253 skb_queue_tail(&conn->commit_queue, nskb);
1254 }
1255 }
1256
1257 return rc;
1258}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001259
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001260/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001262 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
1264/**
1265 * Open an interface.
1266 * Called from generic network layer when ifconfig up is run.
1267 *
1268 * @param dev Pointer to interface struct.
1269 *
1270 * @return 0 on success, -ERRNO on failure. (Never fails.)
1271 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001272static int netiucv_open(struct net_device *dev)
1273{
1274 struct netiucv_priv *priv = netdev_priv(dev);
1275
1276 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 return 0;
1278}
1279
1280/**
1281 * Close an interface.
1282 * Called from generic network layer when ifconfig down is run.
1283 *
1284 * @param dev Pointer to interface struct.
1285 *
1286 * @return 0 on success, -ERRNO on failure. (Never fails.)
1287 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001288static int netiucv_close(struct net_device *dev)
1289{
1290 struct netiucv_priv *priv = netdev_priv(dev);
1291
1292 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 return 0;
1294}
1295
Ursula Braun1175b252009-06-16 10:30:43 +02001296static int netiucv_pm_prepare(struct device *dev)
1297{
1298 IUCV_DBF_TEXT(trace, 3, __func__);
1299 return 0;
1300}
1301
1302static void netiucv_pm_complete(struct device *dev)
1303{
1304 IUCV_DBF_TEXT(trace, 3, __func__);
1305 return;
1306}
1307
1308/**
1309 * netiucv_pm_freeze() - Freeze PM callback
1310 * @dev: netiucv device
1311 *
1312 * close open netiucv interfaces
1313 */
1314static int netiucv_pm_freeze(struct device *dev)
1315{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001316 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001317 struct net_device *ndev = NULL;
1318 int rc = 0;
1319
1320 IUCV_DBF_TEXT(trace, 3, __func__);
1321 if (priv && priv->conn)
1322 ndev = priv->conn->netdev;
1323 if (!ndev)
1324 goto out;
1325 netif_device_detach(ndev);
1326 priv->pm_state = fsm_getstate(priv->fsm);
1327 rc = netiucv_close(ndev);
1328out:
1329 return rc;
1330}
1331
1332/**
1333 * netiucv_pm_restore_thaw() - Thaw and restore PM callback
1334 * @dev: netiucv device
1335 *
1336 * re-open netiucv interfaces closed during freeze
1337 */
1338static int netiucv_pm_restore_thaw(struct device *dev)
1339{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001340 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001341 struct net_device *ndev = NULL;
1342 int rc = 0;
1343
1344 IUCV_DBF_TEXT(trace, 3, __func__);
1345 if (priv && priv->conn)
1346 ndev = priv->conn->netdev;
1347 if (!ndev)
1348 goto out;
1349 switch (priv->pm_state) {
1350 case DEV_STATE_RUNNING:
1351 case DEV_STATE_STARTWAIT:
1352 rc = netiucv_open(ndev);
1353 break;
1354 default:
1355 break;
1356 }
1357 netif_device_attach(ndev);
1358out:
1359 return rc;
1360}
1361
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362/**
1363 * Start transmission of a packet.
1364 * Called from generic network device layer.
1365 *
1366 * @param skb Pointer to buffer containing the packet.
1367 * @param dev Pointer to interface struct.
1368 *
1369 * @return 0 if packet consumed, !0 if packet rejected.
1370 * Note: If we return !0, then the packet is free'd by
1371 * the generic network layer.
1372 */
1373static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1374{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001375 struct netiucv_priv *privptr = netdev_priv(dev);
1376 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001378 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 /**
1380 * Some sanity checks ...
1381 */
1382 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1384 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001385 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 }
1387 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 IUCV_DBF_TEXT(data, 2,
1389 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1390 dev_kfree_skb(skb);
1391 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001392 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 }
1394
1395 /**
1396 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001397 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 */
1399 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 dev_kfree_skb(skb);
1401 privptr->stats.tx_dropped++;
1402 privptr->stats.tx_errors++;
1403 privptr->stats.tx_carrier_errors++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001404 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 }
1406
1407 if (netiucv_test_and_set_busy(dev)) {
1408 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001409 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 }
1411 dev->trans_start = jiffies;
Patrick McHardy5b548142009-06-12 06:22:29 +00001412 rc = netiucv_transmit_skb(privptr->conn, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 netiucv_clear_busy(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001414 return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415}
1416
1417/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001418 * netiucv_stats
1419 * @dev: Pointer to interface struct.
1420 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 * Returns interface statistics of a device.
1422 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001423 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001425static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001427 struct netiucv_priv *priv = netdev_priv(dev);
1428
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001429 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001430 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431}
1432
1433/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001434 * netiucv_change_mtu
1435 * @dev: Pointer to interface struct.
1436 * @new_mtu: The new MTU to use for this interface.
1437 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 * Sets MTU of an interface.
1439 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001440 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 * (valid range is 576 .. NETIUCV_MTU_MAX).
1442 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001443static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001445 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001446 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1448 return -EINVAL;
1449 }
1450 dev->mtu = new_mtu;
1451 return 0;
1452}
1453
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001454/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001456 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001458static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1459 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001461 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001463 IUCV_DBF_TEXT(trace, 5, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001464 return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));
1465}
1466
1467static int netiucv_check_user(const char *buf, size_t count, char *username,
1468 char *userdata)
1469{
1470 const char *p;
1471 int i;
1472
1473 p = strchr(buf, '.');
1474 if ((p && ((count > 26) ||
1475 ((p - buf) > 8) ||
1476 (buf + count - p > 18))) ||
1477 (!p && (count > 9))) {
1478 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1479 return -EINVAL;
1480 }
1481
1482 for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) {
1483 if (isalnum(*p) || *p == '$') {
1484 username[i] = toupper(*p);
1485 continue;
1486 }
1487 if (*p == '\n')
1488 /* trailing lf, grr */
1489 break;
1490 IUCV_DBF_TEXT_(setup, 2,
1491 "conn_write: invalid character %02x\n", *p);
1492 return -EINVAL;
1493 }
1494 while (i < 8)
1495 username[i++] = ' ';
1496 username[8] = '\0';
1497
1498 if (*p == '.') {
1499 p++;
1500 for (i = 0; i < 16 && *p; i++, p++) {
1501 if (*p == '\n')
1502 break;
1503 userdata[i] = toupper(*p);
1504 }
1505 while (i > 0 && i < 16)
1506 userdata[i++] = ' ';
1507 } else
1508 memcpy(userdata, iucvMagic_ascii, 16);
1509 userdata[16] = '\0';
1510 ASCEBC(userdata, 16);
1511
1512 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513}
1514
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001515static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1516 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001518 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 struct net_device *ndev = priv->conn->netdev;
Ursula Braun08e33562011-12-19 22:56:34 +00001520 char username[9];
1521 char userdata[17];
1522 int rc;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001523 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001525 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001526 rc = netiucv_check_user(buf, count, username, userdata);
1527 if (rc)
1528 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001530 if (memcmp(username, priv->conn->userid, 9) &&
1531 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1532 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001533 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001534 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001535 }
1536 read_lock_bh(&iucv_connection_rwlock);
1537 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00001538 if (!strncmp(username, cp->userid, 9) &&
1539 !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001540 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00001541 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s "
1542 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001543 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 }
1545 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001546 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 memcpy(priv->conn->userid, username, 9);
Ursula Braun08e33562011-12-19 22:56:34 +00001548 memcpy(priv->conn->userdata, userdata, 17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550}
1551
1552static DEVICE_ATTR(user, 0644, user_show, user_write);
1553
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001554static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1555 char *buf)
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001556{
1557 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, "%d\n", priv->conn->max_buffsize);
1561}
1562
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001563static ssize_t buffer_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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 struct net_device *ndev = priv->conn->netdev;
1568 char *e;
1569 int bs1;
1570
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001571 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 if (count >= 39)
1573 return -EINVAL;
1574
1575 bs1 = simple_strtoul(buf, &e, 0);
1576
1577 if (e && (!isspace(*e))) {
Ursula Braun08e33562011-12-19 22:56:34 +00001578 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n",
1579 *e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 return -EINVAL;
1581 }
1582 if (bs1 > NETIUCV_BUFSIZE_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 IUCV_DBF_TEXT_(setup, 2,
1584 "buffer_write: buffer size %d too large\n",
1585 bs1);
1586 return -EINVAL;
1587 }
1588 if ((ndev->flags & IFF_RUNNING) &&
1589 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 IUCV_DBF_TEXT_(setup, 2,
1591 "buffer_write: buffer size %d too small\n",
1592 bs1);
1593 return -EINVAL;
1594 }
1595 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596 IUCV_DBF_TEXT_(setup, 2,
1597 "buffer_write: buffer size %d too small\n",
1598 bs1);
1599 return -EINVAL;
1600 }
1601
1602 priv->conn->max_buffsize = bs1;
1603 if (!(ndev->flags & IFF_RUNNING))
1604 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1605
1606 return count;
1607
1608}
1609
1610static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1611
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001612static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1613 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001615 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001617 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1619}
1620
1621static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1622
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001623static ssize_t conn_fsm_show (struct device *dev,
1624 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001626 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001628 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1630}
1631
1632static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1633
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001634static ssize_t maxmulti_show (struct device *dev,
1635 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001637 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001639 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1641}
1642
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001643static ssize_t maxmulti_write (struct device *dev,
1644 struct device_attribute *attr,
1645 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001647 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001649 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 priv->conn->prof.maxmulti = 0;
1651 return count;
1652}
1653
1654static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1655
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001656static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1657 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001659 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001661 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1663}
1664
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001665static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1666 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001668 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001669
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001670 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 priv->conn->prof.maxcqueue = 0;
1672 return count;
1673}
1674
1675static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1676
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001677static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1678 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001680 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001682 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1684}
1685
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001686static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1687 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001689 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001690
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001691 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 priv->conn->prof.doios_single = 0;
1693 return count;
1694}
1695
1696static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1697
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001698static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1699 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001701 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001703 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1705}
1706
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001707static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1708 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001710 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001711
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001712 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 priv->conn->prof.doios_multi = 0;
1714 return count;
1715}
1716
1717static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1718
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001719static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1720 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001722 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001724 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1726}
1727
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001728static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1729 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001731 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001732
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001733 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 priv->conn->prof.txlen = 0;
1735 return count;
1736}
1737
1738static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1739
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001740static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1741 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001743 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001745 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1747}
1748
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001749static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1750 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001752 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001753
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001754 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 priv->conn->prof.tx_time = 0;
1756 return count;
1757}
1758
1759static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1760
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001761static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1762 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001764 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001766 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1768}
1769
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001770static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1771 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001773 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001775 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 priv->conn->prof.tx_pending = 0;
1777 return count;
1778}
1779
1780static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1781
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001782static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1783 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001785 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001787 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1789}
1790
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001791static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1792 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001794 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001796 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 priv->conn->prof.tx_max_pending = 0;
1798 return count;
1799}
1800
1801static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1802
1803static struct attribute *netiucv_attrs[] = {
1804 &dev_attr_buffer.attr,
1805 &dev_attr_user.attr,
1806 NULL,
1807};
1808
1809static struct attribute_group netiucv_attr_group = {
1810 .attrs = netiucv_attrs,
1811};
1812
1813static struct attribute *netiucv_stat_attrs[] = {
1814 &dev_attr_device_fsm_state.attr,
1815 &dev_attr_connection_fsm_state.attr,
1816 &dev_attr_max_tx_buffer_used.attr,
1817 &dev_attr_max_chained_skbs.attr,
1818 &dev_attr_tx_single_write_ops.attr,
1819 &dev_attr_tx_multi_write_ops.attr,
1820 &dev_attr_netto_bytes.attr,
1821 &dev_attr_max_tx_io_time.attr,
1822 &dev_attr_tx_pending.attr,
1823 &dev_attr_tx_max_pending.attr,
1824 NULL,
1825};
1826
1827static struct attribute_group netiucv_stat_attr_group = {
1828 .name = "stats",
1829 .attrs = netiucv_stat_attrs,
1830};
1831
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001832static const struct attribute_group *netiucv_attr_groups[] = {
1833 &netiucv_stat_attr_group,
1834 &netiucv_attr_group,
1835 NULL,
1836};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001838static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001840 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001841 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842 int ret;
1843
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) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001847 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848 dev->bus = &iucv_bus;
1849 dev->parent = iucv_root;
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001850 dev->groups = netiucv_attr_groups;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851 /*
1852 * The release function could be called after the
1853 * module has been unloaded. It's _only_ task is to
1854 * free the struct. Therefore, we specify kfree()
1855 * directly here. (Probably a little bit obfuscating
1856 * but legitime ...).
1857 */
1858 dev->release = (void (*)(struct device *))kfree;
1859 dev->driver = &netiucv_driver;
1860 } else
1861 return -ENOMEM;
1862
1863 ret = device_register(dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02001864 if (ret) {
1865 put_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 return ret;
Sebastian Ottc6304932009-09-11 10:28:38 +02001867 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868 priv->dev = dev;
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001869 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871}
1872
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001873static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001875 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 device_unregister(dev);
1877}
1878
1879/**
1880 * Allocate and initialize a new connection structure.
1881 * Add it to the list of netiucv connections;
1882 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001883static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
Ursula Braun08e33562011-12-19 22:56:34 +00001884 char *username,
1885 char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001887 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001889 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1890 if (!conn)
1891 goto out;
1892 skb_queue_head_init(&conn->collect_queue);
1893 skb_queue_head_init(&conn->commit_queue);
1894 spin_lock_init(&conn->collect_lock);
1895 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1896 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001898 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1899 if (!conn->rx_buff)
1900 goto out_conn;
1901 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1902 if (!conn->tx_buff)
1903 goto out_rx;
1904 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1905 conn_event_names, NR_CONN_STATES,
1906 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1907 GFP_KERNEL);
1908 if (!conn->fsm)
1909 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001911 fsm_settimer(conn->fsm, &conn->timer);
1912 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913
Ursula Braun08e33562011-12-19 22:56:34 +00001914 if (userdata)
1915 memcpy(conn->userdata, userdata, 17);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001916 if (username) {
1917 memcpy(conn->userid, username, 9);
1918 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001920
1921 write_lock_bh(&iucv_connection_rwlock);
1922 list_add_tail(&conn->list, &iucv_connection_list);
1923 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001925
1926out_tx:
1927 kfree_skb(conn->tx_buff);
1928out_rx:
1929 kfree_skb(conn->rx_buff);
1930out_conn:
1931 kfree(conn);
1932out:
1933 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934}
1935
1936/**
1937 * Release a connection structure and remove it from the
1938 * list of netiucv connections.
1939 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001940static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941{
Ursula Braun08e33562011-12-19 22:56:34 +00001942
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001943 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001944 write_lock_bh(&iucv_connection_rwlock);
1945 list_del_init(&conn->list);
1946 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001947 fsm_deltimer(&conn->timer);
1948 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001949 if (conn->path) {
Ursula Braun08e33562011-12-19 22:56:34 +00001950 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001951 kfree(conn->path);
1952 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001954 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001955 kfree_fsm(conn->fsm);
1956 kfree_skb(conn->rx_buff);
1957 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958}
1959
1960/**
1961 * Release everything of a net device.
1962 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001963static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001965 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001967 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968
1969 if (!dev)
1970 return;
1971
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972 if (privptr) {
1973 if (privptr->conn)
1974 netiucv_remove_connection(privptr->conn);
1975 if (privptr->fsm)
1976 kfree_fsm(privptr->fsm);
1977 privptr->conn = NULL; privptr->fsm = NULL;
1978 /* privptr gets freed by free_netdev() */
1979 }
1980 free_netdev(dev);
1981}
1982
1983/**
1984 * Initialize a net device. (Called from kernel in alloc_netdev())
1985 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001986static const struct net_device_ops netiucv_netdev_ops = {
1987 .ndo_open = netiucv_open,
1988 .ndo_stop = netiucv_close,
1989 .ndo_get_stats = netiucv_stats,
1990 .ndo_start_xmit = netiucv_tx,
1991 .ndo_change_mtu = netiucv_change_mtu,
1992};
1993
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001994static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 dev->mtu = NETIUCV_MTU_DEFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 dev->destructor = netiucv_free_netdevice;
1998 dev->hard_header_len = NETIUCV_HDRLEN;
1999 dev->addr_len = 0;
2000 dev->type = ARPHRD_SLIP;
2001 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
2002 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00002003 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004}
2005
2006/**
2007 * Allocate and initialize everything of a net device.
2008 */
Ursula Braun08e33562011-12-19 22:56:34 +00002009static struct net_device *netiucv_init_netdevice(char *username, char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010{
2011 struct netiucv_priv *privptr;
2012 struct net_device *dev;
2013
2014 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
Tom Gundersenc835a672014-07-14 16:37:24 +02002015 NET_NAME_UNKNOWN, netiucv_setup_netdevice);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 if (!dev)
2017 return NULL;
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002018 rtnl_lock();
Ursula Braun1d503562011-11-15 02:31:14 +00002019 if (dev_alloc_name(dev, dev->name) < 0)
2020 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002022 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
2024 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
2025 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002026 if (!privptr->fsm)
2027 goto out_netdev;
2028
Ursula Braun08e33562011-12-19 22:56:34 +00002029 privptr->conn = netiucv_new_connection(dev, username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002032 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033 }
2034 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002036
2037out_fsm:
2038 kfree_fsm(privptr->fsm);
2039out_netdev:
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002040 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002041 free_netdev(dev);
2042 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043}
2044
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002045static ssize_t conn_write(struct device_driver *drv,
2046 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047{
Frank Pavlic16a83b32006-09-15 16:25:19 +02002048 char username[9];
Ursula Braun08e33562011-12-19 22:56:34 +00002049 char userdata[17];
2050 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002052 struct netiucv_priv *priv;
2053 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002055 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00002056 rc = netiucv_check_user(buf, count, username, userdata);
2057 if (rc)
2058 return rc;
Frank Pavlic16a83b32006-09-15 16:25:19 +02002059
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002060 read_lock_bh(&iucv_connection_rwlock);
2061 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00002062 if (!strncmp(username, cp->userid, 9) &&
2063 !strncmp(userdata, cp->userdata, 17)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002064 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00002065 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s "
2066 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002067 return -EEXIST;
2068 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02002069 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002070 read_unlock_bh(&iucv_connection_rwlock);
2071
Ursula Braun08e33562011-12-19 22:56:34 +00002072 dev = netiucv_init_netdevice(username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2075 return -ENODEV;
2076 }
2077
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002078 rc = netiucv_register_device(dev);
2079 if (rc) {
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002080 rtnl_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002082 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 goto out_free_ndev;
2084 }
2085
2086 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002087 priv = netdev_priv(dev);
2088 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002090 rc = register_netdevice(dev);
2091 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002092 if (rc)
2093 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094
Ursula Braun08e33562011-12-19 22:56:34 +00002095 dev_info(priv->dev, "The IUCV interface to %s has been established "
2096 "successfully\n",
2097 netiucv_printuser(priv->conn));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002098
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 return count;
2100
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002101out_unreg:
2102 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002105 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106}
2107
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002108static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002110static ssize_t remove_write (struct device_driver *drv,
2111 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002113 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114 struct net_device *ndev;
2115 struct netiucv_priv *priv;
2116 struct device *dev;
2117 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002118 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 int i;
2120
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002121 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122
2123 if (count >= IFNAMSIZ)
Joe Perchesa419aef2009-08-18 11:18:35 -07002124 count = IFNAMSIZ - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002126 for (i = 0, p = buf; i < count && *p; i++, p++) {
2127 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 /* trailing lf, grr */
2129 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002130 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 }
2132 name[i] = '\0';
2133
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002134 read_lock_bh(&iucv_connection_rwlock);
2135 list_for_each_entry(cp, &iucv_connection_list, list) {
2136 ndev = cp->netdev;
2137 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002139 if (strncmp(name, ndev->name, count))
2140 continue;
2141 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002143 dev_warn(dev, "The IUCV device is connected"
2144 " to %s and cannot be removed\n",
2145 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002147 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148 }
2149 unregister_netdev(ndev);
2150 netiucv_unregister_device(dev);
2151 return count;
2152 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002153 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2155 return -EINVAL;
2156}
2157
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002158static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002160static struct attribute * netiucv_drv_attrs[] = {
2161 &driver_attr_connection.attr,
2162 &driver_attr_remove.attr,
2163 NULL,
2164};
2165
2166static struct attribute_group netiucv_drv_attr_group = {
2167 .attrs = netiucv_drv_attrs,
2168};
2169
David Brownella4dbd672009-06-24 10:06:31 -07002170static const struct attribute_group *netiucv_drv_attr_groups[] = {
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002171 &netiucv_drv_attr_group,
2172 NULL,
2173};
2174
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002175static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002177 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178}
2179
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002180static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002182 struct iucv_connection *cp;
2183 struct net_device *ndev;
2184 struct netiucv_priv *priv;
2185 struct device *dev;
2186
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002187 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002188 while (!list_empty(&iucv_connection_list)) {
2189 cp = list_entry(iucv_connection_list.next,
2190 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002191 ndev = cp->netdev;
2192 priv = netdev_priv(ndev);
2193 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194
2195 unregister_netdev(ndev);
2196 netiucv_unregister_device(dev);
2197 }
2198
Ursula Braun1175b252009-06-16 10:30:43 +02002199 device_unregister(netiucv_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002201 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202 iucv_unregister_dbf_views();
2203
Ursula Braun8f7c5022008-12-25 13:39:47 +01002204 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 return;
2206}
2207
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002208static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002210 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002211
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002212 rc = iucv_register_dbf_views();
2213 if (rc)
2214 goto out;
2215 rc = iucv_register(&netiucv_handler, 1);
2216 if (rc)
2217 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002218 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002219 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002220 rc = driver_register(&netiucv_driver);
2221 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002222 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2223 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224 }
Ursula Braun1175b252009-06-16 10:30:43 +02002225 /* establish dummy device */
2226 netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
2227 if (!netiucv_dev) {
2228 rc = -ENOMEM;
2229 goto out_driver;
2230 }
2231 dev_set_name(netiucv_dev, "netiucv");
2232 netiucv_dev->bus = &iucv_bus;
2233 netiucv_dev->parent = iucv_root;
2234 netiucv_dev->release = (void (*)(struct device *))kfree;
2235 netiucv_dev->driver = &netiucv_driver;
2236 rc = device_register(netiucv_dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02002237 if (rc) {
2238 put_device(netiucv_dev);
Ursula Braun1175b252009-06-16 10:30:43 +02002239 goto out_driver;
Sebastian Ottc6304932009-09-11 10:28:38 +02002240 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002241 netiucv_banner();
2242 return rc;
2243
Ursula Braun1175b252009-06-16 10:30:43 +02002244out_driver:
2245 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002246out_iucv:
2247 iucv_unregister(&netiucv_handler, 1);
2248out_dbf:
2249 iucv_unregister_dbf_views();
2250out:
2251 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002253
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254module_init(netiucv_init);
2255module_exit(netiucv_exit);
2256MODULE_LICENSE("GPL");