blob: 3251333a23df18b6e8f378f0f681ac818145f0e1 [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>
66
Martin Schwidefskyeebce382007-02-08 13:50:33 -080067#include <net/iucv/iucv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#include "fsm.h"
69
70MODULE_AUTHOR
71 ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)");
72MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
73
Martin Schwidefskyeebce382007-02-08 13:50:33 -080074/**
75 * Debug Facility stuff
76 */
77#define IUCV_DBF_SETUP_NAME "iucv_setup"
78#define IUCV_DBF_SETUP_LEN 32
79#define IUCV_DBF_SETUP_PAGES 2
80#define IUCV_DBF_SETUP_NR_AREAS 1
81#define IUCV_DBF_SETUP_LEVEL 3
82
83#define IUCV_DBF_DATA_NAME "iucv_data"
84#define IUCV_DBF_DATA_LEN 128
85#define IUCV_DBF_DATA_PAGES 2
86#define IUCV_DBF_DATA_NR_AREAS 1
87#define IUCV_DBF_DATA_LEVEL 2
88
89#define IUCV_DBF_TRACE_NAME "iucv_trace"
90#define IUCV_DBF_TRACE_LEN 16
91#define IUCV_DBF_TRACE_PAGES 4
92#define IUCV_DBF_TRACE_NR_AREAS 1
93#define IUCV_DBF_TRACE_LEVEL 3
94
95#define IUCV_DBF_TEXT(name,level,text) \
96 do { \
97 debug_text_event(iucv_dbf_##name,level,text); \
98 } while (0)
99
100#define IUCV_DBF_HEX(name,level,addr,len) \
101 do { \
102 debug_event(iucv_dbf_##name,level,(void*)(addr),len); \
103 } while (0)
104
105DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf);
106
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100107/* Allow to sort out low debug levels early to avoid wasted sprints */
108static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
109{
110 return (level <= dbf_grp->level);
111}
112
113#define IUCV_DBF_TEXT_(name, level, text...) \
114 do { \
115 if (iucv_dbf_passes(iucv_dbf_##name, level)) { \
Tejun Heo390dfd92009-10-29 22:34:14 +0900116 char* __buf = get_cpu_var(iucv_dbf_txt_buf); \
117 sprintf(__buf, text); \
118 debug_text_event(iucv_dbf_##name, level, __buf); \
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100119 put_cpu_var(iucv_dbf_txt_buf); \
120 } \
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800121 } while (0)
122
123#define IUCV_DBF_SPRINTF(name,level,text...) \
124 do { \
125 debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
126 debug_sprintf_event(iucv_dbf_trace, level, text ); \
127 } while (0)
128
129/**
130 * some more debug stuff
131 */
132#define IUCV_HEXDUMP16(importance,header,ptr) \
133PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
134 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
135 *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
136 *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
137 *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
138 *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
139 *(((char*)ptr)+12),*(((char*)ptr)+13), \
140 *(((char*)ptr)+14),*(((char*)ptr)+15)); \
141PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
142 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
143 *(((char*)ptr)+16),*(((char*)ptr)+17), \
144 *(((char*)ptr)+18),*(((char*)ptr)+19), \
145 *(((char*)ptr)+20),*(((char*)ptr)+21), \
146 *(((char*)ptr)+22),*(((char*)ptr)+23), \
147 *(((char*)ptr)+24),*(((char*)ptr)+25), \
148 *(((char*)ptr)+26),*(((char*)ptr)+27), \
149 *(((char*)ptr)+28),*(((char*)ptr)+29), \
150 *(((char*)ptr)+30),*(((char*)ptr)+31));
151
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152#define PRINTK_HEADER " iucv: " /* for debugging */
153
Ursula Braun1175b252009-06-16 10:30:43 +0200154/* dummy device to make sure netiucv_pm functions are called */
155static struct device *netiucv_dev;
156
157static int netiucv_pm_prepare(struct device *);
158static void netiucv_pm_complete(struct device *);
159static int netiucv_pm_freeze(struct device *);
160static int netiucv_pm_restore_thaw(struct device *);
161
Alexey Dobriyan47145212009-12-14 18:00:08 -0800162static const struct dev_pm_ops netiucv_pm_ops = {
Ursula Braun1175b252009-06-16 10:30:43 +0200163 .prepare = netiucv_pm_prepare,
164 .complete = netiucv_pm_complete,
165 .freeze = netiucv_pm_freeze,
166 .thaw = netiucv_pm_restore_thaw,
167 .restore = netiucv_pm_restore_thaw,
168};
169
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170static struct device_driver netiucv_driver = {
Cornelia Huck22195102008-02-08 13:09:02 +0100171 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 .name = "netiucv",
173 .bus = &iucv_bus,
Ursula Braun1175b252009-06-16 10:30:43 +0200174 .pm = &netiucv_pm_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175};
176
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800177static int netiucv_callback_connreq(struct iucv_path *,
178 u8 ipvmid[8], u8 ipuser[16]);
179static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
180static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
181static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
182static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
183static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
184static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
185
186static struct iucv_handler netiucv_handler = {
187 .path_pending = netiucv_callback_connreq,
188 .path_complete = netiucv_callback_connack,
189 .path_severed = netiucv_callback_connrej,
190 .path_quiesced = netiucv_callback_connsusp,
191 .path_resumed = netiucv_callback_connres,
192 .message_pending = netiucv_callback_rx,
193 .message_complete = netiucv_callback_txdone
194};
195
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196/**
197 * Per connection profiling data
198 */
199struct connection_profile {
200 unsigned long maxmulti;
201 unsigned long maxcqueue;
202 unsigned long doios_single;
203 unsigned long doios_multi;
204 unsigned long txlen;
205 unsigned long tx_time;
206 struct timespec send_stamp;
207 unsigned long tx_pending;
208 unsigned long tx_max_pending;
209};
210
211/**
212 * Representation of one iucv connection
213 */
214struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800215 struct list_head list;
216 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 struct sk_buff *rx_buff;
218 struct sk_buff *tx_buff;
219 struct sk_buff_head collect_queue;
220 struct sk_buff_head commit_queue;
221 spinlock_t collect_lock;
222 int collect_len;
223 int max_buffsize;
224 fsm_timer timer;
225 fsm_instance *fsm;
226 struct net_device *netdev;
227 struct connection_profile prof;
228 char userid[9];
229};
230
231/**
232 * Linked list of all connection structs.
233 */
Denis Chengc11ca972008-01-26 14:11:13 +0100234static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200235static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
237/**
238 * Representation of event-data for the
239 * connection state machine.
240 */
241struct iucv_event {
242 struct iucv_connection *conn;
243 void *data;
244};
245
246/**
247 * Private part of the network device structure
248 */
249struct netiucv_priv {
250 struct net_device_stats stats;
251 unsigned long tbusy;
252 fsm_instance *fsm;
253 struct iucv_connection *conn;
254 struct device *dev;
Ursula Braun1175b252009-06-16 10:30:43 +0200255 int pm_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256};
257
258/**
259 * Link level header for a packet.
260 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800261struct ll_header {
262 u16 next;
263};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800265#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266#define NETIUCV_BUFSIZE_MAX 32768
267#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
268#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
269#define NETIUCV_MTU_DEFAULT 9216
270#define NETIUCV_QUEUELEN_DEFAULT 50
271#define NETIUCV_TIMEOUT_5SEC 5000
272
273/**
274 * Compatibility macros for busy handling
275 * of network devices.
276 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800277static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800279 struct netiucv_priv *priv = netdev_priv(dev);
280 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 netif_wake_queue(dev);
282}
283
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800284static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800286 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800288 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289}
290
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800291static u8 iucvMagic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
293 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
294};
295
296/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 * Convert an iucv userId to its printable
298 * form (strip whitespace at end).
299 *
300 * @param An iucv userId
301 *
302 * @returns The printable string (static data!!)
303 */
Martin Schwidefskyd4614622007-06-20 13:03:57 +0200304static char *netiucv_printname(char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305{
306 static char tmp[9];
307 char *p = tmp;
308 memcpy(tmp, name, 8);
309 tmp[8] = '\0';
310 while (*p && (!isspace(*p)))
311 p++;
312 *p = '\0';
313 return tmp;
314}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400315
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316/**
317 * States of the interface statemachine.
318 */
319enum dev_states {
320 DEV_STATE_STOPPED,
321 DEV_STATE_STARTWAIT,
322 DEV_STATE_STOPWAIT,
323 DEV_STATE_RUNNING,
324 /**
325 * MUST be always the last element!!
326 */
327 NR_DEV_STATES
328};
329
330static const char *dev_state_names[] = {
331 "Stopped",
332 "StartWait",
333 "StopWait",
334 "Running",
335};
336
337/**
338 * Events of the interface statemachine.
339 */
340enum dev_events {
341 DEV_EVENT_START,
342 DEV_EVENT_STOP,
343 DEV_EVENT_CONUP,
344 DEV_EVENT_CONDOWN,
345 /**
346 * MUST be always the last element!!
347 */
348 NR_DEV_EVENTS
349};
350
351static const char *dev_event_names[] = {
352 "Start",
353 "Stop",
354 "Connection up",
355 "Connection down",
356};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400357
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358/**
359 * Events of the connection statemachine
360 */
361enum conn_events {
362 /**
363 * Events, representing callbacks from
364 * lowlevel iucv layer)
365 */
366 CONN_EVENT_CONN_REQ,
367 CONN_EVENT_CONN_ACK,
368 CONN_EVENT_CONN_REJ,
369 CONN_EVENT_CONN_SUS,
370 CONN_EVENT_CONN_RES,
371 CONN_EVENT_RX,
372 CONN_EVENT_TXDONE,
373
374 /**
375 * Events, representing errors return codes from
376 * calls to lowlevel iucv layer
377 */
378
379 /**
380 * Event, representing timer expiry.
381 */
382 CONN_EVENT_TIMER,
383
384 /**
385 * Events, representing commands from upper levels.
386 */
387 CONN_EVENT_START,
388 CONN_EVENT_STOP,
389
390 /**
391 * MUST be always the last element!!
392 */
393 NR_CONN_EVENTS,
394};
395
396static const char *conn_event_names[] = {
397 "Remote connection request",
398 "Remote connection acknowledge",
399 "Remote connection reject",
400 "Connection suspended",
401 "Connection resumed",
402 "Data received",
403 "Data sent",
404
405 "Timer",
406
407 "Start",
408 "Stop",
409};
410
411/**
412 * States of the connection statemachine.
413 */
414enum conn_states {
415 /**
416 * Connection not assigned to any device,
417 * initial state, invalid
418 */
419 CONN_STATE_INVALID,
420
421 /**
422 * Userid assigned but not operating
423 */
424 CONN_STATE_STOPPED,
425
426 /**
427 * Connection registered,
428 * no connection request sent yet,
429 * no connection request received
430 */
431 CONN_STATE_STARTWAIT,
432
433 /**
434 * Connection registered and connection request sent,
435 * no acknowledge and no connection request received yet.
436 */
437 CONN_STATE_SETUPWAIT,
438
439 /**
440 * Connection up and running idle
441 */
442 CONN_STATE_IDLE,
443
444 /**
445 * Data sent, awaiting CONN_EVENT_TXDONE
446 */
447 CONN_STATE_TX,
448
449 /**
450 * Error during registration.
451 */
452 CONN_STATE_REGERR,
453
454 /**
455 * Error during registration.
456 */
457 CONN_STATE_CONNERR,
458
459 /**
460 * MUST be always the last element!!
461 */
462 NR_CONN_STATES,
463};
464
465static const char *conn_state_names[] = {
466 "Invalid",
467 "Stopped",
468 "StartWait",
469 "SetupWait",
470 "Idle",
471 "TX",
472 "Terminating",
473 "Registration error",
474 "Connect error",
475};
476
Jeff Garzike82b0f22006-05-26 21:58:38 -0400477
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478/**
479 * Debug Facility Stuff
480 */
481static debug_info_t *iucv_dbf_setup = NULL;
482static debug_info_t *iucv_dbf_data = NULL;
483static debug_info_t *iucv_dbf_trace = NULL;
484
485DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
486
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800487static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488{
489 if (iucv_dbf_setup)
490 debug_unregister(iucv_dbf_setup);
491 if (iucv_dbf_data)
492 debug_unregister(iucv_dbf_data);
493 if (iucv_dbf_trace)
494 debug_unregister(iucv_dbf_trace);
495}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800496static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497{
498 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700499 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 IUCV_DBF_SETUP_NR_AREAS,
501 IUCV_DBF_SETUP_LEN);
502 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700503 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 IUCV_DBF_DATA_NR_AREAS,
505 IUCV_DBF_DATA_LEN);
506 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700507 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 IUCV_DBF_TRACE_NR_AREAS,
509 IUCV_DBF_TRACE_LEN);
510
511 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
512 (iucv_dbf_trace == NULL)) {
513 iucv_unregister_dbf_views();
514 return -ENOMEM;
515 }
516 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
517 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
518
519 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
520 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
521
522 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
523 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
524
525 return 0;
526}
527
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800528/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800530 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800532static void netiucv_callback_rx(struct iucv_path *path,
533 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800535 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 struct iucv_event ev;
537
538 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800539 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
541}
542
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800543static void netiucv_callback_txdone(struct iucv_path *path,
544 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800546 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 struct iucv_event ev;
548
549 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800550 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
552}
553
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800554static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800556 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800558 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559}
560
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800561static int netiucv_callback_connreq(struct iucv_path *path,
562 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800564 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 struct iucv_event ev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800566 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Stefan Weil5df979d2011-02-02 06:04:35 +0000568 if (memcmp(iucvMagic, ipuser, 16))
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800569 /* ipuser must match iucvMagic. */
570 return -EINVAL;
571 rc = -EINVAL;
572 read_lock_bh(&iucv_connection_rwlock);
573 list_for_each_entry(conn, &iucv_connection_list, list) {
574 if (strncmp(ipvmid, conn->userid, 8))
575 continue;
576 /* Found a matching connection for this path. */
577 conn->path = path;
578 ev.conn = conn;
579 ev.data = path;
580 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
581 rc = 0;
582 }
583 read_unlock_bh(&iucv_connection_rwlock);
584 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585}
586
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800587static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800589 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800591 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592}
593
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800594static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800596 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800598 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599}
600
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800601static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800603 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800605 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606}
607
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100609 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100611static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612{
613}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400614
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800615/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800617 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618
619/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800620 * netiucv_unpack_skb
621 * @conn: The connection where this skb has been received.
622 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800624 * Unpack a just received skb and hand it over to upper layers.
625 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800627static void netiucv_unpack_skb(struct iucv_connection *conn,
628 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629{
630 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800631 struct netiucv_priv *privptr = netdev_priv(dev);
632 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
634 skb_put(pskb, NETIUCV_HDRLEN);
635 pskb->dev = dev;
636 pskb->ip_summed = CHECKSUM_NONE;
637 pskb->protocol = ntohs(ETH_P_IP);
638
639 while (1) {
640 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800641 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
643 if (!header->next)
644 break;
645
646 skb_pull(pskb, NETIUCV_HDRLEN);
647 header->next -= offset;
648 offset += header->next;
649 header->next -= NETIUCV_HDRLEN;
650 if (skb_tailroom(pskb) < header->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
652 header->next, skb_tailroom(pskb));
653 return;
654 }
655 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700656 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 skb = dev_alloc_skb(pskb->len);
658 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 IUCV_DBF_TEXT(data, 2,
660 "Out of memory in netiucv_unpack_skb\n");
661 privptr->stats.rx_dropped++;
662 return;
663 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300664 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
665 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700666 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 skb->dev = pskb->dev;
668 skb->protocol = pskb->protocol;
669 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800670 privptr->stats.rx_packets++;
671 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 /*
673 * Since receiving is always initiated from a tasklet (in iucv.c),
674 * we must use netif_rx_ni() instead of netif_rx()
675 */
676 netif_rx_ni(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 skb_pull(pskb, header->next);
678 skb_put(pskb, NETIUCV_HDRLEN);
679 }
680}
681
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800682static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800684 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800686 struct iucv_message *msg = ev->data;
687 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 int rc;
689
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200690 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
692 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800693 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800695 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 return;
697 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800698 if (msg->length > conn->max_buffsize) {
699 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 privptr->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800702 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 return;
704 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700705 conn->rx_buff->data = conn->rx_buff->head;
706 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800708 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
709 msg->length, NULL);
710 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 privptr->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
713 return;
714 }
715 netiucv_unpack_skb(conn, conn->rx_buff);
716}
717
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800718static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800720 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800722 struct iucv_message *msg = ev->data;
723 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800725 u32 single_flag = msg->tag;
726 u32 txbytes = 0;
727 u32 txpackets = 0;
728 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 struct sk_buff *skb;
730 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800731 struct ll_header header;
732 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200734 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800736 if (conn && conn->netdev)
737 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 conn->prof.tx_pending--;
739 if (single_flag) {
740 if ((skb = skb_dequeue(&conn->commit_queue))) {
741 atomic_dec(&skb->users);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 if (privptr) {
743 privptr->stats.tx_packets++;
744 privptr->stats.tx_bytes +=
745 (skb->len - NETIUCV_HDRLEN
Ursula Braun998221c2009-11-12 21:46:30 +0000746 - NETIUCV_HDRLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 }
Ursula Braun998221c2009-11-12 21:46:30 +0000748 dev_kfree_skb_any(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 }
750 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700751 conn->tx_buff->data = conn->tx_buff->head;
752 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 conn->tx_buff->len = 0;
754 spin_lock_irqsave(&conn->collect_lock, saveflags);
755 while ((skb = skb_dequeue(&conn->collect_queue))) {
756 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
757 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
758 NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300759 skb_copy_from_linear_data(skb,
760 skb_put(conn->tx_buff, skb->len),
761 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 txbytes += skb->len;
763 txpackets++;
764 stat_maxcq++;
765 atomic_dec(&skb->users);
766 dev_kfree_skb_any(skb);
767 }
768 if (conn->collect_len > conn->prof.maxmulti)
769 conn->prof.maxmulti = conn->collect_len;
770 conn->collect_len = 0;
771 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800772 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800774 return;
775 }
776
777 header.next = 0;
778 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
john stultz2c6b47d2007-07-24 17:47:43 -0700779 conn->prof.send_stamp = current_kernel_time();
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800780 txmsg.class = 0;
781 txmsg.tag = 0;
782 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
783 conn->tx_buff->data, conn->tx_buff->len);
784 conn->prof.doios_multi++;
785 conn->prof.txlen += conn->tx_buff->len;
786 conn->prof.tx_pending++;
787 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
788 conn->prof.tx_max_pending = conn->prof.tx_pending;
789 if (rc) {
790 conn->prof.tx_pending--;
791 fsm_newstate(fi, CONN_STATE_IDLE);
792 if (privptr)
793 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800794 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
795 } else {
796 if (privptr) {
797 privptr->stats.tx_packets += txpackets;
798 privptr->stats.tx_bytes += txbytes;
799 }
800 if (stat_maxcq > conn->prof.maxcqueue)
801 conn->prof.maxcqueue = stat_maxcq;
802 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803}
804
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800805static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800807 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800809 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800811 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200814 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800816 conn->path = path;
817 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
818 path->flags = 0;
819 rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
822 return;
823 }
824 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800825 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
827}
828
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800829static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800831 struct iucv_event *ev = arg;
832 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200834 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800835 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836}
837
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800838static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800840 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800842 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200844 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 fsm_deltimer(&conn->timer);
846 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800847 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
849}
850
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800851static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800853 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200855 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800857 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 fsm_newstate(fi, CONN_STATE_STARTWAIT);
859}
860
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800861static void conn_action_connsever(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_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800865 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200867 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
869 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800870 iucv_path_sever(conn->path, NULL);
Ursula Braun8f7c5022008-12-25 13:39:47 +0100871 dev_info(privptr->dev, "The peer interface of the IUCV device"
872 " has closed the connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800874 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 fsm_newstate(fi, CONN_STATE_STARTWAIT);
876 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
877}
878
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800879static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800881 struct iucv_connection *conn = arg;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100882 struct net_device *netdev = conn->netdev;
883 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 int rc;
885
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200886 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800888 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Ursula Braunf082bca2008-07-14 09:59:30 +0200889 IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n",
Ursula Braun8f7c5022008-12-25 13:39:47 +0100890 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800892 /*
893 * We must set the state before calling iucv_connect because the
894 * callback handler could be called at any point after the connection
895 * request is sent
896 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
898 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800899 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
900 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
901 NULL, iucvMagic, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800903 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100904 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800905 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
906 CONN_EVENT_TIMER, conn);
907 return;
908 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100909 dev_warn(privptr->dev,
910 "The IUCV device failed to connect to z/VM guest %s\n",
911 netiucv_printname(conn->userid));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800912 fsm_newstate(fi, CONN_STATE_STARTWAIT);
913 break;
914 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100915 dev_warn(privptr->dev,
916 "The IUCV device failed to connect to the peer on z/VM"
917 " guest %s\n", netiucv_printname(conn->userid));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800918 fsm_newstate(fi, CONN_STATE_STARTWAIT);
919 break;
920 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100921 dev_err(privptr->dev,
922 "Connecting the IUCV device would exceed the maximum"
923 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800924 fsm_newstate(fi, CONN_STATE_CONNERR);
925 break;
926 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100927 dev_err(privptr->dev,
928 "z/VM guest %s has too many IUCV connections"
929 " to connect with the IUCV device\n",
930 netiucv_printname(conn->userid));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800931 fsm_newstate(fi, CONN_STATE_CONNERR);
932 break;
933 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100934 dev_err(privptr->dev,
935 "The IUCV device cannot connect to a z/VM guest with no"
936 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800937 fsm_newstate(fi, CONN_STATE_CONNERR);
938 break;
939 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100940 dev_err(privptr->dev,
941 "Connecting the IUCV device failed with error %d\n",
942 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800943 fsm_newstate(fi, CONN_STATE_CONNERR);
944 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 }
946 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800947 kfree(conn->path);
948 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949}
950
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800951static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952{
953 struct sk_buff *skb;
954
955 while ((skb = skb_dequeue(q))) {
956 atomic_dec(&skb->users);
957 dev_kfree_skb_any(skb);
958 }
959}
960
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800961static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800963 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 struct iucv_connection *conn = ev->conn;
965 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800966 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200968 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969
970 fsm_deltimer(&conn->timer);
971 fsm_newstate(fi, CONN_STATE_STOPPED);
972 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800973 if (conn->path) {
974 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
975 iucv_path_sever(conn->path, iucvMagic);
976 kfree(conn->path);
977 conn->path = NULL;
978 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 netiucv_purge_skb_queue(&conn->commit_queue);
980 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
981}
982
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800983static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800985 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 struct net_device *netdev = conn->netdev;
987
Ursula Braunf082bca2008-07-14 09:59:30 +0200988 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
989 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990}
991
992static const fsm_node conn_fsm[] = {
993 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
994 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
995
996 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
997 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
998 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
999 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
1000 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
1001 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
1002 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
1003
1004 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
1005 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1006 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
1007 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
1008 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
1009
1010 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
1011 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
1012
1013 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
1014 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
1015 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
1016
1017 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
1018 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1019
1020 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1021 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1022};
1023
1024static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1025
Jeff Garzike82b0f22006-05-26 21:58:38 -04001026
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001027/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001029 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030
1031/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001032 * dev_action_start
1033 * @fi: An instance of an interface statemachine.
1034 * @event: The event, just happened.
1035 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001037 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001039static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001041 struct net_device *dev = arg;
1042 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001044 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001047 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048}
1049
1050/**
1051 * Shutdown connection by sending CONN_EVENT_STOP to it.
1052 *
1053 * @param fi An instance of an interface statemachine.
1054 * @param event The event, just happened.
1055 * @param arg Generic pointer, casted from struct net_device * upon call.
1056 */
1057static void
1058dev_action_stop(fsm_instance *fi, int event, void *arg)
1059{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001060 struct net_device *dev = arg;
1061 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 struct iucv_event ev;
1063
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001064 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065
1066 ev.conn = privptr->conn;
1067
1068 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1069 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1070}
1071
1072/**
1073 * Called from connection statemachine
1074 * when a connection is up and running.
1075 *
1076 * @param fi An instance of an interface statemachine.
1077 * @param event The event, just happened.
1078 * @param arg Generic pointer, casted from struct net_device * upon call.
1079 */
1080static void
1081dev_action_connup(fsm_instance *fi, int event, void *arg)
1082{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001083 struct net_device *dev = arg;
1084 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001086 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087
1088 switch (fsm_getstate(fi)) {
1089 case DEV_STATE_STARTWAIT:
1090 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001091 dev_info(privptr->dev,
1092 "The IUCV device has been connected"
1093 " successfully to %s\n", privptr->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 IUCV_DBF_TEXT(setup, 3,
1095 "connection is up and running\n");
1096 break;
1097 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 IUCV_DBF_TEXT(data, 2,
1099 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1100 break;
1101 }
1102}
1103
1104/**
1105 * Called from connection statemachine
1106 * when a connection has been shutdown.
1107 *
1108 * @param fi An instance of an interface statemachine.
1109 * @param event The event, just happened.
1110 * @param arg Generic pointer, casted from struct net_device * upon call.
1111 */
1112static void
1113dev_action_conndown(fsm_instance *fi, int event, void *arg)
1114{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001115 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116
1117 switch (fsm_getstate(fi)) {
1118 case DEV_STATE_RUNNING:
1119 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1120 break;
1121 case DEV_STATE_STOPWAIT:
1122 fsm_newstate(fi, DEV_STATE_STOPPED);
1123 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1124 break;
1125 }
1126}
1127
1128static const fsm_node dev_fsm[] = {
1129 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1130
1131 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1132 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1133
1134 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1135 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1136
1137 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1138 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001139 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140};
1141
1142static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1143
1144/**
1145 * Transmit a packet.
1146 * This is a helper function for netiucv_tx().
1147 *
1148 * @param conn Connection to be used for sending.
1149 * @param skb Pointer to struct sk_buff of packet to send.
1150 * The linklevel header has already been set up
1151 * by netiucv_tx().
1152 *
1153 * @return 0 on success, -ERRNO on failure. (Never fails.)
1154 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001155static int netiucv_transmit_skb(struct iucv_connection *conn,
1156 struct sk_buff *skb)
1157{
1158 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001160 struct ll_header header;
1161 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162
1163 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1164 int l = skb->len + NETIUCV_HDRLEN;
1165
1166 spin_lock_irqsave(&conn->collect_lock, saveflags);
1167 if (conn->collect_len + l >
1168 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1169 rc = -EBUSY;
1170 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001171 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 } else {
1173 atomic_inc(&skb->users);
1174 skb_queue_tail(&conn->collect_queue, skb);
1175 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001176 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 }
1178 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1179 } else {
1180 struct sk_buff *nskb = skb;
1181 /**
1182 * Copy the skb to a new allocated skb in lowmem only if the
1183 * data is located above 2G in memory or tailroom is < 2.
1184 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001185 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1186 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 int copied = 0;
1188 if (hi || (skb_tailroom(skb) < 2)) {
1189 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1190 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1191 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1193 rc = -ENOMEM;
1194 return rc;
1195 } else {
1196 skb_reserve(nskb, NETIUCV_HDRLEN);
1197 memcpy(skb_put(nskb, skb->len),
1198 skb->data, skb->len);
1199 }
1200 copied = 1;
1201 }
1202 /**
1203 * skb now is below 2G and has enough room. Add headers.
1204 */
1205 header.next = nskb->len + NETIUCV_HDRLEN;
1206 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1207 header.next = 0;
1208 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1209
1210 fsm_newstate(conn->fsm, CONN_STATE_TX);
john stultz2c6b47d2007-07-24 17:47:43 -07001211 conn->prof.send_stamp = current_kernel_time();
Jeff Garzike82b0f22006-05-26 21:58:38 -04001212
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001213 msg.tag = 1;
1214 msg.class = 0;
1215 rc = iucv_message_send(conn->path, &msg, 0, 0,
1216 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 conn->prof.doios_single++;
1218 conn->prof.txlen += skb->len;
1219 conn->prof.tx_pending++;
1220 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1221 conn->prof.tx_max_pending = conn->prof.tx_pending;
1222 if (rc) {
1223 struct netiucv_priv *privptr;
1224 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1225 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001226 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 if (privptr)
1228 privptr->stats.tx_errors++;
1229 if (copied)
1230 dev_kfree_skb(nskb);
1231 else {
1232 /**
1233 * Remove our headers. They get added
1234 * again on retransmit.
1235 */
1236 skb_pull(skb, NETIUCV_HDRLEN);
1237 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1238 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1240 } else {
1241 if (copied)
1242 dev_kfree_skb(skb);
1243 atomic_inc(&nskb->users);
1244 skb_queue_tail(&conn->commit_queue, nskb);
1245 }
1246 }
1247
1248 return rc;
1249}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001250
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001251/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001253 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254
1255/**
1256 * Open an interface.
1257 * Called from generic network layer when ifconfig up is run.
1258 *
1259 * @param dev Pointer to interface struct.
1260 *
1261 * @return 0 on success, -ERRNO on failure. (Never fails.)
1262 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001263static int netiucv_open(struct net_device *dev)
1264{
1265 struct netiucv_priv *priv = netdev_priv(dev);
1266
1267 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 return 0;
1269}
1270
1271/**
1272 * Close an interface.
1273 * Called from generic network layer when ifconfig down is run.
1274 *
1275 * @param dev Pointer to interface struct.
1276 *
1277 * @return 0 on success, -ERRNO on failure. (Never fails.)
1278 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001279static int netiucv_close(struct net_device *dev)
1280{
1281 struct netiucv_priv *priv = netdev_priv(dev);
1282
1283 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 return 0;
1285}
1286
Ursula Braun1175b252009-06-16 10:30:43 +02001287static int netiucv_pm_prepare(struct device *dev)
1288{
1289 IUCV_DBF_TEXT(trace, 3, __func__);
1290 return 0;
1291}
1292
1293static void netiucv_pm_complete(struct device *dev)
1294{
1295 IUCV_DBF_TEXT(trace, 3, __func__);
1296 return;
1297}
1298
1299/**
1300 * netiucv_pm_freeze() - Freeze PM callback
1301 * @dev: netiucv device
1302 *
1303 * close open netiucv interfaces
1304 */
1305static int netiucv_pm_freeze(struct device *dev)
1306{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001307 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001308 struct net_device *ndev = NULL;
1309 int rc = 0;
1310
1311 IUCV_DBF_TEXT(trace, 3, __func__);
1312 if (priv && priv->conn)
1313 ndev = priv->conn->netdev;
1314 if (!ndev)
1315 goto out;
1316 netif_device_detach(ndev);
1317 priv->pm_state = fsm_getstate(priv->fsm);
1318 rc = netiucv_close(ndev);
1319out:
1320 return rc;
1321}
1322
1323/**
1324 * netiucv_pm_restore_thaw() - Thaw and restore PM callback
1325 * @dev: netiucv device
1326 *
1327 * re-open netiucv interfaces closed during freeze
1328 */
1329static int netiucv_pm_restore_thaw(struct device *dev)
1330{
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +02001331 struct netiucv_priv *priv = dev_get_drvdata(dev);
Ursula Braun1175b252009-06-16 10:30:43 +02001332 struct net_device *ndev = NULL;
1333 int rc = 0;
1334
1335 IUCV_DBF_TEXT(trace, 3, __func__);
1336 if (priv && priv->conn)
1337 ndev = priv->conn->netdev;
1338 if (!ndev)
1339 goto out;
1340 switch (priv->pm_state) {
1341 case DEV_STATE_RUNNING:
1342 case DEV_STATE_STARTWAIT:
1343 rc = netiucv_open(ndev);
1344 break;
1345 default:
1346 break;
1347 }
1348 netif_device_attach(ndev);
1349out:
1350 return rc;
1351}
1352
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353/**
1354 * Start transmission of a packet.
1355 * Called from generic network device layer.
1356 *
1357 * @param skb Pointer to buffer containing the packet.
1358 * @param dev Pointer to interface struct.
1359 *
1360 * @return 0 if packet consumed, !0 if packet rejected.
1361 * Note: If we return !0, then the packet is free'd by
1362 * the generic network layer.
1363 */
1364static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1365{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001366 struct netiucv_priv *privptr = netdev_priv(dev);
1367 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001369 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 /**
1371 * Some sanity checks ...
1372 */
1373 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1375 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001376 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 }
1378 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 IUCV_DBF_TEXT(data, 2,
1380 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1381 dev_kfree_skb(skb);
1382 privptr->stats.tx_dropped++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001383 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 }
1385
1386 /**
1387 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001388 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 */
1390 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 dev_kfree_skb(skb);
1392 privptr->stats.tx_dropped++;
1393 privptr->stats.tx_errors++;
1394 privptr->stats.tx_carrier_errors++;
Patrick McHardyec634fe2009-07-05 19:23:38 -07001395 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 }
1397
1398 if (netiucv_test_and_set_busy(dev)) {
1399 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001400 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 }
1402 dev->trans_start = jiffies;
Patrick McHardy5b548142009-06-12 06:22:29 +00001403 rc = netiucv_transmit_skb(privptr->conn, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 netiucv_clear_busy(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001405 return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406}
1407
1408/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001409 * netiucv_stats
1410 * @dev: Pointer to interface struct.
1411 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 * Returns interface statistics of a device.
1413 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001414 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001416static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001418 struct netiucv_priv *priv = netdev_priv(dev);
1419
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001420 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001421 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422}
1423
1424/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001425 * netiucv_change_mtu
1426 * @dev: Pointer to interface struct.
1427 * @new_mtu: The new MTU to use for this interface.
1428 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 * Sets MTU of an interface.
1430 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001431 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 * (valid range is 576 .. NETIUCV_MTU_MAX).
1433 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001434static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001436 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001437 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1439 return -EINVAL;
1440 }
1441 dev->mtu = new_mtu;
1442 return 0;
1443}
1444
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001445/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001447 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001449static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1450 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001452 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001454 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));
1456}
1457
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001458static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1459 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001461 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 struct net_device *ndev = priv->conn->netdev;
1463 char *p;
1464 char *tmp;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001465 char username[9];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 int i;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001467 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001469 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001470 if (count > 9) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001472 "%d is length of username\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 return -EINVAL;
1474 }
1475
1476 tmp = strsep((char **) &buf, "\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001477 for (i = 0, p = tmp; i < 8 && *p; i++, p++) {
1478 if (isalnum(*p) || (*p == '$')) {
Frank Pavlic16a83b32006-09-15 16:25:19 +02001479 username[i]= toupper(*p);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001480 continue;
1481 }
1482 if (*p == '\n') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 /* trailing lf, grr */
1484 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001486 IUCV_DBF_TEXT_(setup, 2,
1487 "username: invalid character %c\n", *p);
1488 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001490 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001492 username[8] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001494 if (memcmp(username, priv->conn->userid, 9) &&
1495 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1496 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001497 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001498 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001499 }
1500 read_lock_bh(&iucv_connection_rwlock);
1501 list_for_each_entry(cp, &iucv_connection_list, list) {
1502 if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
1503 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braunf082bca2008-07-14 09:59:30 +02001504 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection "
1505 "to %s already exists\n", username);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001506 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 }
1508 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001509 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 memcpy(priv->conn->userid, username, 9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512}
1513
1514static DEVICE_ATTR(user, 0644, user_show, user_write);
1515
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001516static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1517 char *buf)
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001518{
1519 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001521 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1523}
1524
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001525static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1526 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001528 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 struct net_device *ndev = priv->conn->netdev;
1530 char *e;
1531 int bs1;
1532
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001533 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 if (count >= 39)
1535 return -EINVAL;
1536
1537 bs1 = simple_strtoul(buf, &e, 0);
1538
1539 if (e && (!isspace(*e))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
1541 return -EINVAL;
1542 }
1543 if (bs1 > NETIUCV_BUFSIZE_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 IUCV_DBF_TEXT_(setup, 2,
1545 "buffer_write: buffer size %d too large\n",
1546 bs1);
1547 return -EINVAL;
1548 }
1549 if ((ndev->flags & IFF_RUNNING) &&
1550 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 IUCV_DBF_TEXT_(setup, 2,
1552 "buffer_write: buffer size %d too small\n",
1553 bs1);
1554 return -EINVAL;
1555 }
1556 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 IUCV_DBF_TEXT_(setup, 2,
1558 "buffer_write: buffer size %d too small\n",
1559 bs1);
1560 return -EINVAL;
1561 }
1562
1563 priv->conn->max_buffsize = bs1;
1564 if (!(ndev->flags & IFF_RUNNING))
1565 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1566
1567 return count;
1568
1569}
1570
1571static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1572
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001573static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1574 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001576 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001578 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1580}
1581
1582static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1583
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001584static ssize_t conn_fsm_show (struct device *dev,
1585 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001587 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001589 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1591}
1592
1593static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1594
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001595static ssize_t maxmulti_show (struct device *dev,
1596 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001598 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001600 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1602}
1603
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001604static ssize_t maxmulti_write (struct device *dev,
1605 struct device_attribute *attr,
1606 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001608 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001610 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 priv->conn->prof.maxmulti = 0;
1612 return count;
1613}
1614
1615static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1616
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001617static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1618 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001620 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001622 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1624}
1625
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001626static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1627 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001629 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001630
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001631 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 priv->conn->prof.maxcqueue = 0;
1633 return count;
1634}
1635
1636static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1637
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001638static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1639 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001641 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001643 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1645}
1646
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001647static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1648 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001650 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001651
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001652 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 priv->conn->prof.doios_single = 0;
1654 return count;
1655}
1656
1657static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1658
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001659static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1660 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001662 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001664 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1666}
1667
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001668static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1669 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001671 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001672
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001673 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 priv->conn->prof.doios_multi = 0;
1675 return count;
1676}
1677
1678static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1679
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001680static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1681 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001683 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001685 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
1687}
1688
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001689static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1690 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001692 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001693
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001694 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 priv->conn->prof.txlen = 0;
1696 return count;
1697}
1698
1699static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1700
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001701static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1702 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001704 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001706 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1708}
1709
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001710static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1711 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001713 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001714
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001715 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 priv->conn->prof.tx_time = 0;
1717 return count;
1718}
1719
1720static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1721
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001722static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1723 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001725 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001727 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1729}
1730
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001731static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1732 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001734 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001736 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 priv->conn->prof.tx_pending = 0;
1738 return count;
1739}
1740
1741static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1742
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001743static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1744 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001746 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001748 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1750}
1751
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001752static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1753 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001755 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001757 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758 priv->conn->prof.tx_max_pending = 0;
1759 return count;
1760}
1761
1762static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1763
1764static struct attribute *netiucv_attrs[] = {
1765 &dev_attr_buffer.attr,
1766 &dev_attr_user.attr,
1767 NULL,
1768};
1769
1770static struct attribute_group netiucv_attr_group = {
1771 .attrs = netiucv_attrs,
1772};
1773
1774static struct attribute *netiucv_stat_attrs[] = {
1775 &dev_attr_device_fsm_state.attr,
1776 &dev_attr_connection_fsm_state.attr,
1777 &dev_attr_max_tx_buffer_used.attr,
1778 &dev_attr_max_chained_skbs.attr,
1779 &dev_attr_tx_single_write_ops.attr,
1780 &dev_attr_tx_multi_write_ops.attr,
1781 &dev_attr_netto_bytes.attr,
1782 &dev_attr_max_tx_io_time.attr,
1783 &dev_attr_tx_pending.attr,
1784 &dev_attr_tx_max_pending.attr,
1785 NULL,
1786};
1787
1788static struct attribute_group netiucv_stat_attr_group = {
1789 .name = "stats",
1790 .attrs = netiucv_stat_attrs,
1791};
1792
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001793static int netiucv_add_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794{
1795 int ret;
1796
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001797 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798 ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group);
1799 if (ret)
1800 return ret;
1801 ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group);
1802 if (ret)
1803 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1804 return ret;
1805}
1806
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001807static void netiucv_remove_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001809 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810 sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group);
1811 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1812}
1813
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001814static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001816 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001817 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 int ret;
1819
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001820 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821
1822 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001823 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824 dev->bus = &iucv_bus;
1825 dev->parent = iucv_root;
1826 /*
1827 * The release function could be called after the
1828 * module has been unloaded. It's _only_ task is to
1829 * free the struct. Therefore, we specify kfree()
1830 * directly here. (Probably a little bit obfuscating
1831 * but legitime ...).
1832 */
1833 dev->release = (void (*)(struct device *))kfree;
1834 dev->driver = &netiucv_driver;
1835 } else
1836 return -ENOMEM;
1837
1838 ret = device_register(dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02001839 if (ret) {
1840 put_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841 return ret;
Sebastian Ottc6304932009-09-11 10:28:38 +02001842 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 ret = netiucv_add_files(dev);
1844 if (ret)
1845 goto out_unreg;
1846 priv->dev = dev;
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001847 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848 return 0;
1849
1850out_unreg:
1851 device_unregister(dev);
1852 return ret;
1853}
1854
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001855static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001857 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858 netiucv_remove_files(dev);
1859 device_unregister(dev);
1860}
1861
1862/**
1863 * Allocate and initialize a new connection structure.
1864 * Add it to the list of netiucv connections;
1865 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001866static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
1867 char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001869 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001871 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1872 if (!conn)
1873 goto out;
1874 skb_queue_head_init(&conn->collect_queue);
1875 skb_queue_head_init(&conn->commit_queue);
1876 spin_lock_init(&conn->collect_lock);
1877 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1878 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001880 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1881 if (!conn->rx_buff)
1882 goto out_conn;
1883 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1884 if (!conn->tx_buff)
1885 goto out_rx;
1886 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1887 conn_event_names, NR_CONN_STATES,
1888 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1889 GFP_KERNEL);
1890 if (!conn->fsm)
1891 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001893 fsm_settimer(conn->fsm, &conn->timer);
1894 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001896 if (username) {
1897 memcpy(conn->userid, username, 9);
1898 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001900
1901 write_lock_bh(&iucv_connection_rwlock);
1902 list_add_tail(&conn->list, &iucv_connection_list);
1903 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001905
1906out_tx:
1907 kfree_skb(conn->tx_buff);
1908out_rx:
1909 kfree_skb(conn->rx_buff);
1910out_conn:
1911 kfree(conn);
1912out:
1913 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914}
1915
1916/**
1917 * Release a connection structure and remove it from the
1918 * list of netiucv connections.
1919 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001920static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001922 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001923 write_lock_bh(&iucv_connection_rwlock);
1924 list_del_init(&conn->list);
1925 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001926 fsm_deltimer(&conn->timer);
1927 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001928 if (conn->path) {
1929 iucv_path_sever(conn->path, iucvMagic);
1930 kfree(conn->path);
1931 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001933 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001934 kfree_fsm(conn->fsm);
1935 kfree_skb(conn->rx_buff);
1936 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937}
1938
1939/**
1940 * Release everything of a net device.
1941 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001942static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001944 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001946 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947
1948 if (!dev)
1949 return;
1950
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 if (privptr) {
1952 if (privptr->conn)
1953 netiucv_remove_connection(privptr->conn);
1954 if (privptr->fsm)
1955 kfree_fsm(privptr->fsm);
1956 privptr->conn = NULL; privptr->fsm = NULL;
1957 /* privptr gets freed by free_netdev() */
1958 }
1959 free_netdev(dev);
1960}
1961
1962/**
1963 * Initialize a net device. (Called from kernel in alloc_netdev())
1964 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001965static const struct net_device_ops netiucv_netdev_ops = {
1966 .ndo_open = netiucv_open,
1967 .ndo_stop = netiucv_close,
1968 .ndo_get_stats = netiucv_stats,
1969 .ndo_start_xmit = netiucv_tx,
1970 .ndo_change_mtu = netiucv_change_mtu,
1971};
1972
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001973static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 dev->mtu = NETIUCV_MTU_DEFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976 dev->destructor = netiucv_free_netdevice;
1977 dev->hard_header_len = NETIUCV_HDRLEN;
1978 dev->addr_len = 0;
1979 dev->type = ARPHRD_SLIP;
1980 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1981 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001982 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983}
1984
1985/**
1986 * Allocate and initialize everything of a net device.
1987 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001988static struct net_device *netiucv_init_netdevice(char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989{
1990 struct netiucv_priv *privptr;
1991 struct net_device *dev;
1992
1993 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
1994 netiucv_setup_netdevice);
1995 if (!dev)
1996 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001998 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
2000 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
2001 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002002 if (!privptr->fsm)
2003 goto out_netdev;
2004
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 privptr->conn = netiucv_new_connection(dev, username);
2006 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002008 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 }
2010 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002012
2013out_fsm:
2014 kfree_fsm(privptr->fsm);
2015out_netdev:
2016 free_netdev(dev);
2017 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018}
2019
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002020static ssize_t conn_write(struct device_driver *drv,
2021 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002023 const char *p;
Frank Pavlic16a83b32006-09-15 16:25:19 +02002024 char username[9];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002025 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002027 struct netiucv_priv *priv;
2028 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002030 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 if (count>9) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
2033 return -EINVAL;
2034 }
2035
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002036 for (i = 0, p = buf; i < 8 && *p; i++, p++) {
2037 if (isalnum(*p) || *p == '$') {
2038 username[i] = toupper(*p);
2039 continue;
2040 }
2041 if (*p == '\n')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 /* trailing lf, grr */
2043 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002044 IUCV_DBF_TEXT_(setup, 2,
2045 "conn_write: invalid character %c\n", *p);
2046 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002048 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02002050 username[8] = '\0';
2051
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002052 read_lock_bh(&iucv_connection_rwlock);
2053 list_for_each_entry(cp, &iucv_connection_list, list) {
2054 if (!strncmp(username, cp->userid, 9)) {
2055 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braunf082bca2008-07-14 09:59:30 +02002056 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection "
2057 "to %s already exists\n", username);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002058 return -EEXIST;
2059 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02002060 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002061 read_unlock_bh(&iucv_connection_rwlock);
2062
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 dev = netiucv_init_netdevice(username);
2064 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
2066 return -ENODEV;
2067 }
2068
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002069 rc = netiucv_register_device(dev);
2070 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002072 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 goto out_free_ndev;
2074 }
2075
2076 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002077 priv = netdev_priv(dev);
2078 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002080 rc = register_netdev(dev);
2081 if (rc)
2082 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083
Ursula Braun8f7c5022008-12-25 13:39:47 +01002084 dev_info(priv->dev, "The IUCV interface to %s has been"
2085 " established successfully\n", netiucv_printname(username));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002086
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087 return count;
2088
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002089out_unreg:
2090 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002093 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094}
2095
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002096static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002098static ssize_t remove_write (struct device_driver *drv,
2099 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002101 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 struct net_device *ndev;
2103 struct netiucv_priv *priv;
2104 struct device *dev;
2105 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002106 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 int i;
2108
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002109 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110
2111 if (count >= IFNAMSIZ)
Joe Perchesa419aef2009-08-18 11:18:35 -07002112 count = IFNAMSIZ - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002114 for (i = 0, p = buf; i < count && *p; i++, p++) {
2115 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 /* trailing lf, grr */
2117 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002118 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 }
2120 name[i] = '\0';
2121
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002122 read_lock_bh(&iucv_connection_rwlock);
2123 list_for_each_entry(cp, &iucv_connection_list, list) {
2124 ndev = cp->netdev;
2125 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002127 if (strncmp(name, ndev->name, count))
2128 continue;
2129 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002131 dev_warn(dev, "The IUCV device is connected"
2132 " to %s and cannot be removed\n",
2133 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002135 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 }
2137 unregister_netdev(ndev);
2138 netiucv_unregister_device(dev);
2139 return count;
2140 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002141 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2143 return -EINVAL;
2144}
2145
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002146static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002148static struct attribute * netiucv_drv_attrs[] = {
2149 &driver_attr_connection.attr,
2150 &driver_attr_remove.attr,
2151 NULL,
2152};
2153
2154static struct attribute_group netiucv_drv_attr_group = {
2155 .attrs = netiucv_drv_attrs,
2156};
2157
David Brownella4dbd672009-06-24 10:06:31 -07002158static const struct attribute_group *netiucv_drv_attr_groups[] = {
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002159 &netiucv_drv_attr_group,
2160 NULL,
2161};
2162
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002163static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002165 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166}
2167
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002168static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002170 struct iucv_connection *cp;
2171 struct net_device *ndev;
2172 struct netiucv_priv *priv;
2173 struct device *dev;
2174
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002175 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002176 while (!list_empty(&iucv_connection_list)) {
2177 cp = list_entry(iucv_connection_list.next,
2178 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002179 ndev = cp->netdev;
2180 priv = netdev_priv(ndev);
2181 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182
2183 unregister_netdev(ndev);
2184 netiucv_unregister_device(dev);
2185 }
2186
Ursula Braun1175b252009-06-16 10:30:43 +02002187 device_unregister(netiucv_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002189 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 iucv_unregister_dbf_views();
2191
Ursula Braun8f7c5022008-12-25 13:39:47 +01002192 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193 return;
2194}
2195
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002196static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002198 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002199
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002200 rc = iucv_register_dbf_views();
2201 if (rc)
2202 goto out;
2203 rc = iucv_register(&netiucv_handler, 1);
2204 if (rc)
2205 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002206 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002207 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002208 rc = driver_register(&netiucv_driver);
2209 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002210 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2211 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 }
Ursula Braun1175b252009-06-16 10:30:43 +02002213 /* establish dummy device */
2214 netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
2215 if (!netiucv_dev) {
2216 rc = -ENOMEM;
2217 goto out_driver;
2218 }
2219 dev_set_name(netiucv_dev, "netiucv");
2220 netiucv_dev->bus = &iucv_bus;
2221 netiucv_dev->parent = iucv_root;
2222 netiucv_dev->release = (void (*)(struct device *))kfree;
2223 netiucv_dev->driver = &netiucv_driver;
2224 rc = device_register(netiucv_dev);
Sebastian Ottc6304932009-09-11 10:28:38 +02002225 if (rc) {
2226 put_device(netiucv_dev);
Ursula Braun1175b252009-06-16 10:30:43 +02002227 goto out_driver;
Sebastian Ottc6304932009-09-11 10:28:38 +02002228 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002229 netiucv_banner();
2230 return rc;
2231
Ursula Braun1175b252009-06-16 10:30:43 +02002232out_driver:
2233 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002234out_iucv:
2235 iucv_unregister(&netiucv_handler, 1);
2236out_dbf:
2237 iucv_unregister_dbf_views();
2238out:
2239 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002241
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242module_init(netiucv_init);
2243module_exit(netiucv_exit);
2244MODULE_LICENSE("GPL");