blob: d52a99f9b70220bef1c1369d6f13acfd4963a14c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * IUCV network driver
3 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08004 * Copyright 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
6 *
Cornelia Huck4ce3b302006-01-14 13:21:04 -08007 * Sysfs integration and all bugs therein by Cornelia Huck
8 * (cornelia.huck@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 *
10 * Documentation used:
11 * the source of the original IUCV driver by:
12 * Stefan Hegewald <hegewald@de.ibm.com>
13 * Hartmut Penner <hpenner@de.ibm.com>
14 * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
15 * Martin Schwidefsky (schwidefsky@de.ibm.com)
16 * Alan Altmark (Alan_Altmark@us.ibm.com) Sept. 2000
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2, or (at your option)
21 * any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070032 */
Jeff Garzike82b0f22006-05-26 21:58:38 -040033
Ursula Braun8f7c5022008-12-25 13:39:47 +010034#define KMSG_COMPONENT "netiucv"
35#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#undef DEBUG
38
39#include <linux/module.h>
40#include <linux/init.h>
41#include <linux/kernel.h>
42#include <linux/slab.h>
43#include <linux/errno.h>
44#include <linux/types.h>
45#include <linux/interrupt.h>
46#include <linux/timer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#include <linux/bitops.h>
48
49#include <linux/signal.h>
50#include <linux/string.h>
51#include <linux/device.h>
52
53#include <linux/ip.h>
54#include <linux/if_arp.h>
55#include <linux/tcp.h>
56#include <linux/skbuff.h>
57#include <linux/ctype.h>
58#include <net/dst.h>
59
60#include <asm/io.h>
61#include <asm/uaccess.h>
62
Martin Schwidefskyeebce382007-02-08 13:50:33 -080063#include <net/iucv/iucv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070064#include "fsm.h"
65
66MODULE_AUTHOR
67 ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)");
68MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
69
Martin Schwidefskyeebce382007-02-08 13:50:33 -080070/**
71 * Debug Facility stuff
72 */
73#define IUCV_DBF_SETUP_NAME "iucv_setup"
74#define IUCV_DBF_SETUP_LEN 32
75#define IUCV_DBF_SETUP_PAGES 2
76#define IUCV_DBF_SETUP_NR_AREAS 1
77#define IUCV_DBF_SETUP_LEVEL 3
78
79#define IUCV_DBF_DATA_NAME "iucv_data"
80#define IUCV_DBF_DATA_LEN 128
81#define IUCV_DBF_DATA_PAGES 2
82#define IUCV_DBF_DATA_NR_AREAS 1
83#define IUCV_DBF_DATA_LEVEL 2
84
85#define IUCV_DBF_TRACE_NAME "iucv_trace"
86#define IUCV_DBF_TRACE_LEN 16
87#define IUCV_DBF_TRACE_PAGES 4
88#define IUCV_DBF_TRACE_NR_AREAS 1
89#define IUCV_DBF_TRACE_LEVEL 3
90
91#define IUCV_DBF_TEXT(name,level,text) \
92 do { \
93 debug_text_event(iucv_dbf_##name,level,text); \
94 } while (0)
95
96#define IUCV_DBF_HEX(name,level,addr,len) \
97 do { \
98 debug_event(iucv_dbf_##name,level,(void*)(addr),len); \
99 } while (0)
100
101DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf);
102
Peter Tiedemannf33780d2008-02-08 13:09:05 +0100103/* Allow to sort out low debug levels early to avoid wasted sprints */
104static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
105{
106 return (level <= dbf_grp->level);
107}
108
109#define IUCV_DBF_TEXT_(name, level, text...) \
110 do { \
111 if (iucv_dbf_passes(iucv_dbf_##name, level)) { \
112 char* iucv_dbf_txt_buf = \
113 get_cpu_var(iucv_dbf_txt_buf); \
114 sprintf(iucv_dbf_txt_buf, text); \
115 debug_text_event(iucv_dbf_##name, level, \
116 iucv_dbf_txt_buf); \
117 put_cpu_var(iucv_dbf_txt_buf); \
118 } \
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800119 } while (0)
120
121#define IUCV_DBF_SPRINTF(name,level,text...) \
122 do { \
123 debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
124 debug_sprintf_event(iucv_dbf_trace, level, text ); \
125 } while (0)
126
127/**
128 * some more debug stuff
129 */
130#define IUCV_HEXDUMP16(importance,header,ptr) \
131PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
132 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
133 *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
134 *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
135 *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
136 *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
137 *(((char*)ptr)+12),*(((char*)ptr)+13), \
138 *(((char*)ptr)+14),*(((char*)ptr)+15)); \
139PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
140 "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
141 *(((char*)ptr)+16),*(((char*)ptr)+17), \
142 *(((char*)ptr)+18),*(((char*)ptr)+19), \
143 *(((char*)ptr)+20),*(((char*)ptr)+21), \
144 *(((char*)ptr)+22),*(((char*)ptr)+23), \
145 *(((char*)ptr)+24),*(((char*)ptr)+25), \
146 *(((char*)ptr)+26),*(((char*)ptr)+27), \
147 *(((char*)ptr)+28),*(((char*)ptr)+29), \
148 *(((char*)ptr)+30),*(((char*)ptr)+31));
149
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150#define PRINTK_HEADER " iucv: " /* for debugging */
151
152static struct device_driver netiucv_driver = {
Cornelia Huck22195102008-02-08 13:09:02 +0100153 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 .name = "netiucv",
155 .bus = &iucv_bus,
156};
157
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800158static int netiucv_callback_connreq(struct iucv_path *,
159 u8 ipvmid[8], u8 ipuser[16]);
160static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
161static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
162static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
163static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
164static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
165static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
166
167static struct iucv_handler netiucv_handler = {
168 .path_pending = netiucv_callback_connreq,
169 .path_complete = netiucv_callback_connack,
170 .path_severed = netiucv_callback_connrej,
171 .path_quiesced = netiucv_callback_connsusp,
172 .path_resumed = netiucv_callback_connres,
173 .message_pending = netiucv_callback_rx,
174 .message_complete = netiucv_callback_txdone
175};
176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177/**
178 * Per connection profiling data
179 */
180struct connection_profile {
181 unsigned long maxmulti;
182 unsigned long maxcqueue;
183 unsigned long doios_single;
184 unsigned long doios_multi;
185 unsigned long txlen;
186 unsigned long tx_time;
187 struct timespec send_stamp;
188 unsigned long tx_pending;
189 unsigned long tx_max_pending;
190};
191
192/**
193 * Representation of one iucv connection
194 */
195struct iucv_connection {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800196 struct list_head list;
197 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 struct sk_buff *rx_buff;
199 struct sk_buff *tx_buff;
200 struct sk_buff_head collect_queue;
201 struct sk_buff_head commit_queue;
202 spinlock_t collect_lock;
203 int collect_len;
204 int max_buffsize;
205 fsm_timer timer;
206 fsm_instance *fsm;
207 struct net_device *netdev;
208 struct connection_profile prof;
209 char userid[9];
210};
211
212/**
213 * Linked list of all connection structs.
214 */
Denis Chengc11ca972008-01-26 14:11:13 +0100215static LIST_HEAD(iucv_connection_list);
Thomas Gleixnerbfac0d02007-06-20 13:02:55 +0200216static DEFINE_RWLOCK(iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217
218/**
219 * Representation of event-data for the
220 * connection state machine.
221 */
222struct iucv_event {
223 struct iucv_connection *conn;
224 void *data;
225};
226
227/**
228 * Private part of the network device structure
229 */
230struct netiucv_priv {
231 struct net_device_stats stats;
232 unsigned long tbusy;
233 fsm_instance *fsm;
234 struct iucv_connection *conn;
235 struct device *dev;
236};
237
238/**
239 * Link level header for a packet.
240 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800241struct ll_header {
242 u16 next;
243};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800245#define NETIUCV_HDRLEN (sizeof(struct ll_header))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246#define NETIUCV_BUFSIZE_MAX 32768
247#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
248#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
249#define NETIUCV_MTU_DEFAULT 9216
250#define NETIUCV_QUEUELEN_DEFAULT 50
251#define NETIUCV_TIMEOUT_5SEC 5000
252
253/**
254 * Compatibility macros for busy handling
255 * of network devices.
256 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800257static inline void netiucv_clear_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800259 struct netiucv_priv *priv = netdev_priv(dev);
260 clear_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 netif_wake_queue(dev);
262}
263
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800264static inline int netiucv_test_and_set_busy(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800266 struct netiucv_priv *priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 netif_stop_queue(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800268 return test_and_set_bit(0, &priv->tbusy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269}
270
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800271static u8 iucvMagic[16] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
273 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
274};
275
276/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 * Convert an iucv userId to its printable
278 * form (strip whitespace at end).
279 *
280 * @param An iucv userId
281 *
282 * @returns The printable string (static data!!)
283 */
Martin Schwidefskyd4614622007-06-20 13:03:57 +0200284static char *netiucv_printname(char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285{
286 static char tmp[9];
287 char *p = tmp;
288 memcpy(tmp, name, 8);
289 tmp[8] = '\0';
290 while (*p && (!isspace(*p)))
291 p++;
292 *p = '\0';
293 return tmp;
294}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296/**
297 * States of the interface statemachine.
298 */
299enum dev_states {
300 DEV_STATE_STOPPED,
301 DEV_STATE_STARTWAIT,
302 DEV_STATE_STOPWAIT,
303 DEV_STATE_RUNNING,
304 /**
305 * MUST be always the last element!!
306 */
307 NR_DEV_STATES
308};
309
310static const char *dev_state_names[] = {
311 "Stopped",
312 "StartWait",
313 "StopWait",
314 "Running",
315};
316
317/**
318 * Events of the interface statemachine.
319 */
320enum dev_events {
321 DEV_EVENT_START,
322 DEV_EVENT_STOP,
323 DEV_EVENT_CONUP,
324 DEV_EVENT_CONDOWN,
325 /**
326 * MUST be always the last element!!
327 */
328 NR_DEV_EVENTS
329};
330
331static const char *dev_event_names[] = {
332 "Start",
333 "Stop",
334 "Connection up",
335 "Connection down",
336};
Jeff Garzike82b0f22006-05-26 21:58:38 -0400337
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338/**
339 * Events of the connection statemachine
340 */
341enum conn_events {
342 /**
343 * Events, representing callbacks from
344 * lowlevel iucv layer)
345 */
346 CONN_EVENT_CONN_REQ,
347 CONN_EVENT_CONN_ACK,
348 CONN_EVENT_CONN_REJ,
349 CONN_EVENT_CONN_SUS,
350 CONN_EVENT_CONN_RES,
351 CONN_EVENT_RX,
352 CONN_EVENT_TXDONE,
353
354 /**
355 * Events, representing errors return codes from
356 * calls to lowlevel iucv layer
357 */
358
359 /**
360 * Event, representing timer expiry.
361 */
362 CONN_EVENT_TIMER,
363
364 /**
365 * Events, representing commands from upper levels.
366 */
367 CONN_EVENT_START,
368 CONN_EVENT_STOP,
369
370 /**
371 * MUST be always the last element!!
372 */
373 NR_CONN_EVENTS,
374};
375
376static const char *conn_event_names[] = {
377 "Remote connection request",
378 "Remote connection acknowledge",
379 "Remote connection reject",
380 "Connection suspended",
381 "Connection resumed",
382 "Data received",
383 "Data sent",
384
385 "Timer",
386
387 "Start",
388 "Stop",
389};
390
391/**
392 * States of the connection statemachine.
393 */
394enum conn_states {
395 /**
396 * Connection not assigned to any device,
397 * initial state, invalid
398 */
399 CONN_STATE_INVALID,
400
401 /**
402 * Userid assigned but not operating
403 */
404 CONN_STATE_STOPPED,
405
406 /**
407 * Connection registered,
408 * no connection request sent yet,
409 * no connection request received
410 */
411 CONN_STATE_STARTWAIT,
412
413 /**
414 * Connection registered and connection request sent,
415 * no acknowledge and no connection request received yet.
416 */
417 CONN_STATE_SETUPWAIT,
418
419 /**
420 * Connection up and running idle
421 */
422 CONN_STATE_IDLE,
423
424 /**
425 * Data sent, awaiting CONN_EVENT_TXDONE
426 */
427 CONN_STATE_TX,
428
429 /**
430 * Error during registration.
431 */
432 CONN_STATE_REGERR,
433
434 /**
435 * Error during registration.
436 */
437 CONN_STATE_CONNERR,
438
439 /**
440 * MUST be always the last element!!
441 */
442 NR_CONN_STATES,
443};
444
445static const char *conn_state_names[] = {
446 "Invalid",
447 "Stopped",
448 "StartWait",
449 "SetupWait",
450 "Idle",
451 "TX",
452 "Terminating",
453 "Registration error",
454 "Connect error",
455};
456
Jeff Garzike82b0f22006-05-26 21:58:38 -0400457
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458/**
459 * Debug Facility Stuff
460 */
461static debug_info_t *iucv_dbf_setup = NULL;
462static debug_info_t *iucv_dbf_data = NULL;
463static debug_info_t *iucv_dbf_trace = NULL;
464
465DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
466
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800467static void iucv_unregister_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468{
469 if (iucv_dbf_setup)
470 debug_unregister(iucv_dbf_setup);
471 if (iucv_dbf_data)
472 debug_unregister(iucv_dbf_data);
473 if (iucv_dbf_trace)
474 debug_unregister(iucv_dbf_trace);
475}
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800476static int iucv_register_dbf_views(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
478 iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700479 IUCV_DBF_SETUP_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 IUCV_DBF_SETUP_NR_AREAS,
481 IUCV_DBF_SETUP_LEN);
482 iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700483 IUCV_DBF_DATA_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 IUCV_DBF_DATA_NR_AREAS,
485 IUCV_DBF_DATA_LEN);
486 iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME,
Michael Holzheu66a464d2005-06-25 14:55:33 -0700487 IUCV_DBF_TRACE_PAGES,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 IUCV_DBF_TRACE_NR_AREAS,
489 IUCV_DBF_TRACE_LEN);
490
491 if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) ||
492 (iucv_dbf_trace == NULL)) {
493 iucv_unregister_dbf_views();
494 return -ENOMEM;
495 }
496 debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view);
497 debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL);
498
499 debug_register_view(iucv_dbf_data, &debug_hex_ascii_view);
500 debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL);
501
502 debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view);
503 debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL);
504
505 return 0;
506}
507
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800508/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 * Callback-wrappers, called from lowlevel iucv layer.
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800510 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800512static void netiucv_callback_rx(struct iucv_path *path,
513 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800515 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 struct iucv_event ev;
517
518 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800519 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 fsm_event(conn->fsm, CONN_EVENT_RX, &ev);
521}
522
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800523static void netiucv_callback_txdone(struct iucv_path *path,
524 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800526 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 struct iucv_event ev;
528
529 ev.conn = conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800530 ev.data = msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev);
532}
533
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800534static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800536 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800538 fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539}
540
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800541static int netiucv_callback_connreq(struct iucv_path *path,
542 u8 ipvmid[8], u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800544 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 struct iucv_event ev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800546 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800548 if (memcmp(iucvMagic, ipuser, sizeof(ipuser)))
549 /* ipuser must match iucvMagic. */
550 return -EINVAL;
551 rc = -EINVAL;
552 read_lock_bh(&iucv_connection_rwlock);
553 list_for_each_entry(conn, &iucv_connection_list, list) {
554 if (strncmp(ipvmid, conn->userid, 8))
555 continue;
556 /* Found a matching connection for this path. */
557 conn->path = path;
558 ev.conn = conn;
559 ev.data = path;
560 fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
561 rc = 0;
562 }
563 read_unlock_bh(&iucv_connection_rwlock);
564 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800567static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800569 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800571 fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
573
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800574static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800576 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800578 fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579}
580
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800581static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800583 struct iucv_connection *conn = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800585 fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586}
587
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588/**
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100589 * NOP action for statemachines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 */
Ursula Braun21b26f2f2008-02-08 13:09:03 +0100591static void netiucv_action_nop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
593}
Jeff Garzike82b0f22006-05-26 21:58:38 -0400594
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800595/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 * Actions of the connection statemachine
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800597 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
599/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800600 * netiucv_unpack_skb
601 * @conn: The connection where this skb has been received.
602 * @pskb: The received skb.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800604 * Unpack a just received skb and hand it over to upper layers.
605 * Helper function for conn_action_rx.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800607static void netiucv_unpack_skb(struct iucv_connection *conn,
608 struct sk_buff *pskb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609{
610 struct net_device *dev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800611 struct netiucv_priv *privptr = netdev_priv(dev);
612 u16 offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
614 skb_put(pskb, NETIUCV_HDRLEN);
615 pskb->dev = dev;
616 pskb->ip_summed = CHECKSUM_NONE;
617 pskb->protocol = ntohs(ETH_P_IP);
618
619 while (1) {
620 struct sk_buff *skb;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800621 struct ll_header *header = (struct ll_header *) pskb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
623 if (!header->next)
624 break;
625
626 skb_pull(pskb, NETIUCV_HDRLEN);
627 header->next -= offset;
628 offset += header->next;
629 header->next -= NETIUCV_HDRLEN;
630 if (skb_tailroom(pskb) < header->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
632 header->next, skb_tailroom(pskb));
633 return;
634 }
635 skb_put(pskb, header->next);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700636 skb_reset_mac_header(pskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 skb = dev_alloc_skb(pskb->len);
638 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 IUCV_DBF_TEXT(data, 2,
640 "Out of memory in netiucv_unpack_skb\n");
641 privptr->stats.rx_dropped++;
642 return;
643 }
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300644 skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
645 pskb->len);
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700646 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 skb->dev = pskb->dev;
648 skb->protocol = pskb->protocol;
649 pskb->ip_summed = CHECKSUM_UNNECESSARY;
Julia Lawall9b3efc02007-12-10 17:17:37 -0800650 privptr->stats.rx_packets++;
651 privptr->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 /*
653 * Since receiving is always initiated from a tasklet (in iucv.c),
654 * we must use netif_rx_ni() instead of netif_rx()
655 */
656 netif_rx_ni(skb);
657 dev->last_rx = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 skb_pull(pskb, header->next);
659 skb_put(pskb, NETIUCV_HDRLEN);
660 }
661}
662
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800663static void conn_action_rx(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800665 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800667 struct iucv_message *msg = ev->data;
668 struct netiucv_priv *privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 int rc;
670
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200671 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
673 if (!conn->netdev) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800674 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800676 "Received data for unlinked connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 return;
678 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800679 if (msg->length > conn->max_buffsize) {
680 iucv_message_reject(conn->path, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 privptr->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800683 msg->length, conn->max_buffsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 return;
685 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700686 conn->rx_buff->data = conn->rx_buff->head;
687 skb_reset_tail_pointer(conn->rx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 conn->rx_buff->len = 0;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800689 rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
690 msg->length, NULL);
691 if (rc || msg->length < 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 privptr->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
694 return;
695 }
696 netiucv_unpack_skb(conn, conn->rx_buff);
697}
698
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800699static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800701 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800703 struct iucv_message *msg = ev->data;
704 struct iucv_message txmsg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 struct netiucv_priv *privptr = NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800706 u32 single_flag = msg->tag;
707 u32 txbytes = 0;
708 u32 txpackets = 0;
709 u32 stat_maxcq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 struct sk_buff *skb;
711 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800712 struct ll_header header;
713 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200715 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800717 if (conn && conn->netdev)
718 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 conn->prof.tx_pending--;
720 if (single_flag) {
721 if ((skb = skb_dequeue(&conn->commit_queue))) {
722 atomic_dec(&skb->users);
723 dev_kfree_skb_any(skb);
724 if (privptr) {
725 privptr->stats.tx_packets++;
726 privptr->stats.tx_bytes +=
727 (skb->len - NETIUCV_HDRLEN
728 - NETIUCV_HDRLEN);
729 }
730 }
731 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700732 conn->tx_buff->data = conn->tx_buff->head;
733 skb_reset_tail_pointer(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 conn->tx_buff->len = 0;
735 spin_lock_irqsave(&conn->collect_lock, saveflags);
736 while ((skb = skb_dequeue(&conn->collect_queue))) {
737 header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
738 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
739 NETIUCV_HDRLEN);
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300740 skb_copy_from_linear_data(skb,
741 skb_put(conn->tx_buff, skb->len),
742 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 txbytes += skb->len;
744 txpackets++;
745 stat_maxcq++;
746 atomic_dec(&skb->users);
747 dev_kfree_skb_any(skb);
748 }
749 if (conn->collect_len > conn->prof.maxmulti)
750 conn->prof.maxmulti = conn->collect_len;
751 conn->collect_len = 0;
752 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800753 if (conn->tx_buff->len == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800755 return;
756 }
757
758 header.next = 0;
759 memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
john stultz2c6b47d2007-07-24 17:47:43 -0700760 conn->prof.send_stamp = current_kernel_time();
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800761 txmsg.class = 0;
762 txmsg.tag = 0;
763 rc = iucv_message_send(conn->path, &txmsg, 0, 0,
764 conn->tx_buff->data, conn->tx_buff->len);
765 conn->prof.doios_multi++;
766 conn->prof.txlen += conn->tx_buff->len;
767 conn->prof.tx_pending++;
768 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
769 conn->prof.tx_max_pending = conn->prof.tx_pending;
770 if (rc) {
771 conn->prof.tx_pending--;
772 fsm_newstate(fi, CONN_STATE_IDLE);
773 if (privptr)
774 privptr->stats.tx_errors += txpackets;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800775 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
776 } else {
777 if (privptr) {
778 privptr->stats.tx_packets += txpackets;
779 privptr->stats.tx_bytes += txbytes;
780 }
781 if (stat_maxcq > conn->prof.maxcqueue)
782 conn->prof.maxcqueue = stat_maxcq;
783 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784}
785
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800786static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800788 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 struct iucv_connection *conn = ev->conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800790 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800792 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200795 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800797 conn->path = path;
798 path->msglim = NETIUCV_QUEUELEN_DEFAULT;
799 path->flags = 0;
800 rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
803 return;
804 }
805 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800806 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
808}
809
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800810static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800812 struct iucv_event *ev = arg;
813 struct iucv_path *path = ev->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200815 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800816 iucv_path_sever(path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817}
818
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800819static void conn_action_connack(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800821 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800823 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200825 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 fsm_deltimer(&conn->timer);
827 fsm_newstate(fi, CONN_STATE_IDLE);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800828 netdev->tx_queue_len = conn->path->msglim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);
830}
831
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800832static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800834 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200836 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800838 iucv_path_sever(conn->path, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 fsm_newstate(fi, CONN_STATE_STARTWAIT);
840}
841
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800842static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800844 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800846 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200848 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849
850 fsm_deltimer(&conn->timer);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800851 iucv_path_sever(conn->path, NULL);
Ursula Braun8f7c5022008-12-25 13:39:47 +0100852 dev_info(privptr->dev, "The peer interface of the IUCV device"
853 " has closed the connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800855 "conn_action_connsever: Remote dropped connection\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 fsm_newstate(fi, CONN_STATE_STARTWAIT);
857 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
858}
859
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800860static void conn_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800862 struct iucv_connection *conn = arg;
Ursula Braun8f7c5022008-12-25 13:39:47 +0100863 struct net_device *netdev = conn->netdev;
864 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 int rc;
866
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200867 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800869 fsm_newstate(fi, CONN_STATE_STARTWAIT);
Ursula Braunf082bca2008-07-14 09:59:30 +0200870 IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n",
Ursula Braun8f7c5022008-12-25 13:39:47 +0100871 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800873 /*
874 * We must set the state before calling iucv_connect because the
875 * callback handler could be called at any point after the connection
876 * request is sent
877 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
879 fsm_newstate(fi, CONN_STATE_SETUPWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800880 conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
881 rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
882 NULL, iucvMagic, conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 switch (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800884 case 0:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100885 netdev->tx_queue_len = conn->path->msglim;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800886 fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
887 CONN_EVENT_TIMER, conn);
888 return;
889 case 11:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100890 dev_warn(privptr->dev,
891 "The IUCV device failed to connect to z/VM guest %s\n",
892 netiucv_printname(conn->userid));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800893 fsm_newstate(fi, CONN_STATE_STARTWAIT);
894 break;
895 case 12:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100896 dev_warn(privptr->dev,
897 "The IUCV device failed to connect to the peer on z/VM"
898 " guest %s\n", netiucv_printname(conn->userid));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800899 fsm_newstate(fi, CONN_STATE_STARTWAIT);
900 break;
901 case 13:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100902 dev_err(privptr->dev,
903 "Connecting the IUCV device would exceed the maximum"
904 " number of IUCV connections\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800905 fsm_newstate(fi, CONN_STATE_CONNERR);
906 break;
907 case 14:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100908 dev_err(privptr->dev,
909 "z/VM guest %s has too many IUCV connections"
910 " to connect with the IUCV device\n",
911 netiucv_printname(conn->userid));
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800912 fsm_newstate(fi, CONN_STATE_CONNERR);
913 break;
914 case 15:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100915 dev_err(privptr->dev,
916 "The IUCV device cannot connect to a z/VM guest with no"
917 " IUCV authorization\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800918 fsm_newstate(fi, CONN_STATE_CONNERR);
919 break;
920 default:
Ursula Braun8f7c5022008-12-25 13:39:47 +0100921 dev_err(privptr->dev,
922 "Connecting the IUCV device failed with error %d\n",
923 rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800924 fsm_newstate(fi, CONN_STATE_CONNERR);
925 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 }
927 IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800928 kfree(conn->path);
929 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930}
931
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800932static void netiucv_purge_skb_queue(struct sk_buff_head *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933{
934 struct sk_buff *skb;
935
936 while ((skb = skb_dequeue(q))) {
937 atomic_dec(&skb->users);
938 dev_kfree_skb_any(skb);
939 }
940}
941
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800942static void conn_action_stop(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800944 struct iucv_event *ev = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 struct iucv_connection *conn = ev->conn;
946 struct net_device *netdev = conn->netdev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800947 struct netiucv_priv *privptr = netdev_priv(netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +0200949 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950
951 fsm_deltimer(&conn->timer);
952 fsm_newstate(fi, CONN_STATE_STOPPED);
953 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800954 if (conn->path) {
955 IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
956 iucv_path_sever(conn->path, iucvMagic);
957 kfree(conn->path);
958 conn->path = NULL;
959 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 netiucv_purge_skb_queue(&conn->commit_queue);
961 fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
962}
963
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800964static void conn_action_inval(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965{
Martin Schwidefskyeebce382007-02-08 13:50:33 -0800966 struct iucv_connection *conn = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 struct net_device *netdev = conn->netdev;
968
Ursula Braunf082bca2008-07-14 09:59:30 +0200969 IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
970 netdev->name, conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971}
972
973static const fsm_node conn_fsm[] = {
974 { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
975 { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
976
977 { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
978 { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
979 { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop },
980 { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop },
981 { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop },
982 { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop },
983 { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop },
984
985 { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject },
986 { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
987 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },
988 { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject },
989 { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
990
991 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
992 { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
993
994 { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
995 { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
996 { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever },
997
998 { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx },
999 { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx },
1000
1001 { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone },
1002 { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone },
1003};
1004
1005static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);
1006
Jeff Garzike82b0f22006-05-26 21:58:38 -04001007
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001008/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 * Actions for interface - statemachine.
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001010 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011
1012/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001013 * dev_action_start
1014 * @fi: An instance of an interface statemachine.
1015 * @event: The event, just happened.
1016 * @arg: Generic pointer, casted from struct net_device * upon call.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001018 * Startup connection by sending CONN_EVENT_START to it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001020static void dev_action_start(fsm_instance *fi, int event, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001022 struct net_device *dev = arg;
1023 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001025 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 fsm_newstate(fi, DEV_STATE_STARTWAIT);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001028 fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029}
1030
1031/**
1032 * Shutdown connection by sending CONN_EVENT_STOP to it.
1033 *
1034 * @param fi An instance of an interface statemachine.
1035 * @param event The event, just happened.
1036 * @param arg Generic pointer, casted from struct net_device * upon call.
1037 */
1038static void
1039dev_action_stop(fsm_instance *fi, int event, void *arg)
1040{
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 struct iucv_event ev;
1044
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001045 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046
1047 ev.conn = privptr->conn;
1048
1049 fsm_newstate(fi, DEV_STATE_STOPWAIT);
1050 fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
1051}
1052
1053/**
1054 * Called from connection statemachine
1055 * when a connection is up and running.
1056 *
1057 * @param fi An instance of an interface statemachine.
1058 * @param event The event, just happened.
1059 * @param arg Generic pointer, casted from struct net_device * upon call.
1060 */
1061static void
1062dev_action_connup(fsm_instance *fi, int event, void *arg)
1063{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001064 struct net_device *dev = arg;
1065 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001067 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068
1069 switch (fsm_getstate(fi)) {
1070 case DEV_STATE_STARTWAIT:
1071 fsm_newstate(fi, DEV_STATE_RUNNING);
Ursula Braun8f7c5022008-12-25 13:39:47 +01001072 dev_info(privptr->dev,
1073 "The IUCV device has been connected"
1074 " successfully to %s\n", privptr->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 IUCV_DBF_TEXT(setup, 3,
1076 "connection is up and running\n");
1077 break;
1078 case DEV_STATE_STOPWAIT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 IUCV_DBF_TEXT(data, 2,
1080 "dev_action_connup: in DEV_STATE_STOPWAIT\n");
1081 break;
1082 }
1083}
1084
1085/**
1086 * Called from connection statemachine
1087 * when a connection has been shutdown.
1088 *
1089 * @param fi An instance of an interface statemachine.
1090 * @param event The event, just happened.
1091 * @param arg Generic pointer, casted from struct net_device * upon call.
1092 */
1093static void
1094dev_action_conndown(fsm_instance *fi, int event, void *arg)
1095{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001096 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097
1098 switch (fsm_getstate(fi)) {
1099 case DEV_STATE_RUNNING:
1100 fsm_newstate(fi, DEV_STATE_STARTWAIT);
1101 break;
1102 case DEV_STATE_STOPWAIT:
1103 fsm_newstate(fi, DEV_STATE_STOPPED);
1104 IUCV_DBF_TEXT(setup, 3, "connection is down\n");
1105 break;
1106 }
1107}
1108
1109static const fsm_node dev_fsm[] = {
1110 { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
1111
1112 { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start },
1113 { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
1114
1115 { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
1116 { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
1117
1118 { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
1119 { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
Ursula Braun21b26f2f2008-02-08 13:09:03 +01001120 { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121};
1122
1123static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
1124
1125/**
1126 * Transmit a packet.
1127 * This is a helper function for netiucv_tx().
1128 *
1129 * @param conn Connection to be used for sending.
1130 * @param skb Pointer to struct sk_buff of packet to send.
1131 * The linklevel header has already been set up
1132 * by netiucv_tx().
1133 *
1134 * @return 0 on success, -ERRNO on failure. (Never fails.)
1135 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001136static int netiucv_transmit_skb(struct iucv_connection *conn,
1137 struct sk_buff *skb)
1138{
1139 struct iucv_message msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 unsigned long saveflags;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001141 struct ll_header header;
1142 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143
1144 if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {
1145 int l = skb->len + NETIUCV_HDRLEN;
1146
1147 spin_lock_irqsave(&conn->collect_lock, saveflags);
1148 if (conn->collect_len + l >
1149 (conn->max_buffsize - NETIUCV_HDRLEN)) {
1150 rc = -EBUSY;
1151 IUCV_DBF_TEXT(data, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001152 "EBUSY from netiucv_transmit_skb\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 } else {
1154 atomic_inc(&skb->users);
1155 skb_queue_tail(&conn->collect_queue, skb);
1156 conn->collect_len += l;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001157 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 }
1159 spin_unlock_irqrestore(&conn->collect_lock, saveflags);
1160 } else {
1161 struct sk_buff *nskb = skb;
1162 /**
1163 * Copy the skb to a new allocated skb in lowmem only if the
1164 * data is located above 2G in memory or tailroom is < 2.
1165 */
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001166 unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
1167 NETIUCV_HDRLEN)) >> 31;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 int copied = 0;
1169 if (hi || (skb_tailroom(skb) < 2)) {
1170 nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
1171 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
1172 if (!nskb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
1174 rc = -ENOMEM;
1175 return rc;
1176 } else {
1177 skb_reserve(nskb, NETIUCV_HDRLEN);
1178 memcpy(skb_put(nskb, skb->len),
1179 skb->data, skb->len);
1180 }
1181 copied = 1;
1182 }
1183 /**
1184 * skb now is below 2G and has enough room. Add headers.
1185 */
1186 header.next = nskb->len + NETIUCV_HDRLEN;
1187 memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1188 header.next = 0;
1189 memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
1190
1191 fsm_newstate(conn->fsm, CONN_STATE_TX);
john stultz2c6b47d2007-07-24 17:47:43 -07001192 conn->prof.send_stamp = current_kernel_time();
Jeff Garzike82b0f22006-05-26 21:58:38 -04001193
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001194 msg.tag = 1;
1195 msg.class = 0;
1196 rc = iucv_message_send(conn->path, &msg, 0, 0,
1197 nskb->data, nskb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 conn->prof.doios_single++;
1199 conn->prof.txlen += skb->len;
1200 conn->prof.tx_pending++;
1201 if (conn->prof.tx_pending > conn->prof.tx_max_pending)
1202 conn->prof.tx_max_pending = conn->prof.tx_pending;
1203 if (rc) {
1204 struct netiucv_priv *privptr;
1205 fsm_newstate(conn->fsm, CONN_STATE_IDLE);
1206 conn->prof.tx_pending--;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001207 privptr = netdev_priv(conn->netdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 if (privptr)
1209 privptr->stats.tx_errors++;
1210 if (copied)
1211 dev_kfree_skb(nskb);
1212 else {
1213 /**
1214 * Remove our headers. They get added
1215 * again on retransmit.
1216 */
1217 skb_pull(skb, NETIUCV_HDRLEN);
1218 skb_trim(skb, skb->len - NETIUCV_HDRLEN);
1219 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
1221 } else {
1222 if (copied)
1223 dev_kfree_skb(skb);
1224 atomic_inc(&nskb->users);
1225 skb_queue_tail(&conn->commit_queue, nskb);
1226 }
1227 }
1228
1229 return rc;
1230}
Jeff Garzike82b0f22006-05-26 21:58:38 -04001231
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001232/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 * Interface API for upper network layers
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001234 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
1236/**
1237 * Open an interface.
1238 * Called from generic network layer when ifconfig up is run.
1239 *
1240 * @param dev Pointer to interface struct.
1241 *
1242 * @return 0 on success, -ERRNO on failure. (Never fails.)
1243 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001244static int netiucv_open(struct net_device *dev)
1245{
1246 struct netiucv_priv *priv = netdev_priv(dev);
1247
1248 fsm_event(priv->fsm, DEV_EVENT_START, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 return 0;
1250}
1251
1252/**
1253 * Close an interface.
1254 * Called from generic network layer when ifconfig down is run.
1255 *
1256 * @param dev Pointer to interface struct.
1257 *
1258 * @return 0 on success, -ERRNO on failure. (Never fails.)
1259 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001260static int netiucv_close(struct net_device *dev)
1261{
1262 struct netiucv_priv *priv = netdev_priv(dev);
1263
1264 fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 return 0;
1266}
1267
1268/**
1269 * Start transmission of a packet.
1270 * Called from generic network device layer.
1271 *
1272 * @param skb Pointer to buffer containing the packet.
1273 * @param dev Pointer to interface struct.
1274 *
1275 * @return 0 if packet consumed, !0 if packet rejected.
1276 * Note: If we return !0, then the packet is free'd by
1277 * the generic network layer.
1278 */
1279static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
1280{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001281 struct netiucv_priv *privptr = netdev_priv(dev);
1282 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001284 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 /**
1286 * Some sanity checks ...
1287 */
1288 if (skb == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
1290 privptr->stats.tx_dropped++;
1291 return 0;
1292 }
1293 if (skb_headroom(skb) < NETIUCV_HDRLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 IUCV_DBF_TEXT(data, 2,
1295 "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
1296 dev_kfree_skb(skb);
1297 privptr->stats.tx_dropped++;
1298 return 0;
1299 }
1300
1301 /**
1302 * If connection is not running, try to restart it
Jeff Garzike82b0f22006-05-26 21:58:38 -04001303 * and throw away packet.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 */
1305 if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 dev_kfree_skb(skb);
1307 privptr->stats.tx_dropped++;
1308 privptr->stats.tx_errors++;
1309 privptr->stats.tx_carrier_errors++;
1310 return 0;
1311 }
1312
1313 if (netiucv_test_and_set_busy(dev)) {
1314 IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
Ursula Braun4e584d62009-03-24 03:27:45 +00001315 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 }
1317 dev->trans_start = jiffies;
Patrick McHardy5b548142009-06-12 06:22:29 +00001318 rc = netiucv_transmit_skb(privptr->conn, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 netiucv_clear_busy(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +00001320 return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321}
1322
1323/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001324 * netiucv_stats
1325 * @dev: Pointer to interface struct.
1326 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 * Returns interface statistics of a device.
1328 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001329 * Returns pointer to stats struct of this interface.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001331static struct net_device_stats *netiucv_stats (struct net_device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001333 struct netiucv_priv *priv = netdev_priv(dev);
1334
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001335 IUCV_DBF_TEXT(trace, 5, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001336 return &priv->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337}
1338
1339/**
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001340 * netiucv_change_mtu
1341 * @dev: Pointer to interface struct.
1342 * @new_mtu: The new MTU to use for this interface.
1343 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 * Sets MTU of an interface.
1345 *
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001346 * Returns 0 on success, -EINVAL if MTU is out of valid range.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 * (valid range is 576 .. NETIUCV_MTU_MAX).
1348 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001349static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001351 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001352 if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
1354 return -EINVAL;
1355 }
1356 dev->mtu = new_mtu;
1357 return 0;
1358}
1359
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001360/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 * attributes in sysfs
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001362 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001364static ssize_t user_show(struct device *dev, struct device_attribute *attr,
1365 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001367 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001369 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));
1371}
1372
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001373static ssize_t user_write(struct device *dev, struct device_attribute *attr,
1374 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001376 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 struct net_device *ndev = priv->conn->netdev;
1378 char *p;
1379 char *tmp;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001380 char username[9];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 int i;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001382 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001384 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001385 if (count > 9) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001387 "%d is length of username\n", (int) count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 return -EINVAL;
1389 }
1390
1391 tmp = strsep((char **) &buf, "\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001392 for (i = 0, p = tmp; i < 8 && *p; i++, p++) {
1393 if (isalnum(*p) || (*p == '$')) {
Frank Pavlic16a83b32006-09-15 16:25:19 +02001394 username[i]= toupper(*p);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001395 continue;
1396 }
1397 if (*p == '\n') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 /* trailing lf, grr */
1399 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001401 IUCV_DBF_TEXT_(setup, 2,
1402 "username: invalid character %c\n", *p);
1403 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001405 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001407 username[8] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001409 if (memcmp(username, priv->conn->userid, 9) &&
1410 (ndev->flags & (IFF_UP | IFF_RUNNING))) {
1411 /* username changed while the interface is active. */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001412 IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02001413 return -EPERM;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001414 }
1415 read_lock_bh(&iucv_connection_rwlock);
1416 list_for_each_entry(cp, &iucv_connection_list, list) {
1417 if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
1418 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braunf082bca2008-07-14 09:59:30 +02001419 IUCV_DBF_TEXT_(setup, 2, "user_write: Connection "
1420 "to %s already exists\n", username);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001421 return -EEXIST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 }
1423 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001424 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 memcpy(priv->conn->userid, username, 9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427}
1428
1429static DEVICE_ATTR(user, 0644, user_show, user_write);
1430
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001431static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
1432 char *buf)
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001433{
1434 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001436 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 return sprintf(buf, "%d\n", priv->conn->max_buffsize);
1438}
1439
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001440static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
1441 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001443 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 struct net_device *ndev = priv->conn->netdev;
1445 char *e;
1446 int bs1;
1447
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001448 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 if (count >= 39)
1450 return -EINVAL;
1451
1452 bs1 = simple_strtoul(buf, &e, 0);
1453
1454 if (e && (!isspace(*e))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
1456 return -EINVAL;
1457 }
1458 if (bs1 > NETIUCV_BUFSIZE_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 IUCV_DBF_TEXT_(setup, 2,
1460 "buffer_write: buffer size %d too large\n",
1461 bs1);
1462 return -EINVAL;
1463 }
1464 if ((ndev->flags & IFF_RUNNING) &&
1465 (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 IUCV_DBF_TEXT_(setup, 2,
1467 "buffer_write: buffer size %d too small\n",
1468 bs1);
1469 return -EINVAL;
1470 }
1471 if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 IUCV_DBF_TEXT_(setup, 2,
1473 "buffer_write: buffer size %d too small\n",
1474 bs1);
1475 return -EINVAL;
1476 }
1477
1478 priv->conn->max_buffsize = bs1;
1479 if (!(ndev->flags & IFF_RUNNING))
1480 ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
1481
1482 return count;
1483
1484}
1485
1486static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
1487
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001488static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
1489 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001491 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001493 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
1495}
1496
1497static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
1498
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001499static ssize_t conn_fsm_show (struct device *dev,
1500 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001502 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001504 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
1506}
1507
1508static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
1509
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001510static ssize_t maxmulti_show (struct device *dev,
1511 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001513 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001515 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
1517}
1518
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001519static ssize_t maxmulti_write (struct device *dev,
1520 struct device_attribute *attr,
1521 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001523 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001525 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 priv->conn->prof.maxmulti = 0;
1527 return count;
1528}
1529
1530static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
1531
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001532static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
1533 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001535 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001537 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
1539}
1540
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001541static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
1542 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001544 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001545
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001546 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 priv->conn->prof.maxcqueue = 0;
1548 return count;
1549}
1550
1551static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
1552
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001553static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
1554 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001556 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001558 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
1560}
1561
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001562static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
1563 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001565 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001566
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001567 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 priv->conn->prof.doios_single = 0;
1569 return count;
1570}
1571
1572static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
1573
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001574static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
1575 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001577 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001579 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
1581}
1582
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001583static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
1584 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001586 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001587
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001588 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 priv->conn->prof.doios_multi = 0;
1590 return count;
1591}
1592
1593static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
1594
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001595static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
1596 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.txlen);
1602}
1603
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001604static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
1605 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001607 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001608
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001609 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 priv->conn->prof.txlen = 0;
1611 return count;
1612}
1613
1614static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
1615
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001616static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
1617 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001619 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001621 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
1623}
1624
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001625static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
1626 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001628 struct netiucv_priv *priv = dev_get_drvdata(dev);
Jeff Garzike82b0f22006-05-26 21:58:38 -04001629
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001630 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 priv->conn->prof.tx_time = 0;
1632 return count;
1633}
1634
1635static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
1636
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001637static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
1638 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001640 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001642 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
1644}
1645
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001646static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
1647 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001649 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001651 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 priv->conn->prof.tx_pending = 0;
1653 return count;
1654}
1655
1656static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
1657
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001658static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
1659 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001661 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001663 IUCV_DBF_TEXT(trace, 5, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
1665}
1666
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001667static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
1668 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001670 struct netiucv_priv *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001672 IUCV_DBF_TEXT(trace, 4, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 priv->conn->prof.tx_max_pending = 0;
1674 return count;
1675}
1676
1677static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);
1678
1679static struct attribute *netiucv_attrs[] = {
1680 &dev_attr_buffer.attr,
1681 &dev_attr_user.attr,
1682 NULL,
1683};
1684
1685static struct attribute_group netiucv_attr_group = {
1686 .attrs = netiucv_attrs,
1687};
1688
1689static struct attribute *netiucv_stat_attrs[] = {
1690 &dev_attr_device_fsm_state.attr,
1691 &dev_attr_connection_fsm_state.attr,
1692 &dev_attr_max_tx_buffer_used.attr,
1693 &dev_attr_max_chained_skbs.attr,
1694 &dev_attr_tx_single_write_ops.attr,
1695 &dev_attr_tx_multi_write_ops.attr,
1696 &dev_attr_netto_bytes.attr,
1697 &dev_attr_max_tx_io_time.attr,
1698 &dev_attr_tx_pending.attr,
1699 &dev_attr_tx_max_pending.attr,
1700 NULL,
1701};
1702
1703static struct attribute_group netiucv_stat_attr_group = {
1704 .name = "stats",
1705 .attrs = netiucv_stat_attrs,
1706};
1707
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001708static int netiucv_add_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709{
1710 int ret;
1711
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001712 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group);
1714 if (ret)
1715 return ret;
1716 ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group);
1717 if (ret)
1718 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1719 return ret;
1720}
1721
Martin Schwidefskyd4614622007-06-20 13:03:57 +02001722static void netiucv_remove_files(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001724 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group);
1726 sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
1727}
1728
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001729static int netiucv_register_device(struct net_device *ndev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001731 struct netiucv_priv *priv = netdev_priv(ndev);
Eric Sesterhenn88abaab2006-03-24 03:15:31 -08001732 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 int ret;
1734
1735
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001736 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737
1738 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +02001739 dev_set_name(dev, "net%s", ndev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740 dev->bus = &iucv_bus;
1741 dev->parent = iucv_root;
1742 /*
1743 * The release function could be called after the
1744 * module has been unloaded. It's _only_ task is to
1745 * free the struct. Therefore, we specify kfree()
1746 * directly here. (Probably a little bit obfuscating
1747 * but legitime ...).
1748 */
1749 dev->release = (void (*)(struct device *))kfree;
1750 dev->driver = &netiucv_driver;
1751 } else
1752 return -ENOMEM;
1753
1754 ret = device_register(dev);
1755
1756 if (ret)
1757 return ret;
1758 ret = netiucv_add_files(dev);
1759 if (ret)
1760 goto out_unreg;
1761 priv->dev = dev;
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -07001762 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 return 0;
1764
1765out_unreg:
1766 device_unregister(dev);
1767 return ret;
1768}
1769
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001770static void netiucv_unregister_device(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001772 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 netiucv_remove_files(dev);
1774 device_unregister(dev);
1775}
1776
1777/**
1778 * Allocate and initialize a new connection structure.
1779 * Add it to the list of netiucv connections;
1780 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001781static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
1782 char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001784 struct iucv_connection *conn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001786 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
1787 if (!conn)
1788 goto out;
1789 skb_queue_head_init(&conn->collect_queue);
1790 skb_queue_head_init(&conn->commit_queue);
1791 spin_lock_init(&conn->collect_lock);
1792 conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
1793 conn->netdev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001795 conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1796 if (!conn->rx_buff)
1797 goto out_conn;
1798 conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA);
1799 if (!conn->tx_buff)
1800 goto out_rx;
1801 conn->fsm = init_fsm("netiucvconn", conn_state_names,
1802 conn_event_names, NR_CONN_STATES,
1803 NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN,
1804 GFP_KERNEL);
1805 if (!conn->fsm)
1806 goto out_tx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001808 fsm_settimer(conn->fsm, &conn->timer);
1809 fsm_newstate(conn->fsm, CONN_STATE_INVALID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001811 if (username) {
1812 memcpy(conn->userid, username, 9);
1813 fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001815
1816 write_lock_bh(&iucv_connection_rwlock);
1817 list_add_tail(&conn->list, &iucv_connection_list);
1818 write_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 return conn;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001820
1821out_tx:
1822 kfree_skb(conn->tx_buff);
1823out_rx:
1824 kfree_skb(conn->rx_buff);
1825out_conn:
1826 kfree(conn);
1827out:
1828 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829}
1830
1831/**
1832 * Release a connection structure and remove it from the
1833 * list of netiucv connections.
1834 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001835static void netiucv_remove_connection(struct iucv_connection *conn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001836{
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001837 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001838 write_lock_bh(&iucv_connection_rwlock);
1839 list_del_init(&conn->list);
1840 write_unlock_bh(&iucv_connection_rwlock);
Ursula Braun0be4ace2007-05-02 15:18:44 +02001841 fsm_deltimer(&conn->timer);
1842 netiucv_purge_skb_queue(&conn->collect_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001843 if (conn->path) {
1844 iucv_path_sever(conn->path, iucvMagic);
1845 kfree(conn->path);
1846 conn->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847 }
Ursula Braun0be4ace2007-05-02 15:18:44 +02001848 netiucv_purge_skb_queue(&conn->commit_queue);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001849 kfree_fsm(conn->fsm);
1850 kfree_skb(conn->rx_buff);
1851 kfree_skb(conn->tx_buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852}
1853
1854/**
1855 * Release everything of a net device.
1856 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001857static void netiucv_free_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001859 struct netiucv_priv *privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001861 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862
1863 if (!dev)
1864 return;
1865
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 if (privptr) {
1867 if (privptr->conn)
1868 netiucv_remove_connection(privptr->conn);
1869 if (privptr->fsm)
1870 kfree_fsm(privptr->fsm);
1871 privptr->conn = NULL; privptr->fsm = NULL;
1872 /* privptr gets freed by free_netdev() */
1873 }
1874 free_netdev(dev);
1875}
1876
1877/**
1878 * Initialize a net device. (Called from kernel in alloc_netdev())
1879 */
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001880static const struct net_device_ops netiucv_netdev_ops = {
1881 .ndo_open = netiucv_open,
1882 .ndo_stop = netiucv_close,
1883 .ndo_get_stats = netiucv_stats,
1884 .ndo_start_xmit = netiucv_tx,
1885 .ndo_change_mtu = netiucv_change_mtu,
1886};
1887
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001888static void netiucv_setup_netdevice(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 dev->mtu = NETIUCV_MTU_DEFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 dev->destructor = netiucv_free_netdevice;
1892 dev->hard_header_len = NETIUCV_HDRLEN;
1893 dev->addr_len = 0;
1894 dev->type = ARPHRD_SLIP;
1895 dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
1896 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
Frank Blaschka4edd73b2009-01-09 03:43:58 +00001897 dev->netdev_ops = &netiucv_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898}
1899
1900/**
1901 * Allocate and initialize everything of a net device.
1902 */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001903static struct net_device *netiucv_init_netdevice(char *username)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904{
1905 struct netiucv_priv *privptr;
1906 struct net_device *dev;
1907
1908 dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d",
1909 netiucv_setup_netdevice);
1910 if (!dev)
1911 return NULL;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001912 if (dev_alloc_name(dev, dev->name) < 0)
1913 goto out_netdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001915 privptr = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 privptr->fsm = init_fsm("netiucvdev", dev_state_names,
1917 dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
1918 dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001919 if (!privptr->fsm)
1920 goto out_netdev;
1921
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 privptr->conn = netiucv_new_connection(dev, username);
1923 if (!privptr->conn) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001925 goto out_fsm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 }
1927 fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 return dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001929
1930out_fsm:
1931 kfree_fsm(privptr->fsm);
1932out_netdev:
1933 free_netdev(dev);
1934 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935}
1936
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001937static ssize_t conn_write(struct device_driver *drv,
1938 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001940 const char *p;
Frank Pavlic16a83b32006-09-15 16:25:19 +02001941 char username[9];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001942 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 struct net_device *dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001944 struct netiucv_priv *priv;
1945 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02001947 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948 if (count>9) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
1950 return -EINVAL;
1951 }
1952
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001953 for (i = 0, p = buf; i < 8 && *p; i++, p++) {
1954 if (isalnum(*p) || *p == '$') {
1955 username[i] = toupper(*p);
1956 continue;
1957 }
1958 if (*p == '\n')
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959 /* trailing lf, grr */
1960 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001961 IUCV_DBF_TEXT_(setup, 2,
1962 "conn_write: invalid character %c\n", *p);
1963 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001965 while (i < 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966 username[i++] = ' ';
Frank Pavlic16a83b32006-09-15 16:25:19 +02001967 username[8] = '\0';
1968
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001969 read_lock_bh(&iucv_connection_rwlock);
1970 list_for_each_entry(cp, &iucv_connection_list, list) {
1971 if (!strncmp(username, cp->userid, 9)) {
1972 read_unlock_bh(&iucv_connection_rwlock);
Ursula Braunf082bca2008-07-14 09:59:30 +02001973 IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection "
1974 "to %s already exists\n", username);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001975 return -EEXIST;
1976 }
Frank Pavlic16a83b32006-09-15 16:25:19 +02001977 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001978 read_unlock_bh(&iucv_connection_rwlock);
1979
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 dev = netiucv_init_netdevice(username);
1981 if (!dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
1983 return -ENODEV;
1984 }
1985
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001986 rc = netiucv_register_device(dev);
1987 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988 IUCV_DBF_TEXT_(setup, 2,
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001989 "ret %d from netiucv_register_device\n", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 goto out_free_ndev;
1991 }
1992
1993 /* sysfs magic */
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001994 priv = netdev_priv(dev);
1995 SET_NETDEV_DEV(dev, priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996
Martin Schwidefskyeebce382007-02-08 13:50:33 -08001997 rc = register_netdev(dev);
1998 if (rc)
1999 goto out_unreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000
Ursula Braun8f7c5022008-12-25 13:39:47 +01002001 dev_info(priv->dev, "The IUCV interface to %s has been"
2002 " established successfully\n", netiucv_printname(username));
Jeff Garzike82b0f22006-05-26 21:58:38 -04002003
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 return count;
2005
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002006out_unreg:
2007 netiucv_unregister_device(priv->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008out_free_ndev:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 netiucv_free_netdevice(dev);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002010 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011}
2012
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002013static DRIVER_ATTR(connection, 0200, NULL, conn_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002015static ssize_t remove_write (struct device_driver *drv,
2016 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002018 struct iucv_connection *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 struct net_device *ndev;
2020 struct netiucv_priv *priv;
2021 struct device *dev;
2022 char name[IFNAMSIZ];
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002023 const char *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 int i;
2025
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002026 IUCV_DBF_TEXT(trace, 3, __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027
2028 if (count >= IFNAMSIZ)
Frank Pavlic16a83b32006-09-15 16:25:19 +02002029 count = IFNAMSIZ - 1;;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002031 for (i = 0, p = buf; i < count && *p; i++, p++) {
2032 if (*p == '\n' || *p == ' ')
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033 /* trailing lf, grr */
2034 break;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002035 name[i] = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 }
2037 name[i] = '\0';
2038
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002039 read_lock_bh(&iucv_connection_rwlock);
2040 list_for_each_entry(cp, &iucv_connection_list, list) {
2041 ndev = cp->netdev;
2042 priv = netdev_priv(ndev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 dev = priv->dev;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002044 if (strncmp(name, ndev->name, count))
2045 continue;
2046 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
Ursula Braun8f7c5022008-12-25 13:39:47 +01002048 dev_warn(dev, "The IUCV device is connected"
2049 " to %s and cannot be removed\n",
2050 priv->conn->userid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
Ursula Braunf082bca2008-07-14 09:59:30 +02002052 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 }
2054 unregister_netdev(ndev);
2055 netiucv_unregister_device(dev);
2056 return count;
2057 }
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002058 read_unlock_bh(&iucv_connection_rwlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
2060 return -EINVAL;
2061}
2062
Heiko Carstens2b67fc42007-02-05 21:16:47 +01002063static DRIVER_ATTR(remove, 0200, NULL, remove_write);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002065static struct attribute * netiucv_drv_attrs[] = {
2066 &driver_attr_connection.attr,
2067 &driver_attr_remove.attr,
2068 NULL,
2069};
2070
2071static struct attribute_group netiucv_drv_attr_group = {
2072 .attrs = netiucv_drv_attrs,
2073};
2074
Cornelia Huck5b88feb2007-12-05 12:50:28 +01002075static struct attribute_group *netiucv_drv_attr_groups[] = {
2076 &netiucv_drv_attr_group,
2077 NULL,
2078};
2079
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002080static void netiucv_banner(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081{
Ursula Braun8f7c5022008-12-25 13:39:47 +01002082 pr_info("driver initialized\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083}
2084
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002085static void __exit netiucv_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002087 struct iucv_connection *cp;
2088 struct net_device *ndev;
2089 struct netiucv_priv *priv;
2090 struct device *dev;
2091
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002092 IUCV_DBF_TEXT(trace, 3, __func__);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002093 while (!list_empty(&iucv_connection_list)) {
2094 cp = list_entry(iucv_connection_list.next,
2095 struct iucv_connection, list);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002096 ndev = cp->netdev;
2097 priv = netdev_priv(ndev);
2098 dev = priv->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099
2100 unregister_netdev(ndev);
2101 netiucv_unregister_device(dev);
2102 }
2103
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 driver_unregister(&netiucv_driver);
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002105 iucv_unregister(&netiucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 iucv_unregister_dbf_views();
2107
Ursula Braun8f7c5022008-12-25 13:39:47 +01002108 pr_info("driver unloaded\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 return;
2110}
2111
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002112static int __init netiucv_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113{
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002114 int rc;
Jeff Garzike82b0f22006-05-26 21:58:38 -04002115
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002116 rc = iucv_register_dbf_views();
2117 if (rc)
2118 goto out;
2119 rc = iucv_register(&netiucv_handler, 1);
2120 if (rc)
2121 goto out_dbf;
Harvey Harrison2a2cf6b2008-04-17 07:46:21 +02002122 IUCV_DBF_TEXT(trace, 3, __func__);
Cornelia Huck0a0a8312008-04-24 10:15:28 +02002123 netiucv_driver.groups = netiucv_drv_attr_groups;
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002124 rc = driver_register(&netiucv_driver);
2125 if (rc) {
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002126 IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
2127 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 }
2129
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002130 netiucv_banner();
2131 return rc;
2132
Martin Schwidefskyeebce382007-02-08 13:50:33 -08002133out_iucv:
2134 iucv_unregister(&netiucv_handler, 1);
2135out_dbf:
2136 iucv_unregister_dbf_views();
2137out:
2138 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139}
Jeff Garzike82b0f22006-05-26 21:58:38 -04002140
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141module_init(netiucv_init);
2142module_exit(netiucv_exit);
2143MODULE_LICENSE("GPL");