blob: 4ffa66c87ea509ebd914938364fb16eb205fde7b [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 */
133#define IUCV_HEXDUMP16(importance,header,ptr) \
134PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
135 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
136 *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
137 *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
138 *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
139 *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
140 *(((char*)ptr)+12),*(((char*)ptr)+13), \
141 *(((char*)ptr)+14),*(((char*)ptr)+15)); \
142PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
143 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
144 *(((char*)ptr)+16),*(((char*)ptr)+17), \
145 *(((char*)ptr)+18),*(((char*)ptr)+19), \
146 *(((char*)ptr)+20),*(((char*)ptr)+21), \
147 *(((char*)ptr)+22),*(((char*)ptr)+23), \
148 *(((char*)ptr)+24),*(((char*)ptr)+25), \
149 *(((char*)ptr)+26),*(((char*)ptr)+27), \
150 *(((char*)ptr)+28),*(((char*)ptr)+29), \
151 *(((char*)ptr)+30),*(((char*)ptr)+31));
152
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153#define PRINTK_HEADER " iucv: " /* for debugging */
154
Ursula Braun1175b252009-06-16 10:30:43 +0200155/* dummy device to make sure netiucv_pm functions are called */
156static struct device *netiucv_dev;
157
158static int netiucv_pm_prepare(struct device *);
159static void netiucv_pm_complete(struct device *);
160static int netiucv_pm_freeze(struct device *);
161static int netiucv_pm_restore_thaw(struct device *);
162
Alexey Dobriyan47145212009-12-14 18:00:08 -0800163static const struct dev_pm_ops netiucv_pm_ops = {
Ursula Braun1175b252009-06-16 10:30:43 +0200164 .prepare = netiucv_pm_prepare,
165 .complete = netiucv_pm_complete,
166 .freeze = netiucv_pm_freeze,
167 .thaw = netiucv_pm_restore_thaw,
168 .restore = netiucv_pm_restore_thaw,
169};
170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171static struct device_driver netiucv_driver = {
Cornelia Huck22195102008-02-08 13:09:02 +0100172 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 .name = "netiucv",
174 .bus = &iucv_bus,
Ursula Braun1175b252009-06-16 10:30:43 +0200175 .pm = &netiucv_pm_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176};
177
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800178static int netiucv_callback_connreq(struct iucv_path *,
179 u8 ipvmid[8], u8 ipuser[16]);
180static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
181static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
182static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
183static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
184static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
185static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
186
187static struct iucv_handler netiucv_handler = {
188 .path_pending = netiucv_callback_connreq,
189 .path_complete = netiucv_callback_connack,
190 .path_severed = netiucv_callback_connrej,
191 .path_quiesced = netiucv_callback_connsusp,
192 .path_resumed = netiucv_callback_connres,
193 .message_pending = netiucv_callback_rx,
194 .message_complete = netiucv_callback_txdone
195};
196
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197/**
198 * Per connection profiling data
199 */
200struct connection_profile {
201 unsigned long maxmulti;
202 unsigned long maxcqueue;
203 unsigned long doios_single;
204 unsigned long doios_multi;
205 unsigned long txlen;
206 unsigned long tx_time;
207 struct timespec send_stamp;
208 unsigned long tx_pending;
209 unsigned long tx_max_pending;
210};
211
212/**
213 * Representation of one iucv connection
214 */
215struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800216 struct list_head list;
217 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 struct sk_buff *rx_buff;
219 struct sk_buff *tx_buff;
220 struct sk_buff_head collect_queue;
221 struct sk_buff_head commit_queue;
222 spinlock_t collect_lock;
223 int collect_len;
224 int max_buffsize;
225 fsm_timer timer;
226 fsm_instance *fsm;
227 struct net_device *netdev;
228 struct connection_profile prof;
229 char userid[9];
Ursula Braun08e33562011-12-19 22:56:34 +0000230 char userdata[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231};
232
233/**
234 * Linked list of all connection structs.
235 */
Denis Chengc11ca972008-01-26 14:11:13 +0100236static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200237static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
239/**
240 * Representation of event-data for the
241 * connection state machine.
242 */
243struct iucv_event {
244 struct iucv_connection *conn;
245 void *data;
246};
247
248/**
249 * Private part of the network device structure
250 */
251struct netiucv_priv {
252 struct net_device_stats stats;
253 unsigned long tbusy;
254 fsm_instance *fsm;
255 struct iucv_connection *conn;
256 struct device *dev;
Ursula Braun1175b252009-06-16 10:30:43 +0200257 int pm_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258};
259
260/**
261 * Link level header for a packet.
262 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800263struct ll_header {
264 u16 next;
265};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800267#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Ursula Braun08e33562011-12-19 22:56:34 +0000268#define NETIUCV_BUFSIZE_MAX 65537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
270#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
271#define NETIUCV_MTU_DEFAULT 9216
272#define NETIUCV_QUEUELEN_DEFAULT 50
273#define NETIUCV_TIMEOUT_5SEC 5000
274
275/**
276 * Compatibility macros for busy handling
277 * of network devices.
278 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800279static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800281 struct netiucv_priv *priv = netdev_priv(dev);
282 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 netif_wake_queue(dev);
284}
285
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800286static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800288 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800290 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291}
292
Ursula Braun08e33562011-12-19 22:56:34 +0000293static u8 iucvMagic_ascii[16] = {
294 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
295 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
296};
297
298static u8 iucvMagic_ebcdic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
300 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
301};
302
303/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 * Convert an iucv userId to its printable
305 * form (strip whitespace at end).
306 *
307 * @param An iucv userId
308 *
309 * @returns The printable string (static data!!)
310 */
Ursula Braun08e33562011-12-19 22:56:34 +0000311static char *netiucv_printname(char *name, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312{
Ursula Braun08e33562011-12-19 22:56:34 +0000313 static char tmp[17];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 char *p = tmp;
Ursula Braun08e33562011-12-19 22:56:34 +0000315 memcpy(tmp, name, len);
316 tmp[len] = '\0';
317 while (*p && ((p - tmp) < len) && (!isspace(*p)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 p++;
319 *p = '\0';
320 return tmp;
321}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400322
Ursula Braun08e33562011-12-19 22:56:34 +0000323static char *netiucv_printuser(struct iucv_connection *conn)
324{
325 static char tmp_uid[9];
326 static char tmp_udat[17];
327 static char buf[100];
328
329 if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) {
330 tmp_uid[8] = '\0';
331 tmp_udat[16] = '\0';
332 memcpy(tmp_uid, conn->userid, 8);
333 memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8);
334 memcpy(tmp_udat, conn->userdata, 16);
335 EBCASC(tmp_udat, 16);
336 memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16);
337 sprintf(buf, "%s.%s", tmp_uid, tmp_udat);
338 return buf;
339 } else
340 return netiucv_printname(conn->userid, 8);
341}
342
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343/**
344 * States of the interface statemachine.
345 */
346enum dev_states {
347 DEV_STATE_STOPPED,
348 DEV_STATE_STARTWAIT,
349 DEV_STATE_STOPWAIT,
350 DEV_STATE_RUNNING,
351 /**
352 * MUST be always the last element!!
353 */
354 NR_DEV_STATES
355};
356
357static const char *dev_state_names[] = {
358 "Stopped",
359 "StartWait",
360 "StopWait",
361 "Running",
362};
363
364/**
365 * Events of the interface statemachine.
366 */
367enum dev_events {
368 DEV_EVENT_START,
369 DEV_EVENT_STOP,
370 DEV_EVENT_CONUP,
371 DEV_EVENT_CONDOWN,
372 /**
373 * MUST be always the last element!!
374 */
375 NR_DEV_EVENTS
376};
377
378static const char *dev_event_names[] = {
379 "Start",
380 "Stop",
381 "Connection up",
382 "Connection down",
383};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385/**
386 * Events of the connection statemachine
387 */
388enum conn_events {
389 /**
390 * Events, representing callbacks from
391 * lowlevel iucv layer)
392 */
393 CONN_EVENT_CONN_REQ,
394 CONN_EVENT_CONN_ACK,
395 CONN_EVENT_CONN_REJ,
396 CONN_EVENT_CONN_SUS,
397 CONN_EVENT_CONN_RES,
398 CONN_EVENT_RX,
399 CONN_EVENT_TXDONE,
400
401 /**
402 * Events, representing errors return codes from
403 * calls to lowlevel iucv layer
404 */
405
406 /**
407 * Event, representing timer expiry.
408 */
409 CONN_EVENT_TIMER,
410
411 /**
412 * Events, representing commands from upper levels.
413 */
414 CONN_EVENT_START,
415 CONN_EVENT_STOP,
416
417 /**
418 * MUST be always the last element!!
419 */
420 NR_CONN_EVENTS,
421};
422
423static const char *conn_event_names[] = {
424 "Remote connection request",
425 "Remote connection acknowledge",
426 "Remote connection reject",
427 "Connection suspended",
428 "Connection resumed",
429 "Data received",
430 "Data sent",
431
432 "Timer",
433
434 "Start",
435 "Stop",
436};
437
438/**
439 * States of the connection statemachine.
440 */
441enum conn_states {
442 /**
443 * Connection not assigned to any device,
444 * initial state, invalid
445 */
446 CONN_STATE_INVALID,
447
448 /**
449 * Userid assigned but not operating
450 */
451 CONN_STATE_STOPPED,
452
453 /**
454 * Connection registered,
455 * no connection request sent yet,
456 * no connection request received
457 */
458 CONN_STATE_STARTWAIT,
459
460 /**
461 * Connection registered and connection request sent,
462 * no acknowledge and no connection request received yet.
463 */
464 CONN_STATE_SETUPWAIT,
465
466 /**
467 * Connection up and running idle
468 */
469 CONN_STATE_IDLE,
470
471 /**
472 * Data sent, awaiting CONN_EVENT_TXDONE
473 */
474 CONN_STATE_TX,
475
476 /**
477 * Error during registration.
478 */
479 CONN_STATE_REGERR,
480
481 /**
482 * Error during registration.
483 */
484 CONN_STATE_CONNERR,
485
486 /**
487 * MUST be always the last element!!
488 */
489 NR_CONN_STATES,
490};
491
492static const char *conn_state_names[] = {
493 "Invalid",
494 "Stopped",
495 "StartWait",
496 "SetupWait",
497 "Idle",
498 "TX",
499 "Terminating",
500 "Registration error",
501 "Connect error",
502};
503
Jeff Garzike82b0f22006-05-26 21:58:38 -0400504
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505/**
506 * Debug Facility Stuff
507 */
508static debug_info_t *iucv_dbf_setup = NULL;
509static debug_info_t *iucv_dbf_data = NULL;
510static debug_info_t *iucv_dbf_trace = NULL;
511
512DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
513
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800514static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515{
516 if (iucv_dbf_setup)
517 debug_unregister(iucv_dbf_setup);
518 if (iucv_dbf_data)
519 debug_unregister(iucv_dbf_data);
520 if (iucv_dbf_trace)
521 debug_unregister(iucv_dbf_trace);
522}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800523static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524{
525 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700526 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 IUCV_DBF_SETUP_NR_AREAS,
528 IUCV_DBF_SETUP_LEN);
529 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700530 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 IUCV_DBF_DATA_NR_AREAS,
532 IUCV_DBF_DATA_LEN);
533 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700534 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 IUCV_DBF_TRACE_NR_AREAS,
536 IUCV_DBF_TRACE_LEN);
537
538 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
539 (iucv_dbf_trace == NULL)) {
540 iucv_unregister_dbf_views();
541 return -ENOMEM;
542 }
543 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
544 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
545
546 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
547 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
548
549 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
550 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
551
552 return 0;
553}
554
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800555/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800557 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800559static void netiucv_callback_rx(struct iucv_path *path,
560 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800562 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 struct iucv_event ev;
564
565 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800566 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
568}
569
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800570static void netiucv_callback_txdone(struct iucv_path *path,
571 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800573 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 struct iucv_event ev;
575
576 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800577 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
579}
580
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800581static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800583 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800585 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586}
587
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800588static int netiucv_callback_connreq(struct iucv_path *path,
589 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800591 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 struct iucv_event ev;
Ursula Braun08e33562011-12-19 22:56:34 +0000593 static char tmp_user[9];
594 static char tmp_udat[17];
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800595 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800597 rc = -EINVAL;
Ursula Braun08e33562011-12-19 22:56:34 +0000598 memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8);
599 memcpy(tmp_udat, ipuser, 16);
600 EBCASC(tmp_udat, 16);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800601 read_lock_bh(&iucv_connection_rwlock);
602 list_for_each_entry(conn, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +0000603 if (strncmp(ipvmid, conn->userid, 8) ||
604 strncmp(ipuser, conn->userdata, 16))
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800605 continue;
606 /* Found a matching connection for this path. */
607 conn->path = path;
608 ev.conn = conn;
609 ev.data = path;
610 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
611 rc = 0;
612 }
Ursula Braun08e33562011-12-19 22:56:34 +0000613 IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n",
614 tmp_user, netiucv_printname(tmp_udat, 16));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800615 read_unlock_bh(&iucv_connection_rwlock);
616 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617}
618
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800619static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800621 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800623 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624}
625
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800626static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800628 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800630 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631}
632
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800633static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800635 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800637 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638}
639
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100641 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100643static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644{
645}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400646
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800647/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800649 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
651/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800652 * netiucv_unpack_skb
653 * @conn: The connection where this skb has been received.
654 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800656 * Unpack a just received skb and hand it over to upper layers.
657 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800659static void netiucv_unpack_skb(struct iucv_connection *conn,
660 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661{
662 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800663 struct netiucv_priv *privptr = netdev_priv(dev);
664 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
666 skb_put(pskb, NETIUCV_HDRLEN);
667 pskb->dev = dev;
668 pskb->ip_summed = CHECKSUM_NONE;
669 pskb->protocol = ntohs(ETH_P_IP);
670
671 while (1) {
672 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800673 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
675 if (!header->next)
676 break;
677
678 skb_pull(pskb, NETIUCV_HDRLEN);
679 header->next -= offset;
680 offset += header->next;
681 header->next -= NETIUCV_HDRLEN;
682 if (skb_tailroom(pskb) < header->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
684 header->next, skb_tailroom(pskb));
685 return;
686 }
687 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700688 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 skb = dev_alloc_skb(pskb->len);
690 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 IUCV_DBF_TEXT(data, 2,
692 "Out of memory in netiucv_unpack_skb\n");
693 privptr->stats.rx_dropped++;
694 return;
695 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300696 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
697 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700698 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 skb->dev = pskb->dev;
700 skb->protocol = pskb->protocol;
701 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800702 privptr->stats.rx_packets++;
703 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 /*
705 * Since receiving is always initiated from a tasklet (in iucv.c),
706 * we must use netif_rx_ni() instead of netif_rx()
707 */
708 netif_rx_ni(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 skb_pull(pskb, header->next);
710 skb_put(pskb, NETIUCV_HDRLEN);
711 }
712}
713
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800714static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800716 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800718 struct iucv_message *msg = ev->data;
719 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 int rc;
721
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200722 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800725 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800727 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 return;
729 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800730 if (msg->length > conn->max_buffsize) {
731 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 privptr->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800734 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 return;
736 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700737 conn->rx_buff->data = conn->rx_buff->head;
738 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800740 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
741 msg->length, NULL);
742 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 privptr->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
745 return;
746 }
747 netiucv_unpack_skb(conn, conn->rx_buff);
748}
749
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800750static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800752 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800754 struct iucv_message *msg = ev->data;
755 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800757 u32 single_flag = msg->tag;
758 u32 txbytes = 0;
759 u32 txpackets = 0;
760 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 struct sk_buff *skb;
762 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800763 struct ll_header header;
764 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200766 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800768 if (conn && conn->netdev)
769 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 conn->prof.tx_pending--;
771 if (single_flag) {
772 if ((skb = skb_dequeue(&conn->commit_queue))) {
773 atomic_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 if (privptr) {
775 privptr->stats.tx_packets++;
776 privptr->stats.tx_bytes +=
777 (skb->len - NETIUCV_HDRLEN
Ursula Braun998221c2009-11-12 21:46:30 +0000778 - NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 }
Ursula Braun998221c2009-11-12 21:46:30 +0000780 dev_kfree_skb_any(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 }
782 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700783 conn->tx_buff->data = conn->tx_buff->head;
784 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 conn->tx_buff->len = 0;
786 spin_lock_irqsave(&conn->collect_lock, saveflags);
787 while ((skb = skb_dequeue(&conn->collect_queue))) {
788 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
789 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
790 NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300791 skb_copy_from_linear_data(skb,
792 skb_put(conn->tx_buff, skb->len),
793 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 txbytes += skb->len;
795 txpackets++;
796 stat_maxcq++;
797 atomic_dec(&skb->users);
798 dev_kfree_skb_any(skb);
799 }
800 if (conn->collect_len > conn->prof.maxmulti)
801 conn->prof.maxmulti = conn->collect_len;
802 conn->collect_len = 0;
803 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800804 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800806 return;
807 }
808
809 header.next = 0;
810 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
john stultz2c6b47d2007-07-24 17:47:43 -0700811 conn->prof.send_stamp = current_kernel_time();
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800812 txmsg.class = 0;
813 txmsg.tag = 0;
814 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
815 conn->tx_buff->data, conn->tx_buff->len);
816 conn->prof.doios_multi++;
817 conn->prof.txlen += conn->tx_buff->len;
818 conn->prof.tx_pending++;
819 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
820 conn->prof.tx_max_pending = conn->prof.tx_pending;
821 if (rc) {
822 conn->prof.tx_pending--;
823 fsm_newstate(fi, CONN_STATE_IDLE);
824 if (privptr)
825 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800826 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
827 } else {
828 if (privptr) {
829 privptr->stats.tx_packets += txpackets;
830 privptr->stats.tx_bytes += txbytes;
831 }
832 if (stat_maxcq > conn->prof.maxcqueue)
833 conn->prof.maxcqueue = stat_maxcq;
834 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835}
836
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800837static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800839 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800841 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800843 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200846 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800848 conn->path = path;
849 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
850 path->flags = 0;
Ursula Braun08e33562011-12-19 22:56:34 +0000851 rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
854 return;
855 }
856 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800857 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
859}
860
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800861static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800863 struct iucv_event *ev = arg;
864 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200866 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800867 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868}
869
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800870static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800872 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800874 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200876 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 fsm_deltimer(&conn->timer);
878 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800879 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
881}
882
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800883static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800885 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200887 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000889 iucv_path_sever(conn->path, conn->userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 fsm_newstate(fi, CONN_STATE_STARTWAIT);
891}
892
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800893static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800895 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800897 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200899 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
901 fsm_deltimer(&conn->timer);
Ursula Braun08e33562011-12-19 22:56:34 +0000902 iucv_path_sever(conn->path, conn->userdata);
903 dev_info(privptr->dev, "The peer z/VM guest %s has closed the "
904 "connection\n", netiucv_printuser(conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800906 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 fsm_newstate(fi, CONN_STATE_STARTWAIT);
908 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
909}
910
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800911static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800913 struct iucv_connection *conn = arg;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100914 struct net_device *netdev = conn->netdev;
915 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 int rc;
917
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200918 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800920 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800922 /*
923 * We must set the state before calling iucv_connect because the
924 * callback handler could be called at any point after the connection
925 * request is sent
926 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927
928 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800929 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
Ursula Braun08e33562011-12-19 22:56:34 +0000930 IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n",
931 netdev->name, netiucv_printuser(conn));
932
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800933 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
Ursula Braun08e33562011-12-19 22:56:34 +0000934 NULL, conn->userdata, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800936 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100937 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800938 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
939 CONN_EVENT_TIMER, conn);
940 return;
941 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100942 dev_warn(privptr->dev,
943 "The IUCV device failed to connect to z/VM guest %s\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000944 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800945 fsm_newstate(fi, CONN_STATE_STARTWAIT);
946 break;
947 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100948 dev_warn(privptr->dev,
949 "The IUCV device failed to connect to the peer on z/VM"
Ursula Braun08e33562011-12-19 22:56:34 +0000950 " guest %s\n", netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800951 fsm_newstate(fi, CONN_STATE_STARTWAIT);
952 break;
953 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100954 dev_err(privptr->dev,
955 "Connecting the IUCV device would exceed the maximum"
956 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800957 fsm_newstate(fi, CONN_STATE_CONNERR);
958 break;
959 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100960 dev_err(privptr->dev,
961 "z/VM guest %s has too many IUCV connections"
962 " to connect with the IUCV device\n",
Ursula Braun08e33562011-12-19 22:56:34 +0000963 netiucv_printname(conn->userid, 8));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800964 fsm_newstate(fi, CONN_STATE_CONNERR);
965 break;
966 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100967 dev_err(privptr->dev,
968 "The IUCV device cannot connect to a z/VM guest with no"
969 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800970 fsm_newstate(fi, CONN_STATE_CONNERR);
971 break;
972 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100973 dev_err(privptr->dev,
974 "Connecting the IUCV device failed with error %d\n",
975 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800976 fsm_newstate(fi, CONN_STATE_CONNERR);
977 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 }
979 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800980 kfree(conn->path);
981 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982}
983
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800984static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985{
986 struct sk_buff *skb;
987
988 while ((skb = skb_dequeue(q))) {
989 atomic_dec(&skb->users);
990 dev_kfree_skb_any(skb);
991 }
992}
993
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800994static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800996 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 struct iucv_connection *conn = ev->conn;
998 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800999 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001001 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
1003 fsm_deltimer(&conn->timer);
1004 fsm_newstate(fi, CONN_STATE_STOPPED);
1005 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001006 if (conn->path) {
1007 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
Ursula Braun08e33562011-12-19 22:56:34 +00001008 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001009 kfree(conn->path);
1010 conn->path = NULL;
1011 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 netiucv_purge_skb_queue(&conn->commit_queue);
1013 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
1014}
1015
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001016static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001018 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 struct net_device *netdev = conn->netdev;
1020
Ursula Braunf082bca2008-07-14 09:59:30 +02001021 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
1022 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023}
1024
1025static const fsm_node conn_fsm[] = {
1026 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
1027 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
1028
1029 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
1030 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
1031 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
1032 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
1033 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
1034 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
1035 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
1036
1037 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
1038 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1039 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1040 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
1041 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
1042
1043 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
1044 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
1045
1046 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
1047 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
1048 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
1049
1050 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
1051 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1052
1053 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1054 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1055};
1056
1057static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1058
Jeff Garzike82b0f22006-05-26 21:58:38 -04001059
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001060/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001062 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063
1064/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001065 * dev_action_start
1066 * @fi: An instance of an interface statemachine.
1067 * @event: The event, just happened.
1068 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001070 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001072static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001074 struct net_device *dev = arg;
1075 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001077 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001080 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081}
1082
1083/**
1084 * Shutdown connection by sending CONN_EVENT_STOP to it.
1085 *
1086 * @param fi An instance of an interface statemachine.
1087 * @param event The event, just happened.
1088 * @param arg Generic pointer, casted from struct net_device * upon call.
1089 */
1090static void
1091dev_action_stop(fsm_instance *fi, int event, void *arg)
1092{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001093 struct net_device *dev = arg;
1094 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 struct iucv_event ev;
1096
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001097 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098
1099 ev.conn = privptr->conn;
1100
1101 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1102 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1103}
1104
1105/**
1106 * Called from connection statemachine
1107 * when a connection is up and running.
1108 *
1109 * @param fi An instance of an interface statemachine.
1110 * @param event The event, just happened.
1111 * @param arg Generic pointer, casted from struct net_device * upon call.
1112 */
1113static void
1114dev_action_connup(fsm_instance *fi, int event, void *arg)
1115{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001116 struct net_device *dev = arg;
1117 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001119 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120
1121 switch (fsm_getstate(fi)) {
1122 case DEV_STATE_STARTWAIT:
1123 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001124 dev_info(privptr->dev,
1125 "The IUCV device has been connected"
Ursula Braun08e33562011-12-19 22:56:34 +00001126 " successfully to %s\n",
1127 netiucv_printuser(privptr->conn));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 IUCV_DBF_TEXT(setup, 3,
1129 "connection is up and running\n");
1130 break;
1131 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 IUCV_DBF_TEXT(data, 2,
1133 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1134 break;
1135 }
1136}
1137
1138/**
1139 * Called from connection statemachine
1140 * when a connection has been shutdown.
1141 *
1142 * @param fi An instance of an interface statemachine.
1143 * @param event The event, just happened.
1144 * @param arg Generic pointer, casted from struct net_device * upon call.
1145 */
1146static void
1147dev_action_conndown(fsm_instance *fi, int event, void *arg)
1148{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001149 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150
1151 switch (fsm_getstate(fi)) {
1152 case DEV_STATE_RUNNING:
1153 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1154 break;
1155 case DEV_STATE_STOPWAIT:
1156 fsm_newstate(fi, DEV_STATE_STOPPED);
1157 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1158 break;
1159 }
1160}
1161
1162static const fsm_node dev_fsm[] = {
1163 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1164
1165 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1166 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1167
1168 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1169 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1170
1171 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1172 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001173 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174};
1175
1176static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1177
1178/**
1179 * Transmit a packet.
1180 * This is a helper function for netiucv_tx().
1181 *
1182 * @param conn Connection to be used for sending.
1183 * @param skb Pointer to struct sk_buff of packet to send.
1184 * The linklevel header has already been set up
1185 * by netiucv_tx().
1186 *
1187 * @return 0 on success, -ERRNO on failure. (Never fails.)
1188 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001189static int netiucv_transmit_skb(struct iucv_connection *conn,
1190 struct sk_buff *skb)
1191{
1192 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001194 struct ll_header header;
1195 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196
1197 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1198 int l = skb->len + NETIUCV_HDRLEN;
1199
1200 spin_lock_irqsave(&conn->collect_lock, saveflags);
1201 if (conn->collect_len + l >
1202 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1203 rc = -EBUSY;
1204 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001205 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 } else {
1207 atomic_inc(&skb->users);
1208 skb_queue_tail(&conn->collect_queue, skb);
1209 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001210 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 }
1212 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1213 } else {
1214 struct sk_buff *nskb = skb;
1215 /**
1216 * Copy the skb to a new allocated skb in lowmem only if the
1217 * data is located above 2G in memory or tailroom is < 2.
1218 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001219 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1220 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 int copied = 0;
1222 if (hi || (skb_tailroom(skb) < 2)) {
1223 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1224 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1225 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1227 rc = -ENOMEM;
1228 return rc;
1229 } else {
1230 skb_reserve(nskb, NETIUCV_HDRLEN);
1231 memcpy(skb_put(nskb, skb->len),
1232 skb->data, skb->len);
1233 }
1234 copied = 1;
1235 }
1236 /**
1237 * skb now is below 2G and has enough room. Add headers.
1238 */
1239 header.next = nskb->len + NETIUCV_HDRLEN;
1240 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1241 header.next = 0;
1242 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1243
1244 fsm_newstate(conn->fsm, CONN_STATE_TX);
john stultz2c6b47d2007-07-24 17:47:43 -07001245 conn->prof.send_stamp = current_kernel_time();
Jeff Garzike82b0f22006-05-26 21:58:38 -04001246
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001247 msg.tag = 1;
1248 msg.class = 0;
1249 rc = iucv_message_send(conn->path, &msg, 0, 0,
1250 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 conn->prof.doios_single++;
1252 conn->prof.txlen += skb->len;
1253 conn->prof.tx_pending++;
1254 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1255 conn->prof.tx_max_pending = conn->prof.tx_pending;
1256 if (rc) {
1257 struct netiucv_priv *privptr;
1258 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1259 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001260 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 if (privptr)
1262 privptr->stats.tx_errors++;
1263 if (copied)
1264 dev_kfree_skb(nskb);
1265 else {
1266 /**
1267 * Remove our headers. They get added
1268 * again on retransmit.
1269 */
1270 skb_pull(skb, NETIUCV_HDRLEN);
1271 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1272 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1274 } else {
1275 if (copied)
1276 dev_kfree_skb(skb);
1277 atomic_inc(&nskb->users);
1278 skb_queue_tail(&conn->commit_queue, nskb);
1279 }
1280 }
1281
1282 return rc;
1283}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001284
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001285/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001287 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
1289/**
1290 * Open an interface.
1291 * Called from generic network layer when ifconfig up is run.
1292 *
1293 * @param dev Pointer to interface struct.
1294 *
1295 * @return 0 on success, -ERRNO on failure. (Never fails.)
1296 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001297static int netiucv_open(struct net_device *dev)
1298{
1299 struct netiucv_priv *priv = netdev_priv(dev);
1300
1301 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 return 0;
1303}
1304
1305/**
1306 * Close an interface.
1307 * Called from generic network layer when ifconfig down is run.
1308 *
1309 * @param dev Pointer to interface struct.
1310 *
1311 * @return 0 on success, -ERRNO on failure. (Never fails.)
1312 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001313static int netiucv_close(struct net_device *dev)
1314{
1315 struct netiucv_priv *priv = netdev_priv(dev);
1316
1317 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 return 0;
1319}
1320
Ursula Braun1175b252009-06-16 10:30:43 +02001321static int netiucv_pm_prepare(struct device *dev)
1322{
1323 IUCV_DBF_TEXT(trace, 3, __func__);
1324 return 0;
1325}
1326
1327static void netiucv_pm_complete(struct device *dev)
1328{
1329 IUCV_DBF_TEXT(trace, 3, __func__);
1330 return;
1331}
1332
1333/**
1334 * netiucv_pm_freeze() - Freeze PM callback
1335 * @dev: netiucv device
1336 *
1337 * close open netiucv interfaces
1338 */
1339static int netiucv_pm_freeze(struct device *dev)
1340{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001341 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001342 struct net_device *ndev = NULL;
1343 int rc = 0;
1344
1345 IUCV_DBF_TEXT(trace, 3, __func__);
1346 if (priv && priv->conn)
1347 ndev = priv->conn->netdev;
1348 if (!ndev)
1349 goto out;
1350 netif_device_detach(ndev);
1351 priv->pm_state = fsm_getstate(priv->fsm);
1352 rc = netiucv_close(ndev);
1353out:
1354 return rc;
1355}
1356
1357/**
1358 * netiucv_pm_restore_thaw() - Thaw and restore PM callback
1359 * @dev: netiucv device
1360 *
1361 * re-open netiucv interfaces closed during freeze
1362 */
1363static int netiucv_pm_restore_thaw(struct device *dev)
1364{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001365 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001366 struct net_device *ndev = NULL;
1367 int rc = 0;
1368
1369 IUCV_DBF_TEXT(trace, 3, __func__);
1370 if (priv && priv->conn)
1371 ndev = priv->conn->netdev;
1372 if (!ndev)
1373 goto out;
1374 switch (priv->pm_state) {
1375 case DEV_STATE_RUNNING:
1376 case DEV_STATE_STARTWAIT:
1377 rc = netiucv_open(ndev);
1378 break;
1379 default:
1380 break;
1381 }
1382 netif_device_attach(ndev);
1383out:
1384 return rc;
1385}
1386
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387/**
1388 * Start transmission of a packet.
1389 * Called from generic network device layer.
1390 *
1391 * @param skb Pointer to buffer containing the packet.
1392 * @param dev Pointer to interface struct.
1393 *
1394 * @return 0 if packet consumed, !0 if packet rejected.
1395 * Note: If we return !0, then the packet is free'd by
1396 * the generic network layer.
1397 */
1398static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1399{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001400 struct netiucv_priv *privptr = netdev_priv(dev);
1401 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001403 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 /**
1405 * Some sanity checks ...
1406 */
1407 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1409 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001410 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 }
1412 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 IUCV_DBF_TEXT(data, 2,
1414 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1415 dev_kfree_skb(skb);
1416 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001417 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 }
1419
1420 /**
1421 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001422 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 */
1424 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 dev_kfree_skb(skb);
1426 privptr->stats.tx_dropped++;
1427 privptr->stats.tx_errors++;
1428 privptr->stats.tx_carrier_errors++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001429 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 }
1431
1432 if (netiucv_test_and_set_busy(dev)) {
1433 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001434 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 }
1436 dev->trans_start = jiffies;
Patrick McHardy5b548142009-06-12 06:22:29 +00001437 rc = netiucv_transmit_skb(privptr->conn, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 netiucv_clear_busy(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001439 return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440}
1441
1442/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001443 * netiucv_stats
1444 * @dev: Pointer to interface struct.
1445 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 * Returns interface statistics of a device.
1447 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001448 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001450static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001452 struct netiucv_priv *priv = netdev_priv(dev);
1453
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001454 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001455 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456}
1457
1458/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001459 * netiucv_change_mtu
1460 * @dev: Pointer to interface struct.
1461 * @new_mtu: The new MTU to use for this interface.
1462 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 * Sets MTU of an interface.
1464 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001465 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 * (valid range is 576 .. NETIUCV_MTU_MAX).
1467 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001468static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001470 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001471 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1473 return -EINVAL;
1474 }
1475 dev->mtu = new_mtu;
1476 return 0;
1477}
1478
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001479/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001481 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001483static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1484 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001486 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001488 IUCV_DBF_TEXT(trace, 5, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001489 return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));
1490}
1491
1492static int netiucv_check_user(const char *buf, size_t count, char *username,
1493 char *userdata)
1494{
1495 const char *p;
1496 int i;
1497
1498 p = strchr(buf, '.');
1499 if ((p && ((count > 26) ||
1500 ((p - buf) > 8) ||
1501 (buf + count - p > 18))) ||
1502 (!p && (count > 9))) {
1503 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1504 return -EINVAL;
1505 }
1506
1507 for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) {
1508 if (isalnum(*p) || *p == '$') {
1509 username[i] = toupper(*p);
1510 continue;
1511 }
1512 if (*p == '\n')
1513 /* trailing lf, grr */
1514 break;
1515 IUCV_DBF_TEXT_(setup, 2,
1516 "conn_write: invalid character %02x\n", *p);
1517 return -EINVAL;
1518 }
1519 while (i < 8)
1520 username[i++] = ' ';
1521 username[8] = '\0';
1522
1523 if (*p == '.') {
1524 p++;
1525 for (i = 0; i < 16 && *p; i++, p++) {
1526 if (*p == '\n')
1527 break;
1528 userdata[i] = toupper(*p);
1529 }
1530 while (i > 0 && i < 16)
1531 userdata[i++] = ' ';
1532 } else
1533 memcpy(userdata, iucvMagic_ascii, 16);
1534 userdata[16] = '\0';
1535 ASCEBC(userdata, 16);
1536
1537 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538}
1539
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001540static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1541 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001543 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 struct net_device *ndev = priv->conn->netdev;
Ursula Braun08e33562011-12-19 22:56:34 +00001545 char username[9];
1546 char userdata[17];
1547 int rc;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001548 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001550 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00001551 rc = netiucv_check_user(buf, count, username, userdata);
1552 if (rc)
1553 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001555 if (memcmp(username, priv->conn->userid, 9) &&
1556 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1557 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001558 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001559 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001560 }
1561 read_lock_bh(&iucv_connection_rwlock);
1562 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00001563 if (!strncmp(username, cp->userid, 9) &&
1564 !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001565 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00001566 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s "
1567 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001568 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 }
1570 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001571 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 memcpy(priv->conn->userid, username, 9);
Ursula Braun08e33562011-12-19 22:56:34 +00001573 memcpy(priv->conn->userdata, userdata, 17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575}
1576
1577static DEVICE_ATTR(user, 0644, user_show, user_write);
1578
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001579static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1580 char *buf)
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001581{
1582 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001584 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1586}
1587
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001588static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1589 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001591 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 struct net_device *ndev = priv->conn->netdev;
1593 char *e;
1594 int bs1;
1595
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001596 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597 if (count >= 39)
1598 return -EINVAL;
1599
1600 bs1 = simple_strtoul(buf, &e, 0);
1601
1602 if (e && (!isspace(*e))) {
Ursula Braun08e33562011-12-19 22:56:34 +00001603 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n",
1604 *e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605 return -EINVAL;
1606 }
1607 if (bs1 > NETIUCV_BUFSIZE_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 IUCV_DBF_TEXT_(setup, 2,
1609 "buffer_write: buffer size %d too large\n",
1610 bs1);
1611 return -EINVAL;
1612 }
1613 if ((ndev->flags & IFF_RUNNING) &&
1614 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 IUCV_DBF_TEXT_(setup, 2,
1616 "buffer_write: buffer size %d too small\n",
1617 bs1);
1618 return -EINVAL;
1619 }
1620 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 IUCV_DBF_TEXT_(setup, 2,
1622 "buffer_write: buffer size %d too small\n",
1623 bs1);
1624 return -EINVAL;
1625 }
1626
1627 priv->conn->max_buffsize = bs1;
1628 if (!(ndev->flags & IFF_RUNNING))
1629 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1630
1631 return count;
1632
1633}
1634
1635static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1636
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001637static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1638 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001640 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001642 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1644}
1645
1646static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1647
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001648static ssize_t conn_fsm_show (struct device *dev,
1649 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001651 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001653 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1655}
1656
1657static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1658
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001659static ssize_t maxmulti_show (struct device *dev,
1660 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001662 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001664 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1666}
1667
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001668static ssize_t maxmulti_write (struct device *dev,
1669 struct device_attribute *attr,
1670 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001672 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001674 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 priv->conn->prof.maxmulti = 0;
1676 return count;
1677}
1678
1679static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1680
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001681static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1682 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001684 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001686 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1688}
1689
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001690static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1691 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001693 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001694
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001695 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 priv->conn->prof.maxcqueue = 0;
1697 return count;
1698}
1699
1700static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1701
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001702static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1703 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001705 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001707 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1709}
1710
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001711static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1712 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001714 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001715
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001716 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 priv->conn->prof.doios_single = 0;
1718 return count;
1719}
1720
1721static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1722
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001723static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1724 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001726 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001728 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1730}
1731
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001732static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1733 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001735 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001736
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001737 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 priv->conn->prof.doios_multi = 0;
1739 return count;
1740}
1741
1742static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1743
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001744static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1745 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001747 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001749 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1751}
1752
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001753static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1754 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001756 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001757
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001758 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759 priv->conn->prof.txlen = 0;
1760 return count;
1761}
1762
1763static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1764
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001765static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1766 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001768 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001770 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1772}
1773
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001774static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1775 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001777 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001778
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001779 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780 priv->conn->prof.tx_time = 0;
1781 return count;
1782}
1783
1784static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1785
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001786static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1787 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001789 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001791 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1793}
1794
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001795static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1796 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001798 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001800 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801 priv->conn->prof.tx_pending = 0;
1802 return count;
1803}
1804
1805static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1806
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001807static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1808 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001810 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001812 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1814}
1815
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001816static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1817 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001819 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001821 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822 priv->conn->prof.tx_max_pending = 0;
1823 return count;
1824}
1825
1826static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1827
1828static struct attribute *netiucv_attrs[] = {
1829 &dev_attr_buffer.attr,
1830 &dev_attr_user.attr,
1831 NULL,
1832};
1833
1834static struct attribute_group netiucv_attr_group = {
1835 .attrs = netiucv_attrs,
1836};
1837
1838static struct attribute *netiucv_stat_attrs[] = {
1839 &dev_attr_device_fsm_state.attr,
1840 &dev_attr_connection_fsm_state.attr,
1841 &dev_attr_max_tx_buffer_used.attr,
1842 &dev_attr_max_chained_skbs.attr,
1843 &dev_attr_tx_single_write_ops.attr,
1844 &dev_attr_tx_multi_write_ops.attr,
1845 &dev_attr_netto_bytes.attr,
1846 &dev_attr_max_tx_io_time.attr,
1847 &dev_attr_tx_pending.attr,
1848 &dev_attr_tx_max_pending.attr,
1849 NULL,
1850};
1851
1852static struct attribute_group netiucv_stat_attr_group = {
1853 .name = "stats",
1854 .attrs = netiucv_stat_attrs,
1855};
1856
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001857static const struct attribute_group *netiucv_attr_groups[] = {
1858 &netiucv_stat_attr_group,
1859 &netiucv_attr_group,
1860 NULL,
1861};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001863static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001865 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001866 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867 int ret;
1868
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001869 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870
1871 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001872 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 dev->bus = &iucv_bus;
1874 dev->parent = iucv_root;
frank.blaschka@de.ibm.com0b945292012-07-24 22:34:28 +00001875 dev->groups = netiucv_attr_groups;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 /*
1877 * The release function could be called after the
1878 * module has been unloaded. It's _only_ task is to
1879 * free the struct. Therefore, we specify kfree()
1880 * directly here. (Probably a little bit obfuscating
1881 * but legitime ...).
1882 */
1883 dev->release = (void (*)(struct device *))kfree;
1884 dev->driver = &netiucv_driver;
1885 } else
1886 return -ENOMEM;
1887
1888 ret = device_register(dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02001889 if (ret) {
1890 put_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 return ret;
Sebastian Ottc6304932009-09-11 10:28:38 +02001892 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 priv->dev = dev;
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001894 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896}
1897
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001898static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001900 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 device_unregister(dev);
1902}
1903
1904/**
1905 * Allocate and initialize a new connection structure.
1906 * Add it to the list of netiucv connections;
1907 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001908static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
Ursula Braun08e33562011-12-19 22:56:34 +00001909 char *username,
1910 char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001912 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001914 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1915 if (!conn)
1916 goto out;
1917 skb_queue_head_init(&conn->collect_queue);
1918 skb_queue_head_init(&conn->commit_queue);
1919 spin_lock_init(&conn->collect_lock);
1920 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1921 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001923 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1924 if (!conn->rx_buff)
1925 goto out_conn;
1926 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1927 if (!conn->tx_buff)
1928 goto out_rx;
1929 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1930 conn_event_names, NR_CONN_STATES,
1931 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1932 GFP_KERNEL);
1933 if (!conn->fsm)
1934 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001936 fsm_settimer(conn->fsm, &conn->timer);
1937 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938
Ursula Braun08e33562011-12-19 22:56:34 +00001939 if (userdata)
1940 memcpy(conn->userdata, userdata, 17);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001941 if (username) {
1942 memcpy(conn->userid, username, 9);
1943 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001945
1946 write_lock_bh(&iucv_connection_rwlock);
1947 list_add_tail(&conn->list, &iucv_connection_list);
1948 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001950
1951out_tx:
1952 kfree_skb(conn->tx_buff);
1953out_rx:
1954 kfree_skb(conn->rx_buff);
1955out_conn:
1956 kfree(conn);
1957out:
1958 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959}
1960
1961/**
1962 * Release a connection structure and remove it from the
1963 * list of netiucv connections.
1964 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001965static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966{
Ursula Braun08e33562011-12-19 22:56:34 +00001967
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001968 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001969 write_lock_bh(&iucv_connection_rwlock);
1970 list_del_init(&conn->list);
1971 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001972 fsm_deltimer(&conn->timer);
1973 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001974 if (conn->path) {
Ursula Braun08e33562011-12-19 22:56:34 +00001975 iucv_path_sever(conn->path, conn->userdata);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001976 kfree(conn->path);
1977 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001979 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001980 kfree_fsm(conn->fsm);
1981 kfree_skb(conn->rx_buff);
1982 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983}
1984
1985/**
1986 * Release everything of a net device.
1987 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001988static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001990 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001992 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993
1994 if (!dev)
1995 return;
1996
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 if (privptr) {
1998 if (privptr->conn)
1999 netiucv_remove_connection(privptr->conn);
2000 if (privptr->fsm)
2001 kfree_fsm(privptr->fsm);
2002 privptr->conn = NULL; privptr->fsm = NULL;
2003 /* privptr gets freed by free_netdev() */
2004 }
2005 free_netdev(dev);
2006}
2007
2008/**
2009 * Initialize a net device. (Called from kernel in alloc_netdev())
2010 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00002011static const struct net_device_ops netiucv_netdev_ops = {
2012 .ndo_open = netiucv_open,
2013 .ndo_stop = netiucv_close,
2014 .ndo_get_stats = netiucv_stats,
2015 .ndo_start_xmit = netiucv_tx,
2016 .ndo_change_mtu = netiucv_change_mtu,
2017};
2018
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002019static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021 dev->mtu = NETIUCV_MTU_DEFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022 dev->destructor = netiucv_free_netdevice;
2023 dev->hard_header_len = NETIUCV_HDRLEN;
2024 dev->addr_len = 0;
2025 dev->type = ARPHRD_SLIP;
2026 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
2027 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00002028 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029}
2030
2031/**
2032 * Allocate and initialize everything of a net device.
2033 */
Ursula Braun08e33562011-12-19 22:56:34 +00002034static struct net_device *netiucv_init_netdevice(char *username, char *userdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035{
2036 struct netiucv_priv *privptr;
2037 struct net_device *dev;
2038
2039 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
2040 netiucv_setup_netdevice);
2041 if (!dev)
2042 return NULL;
Ursula Braun1d503562011-11-15 02:31:14 +00002043 if (dev_alloc_name(dev, dev->name) < 0)
2044 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002046 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
2048 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
2049 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002050 if (!privptr->fsm)
2051 goto out_netdev;
2052
Ursula Braun08e33562011-12-19 22:56:34 +00002053 privptr->conn = netiucv_new_connection(dev, username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002056 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057 }
2058 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002060
2061out_fsm:
2062 kfree_fsm(privptr->fsm);
2063out_netdev:
2064 free_netdev(dev);
2065 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066}
2067
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002068static ssize_t conn_write(struct device_driver *drv,
2069 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070{
Frank Pavlic16a83b32006-09-15 16:25:19 +02002071 char username[9];
Ursula Braun08e33562011-12-19 22:56:34 +00002072 char userdata[17];
2073 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002075 struct netiucv_priv *priv;
2076 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002078 IUCV_DBF_TEXT(trace, 3, __func__);
Ursula Braun08e33562011-12-19 22:56:34 +00002079 rc = netiucv_check_user(buf, count, username, userdata);
2080 if (rc)
2081 return rc;
Frank Pavlic16a83b32006-09-15 16:25:19 +02002082
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002083 read_lock_bh(&iucv_connection_rwlock);
2084 list_for_each_entry(cp, &iucv_connection_list, list) {
Ursula Braun08e33562011-12-19 22:56:34 +00002085 if (!strncmp(username, cp->userid, 9) &&
2086 !strncmp(userdata, cp->userdata, 17)) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002087 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braun08e33562011-12-19 22:56:34 +00002088 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s "
2089 "already exists\n", netiucv_printuser(cp));
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002090 return -EEXIST;
2091 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02002092 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002093 read_unlock_bh(&iucv_connection_rwlock);
2094
Ursula Braun08e33562011-12-19 22:56:34 +00002095 dev = netiucv_init_netdevice(username, userdata);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2098 return -ENODEV;
2099 }
2100
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002101 rc = netiucv_register_device(dev);
2102 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002104 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105 goto out_free_ndev;
2106 }
2107
2108 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002109 priv = netdev_priv(dev);
2110 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002112 rc = register_netdev(dev);
2113 if (rc)
2114 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115
Ursula Braun08e33562011-12-19 22:56:34 +00002116 dev_info(priv->dev, "The IUCV interface to %s has been established "
2117 "successfully\n",
2118 netiucv_printuser(priv->conn));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002119
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 return count;
2121
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002122out_unreg:
2123 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002126 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127}
2128
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002129static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002131static ssize_t remove_write (struct device_driver *drv,
2132 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002134 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 struct net_device *ndev;
2136 struct netiucv_priv *priv;
2137 struct device *dev;
2138 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002139 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 int i;
2141
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002142 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143
2144 if (count >= IFNAMSIZ)
Joe Perchesa419aef2009-08-18 11:18:35 -07002145 count = IFNAMSIZ - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002147 for (i = 0, p = buf; i < count && *p; i++, p++) {
2148 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 /* trailing lf, grr */
2150 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002151 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 }
2153 name[i] = '\0';
2154
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002155 read_lock_bh(&iucv_connection_rwlock);
2156 list_for_each_entry(cp, &iucv_connection_list, list) {
2157 ndev = cp->netdev;
2158 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002160 if (strncmp(name, ndev->name, count))
2161 continue;
2162 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002164 dev_warn(dev, "The IUCV device is connected"
2165 " to %s and cannot be removed\n",
2166 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002168 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 }
2170 unregister_netdev(ndev);
2171 netiucv_unregister_device(dev);
2172 return count;
2173 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002174 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2176 return -EINVAL;
2177}
2178
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002179static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002181static struct attribute * netiucv_drv_attrs[] = {
2182 &driver_attr_connection.attr,
2183 &driver_attr_remove.attr,
2184 NULL,
2185};
2186
2187static struct attribute_group netiucv_drv_attr_group = {
2188 .attrs = netiucv_drv_attrs,
2189};
2190
David Brownella4dbd672009-06-24 10:06:31 -07002191static const struct attribute_group *netiucv_drv_attr_groups[] = {
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002192 &netiucv_drv_attr_group,
2193 NULL,
2194};
2195
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002196static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002198 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199}
2200
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002201static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002203 struct iucv_connection *cp;
2204 struct net_device *ndev;
2205 struct netiucv_priv *priv;
2206 struct device *dev;
2207
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002208 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002209 while (!list_empty(&iucv_connection_list)) {
2210 cp = list_entry(iucv_connection_list.next,
2211 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002212 ndev = cp->netdev;
2213 priv = netdev_priv(ndev);
2214 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215
2216 unregister_netdev(ndev);
2217 netiucv_unregister_device(dev);
2218 }
2219
Ursula Braun1175b252009-06-16 10:30:43 +02002220 device_unregister(netiucv_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002222 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 iucv_unregister_dbf_views();
2224
Ursula Braun8f7c5022008-12-25 13:39:47 +01002225 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226 return;
2227}
2228
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002229static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002231 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002232
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002233 rc = iucv_register_dbf_views();
2234 if (rc)
2235 goto out;
2236 rc = iucv_register(&netiucv_handler, 1);
2237 if (rc)
2238 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002239 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002240 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002241 rc = driver_register(&netiucv_driver);
2242 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002243 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2244 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245 }
Ursula Braun1175b252009-06-16 10:30:43 +02002246 /* establish dummy device */
2247 netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
2248 if (!netiucv_dev) {
2249 rc = -ENOMEM;
2250 goto out_driver;
2251 }
2252 dev_set_name(netiucv_dev, "netiucv");
2253 netiucv_dev->bus = &iucv_bus;
2254 netiucv_dev->parent = iucv_root;
2255 netiucv_dev->release = (void (*)(struct device *))kfree;
2256 netiucv_dev->driver = &netiucv_driver;
2257 rc = device_register(netiucv_dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02002258 if (rc) {
2259 put_device(netiucv_dev);
Ursula Braun1175b252009-06-16 10:30:43 +02002260 goto out_driver;
Sebastian Ottc6304932009-09-11 10:28:38 +02002261 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002262 netiucv_banner();
2263 return rc;
2264
Ursula Braun1175b252009-06-16 10:30:43 +02002265out_driver:
2266 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002267out_iucv:
2268 iucv_unregister(&netiucv_handler, 1);
2269out_dbf:
2270 iucv_unregister_dbf_views();
2271out:
2272 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002274
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275module_init(netiucv_init);
2276module_exit(netiucv_exit);
2277MODULE_LICENSE("GPL");