blob: 279ad504ec3c85e3e410d231fac82e812a75b5d2 [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/* Allow to sort out low debug levels early to avoid wasted sprints */
109static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
110{
111 return (level <= dbf_grp->level);
112}
113
114#define IUCV_DBF_TEXT_(name, level, text...) \
115 do { \
116 if (iucv_dbf_passes(iucv_dbf_##name, level)) { \
Tejun Heo390dfd92009-10-29 22:34:14 +0900117 char* __buf = get_cpu_var(iucv_dbf_txt_buf); \
118 sprintf(__buf, text); \
119 debug_text_event(iucv_dbf_##name, level, __buf); \
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100120 put_cpu_var(iucv_dbf_txt_buf); \
121 } \
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800122 } while (0)
123
124#define IUCV_DBF_SPRINTF(name,level,text...) \
125 do { \
126 debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
127 debug_sprintf_event(iucv_dbf_trace, level, text ); \
128 } while (0)
129
130/**
131 * some more debug stuff
132 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133#define PRINTK_HEADER " iucv: " /* for debugging */
134
Ursula Braun1175b252009-06-16 10:30:43 +0200135/* dummy device to make sure netiucv_pm functions are called */
136static struct device *netiucv_dev;
137
138static int netiucv_pm_prepare(struct device *);
139static void netiucv_pm_complete(struct device *);
140static int netiucv_pm_freeze(struct device *);
141static int netiucv_pm_restore_thaw(struct device *);
142
Alexey Dobriyan47145212009-12-14 18:00:08 -0800143static const struct dev_pm_ops netiucv_pm_ops = {
Ursula Braun1175b252009-06-16 10:30:43 +0200144 .prepare = netiucv_pm_prepare,
145 .complete = netiucv_pm_complete,
146 .freeze = netiucv_pm_freeze,
147 .thaw = netiucv_pm_restore_thaw,
148 .restore = netiucv_pm_restore_thaw,
149};
150
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151static struct device_driver netiucv_driver = {
Cornelia Huck22195102008-02-08 13:09:02 +0100152 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 .name = "netiucv",
154 .bus = &iucv_bus,
Ursula Braun1175b252009-06-16 10:30:43 +0200155 .pm = &netiucv_pm_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156};
157
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800158static int netiucv_callback_connreq(struct iucv_path *,
159 u8 ipvmid[8], u8 ipuser[16]);
160static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
161static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
162static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
163static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
164static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
165static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
166
167static struct iucv_handler netiucv_handler = {
168 .path_pending = netiucv_callback_connreq,
169 .path_complete = netiucv_callback_connack,
170 .path_severed = netiucv_callback_connrej,
171 .path_quiesced = netiucv_callback_connsusp,
172 .path_resumed = netiucv_callback_connres,
173 .message_pending = netiucv_callback_rx,
174 .message_complete = netiucv_callback_txdone
175};
176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177/**
178 * Per connection profiling data
179 */
180struct connection_profile {
181 unsigned long maxmulti;
182 unsigned long maxcqueue;
183 unsigned long doios_single;
184 unsigned long doios_multi;
185 unsigned long txlen;
186 unsigned long tx_time;
187 struct timespec send_stamp;
188 unsigned long tx_pending;
189 unsigned long tx_max_pending;
190};
191
192/**
193 * Representation of one iucv connection
194 */
195struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800196 struct list_head list;
197 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 struct sk_buff *rx_buff;
199 struct sk_buff *tx_buff;
200 struct sk_buff_head collect_queue;
201 struct sk_buff_head commit_queue;
202 spinlock_t collect_lock;
203 int collect_len;
204 int max_buffsize;
205 fsm_timer timer;
206 fsm_instance *fsm;
207 struct net_device *netdev;
208 struct connection_profile prof;
209 char userid[9];
Ursula Braun08e33562011-12-19 22:56:34 +0000210 char userdata[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211};
212
213/**
214 * Linked list of all connection structs.
215 */
Denis Chengc11ca972008-01-26 14:11:13 +0100216static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200217static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
219/**
220 * Representation of event-data for the
221 * connection state machine.
222 */
223struct iucv_event {
224 struct iucv_connection *conn;
225 void *data;
226};
227
228/**
229 * Private part of the network device structure
230 */
231struct netiucv_priv {
232 struct net_device_stats stats;
233 unsigned long tbusy;
234 fsm_instance *fsm;
235 struct iucv_connection *conn;
236 struct device *dev;
Ursula Braun1175b252009-06-16 10:30:43 +0200237 int pm_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238};
239
240/**
241 * Link level header for a packet.
242 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800243struct ll_header {
244 u16 next;
245};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800247#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Ursula Braun08e33562011-12-19 22:56:34 +0000248#define NETIUCV_BUFSIZE_MAX 65537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
250#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
251#define NETIUCV_MTU_DEFAULT 9216
252#define NETIUCV_QUEUELEN_DEFAULT 50
253#define NETIUCV_TIMEOUT_5SEC 5000
254
255/**
256 * Compatibility macros for busy handling
257 * of network devices.
258 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800259static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800261 struct netiucv_priv *priv = netdev_priv(dev);
262 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 netif_wake_queue(dev);
264}
265
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800266static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800268 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800270 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271}
272
Ursula Braun08e33562011-12-19 22:56:34 +0000273static u8 iucvMagic_ascii[16] = {
274 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
275 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
276};
277
278static u8 iucvMagic_ebcdic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
280 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
281};
282
283/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 * Convert an iucv userId to its printable
285 * form (strip whitespace at end).
286 *
287 * @param An iucv userId
288 *
289 * @returns The printable string (static data!!)
290 */
Ursula Braun08e33562011-12-19 22:56:34 +0000291static char *netiucv_printname(char *name, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
Ursula Braun08e33562011-12-19 22:56:34 +0000293 static char tmp[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 char *p = tmp;
Ursula Braun08e33562011-12-19 22:56:34 +0000295 memcpy(tmp, name, len);
296 tmp[len] = '\0';
297 while (*p && ((p - tmp) < len) && (!isspace(*p)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 p++;
299 *p = '\0';
300 return tmp;
301}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400302
Ursula Braun08e33562011-12-19 22:56:34 +0000303static char *netiucv_printuser(struct iucv_connection *conn)
304{
305 static char tmp_uid[9];
306 static char tmp_udat[17];
307 static char buf[100];
308
309 if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) {
310 tmp_uid[8] = '\0';
311 tmp_udat[16] = '\0';
312 memcpy(tmp_uid, conn->userid, 8);
313 memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8);
314 memcpy(tmp_udat, conn->userdata, 16);
315 EBCASC(tmp_udat, 16);
316 memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16);
317 sprintf(buf, "%s.%s", tmp_uid, tmp_udat);
318 return buf;
319 } else
320 return netiucv_printname(conn->userid, 8);
321}
322
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323/**
324 * States of the interface statemachine.
325 */
326enum dev_states {
327 DEV_STATE_STOPPED,
328 DEV_STATE_STARTWAIT,
329 DEV_STATE_STOPWAIT,
330 DEV_STATE_RUNNING,
331 /**
332 * MUST be always the last element!!
333 */
334 NR_DEV_STATES
335};
336
337static const char *dev_state_names[] = {
338 "Stopped",
339 "StartWait",
340 "StopWait",
341 "Running",
342};
343
344/**
345 * Events of the interface statemachine.
346 */
347enum dev_events {
348 DEV_EVENT_START,
349 DEV_EVENT_STOP,
350 DEV_EVENT_CONUP,
351 DEV_EVENT_CONDOWN,
352 /**
353 * MUST be always the last element!!
354 */
355 NR_DEV_EVENTS
356};
357
358static const char *dev_event_names[] = {
359 "Start",
360 "Stop",
361 "Connection up",
362 "Connection down",
363};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400364
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365/**
366 * Events of the connection statemachine
367 */
368enum conn_events {
369 /**
370 * Events, representing callbacks from
371 * lowlevel iucv layer)
372 */
373 CONN_EVENT_CONN_REQ,
374 CONN_EVENT_CONN_ACK,
375 CONN_EVENT_CONN_REJ,
376 CONN_EVENT_CONN_SUS,
377 CONN_EVENT_CONN_RES,
378 CONN_EVENT_RX,
379 CONN_EVENT_TXDONE,
380
381 /**
382 * Events, representing errors return codes from
383 * calls to lowlevel iucv layer
384 */
385
386 /**
387 * Event, representing timer expiry.
388 */
389 CONN_EVENT_TIMER,
390
391 /**
392 * Events, representing commands from upper levels.
393 */
394 CONN_EVENT_START,
395 CONN_EVENT_STOP,
396
397 /**
398 * MUST be always the last element!!
399 */
400 NR_CONN_EVENTS,
401};
402
403static const char *conn_event_names[] = {
404 "Remote connection request",
405 "Remote connection acknowledge",
406 "Remote connection reject",
407 "Connection suspended",
408 "Connection resumed",
409 "Data received",
410 "Data sent",
411
412 "Timer",
413
414 "Start",
415 "Stop",
416};
417
418/**
419 * States of the connection statemachine.
420 */
421enum conn_states {
422 /**
423 * Connection not assigned to any device,
424 * initial state, invalid
425 */
426 CONN_STATE_INVALID,
427
428 /**
429 * Userid assigned but not operating
430 */
431 CONN_STATE_STOPPED,
432
433 /**
434 * Connection registered,
435 * no connection request sent yet,
436 * no connection request received
437 */
438 CONN_STATE_STARTWAIT,
439
440 /**
441 * Connection registered and connection request sent,
442 * no acknowledge and no connection request received yet.
443 */
444 CONN_STATE_SETUPWAIT,
445
446 /**
447 * Connection up and running idle
448 */
449 CONN_STATE_IDLE,
450
451 /**
452 * Data sent, awaiting CONN_EVENT_TXDONE
453 */
454 CONN_STATE_TX,
455
456 /**
457 * Error during registration.
458 */
459 CONN_STATE_REGERR,
460
461 /**
462 * Error during registration.
463 */
464 CONN_STATE_CONNERR,
465
466 /**
467 * MUST be always the last element!!
468 */
469 NR_CONN_STATES,
470};
471
472static const char *conn_state_names[] = {
473 "Invalid",
474 "Stopped",
475 "StartWait",
476 "SetupWait",
477 "Idle",
478 "TX",
479 "Terminating",
480 "Registration error",
481 "Connect error",
482};
483
Jeff Garzike82b0f22006-05-26 21:58:38 -0400484
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485/**
486 * Debug Facility Stuff
487 */
488static debug_info_t *iucv_dbf_setup = NULL;
489static debug_info_t *iucv_dbf_data = NULL;
490static debug_info_t *iucv_dbf_trace = NULL;
491
492DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
493
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800494static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495{
496 if (iucv_dbf_setup)
497 debug_unregister(iucv_dbf_setup);
498 if (iucv_dbf_data)
499 debug_unregister(iucv_dbf_data);
500 if (iucv_dbf_trace)
501 debug_unregister(iucv_dbf_trace);
502}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800503static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504{
505 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700506 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 IUCV_DBF_SETUP_NR_AREAS,
508 IUCV_DBF_SETUP_LEN);
509 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700510 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 IUCV_DBF_DATA_NR_AREAS,
512 IUCV_DBF_DATA_LEN);
513 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700514 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 IUCV_DBF_TRACE_NR_AREAS,
516 IUCV_DBF_TRACE_LEN);
517
518 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
519 (iucv_dbf_trace == NULL)) {
520 iucv_unregister_dbf_views();
521 return -ENOMEM;
522 }
523 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
524 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
525
526 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
527 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
528
529 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
530 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
531
532 return 0;
533}
534
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800535/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800537 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800539static void netiucv_callback_rx(struct iucv_path *path,
540 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800542 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 struct iucv_event ev;
544
545 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800546 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
548}
549
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800550static void netiucv_callback_txdone(struct iucv_path *path,
551 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800553 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 struct iucv_event ev;
555
556 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800557 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
559}
560
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800561static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800563 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800565 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566}
567
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800568static int netiucv_callback_connreq(struct iucv_path *path,
569 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800571 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 struct iucv_event ev;
Ursula Braun08e33562011-12-19 22:56:34 +0000573 static char tmp_user[9];
574 static char tmp_udat[17];
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800575 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800577 rc = -EINVAL;
Ursula Braun08e33562011-12-19 22:56:34 +0000578 memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8);
579 memcpy(tmp_udat, ipuser, 16);
580 EBCASC(tmp_udat, 16);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800581 read_lock_bh(&iucv_connection_rwlock);
582 list_for_each_entry(conn, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +0000583 if (strncmp(ipvmid, conn->userid, 8) ||
584 strncmp(ipuser, conn->userdata, 16))
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800585 continue;
586 /* Found a matching connection for this path. */
587 conn->path = path;
588 ev.conn = conn;
589 ev.data = path;
590 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
591 rc = 0;
592 }
Ursula Braun08e33562011-12-19 22:56:34 +0000593 IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n",
594 tmp_user, netiucv_printname(tmp_udat, 16));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800595 read_unlock_bh(&iucv_connection_rwlock);
596 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597}
598
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800599static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800601 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800603 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604}
605
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800606static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800608 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800610 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611}
612
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800613static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800615 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800617 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618}
619
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100621 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100623static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624{
625}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400626
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800627/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800629 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
631/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800632 * netiucv_unpack_skb
633 * @conn: The connection where this skb has been received.
634 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800636 * Unpack a just received skb and hand it over to upper layers.
637 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800639static void netiucv_unpack_skb(struct iucv_connection *conn,
640 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641{
642 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800643 struct netiucv_priv *privptr = netdev_priv(dev);
644 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
646 skb_put(pskb, NETIUCV_HDRLEN);
647 pskb->dev = dev;
648 pskb->ip_summed = CHECKSUM_NONE;
649 pskb->protocol = ntohs(ETH_P_IP);
650
651 while (1) {
652 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800653 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
655 if (!header->next)
656 break;
657
658 skb_pull(pskb, NETIUCV_HDRLEN);
659 header->next -= offset;
660 offset += header->next;
661 header->next -= NETIUCV_HDRLEN;
662 if (skb_tailroom(pskb) < header->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
664 header->next, skb_tailroom(pskb));
665 return;
666 }
667 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700668 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 skb = dev_alloc_skb(pskb->len);
670 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 IUCV_DBF_TEXT(data, 2,
672 "Out of memory in netiucv_unpack_skb\n");
673 privptr->stats.rx_dropped++;
674 return;
675 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300676 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
677 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700678 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 skb->dev = pskb->dev;
680 skb->protocol = pskb->protocol;
681 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800682 privptr->stats.rx_packets++;
683 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 /*
685 * Since receiving is always initiated from a tasklet (in iucv.c),
686 * we must use netif_rx_ni() instead of netif_rx()
687 */
688 netif_rx_ni(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 skb_pull(pskb, header->next);
690 skb_put(pskb, NETIUCV_HDRLEN);
691 }
692}
693
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800694static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800696 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800698 struct iucv_message *msg = ev->data;
699 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 int rc;
701
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200702 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
704 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800705 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800707 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 return;
709 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800710 if (msg->length > conn->max_buffsize) {
711 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 privptr->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800714 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 return;
716 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700717 conn->rx_buff->data = conn->rx_buff->head;
718 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800720 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
721 msg->length, NULL);
722 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 privptr->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
725 return;
726 }
727 netiucv_unpack_skb(conn, conn->rx_buff);
728}
729
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800730static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800732 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800734 struct iucv_message *msg = ev->data;
735 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800737 u32 single_flag = msg->tag;
738 u32 txbytes = 0;
739 u32 txpackets = 0;
740 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 struct sk_buff *skb;
742 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800743 struct ll_header header;
744 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200746 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800748 if (conn && conn->netdev)
749 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 conn->prof.tx_pending--;
751 if (single_flag) {
752 if ((skb = skb_dequeue(&conn->commit_queue))) {
753 atomic_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 if (privptr) {
755 privptr->stats.tx_packets++;
756 privptr->stats.tx_bytes +=
757 (skb->len - NETIUCV_HDRLEN
Ursula Braun998221c2009-11-12 21:46:30 +0000758 - NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 }
Ursula Braun998221c2009-11-12 21:46:30 +0000760 dev_kfree_skb_any(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 }
762 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700763 conn->tx_buff->data = conn->tx_buff->head;
764 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 conn->tx_buff->len = 0;
766 spin_lock_irqsave(&conn->collect_lock, saveflags);
767 while ((skb = skb_dequeue(&conn->collect_queue))) {
768 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
769 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
770 NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300771 skb_copy_from_linear_data(skb,
772 skb_put(conn->tx_buff, skb->len),
773 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 txbytes += skb->len;
775 txpackets++;
776 stat_maxcq++;
777 atomic_dec(&skb->users);
778 dev_kfree_skb_any(skb);
779 }
780 if (conn->collect_len > conn->prof.maxmulti)
781 conn->prof.maxmulti = conn->collect_len;
782 conn->collect_len = 0;
783 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800784 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800786 return;
787 }
788
789 header.next = 0;
790 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
john stultz2c6b47d2007-07-24 17:47:43 -0700791 conn->prof.send_stamp = current_kernel_time();
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800792 txmsg.class = 0;
793 txmsg.tag = 0;
794 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
795 conn->tx_buff->data, conn->tx_buff->len);
796 conn->prof.doios_multi++;
797 conn->prof.txlen += conn->tx_buff->len;
798 conn->prof.tx_pending++;
799 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
800 conn->prof.tx_max_pending = conn->prof.tx_pending;
801 if (rc) {
802 conn->prof.tx_pending--;
803 fsm_newstate(fi, CONN_STATE_IDLE);
804 if (privptr)
805 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800806 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
807 } else {
808 if (privptr) {
809 privptr->stats.tx_packets += txpackets;
810 privptr->stats.tx_bytes += txbytes;
811 }
812 if (stat_maxcq > conn->prof.maxcqueue)
813 conn->prof.maxcqueue = stat_maxcq;
814 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815}
816
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800817static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800819 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800821 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800823 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200826 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800828 conn->path = path;
829 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
830 path->flags = 0;
Ursula Braun08e33562011-12-19 22:56:34 +0000831 rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
834 return;
835 }
836 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800837 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
839}
840
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800841static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800843 struct iucv_event *ev = arg;
844 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200846 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800847 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848}
849
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800850static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800852 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800854 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200856 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 fsm_deltimer(&conn->timer);
858 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800859 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
861}
862
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800863static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800865 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200867 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000869 iucv_path_sever(conn->path, conn->userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 fsm_newstate(fi, CONN_STATE_STARTWAIT);
871}
872
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800873static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800875 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800877 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200879 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
881 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000882 iucv_path_sever(conn->path, conn->userdata);
883 dev_info(privptr->dev, "The peer z/VM guest %s has closed the "
884 "connection\n", netiucv_printuser(conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800886 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 fsm_newstate(fi, CONN_STATE_STARTWAIT);
888 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
889}
890
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800891static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800893 struct iucv_connection *conn = arg;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100894 struct net_device *netdev = conn->netdev;
895 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 int rc;
897
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200898 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800900 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800902 /*
903 * We must set the state before calling iucv_connect because the
904 * callback handler could be called at any point after the connection
905 * request is sent
906 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
908 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800909 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
Ursula Braun08e33562011-12-19 22:56:34 +0000910 IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n",
911 netdev->name, netiucv_printuser(conn));
912
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800913 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
Ursula Braun08e33562011-12-19 22:56:34 +0000914 NULL, conn->userdata, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800916 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100917 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800918 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
919 CONN_EVENT_TIMER, conn);
920 return;
921 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100922 dev_warn(privptr->dev,
923 "The IUCV device failed to connect to z/VM guest %s\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000924 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800925 fsm_newstate(fi, CONN_STATE_STARTWAIT);
926 break;
927 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100928 dev_warn(privptr->dev,
929 "The IUCV device failed to connect to the peer on z/VM"
Ursula Braun08e33562011-12-19 22:56:34 +0000930 " guest %s\n", netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800931 fsm_newstate(fi, CONN_STATE_STARTWAIT);
932 break;
933 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100934 dev_err(privptr->dev,
935 "Connecting the IUCV device would exceed the maximum"
936 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800937 fsm_newstate(fi, CONN_STATE_CONNERR);
938 break;
939 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100940 dev_err(privptr->dev,
941 "z/VM guest %s has too many IUCV connections"
942 " to connect with the IUCV device\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000943 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800944 fsm_newstate(fi, CONN_STATE_CONNERR);
945 break;
946 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100947 dev_err(privptr->dev,
948 "The IUCV device cannot connect to a z/VM guest with no"
949 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800950 fsm_newstate(fi, CONN_STATE_CONNERR);
951 break;
952 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100953 dev_err(privptr->dev,
954 "Connecting the IUCV device failed with error %d\n",
955 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800956 fsm_newstate(fi, CONN_STATE_CONNERR);
957 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 }
959 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800960 kfree(conn->path);
961 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962}
963
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800964static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965{
966 struct sk_buff *skb;
967
968 while ((skb = skb_dequeue(q))) {
969 atomic_dec(&skb->users);
970 dev_kfree_skb_any(skb);
971 }
972}
973
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800974static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800976 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 struct iucv_connection *conn = ev->conn;
978 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800979 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200981 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
983 fsm_deltimer(&conn->timer);
984 fsm_newstate(fi, CONN_STATE_STOPPED);
985 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800986 if (conn->path) {
987 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
Ursula Braun08e33562011-12-19 22:56:34 +0000988 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800989 kfree(conn->path);
990 conn->path = NULL;
991 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 netiucv_purge_skb_queue(&conn->commit_queue);
993 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
994}
995
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800996static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800998 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 struct net_device *netdev = conn->netdev;
1000
Ursula Braunf082bca2008-07-14 09:59:30 +02001001 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
1002 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003}
1004
1005static const fsm_node conn_fsm[] = {
1006 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
1007 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
1008
1009 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
1010 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
1011 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
1012 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
1013 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
1014 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
1015 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
1016
1017 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
1018 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1019 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1020 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
1021 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
1022
1023 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
1024 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
1025
1026 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
1027 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
1028 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
1029
1030 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
1031 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1032
1033 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1034 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1035};
1036
1037static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1038
Jeff Garzike82b0f22006-05-26 21:58:38 -04001039
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001040/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001042 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043
1044/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001045 * dev_action_start
1046 * @fi: An instance of an interface statemachine.
1047 * @event: The event, just happened.
1048 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001050 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001052static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001054 struct net_device *dev = arg;
1055 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001057 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001060 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061}
1062
1063/**
1064 * Shutdown connection by sending CONN_EVENT_STOP to it.
1065 *
1066 * @param fi An instance of an interface statemachine.
1067 * @param event The event, just happened.
1068 * @param arg Generic pointer, casted from struct net_device * upon call.
1069 */
1070static void
1071dev_action_stop(fsm_instance *fi, int event, void *arg)
1072{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001073 struct net_device *dev = arg;
1074 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 struct iucv_event ev;
1076
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001077 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078
1079 ev.conn = privptr->conn;
1080
1081 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1082 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1083}
1084
1085/**
1086 * Called from connection statemachine
1087 * when a connection is up and running.
1088 *
1089 * @param fi An instance of an interface statemachine.
1090 * @param event The event, just happened.
1091 * @param arg Generic pointer, casted from struct net_device * upon call.
1092 */
1093static void
1094dev_action_connup(fsm_instance *fi, int event, void *arg)
1095{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001096 struct net_device *dev = arg;
1097 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001099 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100
1101 switch (fsm_getstate(fi)) {
1102 case DEV_STATE_STARTWAIT:
1103 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001104 dev_info(privptr->dev,
1105 "The IUCV device has been connected"
Ursula Braun08e33562011-12-19 22:56:34 +00001106 " successfully to %s\n",
1107 netiucv_printuser(privptr->conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 IUCV_DBF_TEXT(setup, 3,
1109 "connection is up and running\n");
1110 break;
1111 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 IUCV_DBF_TEXT(data, 2,
1113 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1114 break;
1115 }
1116}
1117
1118/**
1119 * Called from connection statemachine
1120 * when a connection has been shutdown.
1121 *
1122 * @param fi An instance of an interface statemachine.
1123 * @param event The event, just happened.
1124 * @param arg Generic pointer, casted from struct net_device * upon call.
1125 */
1126static void
1127dev_action_conndown(fsm_instance *fi, int event, void *arg)
1128{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001129 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130
1131 switch (fsm_getstate(fi)) {
1132 case DEV_STATE_RUNNING:
1133 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1134 break;
1135 case DEV_STATE_STOPWAIT:
1136 fsm_newstate(fi, DEV_STATE_STOPPED);
1137 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1138 break;
1139 }
1140}
1141
1142static const fsm_node dev_fsm[] = {
1143 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1144
1145 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1146 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1147
1148 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1149 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1150
1151 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1152 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001153 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154};
1155
1156static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1157
1158/**
1159 * Transmit a packet.
1160 * This is a helper function for netiucv_tx().
1161 *
1162 * @param conn Connection to be used for sending.
1163 * @param skb Pointer to struct sk_buff of packet to send.
1164 * The linklevel header has already been set up
1165 * by netiucv_tx().
1166 *
1167 * @return 0 on success, -ERRNO on failure. (Never fails.)
1168 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001169static int netiucv_transmit_skb(struct iucv_connection *conn,
1170 struct sk_buff *skb)
1171{
1172 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001174 struct ll_header header;
1175 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176
1177 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1178 int l = skb->len + NETIUCV_HDRLEN;
1179
1180 spin_lock_irqsave(&conn->collect_lock, saveflags);
1181 if (conn->collect_len + l >
1182 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1183 rc = -EBUSY;
1184 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001185 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 } else {
1187 atomic_inc(&skb->users);
1188 skb_queue_tail(&conn->collect_queue, skb);
1189 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001190 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 }
1192 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1193 } else {
1194 struct sk_buff *nskb = skb;
1195 /**
1196 * Copy the skb to a new allocated skb in lowmem only if the
1197 * data is located above 2G in memory or tailroom is < 2.
1198 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001199 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1200 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 int copied = 0;
1202 if (hi || (skb_tailroom(skb) < 2)) {
1203 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1204 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1205 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1207 rc = -ENOMEM;
1208 return rc;
1209 } else {
1210 skb_reserve(nskb, NETIUCV_HDRLEN);
1211 memcpy(skb_put(nskb, skb->len),
1212 skb->data, skb->len);
1213 }
1214 copied = 1;
1215 }
1216 /**
1217 * skb now is below 2G and has enough room. Add headers.
1218 */
1219 header.next = nskb->len + NETIUCV_HDRLEN;
1220 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1221 header.next = 0;
1222 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1223
1224 fsm_newstate(conn->fsm, CONN_STATE_TX);
john stultz2c6b47d2007-07-24 17:47:43 -07001225 conn->prof.send_stamp = current_kernel_time();
Jeff Garzike82b0f22006-05-26 21:58:38 -04001226
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001227 msg.tag = 1;
1228 msg.class = 0;
1229 rc = iucv_message_send(conn->path, &msg, 0, 0,
1230 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 conn->prof.doios_single++;
1232 conn->prof.txlen += skb->len;
1233 conn->prof.tx_pending++;
1234 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1235 conn->prof.tx_max_pending = conn->prof.tx_pending;
1236 if (rc) {
1237 struct netiucv_priv *privptr;
1238 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1239 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001240 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 if (privptr)
1242 privptr->stats.tx_errors++;
1243 if (copied)
1244 dev_kfree_skb(nskb);
1245 else {
1246 /**
1247 * Remove our headers. They get added
1248 * again on retransmit.
1249 */
1250 skb_pull(skb, NETIUCV_HDRLEN);
1251 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1252 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1254 } else {
1255 if (copied)
1256 dev_kfree_skb(skb);
1257 atomic_inc(&nskb->users);
1258 skb_queue_tail(&conn->commit_queue, nskb);
1259 }
1260 }
1261
1262 return rc;
1263}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001264
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001265/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001267 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268
1269/**
1270 * Open an interface.
1271 * Called from generic network layer when ifconfig up is run.
1272 *
1273 * @param dev Pointer to interface struct.
1274 *
1275 * @return 0 on success, -ERRNO on failure. (Never fails.)
1276 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001277static int netiucv_open(struct net_device *dev)
1278{
1279 struct netiucv_priv *priv = netdev_priv(dev);
1280
1281 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 return 0;
1283}
1284
1285/**
1286 * Close an interface.
1287 * Called from generic network layer when ifconfig down is run.
1288 *
1289 * @param dev Pointer to interface struct.
1290 *
1291 * @return 0 on success, -ERRNO on failure. (Never fails.)
1292 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001293static int netiucv_close(struct net_device *dev)
1294{
1295 struct netiucv_priv *priv = netdev_priv(dev);
1296
1297 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 return 0;
1299}
1300
Ursula Braun1175b252009-06-16 10:30:43 +02001301static int netiucv_pm_prepare(struct device *dev)
1302{
1303 IUCV_DBF_TEXT(trace, 3, __func__);
1304 return 0;
1305}
1306
1307static void netiucv_pm_complete(struct device *dev)
1308{
1309 IUCV_DBF_TEXT(trace, 3, __func__);
1310 return;
1311}
1312
1313/**
1314 * netiucv_pm_freeze() - Freeze PM callback
1315 * @dev: netiucv device
1316 *
1317 * close open netiucv interfaces
1318 */
1319static int netiucv_pm_freeze(struct device *dev)
1320{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001321 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001322 struct net_device *ndev = NULL;
1323 int rc = 0;
1324
1325 IUCV_DBF_TEXT(trace, 3, __func__);
1326 if (priv && priv->conn)
1327 ndev = priv->conn->netdev;
1328 if (!ndev)
1329 goto out;
1330 netif_device_detach(ndev);
1331 priv->pm_state = fsm_getstate(priv->fsm);
1332 rc = netiucv_close(ndev);
1333out:
1334 return rc;
1335}
1336
1337/**
1338 * netiucv_pm_restore_thaw() - Thaw and restore PM callback
1339 * @dev: netiucv device
1340 *
1341 * re-open netiucv interfaces closed during freeze
1342 */
1343static int netiucv_pm_restore_thaw(struct device *dev)
1344{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001345 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001346 struct net_device *ndev = NULL;
1347 int rc = 0;
1348
1349 IUCV_DBF_TEXT(trace, 3, __func__);
1350 if (priv && priv->conn)
1351 ndev = priv->conn->netdev;
1352 if (!ndev)
1353 goto out;
1354 switch (priv->pm_state) {
1355 case DEV_STATE_RUNNING:
1356 case DEV_STATE_STARTWAIT:
1357 rc = netiucv_open(ndev);
1358 break;
1359 default:
1360 break;
1361 }
1362 netif_device_attach(ndev);
1363out:
1364 return rc;
1365}
1366
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367/**
1368 * Start transmission of a packet.
1369 * Called from generic network device layer.
1370 *
1371 * @param skb Pointer to buffer containing the packet.
1372 * @param dev Pointer to interface struct.
1373 *
1374 * @return 0 if packet consumed, !0 if packet rejected.
1375 * Note: If we return !0, then the packet is free'd by
1376 * the generic network layer.
1377 */
1378static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1379{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001380 struct netiucv_priv *privptr = netdev_priv(dev);
1381 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001383 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 /**
1385 * Some sanity checks ...
1386 */
1387 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1389 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001390 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 }
1392 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 IUCV_DBF_TEXT(data, 2,
1394 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1395 dev_kfree_skb(skb);
1396 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001397 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 }
1399
1400 /**
1401 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001402 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 */
1404 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 dev_kfree_skb(skb);
1406 privptr->stats.tx_dropped++;
1407 privptr->stats.tx_errors++;
1408 privptr->stats.tx_carrier_errors++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001409 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 }
1411
1412 if (netiucv_test_and_set_busy(dev)) {
1413 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001414 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 }
1416 dev->trans_start = jiffies;
Patrick McHardy5b548142009-06-12 06:22:29 +00001417 rc = netiucv_transmit_skb(privptr->conn, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 netiucv_clear_busy(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001419 return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420}
1421
1422/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001423 * netiucv_stats
1424 * @dev: Pointer to interface struct.
1425 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 * Returns interface statistics of a device.
1427 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001428 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001430static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001432 struct netiucv_priv *priv = netdev_priv(dev);
1433
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001434 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001435 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436}
1437
1438/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001439 * netiucv_change_mtu
1440 * @dev: Pointer to interface struct.
1441 * @new_mtu: The new MTU to use for this interface.
1442 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 * Sets MTU of an interface.
1444 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001445 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 * (valid range is 576 .. NETIUCV_MTU_MAX).
1447 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001448static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001450 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001451 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1453 return -EINVAL;
1454 }
1455 dev->mtu = new_mtu;
1456 return 0;
1457}
1458
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001459/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001461 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001463static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1464 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001466 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001468 IUCV_DBF_TEXT(trace, 5, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001469 return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));
1470}
1471
1472static int netiucv_check_user(const char *buf, size_t count, char *username,
1473 char *userdata)
1474{
1475 const char *p;
1476 int i;
1477
1478 p = strchr(buf, '.');
1479 if ((p && ((count > 26) ||
1480 ((p - buf) > 8) ||
1481 (buf + count - p > 18))) ||
1482 (!p && (count > 9))) {
1483 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1484 return -EINVAL;
1485 }
1486
1487 for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) {
1488 if (isalnum(*p) || *p == '$') {
1489 username[i] = toupper(*p);
1490 continue;
1491 }
1492 if (*p == '\n')
1493 /* trailing lf, grr */
1494 break;
1495 IUCV_DBF_TEXT_(setup, 2,
1496 "conn_write: invalid character %02x\n", *p);
1497 return -EINVAL;
1498 }
1499 while (i < 8)
1500 username[i++] = ' ';
1501 username[8] = '\0';
1502
1503 if (*p == '.') {
1504 p++;
1505 for (i = 0; i < 16 && *p; i++, p++) {
1506 if (*p == '\n')
1507 break;
1508 userdata[i] = toupper(*p);
1509 }
1510 while (i > 0 && i < 16)
1511 userdata[i++] = ' ';
1512 } else
1513 memcpy(userdata, iucvMagic_ascii, 16);
1514 userdata[16] = '\0';
1515 ASCEBC(userdata, 16);
1516
1517 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518}
1519
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001520static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1521 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001523 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 struct net_device *ndev = priv->conn->netdev;
Ursula Braun08e33562011-12-19 22:56:34 +00001525 char username[9];
1526 char userdata[17];
1527 int rc;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001528 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001530 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001531 rc = netiucv_check_user(buf, count, username, userdata);
1532 if (rc)
1533 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001535 if (memcmp(username, priv->conn->userid, 9) &&
1536 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1537 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001538 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001539 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001540 }
1541 read_lock_bh(&iucv_connection_rwlock);
1542 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00001543 if (!strncmp(username, cp->userid, 9) &&
1544 !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001545 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00001546 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s "
1547 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001548 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 }
1550 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001551 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 memcpy(priv->conn->userid, username, 9);
Ursula Braun08e33562011-12-19 22:56:34 +00001553 memcpy(priv->conn->userdata, userdata, 17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555}
1556
1557static DEVICE_ATTR(user, 0644, user_show, user_write);
1558
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001559static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1560 char *buf)
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001561{
1562 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001564 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1566}
1567
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001568static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1569 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001571 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 struct net_device *ndev = priv->conn->netdev;
1573 char *e;
1574 int bs1;
1575
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001576 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 if (count >= 39)
1578 return -EINVAL;
1579
1580 bs1 = simple_strtoul(buf, &e, 0);
1581
1582 if (e && (!isspace(*e))) {
Ursula Braun08e33562011-12-19 22:56:34 +00001583 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n",
1584 *e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 return -EINVAL;
1586 }
1587 if (bs1 > NETIUCV_BUFSIZE_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 IUCV_DBF_TEXT_(setup, 2,
1589 "buffer_write: buffer size %d too large\n",
1590 bs1);
1591 return -EINVAL;
1592 }
1593 if ((ndev->flags & IFF_RUNNING) &&
1594 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 IUCV_DBF_TEXT_(setup, 2,
1596 "buffer_write: buffer size %d too small\n",
1597 bs1);
1598 return -EINVAL;
1599 }
1600 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 IUCV_DBF_TEXT_(setup, 2,
1602 "buffer_write: buffer size %d too small\n",
1603 bs1);
1604 return -EINVAL;
1605 }
1606
1607 priv->conn->max_buffsize = bs1;
1608 if (!(ndev->flags & IFF_RUNNING))
1609 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1610
1611 return count;
1612
1613}
1614
1615static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1616
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001617static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1618 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001620 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001622 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1624}
1625
1626static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1627
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001628static ssize_t conn_fsm_show (struct device *dev,
1629 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001631 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001633 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1635}
1636
1637static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1638
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001639static ssize_t maxmulti_show (struct device *dev,
1640 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001642 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001644 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1646}
1647
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001648static ssize_t maxmulti_write (struct device *dev,
1649 struct device_attribute *attr,
1650 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001652 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001654 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 priv->conn->prof.maxmulti = 0;
1656 return count;
1657}
1658
1659static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1660
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001661static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1662 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001664 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001666 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1668}
1669
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001670static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1671 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001673 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001674
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001675 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 priv->conn->prof.maxcqueue = 0;
1677 return count;
1678}
1679
1680static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1681
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001682static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1683 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001685 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001687 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1689}
1690
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001691static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1692 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001694 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001695
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001696 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 priv->conn->prof.doios_single = 0;
1698 return count;
1699}
1700
1701static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1702
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001703static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1704 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001706 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001708 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1710}
1711
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001712static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1713 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001715 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001716
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001717 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718 priv->conn->prof.doios_multi = 0;
1719 return count;
1720}
1721
1722static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1723
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001724static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1725 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001727 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001729 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1731}
1732
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001733static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1734 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001736 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001737
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001738 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 priv->conn->prof.txlen = 0;
1740 return count;
1741}
1742
1743static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1744
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001745static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1746 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001748 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001750 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1752}
1753
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001754static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1755 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001757 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001758
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001759 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 priv->conn->prof.tx_time = 0;
1761 return count;
1762}
1763
1764static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1765
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001766static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1767 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001769 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001771 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1773}
1774
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001775static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1776 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001778 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001780 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 priv->conn->prof.tx_pending = 0;
1782 return count;
1783}
1784
1785static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1786
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001787static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1788 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001790 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001792 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1794}
1795
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001796static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1797 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001799 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001801 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802 priv->conn->prof.tx_max_pending = 0;
1803 return count;
1804}
1805
1806static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1807
1808static struct attribute *netiucv_attrs[] = {
1809 &dev_attr_buffer.attr,
1810 &dev_attr_user.attr,
1811 NULL,
1812};
1813
1814static struct attribute_group netiucv_attr_group = {
1815 .attrs = netiucv_attrs,
1816};
1817
1818static struct attribute *netiucv_stat_attrs[] = {
1819 &dev_attr_device_fsm_state.attr,
1820 &dev_attr_connection_fsm_state.attr,
1821 &dev_attr_max_tx_buffer_used.attr,
1822 &dev_attr_max_chained_skbs.attr,
1823 &dev_attr_tx_single_write_ops.attr,
1824 &dev_attr_tx_multi_write_ops.attr,
1825 &dev_attr_netto_bytes.attr,
1826 &dev_attr_max_tx_io_time.attr,
1827 &dev_attr_tx_pending.attr,
1828 &dev_attr_tx_max_pending.attr,
1829 NULL,
1830};
1831
1832static struct attribute_group netiucv_stat_attr_group = {
1833 .name = "stats",
1834 .attrs = netiucv_stat_attrs,
1835};
1836
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001837static const struct attribute_group *netiucv_attr_groups[] = {
1838 &netiucv_stat_attr_group,
1839 &netiucv_attr_group,
1840 NULL,
1841};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001843static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001845 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001846 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847 int ret;
1848
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001849 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850
1851 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001852 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853 dev->bus = &iucv_bus;
1854 dev->parent = iucv_root;
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001855 dev->groups = netiucv_attr_groups;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856 /*
1857 * The release function could be called after the
1858 * module has been unloaded. It's _only_ task is to
1859 * free the struct. Therefore, we specify kfree()
1860 * directly here. (Probably a little bit obfuscating
1861 * but legitime ...).
1862 */
1863 dev->release = (void (*)(struct device *))kfree;
1864 dev->driver = &netiucv_driver;
1865 } else
1866 return -ENOMEM;
1867
1868 ret = device_register(dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02001869 if (ret) {
1870 put_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871 return ret;
Sebastian Ottc6304932009-09-11 10:28:38 +02001872 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 priv->dev = dev;
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001874 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876}
1877
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001878static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001880 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 device_unregister(dev);
1882}
1883
1884/**
1885 * Allocate and initialize a new connection structure.
1886 * Add it to the list of netiucv connections;
1887 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001888static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
Ursula Braun08e33562011-12-19 22:56:34 +00001889 char *username,
1890 char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001892 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001894 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1895 if (!conn)
1896 goto out;
1897 skb_queue_head_init(&conn->collect_queue);
1898 skb_queue_head_init(&conn->commit_queue);
1899 spin_lock_init(&conn->collect_lock);
1900 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1901 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001903 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1904 if (!conn->rx_buff)
1905 goto out_conn;
1906 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1907 if (!conn->tx_buff)
1908 goto out_rx;
1909 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1910 conn_event_names, NR_CONN_STATES,
1911 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1912 GFP_KERNEL);
1913 if (!conn->fsm)
1914 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001916 fsm_settimer(conn->fsm, &conn->timer);
1917 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918
Ursula Braun08e33562011-12-19 22:56:34 +00001919 if (userdata)
1920 memcpy(conn->userdata, userdata, 17);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001921 if (username) {
1922 memcpy(conn->userid, username, 9);
1923 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001925
1926 write_lock_bh(&iucv_connection_rwlock);
1927 list_add_tail(&conn->list, &iucv_connection_list);
1928 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001930
1931out_tx:
1932 kfree_skb(conn->tx_buff);
1933out_rx:
1934 kfree_skb(conn->rx_buff);
1935out_conn:
1936 kfree(conn);
1937out:
1938 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939}
1940
1941/**
1942 * Release a connection structure and remove it from the
1943 * list of netiucv connections.
1944 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001945static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946{
Ursula Braun08e33562011-12-19 22:56:34 +00001947
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001948 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001949 write_lock_bh(&iucv_connection_rwlock);
1950 list_del_init(&conn->list);
1951 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001952 fsm_deltimer(&conn->timer);
1953 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001954 if (conn->path) {
Ursula Braun08e33562011-12-19 22:56:34 +00001955 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001956 kfree(conn->path);
1957 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001959 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001960 kfree_fsm(conn->fsm);
1961 kfree_skb(conn->rx_buff);
1962 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963}
1964
1965/**
1966 * Release everything of a net device.
1967 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001968static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001970 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001972 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973
1974 if (!dev)
1975 return;
1976
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 if (privptr) {
1978 if (privptr->conn)
1979 netiucv_remove_connection(privptr->conn);
1980 if (privptr->fsm)
1981 kfree_fsm(privptr->fsm);
1982 privptr->conn = NULL; privptr->fsm = NULL;
1983 /* privptr gets freed by free_netdev() */
1984 }
1985 free_netdev(dev);
1986}
1987
1988/**
1989 * Initialize a net device. (Called from kernel in alloc_netdev())
1990 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001991static const struct net_device_ops netiucv_netdev_ops = {
1992 .ndo_open = netiucv_open,
1993 .ndo_stop = netiucv_close,
1994 .ndo_get_stats = netiucv_stats,
1995 .ndo_start_xmit = netiucv_tx,
1996 .ndo_change_mtu = netiucv_change_mtu,
1997};
1998
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001999static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 dev->mtu = NETIUCV_MTU_DEFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 dev->destructor = netiucv_free_netdevice;
2003 dev->hard_header_len = NETIUCV_HDRLEN;
2004 dev->addr_len = 0;
2005 dev->type = ARPHRD_SLIP;
2006 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
2007 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00002008 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009}
2010
2011/**
2012 * Allocate and initialize everything of a net device.
2013 */
Ursula Braun08e33562011-12-19 22:56:34 +00002014static struct net_device *netiucv_init_netdevice(char *username, char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015{
2016 struct netiucv_priv *privptr;
2017 struct net_device *dev;
2018
2019 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
2020 netiucv_setup_netdevice);
2021 if (!dev)
2022 return NULL;
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002023 rtnl_lock();
Ursula Braun1d503562011-11-15 02:31:14 +00002024 if (dev_alloc_name(dev, dev->name) < 0)
2025 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002027 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
2029 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
2030 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002031 if (!privptr->fsm)
2032 goto out_netdev;
2033
Ursula Braun08e33562011-12-19 22:56:34 +00002034 privptr->conn = netiucv_new_connection(dev, username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002037 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038 }
2039 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002041
2042out_fsm:
2043 kfree_fsm(privptr->fsm);
2044out_netdev:
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002045 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002046 free_netdev(dev);
2047 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048}
2049
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002050static ssize_t conn_write(struct device_driver *drv,
2051 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052{
Frank Pavlic16a83b32006-09-15 16:25:19 +02002053 char username[9];
Ursula Braun08e33562011-12-19 22:56:34 +00002054 char userdata[17];
2055 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002057 struct netiucv_priv *priv;
2058 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002060 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00002061 rc = netiucv_check_user(buf, count, username, userdata);
2062 if (rc)
2063 return rc;
Frank Pavlic16a83b32006-09-15 16:25:19 +02002064
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002065 read_lock_bh(&iucv_connection_rwlock);
2066 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00002067 if (!strncmp(username, cp->userid, 9) &&
2068 !strncmp(userdata, cp->userdata, 17)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002069 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00002070 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s "
2071 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002072 return -EEXIST;
2073 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02002074 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002075 read_unlock_bh(&iucv_connection_rwlock);
2076
Ursula Braun08e33562011-12-19 22:56:34 +00002077 dev = netiucv_init_netdevice(username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2080 return -ENODEV;
2081 }
2082
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002083 rc = netiucv_register_device(dev);
2084 if (rc) {
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002085 rtnl_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002087 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 goto out_free_ndev;
2089 }
2090
2091 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002092 priv = netdev_priv(dev);
2093 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094
Benjamin Poirieraaf95222013-06-13 09:09:47 -04002095 rc = register_netdevice(dev);
2096 rtnl_unlock();
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002097 if (rc)
2098 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099
Ursula Braun08e33562011-12-19 22:56:34 +00002100 dev_info(priv->dev, "The IUCV interface to %s has been established "
2101 "successfully\n",
2102 netiucv_printuser(priv->conn));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002103
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 return count;
2105
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002106out_unreg:
2107 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002110 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111}
2112
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002113static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002115static ssize_t remove_write (struct device_driver *drv,
2116 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002118 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 struct net_device *ndev;
2120 struct netiucv_priv *priv;
2121 struct device *dev;
2122 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002123 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 int i;
2125
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002126 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127
2128 if (count >= IFNAMSIZ)
Joe Perchesa419aef2009-08-18 11:18:35 -07002129 count = IFNAMSIZ - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002131 for (i = 0, p = buf; i < count && *p; i++, p++) {
2132 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 /* trailing lf, grr */
2134 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002135 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 }
2137 name[i] = '\0';
2138
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002139 read_lock_bh(&iucv_connection_rwlock);
2140 list_for_each_entry(cp, &iucv_connection_list, list) {
2141 ndev = cp->netdev;
2142 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002144 if (strncmp(name, ndev->name, count))
2145 continue;
2146 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002148 dev_warn(dev, "The IUCV device is connected"
2149 " to %s and cannot be removed\n",
2150 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002152 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153 }
2154 unregister_netdev(ndev);
2155 netiucv_unregister_device(dev);
2156 return count;
2157 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002158 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2160 return -EINVAL;
2161}
2162
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002163static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002165static struct attribute * netiucv_drv_attrs[] = {
2166 &driver_attr_connection.attr,
2167 &driver_attr_remove.attr,
2168 NULL,
2169};
2170
2171static struct attribute_group netiucv_drv_attr_group = {
2172 .attrs = netiucv_drv_attrs,
2173};
2174
David Brownella4dbd672009-06-24 10:06:31 -07002175static const struct attribute_group *netiucv_drv_attr_groups[] = {
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002176 &netiucv_drv_attr_group,
2177 NULL,
2178};
2179
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002180static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002182 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183}
2184
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002185static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002187 struct iucv_connection *cp;
2188 struct net_device *ndev;
2189 struct netiucv_priv *priv;
2190 struct device *dev;
2191
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002192 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002193 while (!list_empty(&iucv_connection_list)) {
2194 cp = list_entry(iucv_connection_list.next,
2195 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002196 ndev = cp->netdev;
2197 priv = netdev_priv(ndev);
2198 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199
2200 unregister_netdev(ndev);
2201 netiucv_unregister_device(dev);
2202 }
2203
Ursula Braun1175b252009-06-16 10:30:43 +02002204 device_unregister(netiucv_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002206 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207 iucv_unregister_dbf_views();
2208
Ursula Braun8f7c5022008-12-25 13:39:47 +01002209 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210 return;
2211}
2212
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002213static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002215 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002216
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002217 rc = iucv_register_dbf_views();
2218 if (rc)
2219 goto out;
2220 rc = iucv_register(&netiucv_handler, 1);
2221 if (rc)
2222 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002223 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002224 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002225 rc = driver_register(&netiucv_driver);
2226 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002227 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2228 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229 }
Ursula Braun1175b252009-06-16 10:30:43 +02002230 /* establish dummy device */
2231 netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
2232 if (!netiucv_dev) {
2233 rc = -ENOMEM;
2234 goto out_driver;
2235 }
2236 dev_set_name(netiucv_dev, "netiucv");
2237 netiucv_dev->bus = &iucv_bus;
2238 netiucv_dev->parent = iucv_root;
2239 netiucv_dev->release = (void (*)(struct device *))kfree;
2240 netiucv_dev->driver = &netiucv_driver;
2241 rc = device_register(netiucv_dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02002242 if (rc) {
2243 put_device(netiucv_dev);
Ursula Braun1175b252009-06-16 10:30:43 +02002244 goto out_driver;
Sebastian Ottc6304932009-09-11 10:28:38 +02002245 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002246 netiucv_banner();
2247 return rc;
2248
Ursula Braun1175b252009-06-16 10:30:43 +02002249out_driver:
2250 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002251out_iucv:
2252 iucv_unregister(&netiucv_handler, 1);
2253out_dbf:
2254 iucv_unregister_dbf_views();
2255out:
2256 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002258
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259module_init(netiucv_init);
2260module_exit(netiucv_exit);
2261MODULE_LICENSE("GPL");