diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile index 7cabb80a2e41..90d4d0ef3dd4 100644 --- a/drivers/s390/net/Makefile +++ b/drivers/s390/net/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o obj-$(CONFIG_SMSGIUCV) += smsgiucv.o obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o obj-$(CONFIG_LCS) += lcs.o cu3088.o -qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o qeth_eddp.o qeth_tso.o +obj-$(CONFIG_CLAW) += claw.o cu3088.o +qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o qeth_eddp.o qeth-$(CONFIG_PROC_FS) += qeth_proc.o obj-$(CONFIG_QETH) += qeth.o diff --git a/drivers/s390/net/ctcdbug.h b/drivers/s390/net/ctcdbug.h index ef8883951720..7fe2ebd1792d 100644 --- a/drivers/s390/net/ctcdbug.h +++ b/drivers/s390/net/ctcdbug.h @@ -1,6 +1,6 @@ /* * - * linux/drivers/s390/net/ctcdbug.h ($Revision: 1.4 $) + * linux/drivers/s390/net/ctcdbug.h ($Revision: 1.5 $) * * CTC / ESCON network driver - s390 dbf exploit. * @@ -9,7 +9,7 @@ * Author(s): Original Code written by * Peter Tiedemann (ptiedem@de.ibm.com) * - * $Revision: 1.4 $ $Date: 2004/10/15 09:26:58 $ + * $Revision: 1.5 $ $Date: 2005/02/27 19:46:44 $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,9 +25,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - +#ifndef _CTCDBUG_H_ +#define _CTCDBUG_H_ #include +#include "ctcmain.h" /** * Debug Facility stuff */ @@ -41,7 +43,7 @@ #define CTC_DBF_DATA_LEN 128 #define CTC_DBF_DATA_INDEX 3 #define CTC_DBF_DATA_NR_AREAS 1 -#define CTC_DBF_DATA_LEVEL 2 +#define CTC_DBF_DATA_LEVEL 3 #define CTC_DBF_TRACE_NAME "ctc_trace" #define CTC_DBF_TRACE_LEN 16 @@ -121,3 +123,5 @@ hex_dump(unsigned char *buf, size_t len) printk("\n"); } + +#endif diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c index 7266bf5ea659..ff3e95e07e89 100644 --- a/drivers/s390/net/ctcmain.c +++ b/drivers/s390/net/ctcmain.c @@ -1,5 +1,5 @@ /* - * $Id: ctcmain.c,v 1.72 2005/03/17 10:51:52 ptiedem Exp $ + * $Id: ctcmain.c,v 1.74 2005/03/24 09:04:17 mschwide Exp $ * * CTC / ESCON network driver * @@ -37,12 +37,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.72 $ + * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.74 $ * */ #undef DEBUG - #include #include #include @@ -74,288 +73,13 @@ #include "ctctty.h" #include "fsm.h" #include "cu3088.h" + #include "ctcdbug.h" +#include "ctcmain.h" MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)"); MODULE_DESCRIPTION("Linux for S/390 CTC/Escon Driver"); MODULE_LICENSE("GPL"); - -/** - * CCW commands, used in this driver. - */ -#define CCW_CMD_WRITE 0x01 -#define CCW_CMD_READ 0x02 -#define CCW_CMD_SET_EXTENDED 0xc3 -#define CCW_CMD_PREPARE 0xe3 - -#define CTC_PROTO_S390 0 -#define CTC_PROTO_LINUX 1 -#define CTC_PROTO_LINUX_TTY 2 -#define CTC_PROTO_OS390 3 -#define CTC_PROTO_MAX 3 - -#define CTC_BUFSIZE_LIMIT 65535 -#define CTC_BUFSIZE_DEFAULT 32768 - -#define CTC_TIMEOUT_5SEC 5000 - -#define CTC_INITIAL_BLOCKLEN 2 - -#define READ 0 -#define WRITE 1 - -#define CTC_ID_SIZE BUS_ID_SIZE+3 - - -struct ctc_profile { - unsigned long maxmulti; - unsigned long maxcqueue; - unsigned long doios_single; - unsigned long doios_multi; - unsigned long txlen; - unsigned long tx_time; - struct timespec send_stamp; -}; - -/** - * Definition of one channel - */ -struct channel { - - /** - * Pointer to next channel in list. - */ - struct channel *next; - char id[CTC_ID_SIZE]; - struct ccw_device *cdev; - - /** - * Type of this channel. - * CTC/A or Escon for valid channels. - */ - enum channel_types type; - - /** - * Misc. flags. See CHANNEL_FLAGS_... below - */ - __u32 flags; - - /** - * The protocol of this channel - */ - __u16 protocol; - - /** - * I/O and irq related stuff - */ - struct ccw1 *ccw; - struct irb *irb; - - /** - * RX/TX buffer size - */ - int max_bufsize; - - /** - * Transmit/Receive buffer. - */ - struct sk_buff *trans_skb; - - /** - * Universal I/O queue. - */ - struct sk_buff_head io_queue; - - /** - * TX queue for collecting skb's during busy. - */ - struct sk_buff_head collect_queue; - - /** - * Amount of data in collect_queue. - */ - int collect_len; - - /** - * spinlock for collect_queue and collect_len - */ - spinlock_t collect_lock; - - /** - * Timer for detecting unresposive - * I/O operations. - */ - fsm_timer timer; - - /** - * Retry counter for misc. operations. - */ - int retry; - - /** - * The finite state machine of this channel - */ - fsm_instance *fsm; - - /** - * The corresponding net_device this channel - * belongs to. - */ - struct net_device *netdev; - - struct ctc_profile prof; - - unsigned char *trans_skb_data; - - __u16 logflags; -}; - -#define CHANNEL_FLAGS_READ 0 -#define CHANNEL_FLAGS_WRITE 1 -#define CHANNEL_FLAGS_INUSE 2 -#define CHANNEL_FLAGS_BUFSIZE_CHANGED 4 -#define CHANNEL_FLAGS_FAILED 8 -#define CHANNEL_FLAGS_WAITIRQ 16 -#define CHANNEL_FLAGS_RWMASK 1 -#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK) - -#define LOG_FLAG_ILLEGALPKT 1 -#define LOG_FLAG_ILLEGALSIZE 2 -#define LOG_FLAG_OVERRUN 4 -#define LOG_FLAG_NOMEM 8 - -#define CTC_LOGLEVEL_INFO 1 -#define CTC_LOGLEVEL_NOTICE 2 -#define CTC_LOGLEVEL_WARN 4 -#define CTC_LOGLEVEL_EMERG 8 -#define CTC_LOGLEVEL_ERR 16 -#define CTC_LOGLEVEL_DEBUG 32 -#define CTC_LOGLEVEL_CRIT 64 - -#define CTC_LOGLEVEL_DEFAULT \ -(CTC_LOGLEVEL_INFO | CTC_LOGLEVEL_NOTICE | CTC_LOGLEVEL_WARN | CTC_LOGLEVEL_CRIT) - -#define CTC_LOGLEVEL_MAX ((CTC_LOGLEVEL_CRIT<<1)-1) - -static int loglevel = CTC_LOGLEVEL_DEFAULT; - -#define ctc_pr_debug(fmt, arg...) \ -do { if (loglevel & CTC_LOGLEVEL_DEBUG) printk(KERN_DEBUG fmt,##arg); } while (0) - -#define ctc_pr_info(fmt, arg...) \ -do { if (loglevel & CTC_LOGLEVEL_INFO) printk(KERN_INFO fmt,##arg); } while (0) - -#define ctc_pr_notice(fmt, arg...) \ -do { if (loglevel & CTC_LOGLEVEL_NOTICE) printk(KERN_NOTICE fmt,##arg); } while (0) - -#define ctc_pr_warn(fmt, arg...) \ -do { if (loglevel & CTC_LOGLEVEL_WARN) printk(KERN_WARNING fmt,##arg); } while (0) - -#define ctc_pr_emerg(fmt, arg...) \ -do { if (loglevel & CTC_LOGLEVEL_EMERG) printk(KERN_EMERG fmt,##arg); } while (0) - -#define ctc_pr_err(fmt, arg...) \ -do { if (loglevel & CTC_LOGLEVEL_ERR) printk(KERN_ERR fmt,##arg); } while (0) - -#define ctc_pr_crit(fmt, arg...) \ -do { if (loglevel & CTC_LOGLEVEL_CRIT) printk(KERN_CRIT fmt,##arg); } while (0) - -/** - * Linked list of all detected channels. - */ -static struct channel *channels = NULL; - -struct ctc_priv { - struct net_device_stats stats; - unsigned long tbusy; - /** - * The finite state machine of this interface. - */ - fsm_instance *fsm; - /** - * The protocol of this device - */ - __u16 protocol; - /** - * Timer for restarting after I/O Errors - */ - fsm_timer restart_timer; - - int buffer_size; - - struct channel *channel[2]; -}; - -/** - * Definition of our link level header. - */ -struct ll_header { - __u16 length; - __u16 type; - __u16 unused; -}; -#define LL_HEADER_LENGTH (sizeof(struct ll_header)) - -/** - * Compatibility macros for busy handling - * of network devices. - */ -static __inline__ void -ctc_clear_busy(struct net_device * dev) -{ - clear_bit(0, &(((struct ctc_priv *) dev->priv)->tbusy)); - if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY) - netif_wake_queue(dev); -} - -static __inline__ int -ctc_test_and_set_busy(struct net_device * dev) -{ - if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY) - netif_stop_queue(dev); - return test_and_set_bit(0, &((struct ctc_priv *) dev->priv)->tbusy); -} - -/** - * Print Banner. - */ -static void -print_banner(void) -{ - static int printed = 0; - char vbuf[] = "$Revision: 1.72 $"; - char *version = vbuf; - - if (printed) - return; - if ((version = strchr(version, ':'))) { - char *p = strchr(version + 1, '$'); - if (p) - *p = '\0'; - } else - version = " ??? "; - printk(KERN_INFO "CTC driver Version%s" -#ifdef DEBUG - " (DEBUG-VERSION, " __DATE__ __TIME__ ")" -#endif - " initialized\n", version); - printed = 1; -} - -/** - * Return type of a detected device. - */ -static enum channel_types -get_channel_type(struct ccw_device_id *id) -{ - enum channel_types type = (enum channel_types) id->driver_info; - - if (type == channel_type_ficon) - type = channel_type_escon; - - return type; -} - /** * States of the interface statemachine. */ @@ -371,7 +95,7 @@ enum dev_states { /** * MUST be always the last element!! */ - NR_DEV_STATES + CTC_NR_DEV_STATES }; static const char *dev_state_names[] = { @@ -399,7 +123,7 @@ enum dev_events { /** * MUST be always the last element!! */ - NR_DEV_EVENTS + CTC_NR_DEV_EVENTS }; static const char *dev_event_names[] = { @@ -476,40 +200,6 @@ enum ch_events { NR_CH_EVENTS, }; -static const char *ch_event_names[] = { - "ccw_device success", - "ccw_device busy", - "ccw_device enodev", - "ccw_device ioerr", - "ccw_device unknown", - - "Status ATTN & BUSY", - "Status ATTN", - "Status BUSY", - - "Unit check remote reset", - "Unit check remote system reset", - "Unit check TX timeout", - "Unit check TX parity", - "Unit check Hardware failure", - "Unit check RX parity", - "Unit check ZERO", - "Unit check Unknown", - - "SubChannel check Unknown", - - "Machine check failure", - "Machine check operational", - - "IRQ normal", - "IRQ final", - - "Timer", - - "Start", - "Stop", -}; - /** * States of the channel statemachine. */ @@ -545,6 +235,87 @@ enum ch_states { NR_CH_STATES, }; +static int loglevel = CTC_LOGLEVEL_DEFAULT; + +/** + * Linked list of all detected channels. + */ +static struct channel *channels = NULL; + +/** + * Print Banner. + */ +static void +print_banner(void) +{ + static int printed = 0; + char vbuf[] = "$Revision: 1.74 $"; + char *version = vbuf; + + if (printed) + return; + if ((version = strchr(version, ':'))) { + char *p = strchr(version + 1, '$'); + if (p) + *p = '\0'; + } else + version = " ??? "; + printk(KERN_INFO "CTC driver Version%s" +#ifdef DEBUG + " (DEBUG-VERSION, " __DATE__ __TIME__ ")" +#endif + " initialized\n", version); + printed = 1; +} + +/** + * Return type of a detected device. + */ +static enum channel_types +get_channel_type(struct ccw_device_id *id) +{ + enum channel_types type = (enum channel_types) id->driver_info; + + if (type == channel_type_ficon) + type = channel_type_escon; + + return type; +} + +static const char *ch_event_names[] = { + "ccw_device success", + "ccw_device busy", + "ccw_device enodev", + "ccw_device ioerr", + "ccw_device unknown", + + "Status ATTN & BUSY", + "Status ATTN", + "Status BUSY", + + "Unit check remote reset", + "Unit check remote system reset", + "Unit check TX timeout", + "Unit check TX parity", + "Unit check Hardware failure", + "Unit check RX parity", + "Unit check ZERO", + "Unit check Unknown", + + "SubChannel check Unknown", + + "Machine check failure", + "Machine check operational", + + "IRQ normal", + "IRQ final", + + "Timer", + + "Start", + "Stop", +}; + static const char *ch_state_names[] = { "Idle", "Stopped", @@ -1934,7 +1705,6 @@ add_channel(struct ccw_device *cdev, enum channel_types type) ch->cdev = cdev; snprintf(ch->id, CTC_ID_SIZE, "ch-%s", cdev->dev.bus_id); ch->type = type; - loglevel = CTC_LOGLEVEL_DEFAULT; ch->fsm = init_fsm(ch->id, ch_state_names, ch_event_names, NR_CH_STATES, NR_CH_EVENTS, ch_fsm, CH_FSM_LEN, GFP_KERNEL); @@ -2697,6 +2467,7 @@ ctc_stats(struct net_device * dev) /* * sysfs attributes */ + static ssize_t buffer_show(struct device *dev, char *buf) { @@ -2715,57 +2486,61 @@ buffer_write(struct device *dev, const char *buf, size_t count) struct ctc_priv *priv; struct net_device *ndev; int bs1; + char buffer[16]; DBF_TEXT(trace, 3, __FUNCTION__); + DBF_TEXT(trace, 3, buf); priv = dev->driver_data; - if (!priv) + if (!priv) { + DBF_TEXT(trace, 3, "bfnopriv"); return -ENODEV; - ndev = priv->channel[READ]->netdev; - if (!ndev) - return -ENODEV; - sscanf(buf, "%u", &bs1); + } + sscanf(buf, "%u", &bs1); if (bs1 > CTC_BUFSIZE_LIMIT) - return -EINVAL; + goto einval; + if (bs1 < (576 + LL_HEADER_LENGTH + 2)) + goto einval; + priv->buffer_size = bs1; // just to overwrite the default + + ndev = priv->channel[READ]->netdev; + if (!ndev) { + DBF_TEXT(trace, 3, "bfnondev"); + return -ENODEV; + } + if ((ndev->flags & IFF_RUNNING) && (bs1 < (ndev->mtu + LL_HEADER_LENGTH + 2))) - return -EINVAL; - if (bs1 < (576 + LL_HEADER_LENGTH + 2)) - return -EINVAL; + goto einval; - priv->buffer_size = bs1; - priv->channel[READ]->max_bufsize = - priv->channel[WRITE]->max_bufsize = bs1; + priv->channel[READ]->max_bufsize = bs1; + priv->channel[WRITE]->max_bufsize = bs1; if (!(ndev->flags & IFF_RUNNING)) ndev->mtu = bs1 - LL_HEADER_LENGTH - 2; priv->channel[READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED; priv->channel[WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED; + sprintf(buffer, "%d",priv->buffer_size); + DBF_TEXT(trace, 3, buffer); return count; +einval: + DBF_TEXT(trace, 3, "buff_err"); + return -EINVAL; } static ssize_t loglevel_show(struct device *dev, char *buf) { - struct ctc_priv *priv; - - priv = dev->driver_data; - if (!priv) - return -ENODEV; return sprintf(buf, "%d\n", loglevel); } static ssize_t loglevel_write(struct device *dev, const char *buf, size_t count) { - struct ctc_priv *priv; int ll1; DBF_TEXT(trace, 5, __FUNCTION__); - priv = dev->driver_data; - if (!priv) - return -ENODEV; sscanf(buf, "%i", &ll1); if ((ll1 > CTC_LOGLEVEL_MAX) || (ll1 < 0)) @@ -2835,27 +2610,6 @@ stats_write(struct device *dev, const char *buf, size_t count) return count; } -static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write); -static DEVICE_ATTR(loglevel, 0644, loglevel_show, loglevel_write); -static DEVICE_ATTR(stats, 0644, stats_show, stats_write); - -static int -ctc_add_attributes(struct device *dev) -{ -// device_create_file(dev, &dev_attr_buffer); - device_create_file(dev, &dev_attr_loglevel); - device_create_file(dev, &dev_attr_stats); - return 0; -} - -static void -ctc_remove_attributes(struct device *dev) -{ - device_remove_file(dev, &dev_attr_stats); - device_remove_file(dev, &dev_attr_loglevel); -// device_remove_file(dev, &dev_attr_buffer); -} - static void ctc_netdev_unregister(struct net_device * dev) @@ -2899,52 +2653,6 @@ ctc_free_netdevice(struct net_device * dev, int free_dev) #endif } -/** - * Initialize everything of the net device except the name and the - * channel structs. - */ -static struct net_device * -ctc_init_netdevice(struct net_device * dev, int alloc_device, - struct ctc_priv *privptr) -{ - if (!privptr) - return NULL; - - DBF_TEXT(setup, 3, __FUNCTION__); - if (alloc_device) { - dev = kmalloc(sizeof (struct net_device), GFP_KERNEL); - if (!dev) - return NULL; - memset(dev, 0, sizeof (struct net_device)); - } - - dev->priv = privptr; - privptr->fsm = init_fsm("ctcdev", dev_state_names, - dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS, - dev_fsm, DEV_FSM_LEN, GFP_KERNEL); - if (privptr->fsm == NULL) { - if (alloc_device) - kfree(dev); - return NULL; - } - fsm_newstate(privptr->fsm, DEV_STATE_STOPPED); - fsm_settimer(privptr->fsm, &privptr->restart_timer); - if (dev->mtu == 0) - dev->mtu = CTC_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2; - dev->hard_start_xmit = ctc_tx; - dev->open = ctc_open; - dev->stop = ctc_close; - dev->get_stats = ctc_stats; - dev->change_mtu = ctc_change_mtu; - dev->hard_header_len = LL_HEADER_LENGTH + 2; - dev->addr_len = 0; - dev->type = ARPHRD_SLIP; - dev->tx_queue_len = 100; - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - SET_MODULE_OWNER(dev); - return dev; -} - static ssize_t ctc_proto_show(struct device *dev, char *buf) { @@ -2977,7 +2685,6 @@ ctc_proto_store(struct device *dev, const char *buf, size_t count) return count; } -static DEVICE_ATTR(protocol, 0644, ctc_proto_show, ctc_proto_store); static ssize_t ctc_type_show(struct device *dev, char *buf) @@ -2991,8 +2698,13 @@ ctc_type_show(struct device *dev, char *buf) return sprintf(buf, "%s\n", cu3088_type[cgdev->cdev[0]->id.driver_info]); } +static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write); +static DEVICE_ATTR(protocol, 0644, ctc_proto_show, ctc_proto_store); static DEVICE_ATTR(type, 0444, ctc_type_show, NULL); +static DEVICE_ATTR(loglevel, 0644, loglevel_show, loglevel_write); +static DEVICE_ATTR(stats, 0644, stats_show, stats_write); + static struct attribute *ctc_attr[] = { &dev_attr_protocol.attr, &dev_attr_type.attr, @@ -3004,6 +2716,21 @@ static struct attribute_group ctc_attr_group = { .attrs = ctc_attr, }; +static int +ctc_add_attributes(struct device *dev) +{ + device_create_file(dev, &dev_attr_loglevel); + device_create_file(dev, &dev_attr_stats); + return 0; +} + +static void +ctc_remove_attributes(struct device *dev) +{ + device_remove_file(dev, &dev_attr_stats); + device_remove_file(dev, &dev_attr_loglevel); +} + static int ctc_add_files(struct device *dev) { @@ -3028,15 +2755,15 @@ ctc_remove_files(struct device *dev) * * @returns 0 on success, !0 on failure. */ - static int ctc_probe_device(struct ccwgroup_device *cgdev) { struct ctc_priv *priv; int rc; + char buffer[16]; pr_debug("%s() called\n", __FUNCTION__); - DBF_TEXT(trace, 3, __FUNCTION__); + DBF_TEXT(setup, 3, __FUNCTION__); if (!get_device(&cgdev->dev)) return -ENODEV; @@ -3060,9 +2787,69 @@ ctc_probe_device(struct ccwgroup_device *cgdev) cgdev->cdev[1]->handler = ctc_irq_handler; cgdev->dev.driver_data = priv; + sprintf(buffer, "%p", priv); + DBF_TEXT(data, 3, buffer); + + sprintf(buffer, "%u", (unsigned int)sizeof(struct ctc_priv)); + DBF_TEXT(data, 3, buffer); + + sprintf(buffer, "%p", &channels); + DBF_TEXT(data, 3, buffer); + + sprintf(buffer, "%u", (unsigned int)sizeof(struct channel)); + DBF_TEXT(data, 3, buffer); + return 0; } +/** + * Initialize everything of the net device except the name and the + * channel structs. + */ +static struct net_device * +ctc_init_netdevice(struct net_device * dev, int alloc_device, + struct ctc_priv *privptr) +{ + if (!privptr) + return NULL; + + DBF_TEXT(setup, 3, __FUNCTION__); + + if (alloc_device) { + dev = kmalloc(sizeof (struct net_device), GFP_KERNEL); + if (!dev) + return NULL; + memset(dev, 0, sizeof (struct net_device)); + } + + dev->priv = privptr; + privptr->fsm = init_fsm("ctcdev", dev_state_names, + dev_event_names, CTC_NR_DEV_STATES, CTC_NR_DEV_EVENTS, + dev_fsm, DEV_FSM_LEN, GFP_KERNEL); + if (privptr->fsm == NULL) { + if (alloc_device) + kfree(dev); + return NULL; + } + fsm_newstate(privptr->fsm, DEV_STATE_STOPPED); + fsm_settimer(privptr->fsm, &privptr->restart_timer); + if (dev->mtu == 0) + dev->mtu = CTC_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2; + dev->hard_start_xmit = ctc_tx; + dev->open = ctc_open; + dev->stop = ctc_close; + dev->get_stats = ctc_stats; + dev->change_mtu = ctc_change_mtu; + dev->hard_header_len = LL_HEADER_LENGTH + 2; + dev->addr_len = 0; + dev->type = ARPHRD_SLIP; + dev->tx_queue_len = 100; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + SET_MODULE_OWNER(dev); + return dev; +} + + /** * * Setup an interface. @@ -3081,6 +2868,7 @@ ctc_new_device(struct ccwgroup_device *cgdev) struct ctc_priv *privptr; struct net_device *dev; int ret; + char buffer[16]; pr_debug("%s() called\n", __FUNCTION__); DBF_TEXT(setup, 3, __FUNCTION__); @@ -3089,6 +2877,9 @@ ctc_new_device(struct ccwgroup_device *cgdev) if (!privptr) return -ENODEV; + sprintf(buffer, "%d", privptr->buffer_size); + DBF_TEXT(setup, 3, buffer); + type = get_channel_type(&cgdev->cdev[0]->id); snprintf(read_id, CTC_ID_SIZE, "ch-%s", cgdev->cdev[0]->dev.bus_id); @@ -3177,9 +2968,10 @@ ctc_shutdown_device(struct ccwgroup_device *cgdev) struct ctc_priv *priv; struct net_device *ndev; - DBF_TEXT(trace, 3, __FUNCTION__); + DBF_TEXT(setup, 3, __FUNCTION__); pr_debug("%s() called\n", __FUNCTION__); + priv = cgdev->dev.driver_data; ndev = NULL; if (!priv) @@ -3215,7 +3007,6 @@ ctc_shutdown_device(struct ccwgroup_device *cgdev) channel_remove(priv->channel[READ]); if (priv->channel[WRITE]) channel_remove(priv->channel[WRITE]); - priv->channel[READ] = priv->channel[WRITE] = NULL; return 0; @@ -3228,7 +3019,7 @@ ctc_remove_device(struct ccwgroup_device *cgdev) struct ctc_priv *priv; pr_debug("%s() called\n", __FUNCTION__); - DBF_TEXT(trace, 3, __FUNCTION__); + DBF_TEXT(setup, 3, __FUNCTION__); priv = cgdev->dev.driver_data; if (!priv) @@ -3265,6 +3056,7 @@ static struct ccwgroup_driver ctc_group_driver = { static void __exit ctc_exit(void) { + DBF_TEXT(setup, 3, __FUNCTION__); unregister_cu3088_discipline(&ctc_group_driver); ctc_tty_cleanup(); ctc_unregister_dbf_views(); @@ -3282,6 +3074,10 @@ ctc_init(void) { int ret = 0; + loglevel = CTC_LOGLEVEL_DEFAULT; + + DBF_TEXT(setup, 3, __FUNCTION__); + print_banner(); ret = ctc_register_dbf_views(); diff --git a/drivers/s390/net/ctcmain.h b/drivers/s390/net/ctcmain.h new file mode 100644 index 000000000000..ba3605f16335 --- /dev/null +++ b/drivers/s390/net/ctcmain.h @@ -0,0 +1,276 @@ +/* + * $Id: ctcmain.h,v 1.4 2005/03/24 09:04:17 mschwide Exp $ + * + * CTC / ESCON network driver + * + * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com) + Peter Tiedemann (ptiedem@de.ibm.com) + * + * + * Documentation used: + * - Principles of Operation (IBM doc#: SA22-7201-06) + * - Common IO/-Device Commands and Self Description (IBM doc#: SA22-7204-02) + * - Common IO/-Device Commands and Self Description (IBM doc#: SN22-5535) + * - ESCON Channel-to-Channel Adapter (IBM doc#: SA22-7203-00) + * - ESCON I/O Interface (IBM doc#: SA22-7202-029 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.4 $ + * + */ + +#ifndef _CTCMAIN_H_ +#define _CTCMAIN_H_ + +#include +#include + +#include "ctctty.h" +#include "fsm.h" +#include "cu3088.h" + + +/** + * CCW commands, used in this driver. + */ +#define CCW_CMD_WRITE 0x01 +#define CCW_CMD_READ 0x02 +#define CCW_CMD_SET_EXTENDED 0xc3 +#define CCW_CMD_PREPARE 0xe3 + +#define CTC_PROTO_S390 0 +#define CTC_PROTO_LINUX 1 +#define CTC_PROTO_LINUX_TTY 2 +#define CTC_PROTO_OS390 3 +#define CTC_PROTO_MAX 3 + +#define CTC_BUFSIZE_LIMIT 65535 +#define CTC_BUFSIZE_DEFAULT 32768 + +#define CTC_TIMEOUT_5SEC 5000 + +#define CTC_INITIAL_BLOCKLEN 2 + +#define READ 0 +#define WRITE 1 + +#define CTC_ID_SIZE BUS_ID_SIZE+3 + + +struct ctc_profile { + unsigned long maxmulti; + unsigned long maxcqueue; + unsigned long doios_single; + unsigned long doios_multi; + unsigned long txlen; + unsigned long tx_time; + struct timespec send_stamp; +}; + +/** + * Definition of one channel + */ +struct channel { + + /** + * Pointer to next channel in list. + */ + struct channel *next; + char id[CTC_ID_SIZE]; + struct ccw_device *cdev; + + /** + * Type of this channel. + * CTC/A or Escon for valid channels. + */ + enum channel_types type; + + /** + * Misc. flags. See CHANNEL_FLAGS_... below + */ + __u32 flags; + + /** + * The protocol of this channel + */ + __u16 protocol; + + /** + * I/O and irq related stuff + */ + struct ccw1 *ccw; + struct irb *irb; + + /** + * RX/TX buffer size + */ + int max_bufsize; + + /** + * Transmit/Receive buffer. + */ + struct sk_buff *trans_skb; + + /** + * Universal I/O queue. + */ + struct sk_buff_head io_queue; + + /** + * TX queue for collecting skb's during busy. + */ + struct sk_buff_head collect_queue; + + /** + * Amount of data in collect_queue. + */ + int collect_len; + + /** + * spinlock for collect_queue and collect_len + */ + spinlock_t collect_lock; + + /** + * Timer for detecting unresposive + * I/O operations. + */ + fsm_timer timer; + + /** + * Retry counter for misc. operations. + */ + int retry; + + /** + * The finite state machine of this channel + */ + fsm_instance *fsm; + + /** + * The corresponding net_device this channel + * belongs to. + */ + struct net_device *netdev; + + struct ctc_profile prof; + + unsigned char *trans_skb_data; + + __u16 logflags; +}; + +#define CHANNEL_FLAGS_READ 0 +#define CHANNEL_FLAGS_WRITE 1 +#define CHANNEL_FLAGS_INUSE 2 +#define CHANNEL_FLAGS_BUFSIZE_CHANGED 4 +#define CHANNEL_FLAGS_FAILED 8 +#define CHANNEL_FLAGS_WAITIRQ 16 +#define CHANNEL_FLAGS_RWMASK 1 +#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK) + +#define LOG_FLAG_ILLEGALPKT 1 +#define LOG_FLAG_ILLEGALSIZE 2 +#define LOG_FLAG_OVERRUN 4 +#define LOG_FLAG_NOMEM 8 + +#define CTC_LOGLEVEL_INFO 1 +#define CTC_LOGLEVEL_NOTICE 2 +#define CTC_LOGLEVEL_WARN 4 +#define CTC_LOGLEVEL_EMERG 8 +#define CTC_LOGLEVEL_ERR 16 +#define CTC_LOGLEVEL_DEBUG 32 +#define CTC_LOGLEVEL_CRIT 64 + +#define CTC_LOGLEVEL_DEFAULT \ +(CTC_LOGLEVEL_INFO | CTC_LOGLEVEL_NOTICE | CTC_LOGLEVEL_WARN | CTC_LOGLEVEL_CRIT) + +#define CTC_LOGLEVEL_MAX ((CTC_LOGLEVEL_CRIT<<1)-1) + +#define ctc_pr_debug(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_DEBUG) printk(KERN_DEBUG fmt,##arg); } while (0) + +#define ctc_pr_info(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_INFO) printk(KERN_INFO fmt,##arg); } while (0) + +#define ctc_pr_notice(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_NOTICE) printk(KERN_NOTICE fmt,##arg); } while (0) + +#define ctc_pr_warn(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_WARN) printk(KERN_WARNING fmt,##arg); } while (0) + +#define ctc_pr_emerg(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_EMERG) printk(KERN_EMERG fmt,##arg); } while (0) + +#define ctc_pr_err(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_ERR) printk(KERN_ERR fmt,##arg); } while (0) + +#define ctc_pr_crit(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_CRIT) printk(KERN_CRIT fmt,##arg); } while (0) + +struct ctc_priv { + struct net_device_stats stats; + unsigned long tbusy; + /** + * The finite state machine of this interface. + */ + fsm_instance *fsm; + /** + * The protocol of this device + */ + __u16 protocol; + /** + * Timer for restarting after I/O Errors + */ + fsm_timer restart_timer; + + int buffer_size; + + struct channel *channel[2]; +}; + +/** + * Definition of our link level header. + */ +struct ll_header { + __u16 length; + __u16 type; + __u16 unused; +}; +#define LL_HEADER_LENGTH (sizeof(struct ll_header)) + +/** + * Compatibility macros for busy handling + * of network devices. + */ +static __inline__ void +ctc_clear_busy(struct net_device * dev) +{ + clear_bit(0, &(((struct ctc_priv *) dev->priv)->tbusy)); + if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY) + netif_wake_queue(dev); +} + +static __inline__ int +ctc_test_and_set_busy(struct net_device * dev) +{ + if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY) + netif_stop_queue(dev); + return test_and_set_bit(0, &((struct ctc_priv *) dev->priv)->tbusy); +} + +#endif diff --git a/drivers/s390/net/ctctty.c b/drivers/s390/net/ctctty.c index 9257d60c7833..3080393e823d 100644 --- a/drivers/s390/net/ctctty.c +++ b/drivers/s390/net/ctctty.c @@ -1,5 +1,5 @@ /* - * $Id: ctctty.c,v 1.26 2004/08/04 11:06:55 mschwide Exp $ + * $Id: ctctty.c,v 1.29 2005/04/05 08:50:44 mschwide Exp $ * * CTC / ESCON network driver, tty interface. * @@ -1056,8 +1056,7 @@ ctc_tty_close(struct tty_struct *tty, struct file *filp) info->tty = 0; tty->closing = 0; if (info->blocked_open) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/2); + msleep_interruptible(500); wake_up_interruptible(&info->open_wait); } info->flags &= ~(CTC_ASYNC_NORMAL_ACTIVE | CTC_ASYNC_CLOSING); diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c index 1b0a9f16024c..0075894c71db 100644 --- a/drivers/s390/net/cu3088.c +++ b/drivers/s390/net/cu3088.c @@ -1,5 +1,5 @@ /* - * $Id: cu3088.c,v 1.34 2004/06/15 13:16:27 pavlic Exp $ + * $Id: cu3088.c,v 1.35 2005/03/30 19:28:52 richtera Exp $ * * CTC / LCS ccw_device driver * @@ -39,6 +39,7 @@ const char *cu3088_type[] = { "FICON channel", "P390 LCS card", "OSA LCS card", + "CLAW channel device", "unknown channel type", "unsupported channel type", }; @@ -51,6 +52,7 @@ static struct ccw_device_id cu3088_ids[] = { { CCW_DEVICE(0x3088, 0x1e), .driver_info = channel_type_ficon }, { CCW_DEVICE(0x3088, 0x01), .driver_info = channel_type_p390 }, { CCW_DEVICE(0x3088, 0x60), .driver_info = channel_type_osa2 }, + { CCW_DEVICE(0x3088, 0x61), .driver_info = channel_type_claw }, { /* end of list */ } }; diff --git a/drivers/s390/net/cu3088.h b/drivers/s390/net/cu3088.h index 0ec49a8b3adc..1753661f702a 100644 --- a/drivers/s390/net/cu3088.h +++ b/drivers/s390/net/cu3088.h @@ -23,6 +23,9 @@ enum channel_types { /* Device is a OSA2 card */ channel_type_osa2, + /* Device is a CLAW channel device */ + channel_type_claw, + /* Device is a channel, but we don't know * anything about it */ channel_type_unknown, diff --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c index 1ac6563ee3e0..e08e74e16124 100644 --- a/drivers/s390/net/iucv.c +++ b/drivers/s390/net/iucv.c @@ -1,5 +1,5 @@ /* - * $Id: iucv.c,v 1.43 2005/02/09 14:47:43 braunu Exp $ + * $Id: iucv.c,v 1.45 2005/04/26 22:59:06 braunu Exp $ * * IUCV network driver * @@ -29,7 +29,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.43 $ + * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.45 $ * */ @@ -355,7 +355,7 @@ do { \ static void iucv_banner(void) { - char vbuf[] = "$Revision: 1.43 $"; + char vbuf[] = "$Revision: 1.45 $"; char *version = vbuf; if ((version = strchr(version, ':'))) { @@ -2553,12 +2553,12 @@ EXPORT_SYMBOL (iucv_resume); #endif EXPORT_SYMBOL (iucv_reply_prmmsg); EXPORT_SYMBOL (iucv_send); -#if 0 EXPORT_SYMBOL (iucv_send2way); EXPORT_SYMBOL (iucv_send2way_array); -EXPORT_SYMBOL (iucv_send_array); EXPORT_SYMBOL (iucv_send2way_prmmsg); EXPORT_SYMBOL (iucv_send2way_prmmsg_array); +#if 0 +EXPORT_SYMBOL (iucv_send_array); EXPORT_SYMBOL (iucv_send_prmmsg); EXPORT_SYMBOL (iucv_setmask); #endif diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 0f76e945b984..cccfed248e70 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -11,7 +11,7 @@ * Frank Pavlic (pavlic@de.ibm.com) and * Martin Schwidefsky * - * $Revision: 1.96 $ $Date: 2004/11/11 13:42:33 $ + * $Revision: 1.98 $ $Date: 2005/04/18 13:41:29 $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -59,7 +59,7 @@ /** * initialization string for output */ -#define VERSION_LCS_C "$Revision: 1.96 $" +#define VERSION_LCS_C "$Revision: 1.98 $" static char version[] __initdata = "LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")"; static char debug_buffer[255]; @@ -1098,14 +1098,6 @@ lcs_check_multicast_support(struct lcs_card *card) PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n"); return -EOPNOTSUPP; } - /* Print out supported assists: IPv6 */ - PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name, - (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ? - "with" : "without"); - /* Print out supported assist: Multicast */ - PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name, - (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ? - "with" : "without"); if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) return 0; return -EOPNOTSUPP; @@ -1160,7 +1152,7 @@ list_modified: } } /* re-insert all entries from the failed_list into ipm_list */ - list_for_each_entry(ipm, &failed_list, list) { + list_for_each_entry_safe(ipm, tmp, &failed_list, list) { list_del_init(&ipm->list); list_add_tail(&ipm->list, &card->ipm_list); } @@ -2198,30 +2190,39 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) if (!dev) goto out; card->dev = dev; -netdev_out: card->dev->priv = card; card->dev->open = lcs_open_device; card->dev->stop = lcs_stop_device; card->dev->hard_start_xmit = lcs_start_xmit; card->dev->get_stats = lcs_getstats; SET_MODULE_OWNER(dev); - if (lcs_register_netdev(ccwgdev) != 0) - goto out; memcpy(card->dev->dev_addr, card->mac, LCS_MAC_LENGTH); #ifdef CONFIG_IP_MULTICAST if (!lcs_check_multicast_support(card)) card->dev->set_multicast_list = lcs_set_multicast_list; #endif - netif_stop_queue(card->dev); +netdev_out: lcs_set_allowed_threads(card,0xffffffff); if (recover_state == DEV_STATE_RECOVER) { lcs_set_multicast_list(card->dev); card->dev->flags |= IFF_UP; netif_wake_queue(card->dev); card->state = DEV_STATE_UP; - } else + } else { lcs_stopcard(card); + } + if (lcs_register_netdev(ccwgdev) != 0) + goto out; + + /* Print out supported assists: IPv6 */ + PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name, + (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ? + "with" : "without"); + /* Print out supported assist: Multicast */ + PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name, + (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ? + "with" : "without"); return 0; out: diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h index a341041a6cf7..a755b57db46b 100644 --- a/drivers/s390/net/qeth.h +++ b/drivers/s390/net/qeth.h @@ -24,7 +24,7 @@ #include "qeth_mpc.h" -#define VERSION_QETH_H "$Revision: 1.135 $" +#define VERSION_QETH_H "$Revision: 1.139 $" #ifdef CONFIG_QETH_IPV6 #define QETH_VERSION_IPV6 ":IPv6" @@ -288,7 +288,8 @@ qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, enum qeth_ipa_funcs func) #define QETH_TX_TIMEOUT 100 * HZ #define QETH_HEADER_SIZE 32 #define MAX_PORTNO 15 -#define QETH_FAKE_LL_LEN ETH_HLEN +#define QETH_FAKE_LL_LEN_ETH ETH_HLEN +#define QETH_FAKE_LL_LEN_TR (sizeof(struct trh_hdr)-TR_MAXRIFLEN+sizeof(struct trllc)) #define QETH_FAKE_LL_V6_ADDR_POS 24 /*IPv6 address autoconfiguration stuff*/ @@ -369,6 +370,25 @@ struct qeth_hdr { } hdr; } __attribute__ ((packed)); +/*TCP Segmentation Offload header*/ +struct qeth_hdr_ext_tso { + __u16 hdr_tot_len; + __u8 imb_hdr_no; + __u8 reserved; + __u8 hdr_type; + __u8 hdr_version; + __u16 hdr_len; + __u32 payload_len; + __u16 mss; + __u16 dg_hdr_len; + __u8 padding[16]; +} __attribute__ ((packed)); + +struct qeth_hdr_tso { + struct qeth_hdr hdr; /*hdr->hdr.l3.xxx*/ + struct qeth_hdr_ext_tso ext; +} __attribute__ ((packed)); + /* flags for qeth_hdr.flags */ #define QETH_HDR_PASSTHRU 0x10 @@ -866,6 +886,7 @@ qeth_push_skb(struct qeth_card *card, struct sk_buff **skb, int size) return hdr; } + inline static int qeth_get_hlen(__u8 link_type) { @@ -873,19 +894,19 @@ qeth_get_hlen(__u8 link_type) switch (link_type) { case QETH_LINK_TYPE_HSTR: case QETH_LINK_TYPE_LANE_TR: - return sizeof(struct qeth_hdr) + TR_HLEN; + return sizeof(struct qeth_hdr_tso) + TR_HLEN; default: #ifdef CONFIG_QETH_VLAN - return sizeof(struct qeth_hdr) + VLAN_ETH_HLEN; + return sizeof(struct qeth_hdr_tso) + VLAN_ETH_HLEN; #else - return sizeof(struct qeth_hdr) + ETH_HLEN; + return sizeof(struct qeth_hdr_tso) + ETH_HLEN; #endif } #else /* CONFIG_QETH_IPV6 */ #ifdef CONFIG_QETH_VLAN - return sizeof(struct qeth_hdr) + VLAN_HLEN; + return sizeof(struct qeth_hdr_tso) + VLAN_HLEN; #else - return sizeof(struct qeth_hdr); + return sizeof(struct qeth_hdr_tso); #endif #endif /* CONFIG_QETH_IPV6 */ } diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c index 7ee1c06ed68a..f94f1f25eec6 100644 --- a/drivers/s390/net/qeth_eddp.c +++ b/drivers/s390/net/qeth_eddp.c @@ -1,6 +1,6 @@ /* * - * linux/drivers/s390/net/qeth_eddp.c ($Revision: 1.11 $) + * linux/drivers/s390/net/qeth_eddp.c ($Revision: 1.13 $) * * Enhanced Device Driver Packing (EDDP) support for the qeth driver. * @@ -8,7 +8,7 @@ * * Author(s): Thomas Spatzier * - * $Revision: 1.11 $ $Date: 2005/03/24 09:04:18 $ + * $Revision: 1.13 $ $Date: 2005/05/04 20:19:18 $ * */ #include @@ -85,7 +85,7 @@ void qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *buf) { struct qeth_eddp_context_reference *ref; - + QETH_DBF_TEXT(trace, 6, "eddprctx"); while (!list_empty(&buf->ctx_list)){ ref = list_entry(buf->ctx_list.next, @@ -139,7 +139,7 @@ qeth_eddp_fill_buffer(struct qeth_qdio_out_q *queue, "buffer!\n"); goto out; } - } + } /* check if the whole next skb fits into current buffer */ if ((QETH_MAX_BUFFER_ELEMENTS(queue->card) - buf->next_element_to_fill) @@ -152,7 +152,7 @@ qeth_eddp_fill_buffer(struct qeth_qdio_out_q *queue, * and increment ctx's refcnt */ must_refcnt = 1; continue; - } + } if (must_refcnt){ must_refcnt = 0; if (qeth_eddp_buf_ref_context(buf, ctx)){ @@ -202,40 +202,29 @@ out: return flush_cnt; } -static inline int -qeth_get_skb_data_len(struct sk_buff *skb) -{ - int len = skb->len; - int i; - - for (i = 0; i < skb_shinfo(skb)->nr_frags; ++i) - len -= skb_shinfo(skb)->frags[i].size; - return len; -} - static inline void qeth_eddp_create_segment_hdrs(struct qeth_eddp_context *ctx, - struct qeth_eddp_data *eddp) + struct qeth_eddp_data *eddp, int data_len) { u8 *page; int page_remainder; int page_offset; - int hdr_len; + int pkt_len; struct qeth_eddp_element *element; QETH_DBF_TEXT(trace, 5, "eddpcrsh"); page = ctx->pages[ctx->offset >> PAGE_SHIFT]; page_offset = ctx->offset % PAGE_SIZE; element = &ctx->elements[ctx->num_elements]; - hdr_len = eddp->nhl + eddp->thl; + pkt_len = eddp->nhl + eddp->thl + data_len; /* FIXME: layer2 and VLAN !!! */ if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2) - hdr_len += ETH_HLEN; + pkt_len += ETH_HLEN; if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) - hdr_len += VLAN_HLEN; - /* does complete header fit in current page ? */ + pkt_len += VLAN_HLEN; + /* does complete packet fit in current page ? */ page_remainder = PAGE_SIZE - page_offset; - if (page_remainder < (sizeof(struct qeth_hdr) + hdr_len)){ + if (page_remainder < (sizeof(struct qeth_hdr) + pkt_len)){ /* no -> go to start of next page */ ctx->offset += page_remainder; page = ctx->pages[ctx->offset >> PAGE_SHIFT]; @@ -281,7 +270,7 @@ qeth_eddp_copy_data_tcp(char *dst, struct qeth_eddp_data *eddp, int len, int left_in_frag; int copy_len; u8 *src; - + QETH_DBF_TEXT(trace, 5, "eddpcdtc"); if (skb_shinfo(eddp->skb)->nr_frags == 0) { memcpy(dst, eddp->skb->data + eddp->skb_offset, len); @@ -292,7 +281,7 @@ qeth_eddp_copy_data_tcp(char *dst, struct qeth_eddp_data *eddp, int len, while (len > 0) { if (eddp->frag < 0) { /* we're in skb->data */ - left_in_frag = qeth_get_skb_data_len(eddp->skb) + left_in_frag = (eddp->skb->len - eddp->skb->data_len) - eddp->skb_offset; src = eddp->skb->data + eddp->skb_offset; } else { @@ -424,7 +413,7 @@ __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, struct tcphdr *tcph; int data_len; u32 hcsum; - + QETH_DBF_TEXT(trace, 5, "eddpftcp"); eddp->skb_offset = sizeof(struct qeth_hdr) + eddp->nhl + eddp->thl; tcph = eddp->skb->h.th; @@ -464,7 +453,7 @@ __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, else hcsum = qeth_eddp_check_tcp6_hdr(eddp, data_len); /* fill the next segment into the context */ - qeth_eddp_create_segment_hdrs(ctx, eddp); + qeth_eddp_create_segment_hdrs(ctx, eddp, data_len); qeth_eddp_create_segment_data_tcp(ctx, eddp, data_len, hcsum); if (eddp->skb_offset >= eddp->skb->len) break; @@ -474,13 +463,13 @@ __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, eddp->th.tcp.h.seq += data_len; } } - + static inline int qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, struct sk_buff *skb, struct qeth_hdr *qhdr) { struct qeth_eddp_data *eddp = NULL; - + QETH_DBF_TEXT(trace, 5, "eddpficx"); /* create our segmentation headers and copy original headers */ if (skb->protocol == ETH_P_IP) @@ -520,7 +509,7 @@ qeth_eddp_calc_num_pages(struct qeth_eddp_context *ctx, struct sk_buff *skb, int hdr_len) { int skbs_per_page; - + QETH_DBF_TEXT(trace, 5, "eddpcanp"); /* can we put multiple skbs in one page? */ skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->tso_size + hdr_len); @@ -600,7 +589,7 @@ qeth_eddp_create_context_tcp(struct qeth_card *card, struct sk_buff *skb, struct qeth_hdr *qhdr) { struct qeth_eddp_context *ctx = NULL; - + QETH_DBF_TEXT(trace, 5, "creddpct"); if (skb->protocol == ETH_P_IP) ctx = qeth_eddp_create_context_generic(card, skb, diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index 607b92542df6..208127a5033a 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -1,6 +1,6 @@ /* * - * linux/drivers/s390/net/qeth_main.c ($Revision: 1.206 $) + * linux/drivers/s390/net/qeth_main.c ($Revision: 1.214 $) * * Linux on zSeries OSA Express and HiperSockets support * @@ -12,7 +12,7 @@ * Frank Pavlic (pavlic@de.ibm.com) and * Thomas Spatzier * - * $Revision: 1.206 $ $Date: 2005/03/24 09:04:18 $ + * $Revision: 1.214 $ $Date: 2005/05/04 20:19:18 $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -80,7 +80,7 @@ qeth_eyecatcher(void) #include "qeth_eddp.h" #include "qeth_tso.h" -#define VERSION_QETH_C "$Revision: 1.206 $" +#define VERSION_QETH_C "$Revision: 1.214 $" static const char *version = "qeth S/390 OSA-Express driver"; /** @@ -158,6 +158,9 @@ qeth_irq_tasklet(unsigned long); static int qeth_set_online(struct ccwgroup_device *); +static int +__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode); + static struct qeth_ipaddr * qeth_get_addr_buffer(enum qeth_prot_versions); @@ -510,10 +513,10 @@ qeth_irq_tasklet(unsigned long data) wake_up(&card->wait_q); } -static int qeth_stop_card(struct qeth_card *); +static int qeth_stop_card(struct qeth_card *, int); static int -qeth_set_offline(struct ccwgroup_device *cgdev) +__qeth_set_offline(struct ccwgroup_device *cgdev, int recovery_mode) { struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data; int rc = 0; @@ -523,7 +526,7 @@ qeth_set_offline(struct ccwgroup_device *cgdev) QETH_DBF_HEX(setup, 3, &card, sizeof(void *)); recover_flag = card->state; - if (qeth_stop_card(card) == -ERESTARTSYS){ + if (qeth_stop_card(card, recovery_mode) == -ERESTARTSYS){ PRINT_WARN("Stopping card %s interrupted by user!\n", CARD_BUS_ID(card)); return -ERESTARTSYS; @@ -539,6 +542,12 @@ qeth_set_offline(struct ccwgroup_device *cgdev) return 0; } +static int +qeth_set_offline(struct ccwgroup_device *cgdev) +{ + return __qeth_set_offline(cgdev, 0); +} + static int qeth_wait_for_threads(struct qeth_card *card, unsigned long threads); @@ -953,8 +962,8 @@ qeth_recover(void *ptr) PRINT_WARN("Recovery of device %s started ...\n", CARD_BUS_ID(card)); card->use_hard_stop = 1; - qeth_set_offline(card->gdev); - rc = qeth_set_online(card->gdev); + __qeth_set_offline(card->gdev,1); + rc = __qeth_set_online(card->gdev,1); if (!rc) PRINT_INFO("Device %s successfully recovered!\n", CARD_BUS_ID(card)); @@ -2152,9 +2161,15 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, if (!skb_len) return NULL; if (card->options.fake_ll){ - if (!(skb = qeth_get_skb(skb_len + QETH_FAKE_LL_LEN))) - goto no_mem; - skb_pull(skb, QETH_FAKE_LL_LEN); + if(card->dev->type == ARPHRD_IEEE802_TR){ + if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_TR))) + goto no_mem; + skb_reserve(skb,QETH_FAKE_LL_LEN_TR); + } else { + if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_ETH))) + goto no_mem; + skb_reserve(skb,QETH_FAKE_LL_LEN_ETH); + } } else if (!(skb = qeth_get_skb(skb_len))) goto no_mem; data_ptr = element->addr + offset; @@ -2229,14 +2244,68 @@ qeth_type_trans(struct sk_buff *skb, struct net_device *dev) } static inline void -qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb, +qeth_rebuild_skb_fake_ll_tr(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr *hdr) +{ + struct trh_hdr *fake_hdr; + struct trllc *fake_llc; + struct iphdr *ip_hdr; + + QETH_DBF_TEXT(trace,5,"skbfktr"); + skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_TR; + /* this is a fake ethernet header */ + fake_hdr = (struct trh_hdr *) skb->mac.raw; + + /* the destination MAC address */ + switch (skb->pkt_type){ + case PACKET_MULTICAST: + switch (skb->protocol){ +#ifdef CONFIG_QETH_IPV6 + case __constant_htons(ETH_P_IPV6): + ndisc_mc_map((struct in6_addr *) + skb->data + QETH_FAKE_LL_V6_ADDR_POS, + fake_hdr->daddr, card->dev, 0); + break; +#endif /* CONFIG_QETH_IPV6 */ + case __constant_htons(ETH_P_IP): + ip_hdr = (struct iphdr *)skb->data; + ip_tr_mc_map(ip_hdr->daddr, fake_hdr->daddr); + break; + default: + memcpy(fake_hdr->daddr, card->dev->dev_addr, TR_ALEN); + } + break; + case PACKET_BROADCAST: + memset(fake_hdr->daddr, 0xff, TR_ALEN); + break; + default: + memcpy(fake_hdr->daddr, card->dev->dev_addr, TR_ALEN); + } + /* the source MAC address */ + if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR) + memcpy(fake_hdr->saddr, &hdr->hdr.l3.dest_addr[2], TR_ALEN); + else + memset(fake_hdr->saddr, 0, TR_ALEN); + fake_hdr->rcf=0; + fake_llc = (struct trllc*)&(fake_hdr->rcf); + fake_llc->dsap = EXTENDED_SAP; + fake_llc->ssap = EXTENDED_SAP; + fake_llc->llc = UI_CMD; + fake_llc->protid[0] = 0; + fake_llc->protid[1] = 0; + fake_llc->protid[2] = 0; + fake_llc->ethertype = ETH_P_IP; +} + +static inline void +qeth_rebuild_skb_fake_ll_eth(struct qeth_card *card, struct sk_buff *skb, struct qeth_hdr *hdr) { struct ethhdr *fake_hdr; struct iphdr *ip_hdr; - QETH_DBF_TEXT(trace,5,"skbfake"); - skb->mac.raw = skb->data - QETH_FAKE_LL_LEN; + QETH_DBF_TEXT(trace,5,"skbfketh"); + skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_ETH; /* this is a fake ethernet header */ fake_hdr = (struct ethhdr *) skb->mac.raw; @@ -2253,10 +2322,7 @@ qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb, #endif /* CONFIG_QETH_IPV6 */ case __constant_htons(ETH_P_IP): ip_hdr = (struct iphdr *)skb->data; - if (card->dev->type == ARPHRD_IEEE802_TR) - ip_tr_mc_map(ip_hdr->daddr, fake_hdr->h_dest); - else - ip_eth_mc_map(ip_hdr->daddr, fake_hdr->h_dest); + ip_eth_mc_map(ip_hdr->daddr, fake_hdr->h_dest); break; default: memcpy(fake_hdr->h_dest, card->dev->dev_addr, ETH_ALEN); @@ -2277,6 +2343,16 @@ qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb, fake_hdr->h_proto = skb->protocol; } +static inline void +qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr *hdr) +{ + if (card->dev->type == ARPHRD_IEEE802_TR) + qeth_rebuild_skb_fake_ll_tr(card, skb, hdr); + else + qeth_rebuild_skb_fake_ll_eth(card, skb, hdr); +} + static inline void qeth_rebuild_skb_vlan(struct qeth_card *card, struct sk_buff *skb, struct qeth_hdr *hdr) @@ -3440,16 +3516,25 @@ qeth_fake_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { - struct ethhdr *hdr; + if(dev->type == ARPHRD_IEEE802_TR){ + struct trh_hdr *hdr; + hdr = (struct trh_hdr *)skb_push(skb, QETH_FAKE_LL_LEN_TR); + memcpy(hdr->saddr, dev->dev_addr, TR_ALEN); + memcpy(hdr->daddr, "FAKELL", TR_ALEN); + return QETH_FAKE_LL_LEN_TR; - hdr = (struct ethhdr *)skb_push(skb, QETH_FAKE_LL_LEN); - memcpy(hdr->h_source, dev->dev_addr, ETH_ALEN); - memcpy(hdr->h_dest, "FAKELL", ETH_ALEN); - if (type != ETH_P_802_3) - hdr->h_proto = htons(type); - else - hdr->h_proto = htons(len); - return QETH_FAKE_LL_LEN; + } else { + struct ethhdr *hdr; + hdr = (struct ethhdr *)skb_push(skb, QETH_FAKE_LL_LEN_ETH); + memcpy(hdr->h_source, dev->dev_addr, ETH_ALEN); + memcpy(hdr->h_dest, "FAKELL", ETH_ALEN); + if (type != ETH_P_802_3) + hdr->h_proto = htons(type); + else + hdr->h_proto = htons(len); + return QETH_FAKE_LL_LEN_ETH; + + } } static inline int @@ -3710,16 +3795,12 @@ static inline int qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb, struct qeth_hdr **hdr, int ipv) { - int rc = 0; #ifdef CONFIG_QETH_VLAN u16 *tag; #endif QETH_DBF_TEXT(trace, 6, "prepskb"); - rc = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr)); - if (rc) - return rc; #ifdef CONFIG_QETH_VLAN if (card->vlangrp && vlan_tx_tag_present(*skb) && ((ipv == 6) || card->options.layer2) ) { @@ -3882,9 +3963,15 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, memcpy(hdr->hdr.l3.dest_addr, &skb->nh.ipv6h->daddr, 16); } } else { /* passthrough */ - if (!memcmp(skb->data + sizeof(struct qeth_hdr), + if((skb->dev->type == ARPHRD_IEEE802_TR) && + !memcmp(skb->data + sizeof(struct qeth_hdr) + + sizeof(__u16), skb->dev->broadcast, 6)) { + hdr->hdr.l3.flags = QETH_CAST_BROADCAST | + QETH_HDR_PASSTHRU; + } else if (!memcmp(skb->data + sizeof(struct qeth_hdr), skb->dev->broadcast, 6)) { /* broadcast? */ - hdr->hdr.l3.flags = QETH_CAST_BROADCAST | QETH_HDR_PASSTHRU; + hdr->hdr.l3.flags = QETH_CAST_BROADCAST | + QETH_HDR_PASSTHRU; } else { hdr->hdr.l3.flags = (cast_type == RTN_MULTICAST) ? QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU : @@ -3893,68 +3980,30 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, } } -static inline void -__qeth_fill_buffer_frag(struct sk_buff *skb, struct qdio_buffer *buffer, - int *next_element_to_fill) -{ - int length = skb->len; - struct skb_frag_struct *frag; - int fragno; - unsigned long addr; - int element; - int first_lap = 1; - - fragno = skb_shinfo(skb)->nr_frags; /* start with last frag */ - element = *next_element_to_fill + fragno; - while (length > 0) { - if (fragno > 0) { - frag = &skb_shinfo(skb)->frags[fragno - 1]; - addr = (page_to_pfn(frag->page) << PAGE_SHIFT) + - frag->page_offset; - buffer->element[element].addr = (char *)addr; - buffer->element[element].length = frag->size; - length -= frag->size; - if (first_lap) - buffer->element[element].flags = - SBAL_FLAGS_LAST_FRAG; - else - buffer->element[element].flags = - SBAL_FLAGS_MIDDLE_FRAG; - } else { - buffer->element[element].addr = skb->data; - buffer->element[element].length = length; - length = 0; - buffer->element[element].flags = - SBAL_FLAGS_FIRST_FRAG; - } - element--; - fragno--; - first_lap = 0; - } - *next_element_to_fill += skb_shinfo(skb)->nr_frags + 1; -} - static inline void __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer, - int *next_element_to_fill) + int is_tso, int *next_element_to_fill) { int length = skb->len; int length_here; int element; char *data; - int first_lap = 1; + int first_lap ; element = *next_element_to_fill; data = skb->data; + first_lap = (is_tso == 0 ? 1 : 0); + while (length > 0) { /* length_here is the remaining amount of data in this page */ length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); if (length < length_here) length_here = length; + buffer->element[element].addr = data; buffer->element[element].length = length_here; length -= length_here; - if (!length){ + if (!length) { if (first_lap) buffer->element[element].flags = 0; else @@ -3981,17 +4030,35 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct sk_buff *skb) { struct qdio_buffer *buffer; - int flush_cnt = 0; + struct qeth_hdr_tso *hdr; + int flush_cnt = 0, hdr_len, large_send = 0; QETH_DBF_TEXT(trace, 6, "qdfillbf"); + buffer = buf->buffer; atomic_inc(&skb->users); skb_queue_tail(&buf->skb_list, skb); + + hdr = (struct qeth_hdr_tso *) skb->data; + /*check first on TSO ....*/ + if (hdr->hdr.hdr.l3.id == QETH_HEADER_TYPE_TSO) { + int element = buf->next_element_to_fill; + + hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len; + /*fill first buffer entry only with header information */ + buffer->element[element].addr = skb->data; + buffer->element[element].length = hdr_len; + buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG; + buf->next_element_to_fill++; + skb->data += hdr_len; + skb->len -= hdr_len; + large_send = 1; + } if (skb_shinfo(skb)->nr_frags == 0) - __qeth_fill_buffer(skb, buffer, + __qeth_fill_buffer(skb, buffer, large_send, (int *)&buf->next_element_to_fill); else - __qeth_fill_buffer_frag(skb, buffer, + __qeth_fill_buffer_frag(skb, buffer, large_send, (int *)&buf->next_element_to_fill); if (!queue->do_pack) { @@ -4183,6 +4250,25 @@ out: return rc; } +static inline int +qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb) +{ + int elements_needed = 0; + + if (skb_shinfo(skb)->nr_frags > 0) { + elements_needed = (skb_shinfo(skb)->nr_frags + 1); + } + if (elements_needed == 0 ) + elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) + + skb->len) >> PAGE_SHIFT); + if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){ + PRINT_ERR("qeth_do_send_packet: invalid size of " + "IP packet. Discarded."); + return 0; + } + return elements_needed; +} + static inline int qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) { @@ -4205,7 +4291,11 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) dev_kfree_skb_irq(skb); return 0; } - skb_pull(skb, QETH_FAKE_LL_LEN); + if(card->dev->type == ARPHRD_IEEE802_TR){ + skb_pull(skb, QETH_FAKE_LL_LEN_TR); + } else { + skb_pull(skb, QETH_FAKE_LL_LEN_ETH); + } } } cast_type = qeth_get_cast_type(card, skb); @@ -4221,19 +4311,25 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) if (skb_shinfo(skb)->tso_size) large_send = card->options.large_send; - if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){ - QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc); - return rc; - } /*are we able to do TSO ? If so ,prepare and send it from here */ if ((large_send == QETH_LARGE_SEND_TSO) && (cast_type == RTN_UNSPEC)) { - rc = qeth_tso_send_packet(card, skb, queue, - ipv, cast_type); - goto do_statistics; + rc = qeth_tso_prepare_packet(card, skb, ipv, cast_type); + if (rc) { + card->stats.tx_dropped++; + card->stats.tx_errors++; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + elements_needed++; + } else { + if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))) { + QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc); + return rc; + } + qeth_fill_header(card, hdr, skb, ipv, cast_type); } - qeth_fill_header(card, hdr, skb, ipv, cast_type); if (large_send == QETH_LARGE_SEND_EDDP) { ctx = qeth_eddp_create_context(card, skb, hdr); if (ctx == NULL) { @@ -4241,7 +4337,7 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) return -EINVAL; } } else { - elements_needed = qeth_get_elements_no(card,(void*) hdr, skb); + elements_needed += qeth_get_elements_no(card,(void*) hdr, skb); if (!elements_needed) return -EINVAL; } @@ -4252,12 +4348,12 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) else rc = qeth_do_send_packet_fast(card, queue, skb, hdr, elements_needed, ctx); -do_statistics: if (!rc){ card->stats.tx_packets++; card->stats.tx_bytes += skb->len; #ifdef CONFIG_QETH_PERF_STATS - if (skb_shinfo(skb)->tso_size) { + if (skb_shinfo(skb)->tso_size && + !(large_send == QETH_LARGE_SEND_NO)) { card->perf_stats.large_send_bytes += skb->len; card->perf_stats.large_send_cnt++; } @@ -7154,7 +7250,7 @@ qeth_wait_for_threads(struct qeth_card *card, unsigned long threads) } static int -qeth_stop_card(struct qeth_card *card) +qeth_stop_card(struct qeth_card *card, int recovery_mode) { int rc = 0; @@ -7167,9 +7263,13 @@ qeth_stop_card(struct qeth_card *card) if (card->read.state == CH_STATE_UP && card->write.state == CH_STATE_UP && (card->state == CARD_STATE_UP)) { - rtnl_lock(); - dev_close(card->dev); - rtnl_unlock(); + if(recovery_mode) { + qeth_stop(card->dev); + } else { + rtnl_lock(); + dev_close(card->dev); + rtnl_unlock(); + } if (!card->use_hard_stop) { __u8 *mac = &card->dev->dev_addr[0]; rc = qeth_layer2_send_delmac(card, mac); @@ -7341,13 +7441,17 @@ qeth_register_netdev(struct qeth_card *card) } static void -qeth_start_again(struct qeth_card *card) +qeth_start_again(struct qeth_card *card, int recovery_mode) { QETH_DBF_TEXT(setup ,2, "startag"); - rtnl_lock(); - dev_open(card->dev); - rtnl_unlock(); + if(recovery_mode) { + qeth_open(card->dev); + } else { + rtnl_lock(); + dev_open(card->dev); + rtnl_unlock(); + } /* this also sets saved unicast addresses */ qeth_set_multicast_list(card->dev); } @@ -7404,7 +7508,7 @@ static void qeth_make_parameters_consistent(struct qeth_card *card) static int -qeth_set_online(struct ccwgroup_device *gdev) +__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode) { struct qeth_card *card = gdev->dev.driver_data; int rc = 0; @@ -7464,12 +7568,12 @@ qeth_set_online(struct ccwgroup_device *gdev) * we can also use this state for recovery purposes*/ qeth_set_allowed_threads(card, 0xffffffff, 0); if (recover_flag == CARD_STATE_RECOVER) - qeth_start_again(card); + qeth_start_again(card, recovery_mode); qeth_notify_processes(); return 0; out_remove: card->use_hard_stop = 1; - qeth_stop_card(card); + qeth_stop_card(card, 0); ccw_device_set_offline(CARD_DDEV(card)); ccw_device_set_offline(CARD_WDEV(card)); ccw_device_set_offline(CARD_RDEV(card)); @@ -7480,6 +7584,12 @@ out_remove: return -ENODEV; } +static int +qeth_set_online(struct ccwgroup_device *gdev) +{ + return __qeth_set_online(gdev, 0); +} + static struct ccw_device_id qeth_ids[] = { {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE}, {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD}, diff --git a/drivers/s390/net/qeth_tso.c b/drivers/s390/net/qeth_tso.c deleted file mode 100644 index c91976274e7b..000000000000 --- a/drivers/s390/net/qeth_tso.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * linux/drivers/s390/net/qeth_tso.c ($Revision: 1.6 $) - * - * Header file for qeth TCP Segmentation Offload support. - * - * Copyright 2004 IBM Corporation - * - * Author(s): Frank Pavlic - * - * $Revision: 1.6 $ $Date: 2005/03/24 09:04:18 $ - * - */ - -#include -#include -#include -#include -#include -#include "qeth.h" -#include "qeth_mpc.h" -#include "qeth_tso.h" - -/** - * skb already partially prepared - * classic qdio header in skb->data - * */ -static inline struct qeth_hdr_tso * -qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb) -{ - int rc = 0; - - QETH_DBF_TEXT(trace, 5, "tsoprsk"); - rc = qeth_realloc_headroom(card, skb,sizeof(struct qeth_hdr_ext_tso)); - if (rc) - return NULL; - - return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_ext_tso)); -} - -/** - * fill header for a TSO packet - */ -static inline void -qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb) -{ - struct qeth_hdr_tso *hdr; - struct tcphdr *tcph; - struct iphdr *iph; - - QETH_DBF_TEXT(trace, 5, "tsofhdr"); - - hdr = (struct qeth_hdr_tso *) skb->data; - iph = skb->nh.iph; - tcph = skb->h.th; - /*fix header to TSO values ...*/ - hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO; - /*set values which are fix for the first approach ...*/ - hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso); - hdr->ext.imb_hdr_no = 1; - hdr->ext.hdr_type = 1; - hdr->ext.hdr_version = 1; - hdr->ext.hdr_len = 28; - /*insert non-fix values */ - hdr->ext.mss = skb_shinfo(skb)->tso_size; - hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4); - hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len - - sizeof(struct qeth_hdr_tso)); -} - -/** - * change some header values as requested by hardware - */ -static inline void -qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb) -{ - struct iphdr *iph; - struct ipv6hdr *ip6h; - struct tcphdr *tcph; - - iph = skb->nh.iph; - ip6h = skb->nh.ipv6h; - tcph = skb->h.th; - - tcph->check = 0; - if (skb->protocol == ETH_P_IPV6) { - ip6h->payload_len = 0; - tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, - 0, IPPROTO_TCP, 0); - return; - } - /*OSA want us to set these values ...*/ - tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, - 0, IPPROTO_TCP, 0); - iph->tot_len = 0; - iph->check = 0; -} - -static inline struct qeth_hdr_tso * -qeth_tso_prepare_packet(struct qeth_card *card, struct sk_buff *skb, - int ipv, int cast_type) -{ - struct qeth_hdr_tso *hdr; - int rc = 0; - - QETH_DBF_TEXT(trace, 5, "tsoprep"); - - /*get headroom for tso qdio header */ - hdr = (struct qeth_hdr_tso *) qeth_tso_prepare_skb(card, &skb); - if (hdr == NULL) { - QETH_DBF_TEXT_(trace, 4, "2err%d", rc); - return NULL; - } - memset(hdr, 0, sizeof(struct qeth_hdr_tso)); - /*fill first 32 bytes of qdio header as used - *FIXME: TSO has two struct members - * with different names but same size - * */ - qeth_fill_header(card, &hdr->hdr, skb, ipv, cast_type); - qeth_tso_fill_header(card, skb); - qeth_tso_set_tcpip_header(card, skb); - return hdr; -} - -static inline int -qeth_tso_get_queue_buffer(struct qeth_qdio_out_q *queue) -{ - struct qeth_qdio_out_buffer *buffer; - int flush_cnt = 0; - - QETH_DBF_TEXT(trace, 5, "tsobuf"); - - /* force to non-packing*/ - if (queue->do_pack) - queue->do_pack = 0; - buffer = &queue->bufs[queue->next_buf_to_fill]; - /* get a new buffer if current is already in use*/ - if ((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) && - (buffer->next_element_to_fill > 0)) { - atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); - queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % - QDIO_MAX_BUFFERS_PER_Q; - flush_cnt++; - } - return flush_cnt; -} - -static inline void -__qeth_tso_fill_buffer_frag(struct qeth_qdio_out_buffer *buf, - struct sk_buff *skb) -{ - struct skb_frag_struct *frag; - struct qdio_buffer *buffer; - int fragno, cnt, element; - unsigned long addr; - - QETH_DBF_TEXT(trace, 6, "tsfilfrg"); - - /*initialize variables ...*/ - fragno = skb_shinfo(skb)->nr_frags; - buffer = buf->buffer; - element = buf->next_element_to_fill; - /*fill buffer elements .....*/ - for (cnt = 0; cnt < fragno; cnt++) { - frag = &skb_shinfo(skb)->frags[cnt]; - addr = (page_to_pfn(frag->page) << PAGE_SHIFT) + - frag->page_offset; - buffer->element[element].addr = (char *)addr; - buffer->element[element].length = frag->size; - if (cnt < (fragno - 1)) - buffer->element[element].flags = - SBAL_FLAGS_MIDDLE_FRAG; - else - buffer->element[element].flags = - SBAL_FLAGS_LAST_FRAG; - element++; - } - buf->next_element_to_fill = element; -} - -static inline int -qeth_tso_fill_buffer(struct qeth_qdio_out_buffer *buf, - struct sk_buff *skb) -{ - int length, length_here, element; - int hdr_len; - struct qdio_buffer *buffer; - struct qeth_hdr_tso *hdr; - char *data; - - QETH_DBF_TEXT(trace, 3, "tsfilbuf"); - - /*increment user count and queue skb ...*/ - atomic_inc(&skb->users); - skb_queue_tail(&buf->skb_list, skb); - - /*initialize all variables...*/ - buffer = buf->buffer; - hdr = (struct qeth_hdr_tso *)skb->data; - hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len; - data = skb->data + hdr_len; - length = skb->len - hdr_len; - element = buf->next_element_to_fill; - /*fill first buffer entry only with header information */ - buffer->element[element].addr = skb->data; - buffer->element[element].length = hdr_len; - buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG; - buf->next_element_to_fill++; - - if (skb_shinfo(skb)->nr_frags > 0) { - __qeth_tso_fill_buffer_frag(buf, skb); - goto out; - } - - /*start filling buffer entries ...*/ - element++; - while (length > 0) { - /* length_here is the remaining amount of data in this page */ - length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); - if (length < length_here) - length_here = length; - buffer->element[element].addr = data; - buffer->element[element].length = length_here; - length -= length_here; - if (!length) - buffer->element[element].flags = - SBAL_FLAGS_LAST_FRAG; - else - buffer->element[element].flags = - SBAL_FLAGS_MIDDLE_FRAG; - data += length_here; - element++; - } - /*set the buffer to primed ...*/ - buf->next_element_to_fill = element; -out: - atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED); - return 1; -} - -int -qeth_tso_send_packet(struct qeth_card *card, struct sk_buff *skb, - struct qeth_qdio_out_q *queue, int ipv, int cast_type) -{ - int flush_cnt = 0; - struct qeth_hdr_tso *hdr; - struct qeth_qdio_out_buffer *buffer; - int start_index; - - QETH_DBF_TEXT(trace, 3, "tsosend"); - - if (!(hdr = qeth_tso_prepare_packet(card, skb, ipv, cast_type))) - return -ENOMEM; - /*check if skb fits in one SBAL ...*/ - if (!(qeth_get_elements_no(card, (void*)hdr, skb))) - return -EINVAL; - /*lock queue, force switching to non-packing and send it ...*/ - while (atomic_compare_and_swap(QETH_OUT_Q_UNLOCKED, - QETH_OUT_Q_LOCKED, - &queue->state)); - start_index = queue->next_buf_to_fill; - buffer = &queue->bufs[queue->next_buf_to_fill]; - /*check if card is too busy ...*/ - if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){ - card->stats.tx_dropped++; - goto out; - } - /*let's force to non-packing and get a new SBAL*/ - flush_cnt += qeth_tso_get_queue_buffer(queue); - buffer = &queue->bufs[queue->next_buf_to_fill]; - if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) { - card->stats.tx_dropped++; - goto out; - } - flush_cnt += qeth_tso_fill_buffer(buffer, skb); - queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % - QDIO_MAX_BUFFERS_PER_Q; -out: - atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); - if (flush_cnt) - qeth_flush_buffers(queue, 0, start_index, flush_cnt); - /*do some statistics */ - card->stats.tx_packets++; - card->stats.tx_bytes += skb->len; - return 0; -} diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h index 83504dee3f57..ad33e6f466f1 100644 --- a/drivers/s390/net/qeth_tso.h +++ b/drivers/s390/net/qeth_tso.h @@ -1,5 +1,5 @@ /* - * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.4 $) + * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.7 $) * * Header file for qeth TCP Segmentation Offload support. * @@ -7,52 +7,148 @@ * * Author(s): Frank Pavlic * - * $Revision: 1.4 $ $Date: 2005/03/24 09:04:18 $ + * $Revision: 1.7 $ $Date: 2005/05/04 20:19:18 $ * */ #ifndef __QETH_TSO_H__ #define __QETH_TSO_H__ +#include +#include +#include +#include +#include +#include "qeth.h" +#include "qeth_mpc.h" -extern int -qeth_tso_send_packet(struct qeth_card *, struct sk_buff *, - struct qeth_qdio_out_q *, int , int); -struct qeth_hdr_ext_tso { - __u16 hdr_tot_len; - __u8 imb_hdr_no; - __u8 reserved; - __u8 hdr_type; - __u8 hdr_version; - __u16 hdr_len; - __u32 payload_len; - __u16 mss; - __u16 dg_hdr_len; - __u8 padding[16]; -} __attribute__ ((packed)); +static inline struct qeth_hdr_tso * +qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb) +{ + QETH_DBF_TEXT(trace, 5, "tsoprsk"); + return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_tso)); +} -struct qeth_hdr_tso { - struct qeth_hdr hdr; /*hdr->hdr.l3.xxx*/ - struct qeth_hdr_ext_tso ext; -} __attribute__ ((packed)); +/** + * fill header for a TSO packet + */ +static inline void +qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb) +{ + struct qeth_hdr_tso *hdr; + struct tcphdr *tcph; + struct iphdr *iph; -/*some helper functions*/ + QETH_DBF_TEXT(trace, 5, "tsofhdr"); + + hdr = (struct qeth_hdr_tso *) skb->data; + iph = skb->nh.iph; + tcph = skb->h.th; + /*fix header to TSO values ...*/ + hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO; + /*set values which are fix for the first approach ...*/ + hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso); + hdr->ext.imb_hdr_no = 1; + hdr->ext.hdr_type = 1; + hdr->ext.hdr_version = 1; + hdr->ext.hdr_len = 28; + /*insert non-fix values */ + hdr->ext.mss = skb_shinfo(skb)->tso_size; + hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4); + hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len - + sizeof(struct qeth_hdr_tso)); +} + +/** + * change some header values as requested by hardware + */ +static inline void +qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb) +{ + struct iphdr *iph; + struct ipv6hdr *ip6h; + struct tcphdr *tcph; + + iph = skb->nh.iph; + ip6h = skb->nh.ipv6h; + tcph = skb->h.th; + + tcph->check = 0; + if (skb->protocol == ETH_P_IPV6) { + ip6h->payload_len = 0; + tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, + 0, IPPROTO_TCP, 0); + return; + } + /*OSA want us to set these values ...*/ + tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, + 0, IPPROTO_TCP, 0); + iph->tot_len = 0; + iph->check = 0; +} static inline int -qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb) +qeth_tso_prepare_packet(struct qeth_card *card, struct sk_buff *skb, + int ipv, int cast_type) { - int elements_needed = 0; + struct qeth_hdr_tso *hdr; - if (skb_shinfo(skb)->nr_frags > 0) - elements_needed = (skb_shinfo(skb)->nr_frags + 1); - if (elements_needed == 0 ) - elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) - + skb->len) >> PAGE_SHIFT); - if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){ - PRINT_ERR("qeth_do_send_packet: invalid size of " - "IP packet. Discarded."); - return 0; + QETH_DBF_TEXT(trace, 5, "tsoprep"); + + hdr = (struct qeth_hdr_tso *) qeth_tso_prepare_skb(card, &skb); + if (hdr == NULL) { + QETH_DBF_TEXT(trace, 4, "tsoperr"); + return -ENOMEM; } - return elements_needed; + memset(hdr, 0, sizeof(struct qeth_hdr_tso)); + /*fill first 32 bytes of qdio header as used + *FIXME: TSO has two struct members + * with different names but same size + * */ + qeth_fill_header(card, &hdr->hdr, skb, ipv, cast_type); + qeth_tso_fill_header(card, skb); + qeth_tso_set_tcpip_header(card, skb); + return 0; +} + +static inline void +__qeth_fill_buffer_frag(struct sk_buff *skb, struct qdio_buffer *buffer, + int is_tso, int *next_element_to_fill) +{ + struct skb_frag_struct *frag; + int fragno; + unsigned long addr; + int element, cnt, dlen; + + fragno = skb_shinfo(skb)->nr_frags; + element = *next_element_to_fill; + dlen = 0; + + if (is_tso) + buffer->element[element].flags = + SBAL_FLAGS_MIDDLE_FRAG; + else + buffer->element[element].flags = + SBAL_FLAGS_FIRST_FRAG; + if ( (dlen = (skb->len - skb->data_len)) ) { + buffer->element[element].addr = skb->data; + buffer->element[element].length = dlen; + element++; + } + for (cnt = 0; cnt < fragno; cnt++) { + frag = &skb_shinfo(skb)->frags[cnt]; + addr = (page_to_pfn(frag->page) << PAGE_SHIFT) + + frag->page_offset; + buffer->element[element].addr = (char *)addr; + buffer->element[element].length = frag->size; + if (cnt < (fragno - 1)) + buffer->element[element].flags = + SBAL_FLAGS_MIDDLE_FRAG; + else + buffer->element[element].flags = + SBAL_FLAGS_LAST_FRAG; + element++; + } + *next_element_to_fill = element; } #endif /* __QETH_TSO_H__ */