diff options
Diffstat (limited to 'drivers/s390')
26 files changed, 808 insertions, 434 deletions
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 1b78f63..7676997 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -125,7 +125,7 @@ static int qstat_seq_open(struct inode *inode, struct file *filp) filp->f_path.dentry->d_inode->i_private); } -static struct file_operations debugfs_fops = { +static const struct file_operations debugfs_fops = { .owner = THIS_MODULE, .open = qstat_seq_open, .read = seq_read, diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c index eff9439..968e3c7 100644 --- a/drivers/s390/cio/qdio_perf.c +++ b/drivers/s390/cio/qdio_perf.c @@ -84,7 +84,7 @@ static int qdio_perf_seq_open(struct inode *inode, struct file *filp) return single_open(filp, qdio_perf_proc_show, NULL); } -static struct file_operations qdio_perf_proc_fops = { +static const struct file_operations qdio_perf_proc_fops = { .owner = THIS_MODULE, .open = qdio_perf_seq_open, .read = seq_read, diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile index 96eddb3..6cab5a6 100644 --- a/drivers/s390/net/Makefile +++ b/drivers/s390/net/Makefile @@ -3,11 +3,11 @@ # ctcm-y += ctcm_main.o ctcm_fsms.o ctcm_mpc.o ctcm_sysfs.o ctcm_dbug.o -obj-$(CONFIG_CTCM) += ctcm.o fsm.o cu3088.o +obj-$(CONFIG_CTCM) += ctcm.o fsm.o obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o obj-$(CONFIG_SMSGIUCV) += smsgiucv.o -obj-$(CONFIG_LCS) += lcs.o cu3088.o -obj-$(CONFIG_CLAW) += claw.o cu3088.o +obj-$(CONFIG_LCS) += lcs.o +obj-$(CONFIG_CLAW) += claw.o qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o obj-$(CONFIG_QETH) += qeth.o qeth_l2-y += qeth_l2_main.o diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index c63babe..cf283e3 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -90,7 +90,6 @@ #include <linux/timer.h> #include <linux/types.h> -#include "cu3088.h" #include "claw.h" /* @@ -258,6 +257,9 @@ static int claw_pm_prepare(struct ccwgroup_device *gdev) return -EPERM; } +/* the root device for claw group devices */ +static struct device *claw_root_dev; + /* ccwgroup table */ static struct ccwgroup_driver claw_group_driver = { @@ -272,6 +274,47 @@ static struct ccwgroup_driver claw_group_driver = { .prepare = claw_pm_prepare, }; +static struct ccw_device_id claw_ids[] = { + {CCW_DEVICE(0x3088, 0x61), .driver_info = claw_channel_type_claw}, + {}, +}; +MODULE_DEVICE_TABLE(ccw, claw_ids); + +static struct ccw_driver claw_ccw_driver = { + .owner = THIS_MODULE, + .name = "claw", + .ids = claw_ids, + .probe = ccwgroup_probe_ccwdev, + .remove = ccwgroup_remove_ccwdev, +}; + +static ssize_t +claw_driver_group_store(struct device_driver *ddrv, const char *buf, + size_t count) +{ + int err; + err = ccwgroup_create_from_string(claw_root_dev, + claw_group_driver.driver_id, + &claw_ccw_driver, 3, buf); + return err ? err : count; +} + +static DRIVER_ATTR(group, 0200, NULL, claw_driver_group_store); + +static struct attribute *claw_group_attrs[] = { + &driver_attr_group.attr, + NULL, +}; + +static struct attribute_group claw_group_attr_group = { + .attrs = claw_group_attrs, +}; + +static struct attribute_group *claw_group_attr_groups[] = { + &claw_group_attr_group, + NULL, +}; + /* * Key functions */ @@ -3326,7 +3369,11 @@ claw_remove_files(struct device *dev) static void __exit claw_cleanup(void) { - unregister_cu3088_discipline(&claw_group_driver); + driver_remove_file(&claw_group_driver.driver, + &driver_attr_group); + ccwgroup_driver_unregister(&claw_group_driver); + ccw_driver_unregister(&claw_ccw_driver); + root_device_unregister(claw_root_dev); claw_unregister_debug_facility(); pr_info("Driver unloaded\n"); @@ -3348,16 +3395,31 @@ claw_init(void) if (ret) { pr_err("Registering with the S/390 debug feature" " failed with error code %d\n", ret); - return ret; + goto out_err; } CLAW_DBF_TEXT(2, setup, "init_mod"); - ret = register_cu3088_discipline(&claw_group_driver); - if (ret) { - CLAW_DBF_TEXT(2, setup, "init_bad"); - claw_unregister_debug_facility(); - pr_err("Registering with the cu3088 device driver failed " - "with error code %d\n", ret); - } + claw_root_dev = root_device_register("qeth"); + ret = IS_ERR(claw_root_dev) ? PTR_ERR(claw_root_dev) : 0; + if (ret) + goto register_err; + ret = ccw_driver_register(&claw_ccw_driver); + if (ret) + goto ccw_err; + claw_group_driver.driver.groups = claw_group_attr_groups; + ret = ccwgroup_driver_register(&claw_group_driver); + if (ret) + goto ccwgroup_err; + return 0; + +ccwgroup_err: + ccw_driver_unregister(&claw_ccw_driver); +ccw_err: + root_device_unregister(claw_root_dev); +register_err: + CLAW_DBF_TEXT(2, setup, "init_bad"); + claw_unregister_debug_facility(); +out_err: + pr_err("Initializing the claw device driver failed\n"); return ret; } diff --git a/drivers/s390/net/claw.h b/drivers/s390/net/claw.h index 005072c..46d59a1 100644 --- a/drivers/s390/net/claw.h +++ b/drivers/s390/net/claw.h @@ -129,6 +129,18 @@ static inline int claw_dbf_passes(debug_info_t *dbf_grp, int level) } \ } while (0) +/** + * Enum for classifying detected devices. + */ +enum claw_channel_types { + /* Device is not a channel */ + claw_channel_type_none, + + /* Device is a CLAW channel device */ + claw_channel_type_claw +}; + + /******************************************************* * Define Control Blocks * * * diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index 4ded9ac..70eb7f1 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -44,7 +44,6 @@ #include <asm/idals.h> #include "fsm.h" -#include "cu3088.h" #include "ctcm_dbug.h" #include "ctcm_main.h" diff --git a/drivers/s390/net/ctcm_fsms.h b/drivers/s390/net/ctcm_fsms.h index 2326aba..046d077 100644 --- a/drivers/s390/net/ctcm_fsms.h +++ b/drivers/s390/net/ctcm_fsms.h @@ -39,7 +39,6 @@ #include <asm/idals.h> #include "fsm.h" -#include "cu3088.h" #include "ctcm_main.h" /* diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index c5b8387..558dc32 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -51,12 +51,16 @@ #include <asm/idals.h> -#include "cu3088.h" #include "ctcm_fsms.h" #include "ctcm_main.h" /* Some common global variables */ +/** + * The root device for ctcm group devices + */ +static struct device *ctcm_root_dev; + /* * Linked list of all detected channels. */ @@ -246,7 +250,7 @@ static void channel_remove(struct channel *ch) * * returns Pointer to a channel or NULL if no matching channel available. */ -static struct channel *channel_get(enum channel_types type, +static struct channel *channel_get(enum ctcm_channel_types type, char *id, int direction) { struct channel *ch = channels; @@ -1342,7 +1346,7 @@ static int ctcm_probe_device(struct ccwgroup_device *cgdev) * * returns 0 on success, !0 on error. */ -static int add_channel(struct ccw_device *cdev, enum channel_types type, +static int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type, struct ctcm_priv *priv) { struct channel **c = &channels; @@ -1501,13 +1505,13 @@ free_return: /* note that all channel pointers are 0 or valid */ /* * Return type of a detected device. */ -static enum channel_types get_channel_type(struct ccw_device_id *id) +static enum ctcm_channel_types get_channel_type(struct ccw_device_id *id) { - enum channel_types type; - type = (enum channel_types)id->driver_info; + enum ctcm_channel_types type; + type = (enum ctcm_channel_types)id->driver_info; - if (type == channel_type_ficon) - type = channel_type_escon; + if (type == ctcm_channel_type_ficon) + type = ctcm_channel_type_escon; return type; } @@ -1525,16 +1529,21 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) char read_id[CTCM_ID_SIZE]; char write_id[CTCM_ID_SIZE]; int direction; - enum channel_types type; + enum ctcm_channel_types type; struct ctcm_priv *priv; struct net_device *dev; struct ccw_device *cdev0; struct ccw_device *cdev1; + struct channel *readc; + struct channel *writec; int ret; + int result; priv = dev_get_drvdata(&cgdev->dev); - if (!priv) - return -ENODEV; + if (!priv) { + result = -ENODEV; + goto out_err_result; + } cdev0 = cgdev->cdev[0]; cdev1 = cgdev->cdev[1]; @@ -1545,31 +1554,40 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) snprintf(write_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev1->dev)); ret = add_channel(cdev0, type, priv); - if (ret) - return ret; + if (ret) { + result = ret; + goto out_err_result; + } ret = add_channel(cdev1, type, priv); - if (ret) - return ret; + if (ret) { + result = ret; + goto out_remove_channel1; + } ret = ccw_device_set_online(cdev0); if (ret != 0) { - /* may be ok to fail now - can be done later */ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, "%s(%s) set_online rc=%d", CTCM_FUNTAIL, read_id, ret); + result = -EIO; + goto out_remove_channel2; } ret = ccw_device_set_online(cdev1); if (ret != 0) { - /* may be ok to fail now - can be done later */ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, "%s(%s) set_online rc=%d", CTCM_FUNTAIL, write_id, ret); + + result = -EIO; + goto out_ccw1; } dev = ctcm_init_netdevice(priv); - if (dev == NULL) - goto out; + if (dev == NULL) { + result = -ENODEV; + goto out_ccw2; + } for (direction = READ; direction <= WRITE; direction++) { priv->channel[direction] = @@ -1587,12 +1605,14 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) /* sysfs magic */ SET_NETDEV_DEV(dev, &cgdev->dev); - if (register_netdev(dev)) - goto out_dev; + if (register_netdev(dev)) { + result = -ENODEV; + goto out_dev; + } if (ctcm_add_attributes(&cgdev->dev)) { - unregister_netdev(dev); - goto out_dev; + result = -ENODEV; + goto out_unregister; } strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name)); @@ -1608,13 +1628,22 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) priv->channel[WRITE]->id, priv->protocol); return 0; +out_unregister: + unregister_netdev(dev); out_dev: ctcm_free_netdevice(dev); -out: +out_ccw2: ccw_device_set_offline(cgdev->cdev[1]); +out_ccw1: ccw_device_set_offline(cgdev->cdev[0]); - - return -ENODEV; +out_remove_channel2: + readc = channel_get(type, read_id, READ); + channel_remove(readc); +out_remove_channel1: + writec = channel_get(type, write_id, WRITE); + channel_remove(writec); +out_err_result: + return result; } /** @@ -1695,6 +1724,11 @@ static int ctcm_pm_suspend(struct ccwgroup_device *gdev) return 0; netif_device_detach(priv->channel[READ]->netdev); ctcm_close(priv->channel[READ]->netdev); + if (!wait_event_timeout(priv->fsm->wait_q, + fsm_getstate(priv->fsm) == DEV_STATE_STOPPED, CTCM_TIME_5_SEC)) { + netif_device_attach(priv->channel[READ]->netdev); + return -EBUSY; + } ccw_device_set_offline(gdev->cdev[1]); ccw_device_set_offline(gdev->cdev[0]); return 0; @@ -1719,6 +1753,22 @@ err_out: return rc; } +static struct ccw_device_id ctcm_ids[] = { + {CCW_DEVICE(0x3088, 0x08), .driver_info = ctcm_channel_type_parallel}, + {CCW_DEVICE(0x3088, 0x1e), .driver_info = ctcm_channel_type_ficon}, + {CCW_DEVICE(0x3088, 0x1f), .driver_info = ctcm_channel_type_escon}, + {}, +}; +MODULE_DEVICE_TABLE(ccw, ctcm_ids); + +static struct ccw_driver ctcm_ccw_driver = { + .owner = THIS_MODULE, + .name = "ctcm", + .ids = ctcm_ids, + .probe = ccwgroup_probe_ccwdev, + .remove = ccwgroup_remove_ccwdev, +}; + static struct ccwgroup_driver ctcm_group_driver = { .owner = THIS_MODULE, .name = CTC_DRIVER_NAME, @@ -1733,6 +1783,33 @@ static struct ccwgroup_driver ctcm_group_driver = { .restore = ctcm_pm_resume, }; +static ssize_t +ctcm_driver_group_store(struct device_driver *ddrv, const char *buf, + size_t count) +{ + int err; + + err = ccwgroup_create_from_string(ctcm_root_dev, + ctcm_group_driver.driver_id, + &ctcm_ccw_driver, 2, buf); + return err ? err : count; +} + +static DRIVER_ATTR(group, 0200, NULL, ctcm_driver_group_store); + +static struct attribute *ctcm_group_attrs[] = { + &driver_attr_group.attr, + NULL, +}; + +static struct attribute_group ctcm_group_attr_group = { + .attrs = ctcm_group_attrs, +}; + +static struct attribute_group *ctcm_group_attr_groups[] = { + &ctcm_group_attr_group, + NULL, +}; /* * Module related routines @@ -1746,7 +1823,10 @@ static struct ccwgroup_driver ctcm_group_driver = { */ static void __exit ctcm_exit(void) { - unregister_cu3088_discipline(&ctcm_group_driver); + driver_remove_file(&ctcm_group_driver.driver, &driver_attr_group); + ccwgroup_driver_unregister(&ctcm_group_driver); + ccw_driver_unregister(&ctcm_ccw_driver); + root_device_unregister(ctcm_root_dev); ctcm_unregister_dbf_views(); pr_info("CTCM driver unloaded\n"); } @@ -1772,17 +1852,31 @@ static int __init ctcm_init(void) channels = NULL; ret = ctcm_register_dbf_views(); - if (ret) { - return ret; - } - ret = register_cu3088_discipline(&ctcm_group_driver); - if (ret) { - ctcm_unregister_dbf_views(); - pr_err("%s / register_cu3088_discipline failed, ret = %d\n", - __func__, ret); - return ret; - } + if (ret) + goto out_err; + ctcm_root_dev = root_device_register("ctcm"); + ret = IS_ERR(ctcm_root_dev) ? PTR_ERR(ctcm_root_dev) : 0; + if (ret) + goto register_err; + ret = ccw_driver_register(&ctcm_ccw_driver); + if (ret) + goto ccw_err; + ctcm_group_driver.driver.groups = ctcm_group_attr_groups; + ret = ccwgroup_driver_register(&ctcm_group_driver); + if (ret) + goto ccwgroup_err; print_banner(); + return 0; + +ccwgroup_err: + ccw_driver_unregister(&ctcm_ccw_driver); +ccw_err: + root_device_unregister(ctcm_root_dev); +register_err: + ctcm_unregister_dbf_views(); +out_err: + pr_err("%s / Initializing the ctcm device driver failed, ret = %d\n", + __func__, ret); return ret; } diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h index d925e73..d34fa14 100644 --- a/drivers/s390/net/ctcm_main.h +++ b/drivers/s390/net/ctcm_main.h @@ -16,7 +16,6 @@ #include <linux/netdevice.h> #include "fsm.h" -#include "cu3088.h" #include "ctcm_dbug.h" #include "ctcm_mpc.h" @@ -66,6 +65,23 @@ ctcmpc_dumpit(buf, len); \ } while (0) +/** + * Enum for classifying detected devices + */ +enum ctcm_channel_types { + /* Device is not a channel */ + ctcm_channel_type_none, + + /* Device is a CTC/A */ + ctcm_channel_type_parallel, + + /* Device is a FICON channel */ + ctcm_channel_type_ficon, + + /* Device is a ESCON channel */ + ctcm_channel_type_escon +}; + /* * CCW commands, used in this driver. */ @@ -121,7 +137,7 @@ struct channel { * Type of this channel. * CTC/A or Escon for valid channels. */ - enum channel_types type; + enum ctcm_channel_types type; /* * Misc. flags. See CHANNEL_FLAGS_... below */ diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index 781e18b..5978b39 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -53,7 +53,6 @@ #include <linux/moduleparam.h> #include <asm/idals.h> -#include "cu3088.h" #include "ctcm_mpc.h" #include "ctcm_main.h" #include "ctcm_fsms.h" diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c index 8452bb0..738ad26 100644 --- a/drivers/s390/net/ctcm_sysfs.c +++ b/drivers/s390/net/ctcm_sysfs.c @@ -158,6 +158,15 @@ static ssize_t ctcm_proto_store(struct device *dev, return count; } +const char *ctcm_type[] = { + "not a channel", + "CTC/A", + "FICON channel", + "ESCON channel", + "unknown channel type", + "unsupported channel type", +}; + static ssize_t ctcm_type_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -168,7 +177,7 @@ static ssize_t ctcm_type_show(struct device *dev, return -ENODEV; return sprintf(buf, "%s\n", - cu3088_type[cgdev->cdev[0]->id.driver_info]); + ctcm_type[cgdev->cdev[0]->id.driver_info]); } static DEVICE_ATTR(buffer, 0644, ctcm_buffer_show, ctcm_buffer_write); diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c deleted file mode 100644 index 4838345..0000000 --- a/drivers/s390/net/cu3088.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * CTC / LCS ccw_device driver - * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Arnd Bergmann <arndb@de.ibm.com> - * Cornelia Huck <cornelia.huck@de.ibm.com> - * - * 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. - * - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/err.h> - -#include <asm/ccwdev.h> -#include <asm/ccwgroup.h> - -#include "cu3088.h" - -const char *cu3088_type[] = { - "not a channel", - "CTC/A", - "ESCON channel", - "FICON channel", - "OSA LCS card", - "CLAW channel device", - "unknown channel type", - "unsupported channel type", -}; - -/* static definitions */ - -static struct ccw_device_id cu3088_ids[] = { - { CCW_DEVICE(0x3088, 0x08), .driver_info = channel_type_parallel }, - { CCW_DEVICE(0x3088, 0x1f), .driver_info = channel_type_escon }, - { CCW_DEVICE(0x3088, 0x1e), .driver_info = channel_type_ficon }, - { CCW_DEVICE(0x3088, 0x60), .driver_info = channel_type_osa2 }, - { CCW_DEVICE(0x3088, 0x61), .driver_info = channel_type_claw }, - { /* end of list */ } -}; - -static struct ccw_driver cu3088_driver; - -static struct device *cu3088_root_dev; - -static ssize_t -group_write(struct device_driver *drv, const char *buf, size_t count) -{ - int ret; - struct ccwgroup_driver *cdrv; - - cdrv = to_ccwgroupdrv(drv); - if (!cdrv) - return -EINVAL; - ret = ccwgroup_create_from_string(cu3088_root_dev, cdrv->driver_id, - &cu3088_driver, 2, buf); - - return (ret == 0) ? count : ret; -} - -static DRIVER_ATTR(group, 0200, NULL, group_write); - -/* Register-unregister for ctc&lcs */ -int -register_cu3088_discipline(struct ccwgroup_driver *dcp) -{ - int rc; - - if (!dcp) - return -EINVAL; - - /* Register discipline.*/ - rc = ccwgroup_driver_register(dcp); - if (rc) - return rc; - - rc = driver_create_file(&dcp->driver, &driver_attr_group); - if (rc) - ccwgroup_driver_unregister(dcp); - - return rc; - -} - -void -unregister_cu3088_discipline(struct ccwgroup_driver *dcp) -{ - if (!dcp) - return; - - driver_remove_file(&dcp->driver, &driver_attr_group); - ccwgroup_driver_unregister(dcp); -} - -static struct ccw_driver cu3088_driver = { - .owner = THIS_MODULE, - .ids = cu3088_ids, - .name = "cu3088", - .probe = ccwgroup_probe_ccwdev, - .remove = ccwgroup_remove_ccwdev, -}; - -/* module setup */ -static int __init -cu3088_init (void) -{ - int rc; - - cu3088_root_dev = root_device_register("cu3088"); - if (IS_ERR(cu3088_root_dev)) - return PTR_ERR(cu3088_root_dev); - rc = ccw_driver_register(&cu3088_driver); - if (rc) - root_device_unregister(cu3088_root_dev); - - return rc; -} - -static void __exit -cu3088_exit (void) -{ - ccw_driver_unregister(&cu3088_driver); - root_device_unregister(cu3088_root_dev); -} - -MODULE_DEVICE_TABLE(ccw,cu3088_ids); -MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); -MODULE_LICENSE("GPL"); - -module_init(cu3088_init); -module_exit(cu3088_exit); - -EXPORT_SYMBOL_GPL(cu3088_type); -EXPORT_SYMBOL_GPL(register_cu3088_discipline); -EXPORT_SYMBOL_GPL(unregister_cu3088_discipline); diff --git a/drivers/s390/net/cu3088.h b/drivers/s390/net/cu3088.h deleted file mode 100644 index d8558a7..0000000 --- a/drivers/s390/net/cu3088.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _CU3088_H -#define _CU3088_H - -/** - * Enum for classifying detected devices. - */ -enum channel_types { - /* Device is not a channel */ - channel_type_none, - - /* Device is a CTC/A */ - channel_type_parallel, - - /* Device is a ESCON channel */ - channel_type_escon, - - /* Device is a FICON channel */ - channel_type_ficon, - - /* 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, - - /* Device is an unsupported model */ - channel_type_unsupported, - - /* number of type entries */ - num_channel_types -}; - -extern const char *cu3088_type[num_channel_types]; -extern int register_cu3088_discipline(struct ccwgroup_driver *); -extern void unregister_cu3088_discipline(struct ccwgroup_driver *); - -#endif diff --git a/drivers/s390/net/fsm.c b/drivers/s390/net/fsm.c index 2c1db80..cae48cb 100644 --- a/drivers/s390/net/fsm.c +++ b/drivers/s390/net/fsm.c @@ -27,6 +27,7 @@ init_fsm(char *name, const char **state_names, const char **event_names, int nr_ return NULL; } strlcpy(this->name, name, sizeof(this->name)); + init_waitqueue_head(&this->wait_q); f = kzalloc(sizeof(fsm), order); if (f == NULL) { diff --git a/drivers/s390/net/fsm.h b/drivers/s390/net/fsm.h index af679c1..1e8b235 100644 --- a/drivers/s390/net/fsm.h +++ b/drivers/s390/net/fsm.h @@ -66,6 +66,7 @@ typedef struct fsm_instance_t { char name[16]; void *userdata; int userint; + wait_queue_head_t wait_q; #if FSM_DEBUG_HISTORY int history_index; int history_size; @@ -197,6 +198,7 @@ fsm_newstate(fsm_instance *fi, int newstate) printk(KERN_DEBUG "fsm(%s): New state %s\n", fi->name, fi->f->state_names[newstate]); #endif + wake_up(&fi->wait_q); } /** diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index a70de9b..1d43d23 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -47,7 +47,6 @@ #include <asm/ccwgroup.h> #include "lcs.h" -#include "cu3088.h" #if !defined(CONFIG_NET_ETHERNET) && \ @@ -60,7 +59,11 @@ */ static char version[] __initdata = "LCS driver"; -static char debug_buffer[255]; + +/** + * the root device for lcs group devices + */ +static struct device *lcs_root_dev; /** * Some prototypes. @@ -76,6 +79,7 @@ static int lcs_recovery(void *ptr); /** * Debug Facility Stuff */ +static char debug_buffer[255]; static debug_info_t *lcs_dbf_setup; static debug_info_t *lcs_dbf_trace; @@ -889,7 +893,7 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer, rc = lcs_ready_buffer(&card->write, buffer); if (rc) return rc; - init_timer(&timer); + init_timer_on_stack(&timer); timer.function = lcs_lancmd_timeout; timer.data = (unsigned long) reply; timer.expires = jiffies + HZ*card->lancmd_timeout; @@ -1968,6 +1972,15 @@ lcs_portno_store (struct device *dev, struct device_attribute *attr, const char static DEVICE_ATTR(portno, 0644, lcs_portno_show, lcs_portno_store); +const char *lcs_type[] = { + "not a channel", + "2216 parallel", + "2216 channel", + "OSA LCS card", + "unknown channel type", + "unsupported channel type", +}; + static ssize_t lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1977,7 +1990,7 @@ lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf) if (!cgdev) return -ENODEV; - return sprintf(buf, "%s\n", cu3088_type[cgdev->cdev[0]->id.driver_info]); + return sprintf(buf, "%s\n", lcs_type[cgdev->cdev[0]->id.driver_info]); } static DEVICE_ATTR(type, 0444, lcs_type_show, NULL); @@ -2130,8 +2143,12 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) card->write.ccwdev = ccwgdev->cdev[1]; recover_state = card->state; - ccw_device_set_online(card->read.ccwdev); - ccw_device_set_online(card->write.ccwdev); + rc = ccw_device_set_online(card->read.ccwdev); + if (rc) + goto out_err; + rc = ccw_device_set_online(card->write.ccwdev); + if (rc) + goto out_werr; LCS_DBF_TEXT(3, setup, "lcsnewdv"); @@ -2210,8 +2227,10 @@ netdev_out: return 0; out: - ccw_device_set_offline(card->read.ccwdev); ccw_device_set_offline(card->write.ccwdev); +out_werr: + ccw_device_set_offline(card->read.ccwdev); +out_err: return -ENODEV; } @@ -2364,6 +2383,22 @@ static int lcs_restore(struct ccwgroup_device *gdev) return lcs_pm_resume(card); } +static struct ccw_device_id lcs_ids[] = { + {CCW_DEVICE(0x3088, 0x08), .driver_info = lcs_channel_type_parallel}, + {CCW_DEVICE(0x3088, 0x1f), .driver_info = lcs_channel_type_2216}, + {CCW_DEVICE(0x3088, 0x60), .driver_info = lcs_channel_type_osa2}, + {}, +}; +MODULE_DEVICE_TABLE(ccw, lcs_ids); + +static struct ccw_driver lcs_ccw_driver = { + .owner = THIS_MODULE, + .name = "lcs", + .ids = lcs_ids, + .probe = ccwgroup_probe_ccwdev, + .remove = ccwgroup_remove_ccwdev, +}; + /** * LCS ccwgroup driver registration */ @@ -2383,6 +2418,33 @@ static struct ccwgroup_driver lcs_group_driver = { .restore = lcs_restore, }; +static ssize_t +lcs_driver_group_store(struct device_driver *ddrv, const char *buf, + size_t count) +{ + int err; + err = ccwgroup_create_from_string(lcs_root_dev, + lcs_group_driver.driver_id, + &lcs_ccw_driver, 2, buf); + return err ? err : count; +} + +static DRIVER_ATTR(group, 0200, NULL, lcs_driver_group_store); + +static struct attribute *lcs_group_attrs[] = { + &driver_attr_group.attr, + NULL, +}; + +static struct attribute_group lcs_group_attr_group = { + .attrs = lcs_group_attrs, +}; + +static struct attribute_group *lcs_group_attr_groups[] = { + &lcs_group_attr_group, + NULL, +}; + /** * LCS Module/Kernel initialization function */ @@ -2394,17 +2456,30 @@ __init lcs_init_module(void) pr_info("Loading %s\n", version); rc = lcs_register_debug_facility(); LCS_DBF_TEXT(0, setup, "lcsinit"); - if (rc) { - pr_err("Initialization failed\n"); - return rc; - } - - rc = register_cu3088_discipline(&lcs_group_driver); - if (rc) { - pr_err("Initialization failed\n"); - return rc; - } + if (rc) + goto out_err; + lcs_root_dev = root_device_register("lcs"); + rc = IS_ERR(lcs_root_dev) ? PTR_ERR(lcs_root_dev) : 0; + if (rc) + goto register_err; + rc = ccw_driver_register(&lcs_ccw_driver); + if (rc) + goto ccw_err; + lcs_group_driver.driver.groups = lcs_group_attr_groups; + rc = ccwgroup_driver_register(&lcs_group_driver); + if (rc) + goto ccwgroup_err; return 0; + +ccwgroup_err: + ccw_driver_unregister(&lcs_ccw_driver); +ccw_err: + root_device_unregister(lcs_root_dev); +register_err: + lcs_unregister_debug_facility(); +out_err: + pr_err("Initializing the lcs device driver failed\n"); + return rc; } @@ -2416,7 +2491,11 @@ __exit lcs_cleanup_module(void) { pr_info("Terminating lcs module.\n"); LCS_DBF_TEXT(0, trace, "cleanup"); - unregister_cu3088_discipline(&lcs_group_driver); + driver_remove_file(&lcs_group_driver.driver, + &driver_attr_group); + ccwgroup_driver_unregister(&lcs_group_driver); + ccw_driver_unregister(&lcs_ccw_driver); + root_device_unregister(lcs_root_dev); lcs_unregister_debug_facility(); } diff --git a/drivers/s390/net/lcs.h b/drivers/s390/net/lcs.h index 6d668642..8c03392 100644 --- a/drivers/s390/net/lcs.h +++ b/drivers/s390/net/lcs.h @@ -36,6 +36,24 @@ static inline int lcs_dbf_passes(debug_info_t *dbf_grp, int level) #define CARD_FROM_DEV(cdev) \ (struct lcs_card *) dev_get_drvdata( \ &((struct ccwgroup_device *)dev_get_drvdata(&cdev->dev))->dev); + +/** + * Enum for classifying detected devices. + */ +enum lcs_channel_types { + /* Device is not a channel */ + lcs_channel_type_none, + + /* Device is a 2216 channel */ + lcs_channel_type_parallel, + + /* Device is a 2216 channel */ + lcs_channel_type_2216, + + /* Device is a OSA2 card */ + lcs_channel_type_osa2 +}; + /** * CCW commands used in this driver */ diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index c84eadd..395c04c 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -741,13 +741,13 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg) if (single_flag) { if ((skb = skb_dequeue(&conn->commit_queue))) { atomic_dec(&skb->users); - dev_kfree_skb_any(skb); if (privptr) { privptr->stats.tx_packets++; privptr->stats.tx_bytes += (skb->len - NETIUCV_HDRLEN - - NETIUCV_HDRLEN); + - NETIUCV_HDRLEN); } + dev_kfree_skb_any(skb); } } conn->tx_buff->data = conn->tx_buff->head; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 31a2b4e..b232693 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -122,7 +122,6 @@ struct qeth_perf_stats { __u64 outbound_do_qdio_start_time; unsigned int outbound_do_qdio_cnt; unsigned int outbound_do_qdio_time; - /* eddp data */ unsigned int large_send_bytes; unsigned int large_send_cnt; unsigned int sg_skbs_sent; @@ -135,6 +134,7 @@ struct qeth_perf_stats { unsigned int sg_frags_rx; unsigned int sg_alloc_page_rx; unsigned int tx_csum; + unsigned int tx_lin; }; /* Routing stuff */ @@ -648,6 +648,7 @@ struct qeth_card_options { enum qeth_large_send_types large_send; int performance_stats; int rx_sg_cb; + enum qeth_ipa_isolation_modes isolation; }; /* @@ -776,7 +777,6 @@ static inline void qeth_put_buffer_pool_entry(struct qeth_card *card, list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list); } -struct qeth_eddp_context; extern struct ccwgroup_driver qeth_l2_ccwgroup_driver; extern struct ccwgroup_driver qeth_l3_ccwgroup_driver; const char *qeth_get_cardname_short(struct qeth_card *); @@ -836,7 +836,6 @@ void qeth_prepare_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, char); struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *); int qeth_mdio_read(struct net_device *, int, int); int qeth_snmp_command(struct qeth_card *, char __user *); -int qeth_set_large_send(struct qeth_card *, enum qeth_large_send_types); struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *, __u32, __u32); int qeth_default_setadapterparms_cb(struct qeth_card *, struct qeth_reply *, unsigned long); @@ -849,13 +848,14 @@ int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *, struct sk_buff *, struct qeth_hdr *, int, int, int); int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *, struct sk_buff *, struct qeth_hdr *, int); -int qeth_core_get_stats_count(struct net_device *); +int qeth_core_get_sset_count(struct net_device *, int); void qeth_core_get_ethtool_stats(struct net_device *, struct ethtool_stats *, u64 *); void qeth_core_get_strings(struct net_device *, u32, u8 *); void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *); void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *text, ...); int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *); +int qeth_set_access_ctrl_online(struct qeth_card *card); /* exports for OSN */ int qeth_osn_assist(struct net_device *, void *, int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index c4a42d9..d34804d 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -270,41 +270,6 @@ int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt) return qeth_alloc_buffer_pool(card); } -int qeth_set_large_send(struct qeth_card *card, - enum qeth_large_send_types type) -{ - int rc = 0; - - if (card->dev == NULL) { - card->options.large_send = type; - return 0; - } - if (card->state == CARD_STATE_UP) - netif_tx_disable(card->dev); - card->options.large_send = type; - switch (card->options.large_send) { - case QETH_LARGE_SEND_TSO: - if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) { - card->dev->features |= NETIF_F_TSO | NETIF_F_SG | - NETIF_F_HW_CSUM; - } else { - card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | - NETIF_F_HW_CSUM); - card->options.large_send = QETH_LARGE_SEND_NO; - rc = -EOPNOTSUPP; - } - break; - default: /* includes QETH_LARGE_SEND_NO */ - card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | - NETIF_F_HW_CSUM); - break; - } - if (card->state == CARD_STATE_UP) - netif_wake_queue(card->dev); - return rc; -} -EXPORT_SYMBOL_GPL(qeth_set_large_send); - static int qeth_issue_next_read(struct qeth_card *card) { int rc; @@ -1079,6 +1044,7 @@ static void qeth_set_intial_options(struct qeth_card *card) card->options.add_hhlen = DEFAULT_ADD_HHLEN; card->options.performance_stats = 0; card->options.rx_sg_cb = QETH_RX_SG_CB; + card->options.isolation = ISOLATION_MODE_NONE; } static int qeth_do_start_thread(struct qeth_card *card, unsigned long thread) @@ -3389,6 +3355,156 @@ int qeth_setadpparms_change_macaddr(struct qeth_card *card) } EXPORT_SYMBOL_GPL(qeth_setadpparms_change_macaddr); +static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card, + struct qeth_reply *reply, unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + struct qeth_set_access_ctrl *access_ctrl_req; + int rc; + + QETH_DBF_TEXT(TRACE, 4, "setaccb"); + + cmd = (struct qeth_ipa_cmd *) data; + access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; + QETH_DBF_TEXT_(SETUP, 2, "setaccb"); + QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); + QETH_DBF_TEXT_(SETUP, 2, "rc=%d", + cmd->data.setadapterparms.hdr.return_code); + switch (cmd->data.setadapterparms.hdr.return_code) { + case SET_ACCESS_CTRL_RC_SUCCESS: + case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED: + case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED: + { + card->options.isolation = access_ctrl_req->subcmd_code; + if (card->options.isolation == ISOLATION_MODE_NONE) { + dev_info(&card->gdev->dev, + "QDIO data connection isolation is deactivated\n"); + } else { + dev_info(&card->gdev->dev, + "QDIO data connection isolation is activated\n"); + } + QETH_DBF_MESSAGE(3, "OK:SET_ACCESS_CTRL(%s, %d)==%d\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); + rc = 0; + break; + } + case SET_ACCESS_CTRL_RC_NOT_SUPPORTED: + { + QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); + dev_err(&card->gdev->dev, "Adapter does not " + "support QDIO data connection isolation\n"); + + /* ensure isolation mode is "none" */ + card->options.isolation = ISOLATION_MODE_NONE; + rc = -EOPNOTSUPP; + break; + } + case SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER: + { + QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); + dev_err(&card->gdev->dev, + "Adapter is dedicated. " + "QDIO data connection isolation not supported\n"); + + /* ensure isolation mode is "none" */ + card->options.isolation = ISOLATION_MODE_NONE; + rc = -EOPNOTSUPP; + break; + } + case SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF: + { + QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); + dev_err(&card->gdev->dev, + "TSO does not permit QDIO data connection isolation\n"); + + /* ensure isolation mode is "none" */ + card->options.isolation = ISOLATION_MODE_NONE; + rc = -EPERM; + break; + } + default: + { + /* this should never happen */ + QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d" + "==UNKNOWN\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); + + /* ensure isolation mode is "none" */ + card->options.isolation = ISOLATION_MODE_NONE; + rc = 0; + break; + } + } + qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); + return rc; +} + +static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card, + enum qeth_ipa_isolation_modes isolation) +{ + int rc; + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + struct qeth_set_access_ctrl *access_ctrl_req; + + QETH_DBF_TEXT(TRACE, 4, "setacctl"); + + QETH_DBF_TEXT_(SETUP, 2, "setacctl"); + QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); + + iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_ACCESS_CONTROL, + sizeof(struct qeth_ipacmd_setadpparms_hdr) + + sizeof(struct qeth_set_access_ctrl)); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; + access_ctrl_req->subcmd_code = isolation; + + rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb, + NULL); + QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc); + return rc; +} + +int qeth_set_access_ctrl_online(struct qeth_card *card) +{ + int rc = 0; + + QETH_DBF_TEXT(TRACE, 4, "setactlo"); + + if (card->info.type == QETH_CARD_TYPE_OSAE && + qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) { + rc = qeth_setadpparms_set_access_ctrl(card, + card->options.isolation); + if (rc) { + QETH_DBF_MESSAGE(3, + "IPA(SET_ACCESS_CTRL,%s,%d) sent failed", + card->gdev->dev.kobj.name, + rc); + } + } else if (card->options.isolation != ISOLATION_MODE_NONE) { + card->options.isolation = ISOLATION_MODE_NONE; + + dev_err(&card->gdev->dev, "Adapter does not " + "support QDIO data connection isolation\n"); + rc = -EOPNOTSUPP; + } + return rc; +} +EXPORT_SYMBOL_GPL(qeth_set_access_ctrl_online); + void qeth_tx_timeout(struct net_device *dev) { struct qeth_card *card; @@ -3732,30 +3848,36 @@ static int qeth_core_driver_group(const char *buf, struct device *root_dev, int qeth_core_hardsetup_card(struct qeth_card *card) { struct qdio_ssqd_desc *ssqd; - int retries = 3; + int retries = 0; int mpno = 0; int rc; QETH_DBF_TEXT(SETUP, 2, "hrdsetup"); atomic_set(&card->force_alloc_skb, 0); retry: - if (retries < 3) { + if (retries) QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n", dev_name(&card->gdev->dev)); - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); - ccw_device_set_online(CARD_RDEV(card)); - ccw_device_set_online(CARD_WDEV(card)); - ccw_device_set_online(CARD_DDEV(card)); - } + ccw_device_set_offline(CARD_DDEV(card)); + ccw_device_set_offline(CARD_WDEV(card)); + ccw_device_set_offline(CARD_RDEV(card)); + rc = ccw_device_set_online(CARD_RDEV(card)); + if (rc) + goto retriable; + rc = ccw_device_set_online(CARD_WDEV(card)); + if (rc) + goto retriable; + rc = ccw_device_set_online(CARD_DDEV(card)); + if (rc) + goto retriable; rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); +retriable: if (rc == -ERESTARTSYS) { QETH_DBF_TEXT(SETUP, 2, "break1"); return rc; } else if (rc) { QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - if (--retries < 0) + if (++retries > 3) goto out; else goto retry; @@ -4303,13 +4425,19 @@ static struct { {"tx do_QDIO time"}, {"tx do_QDIO count"}, {"tx csum"}, + {"tx lin"}, }; -int qeth_core_get_stats_count(struct net_device *dev) +int qeth_core_get_sset_count(struct net_device *dev, int stringset) { - return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN); + switch (stringset) { + case ETH_SS_STATS: + return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN); + default: + return -EINVAL; + } } -EXPORT_SYMBOL_GPL(qeth_core_get_stats_count); +EXPORT_SYMBOL_GPL(qeth_core_get_sset_count); void qeth_core_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) @@ -4355,6 +4483,7 @@ void qeth_core_get_ethtool_stats(struct net_device *dev, data[31] = card->perf_stats.outbound_do_qdio_time; data[32] = card->perf_stats.outbound_do_qdio_cnt; data[33] = card->perf_stats.tx_csum; + data[34] = card->perf_stats.tx_lin; } EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats); diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index eecb2ee..52c0343 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -234,18 +234,19 @@ enum qeth_ipa_setdelip_flags { /* SETADAPTER IPA Command: ****************************************************/ enum qeth_ipa_setadp_cmd { - IPA_SETADP_QUERY_COMMANDS_SUPPORTED = 0x0001, - IPA_SETADP_ALTER_MAC_ADDRESS = 0x0002, - IPA_SETADP_ADD_DELETE_GROUP_ADDRESS = 0x0004, - IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR = 0x0008, - IPA_SETADP_SET_ADDRESSING_MODE = 0x0010, - IPA_SETADP_SET_CONFIG_PARMS = 0x0020, - IPA_SETADP_SET_CONFIG_PARMS_EXTENDED = 0x0040, - IPA_SETADP_SET_BROADCAST_MODE = 0x0080, - IPA_SETADP_SEND_OSA_MESSAGE = 0x0100, - IPA_SETADP_SET_SNMP_CONTROL = 0x0200, - IPA_SETADP_QUERY_CARD_INFO = 0x0400, - IPA_SETADP_SET_PROMISC_MODE = 0x0800, + IPA_SETADP_QUERY_COMMANDS_SUPPORTED = 0x00000001L, + IPA_SETADP_ALTER_MAC_ADDRESS = 0x00000002L, + IPA_SETADP_ADD_DELETE_GROUP_ADDRESS = 0x00000004L, + IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR = 0x00000008L, + IPA_SETADP_SET_ADDRESSING_MODE = 0x00000010L, + IPA_SETADP_SET_CONFIG_PARMS = 0x00000020L, + IPA_SETADP_SET_CONFIG_PARMS_EXTENDED = 0x00000040L, + IPA_SETADP_SET_BROADCAST_MODE = 0x00000080L, + IPA_SETADP_SEND_OSA_MESSAGE = 0x00000100L, + IPA_SETADP_SET_SNMP_CONTROL = 0x00000200L, + IPA_SETADP_QUERY_CARD_INFO = 0x00000400L, + IPA_SETADP_SET_PROMISC_MODE = 0x00000800L, + IPA_SETADP_SET_ACCESS_CONTROL = 0x00010000L, }; enum qeth_ipa_mac_ops { CHANGE_ADDR_READ_MAC = 0, @@ -264,6 +265,20 @@ enum qeth_ipa_promisc_modes { SET_PROMISC_MODE_OFF = 0, SET_PROMISC_MODE_ON = 1, }; +enum qeth_ipa_isolation_modes { + ISOLATION_MODE_NONE = 0x00000000L, + ISOLATION_MODE_FWD = 0x00000001L, + ISOLATION_MODE_DROP = 0x00000002L, +}; +enum qeth_ipa_set_access_mode_rc { + SET_ACCESS_CTRL_RC_SUCCESS = 0x0000, + SET_ACCESS_CTRL_RC_NOT_SUPPORTED = 0x0004, + SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED = 0x0008, + SET_ACCESS_CTRL_RC_ALREADY_ISOLATED = 0x0010, + SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER = 0x0014, + SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF = 0x0018, +}; + /* (SET)DELIP(M) IPA stuff ***************************************************/ struct qeth_ipacmd_setdelip4 { @@ -376,6 +391,11 @@ struct qeth_snmp_ureq { struct qeth_snmp_cmd cmd; } __attribute__((packed)); +/* SET_ACCESS_CONTROL: same format for request and reply */ +struct qeth_set_access_ctrl { + __u32 subcmd_code; +} __attribute__((packed)); + struct qeth_ipacmd_setadpparms_hdr { __u32 supp_hw_cmds; __u32 reserved1; @@ -394,6 +414,7 @@ struct qeth_ipacmd_setadpparms { struct qeth_query_cmds_supp query_cmds_supp; struct qeth_change_addr change_addr; struct qeth_snmp_cmd snmp; + struct qeth_set_access_ctrl set_access_ctrl; __u32 mode; } data; } __attribute__ ((packed)); diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index 33505c2..9ff2b36 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -416,7 +416,11 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, static DEVICE_ATTR(layer2, 0644, qeth_dev_layer2_show, qeth_dev_layer2_store); -static ssize_t qeth_dev_large_send_show(struct device *dev, +#define ATTR_QETH_ISOLATION_NONE ("none") +#define ATTR_QETH_ISOLATION_FWD ("forward") +#define ATTR_QETH_ISOLATION_DROP ("drop") + +static ssize_t qeth_dev_isolation_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); @@ -424,44 +428,69 @@ static ssize_t qeth_dev_large_send_show(struct device *dev, if (!card) return -EINVAL; - switch (card->options.large_send) { - case QETH_LARGE_SEND_NO: - return sprintf(buf, "%s\n", "no"); - case QETH_LARGE_SEND_TSO: - return sprintf(buf, "%s\n", "TSO"); + switch (card->options.isolation) { + case ISOLATION_MODE_NONE: + return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_NONE); + case ISOLATION_MODE_FWD: + return snprintf(buf, 9, "%s\n", ATTR_QETH_ISOLATION_FWD); + case ISOLATION_MODE_DROP: + return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_DROP); default: - return sprintf(buf, "%s\n", "N/A"); + return snprintf(buf, 5, "%s\n", "N/A"); } } -static ssize_t qeth_dev_large_send_store(struct device *dev, +static ssize_t qeth_dev_isolation_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); - enum qeth_large_send_types type; + enum qeth_ipa_isolation_modes isolation; int rc = 0; - char *tmp; + char *tmp, *curtoken; + curtoken = (char *) buf; - if (!card) - return -EINVAL; - tmp = strsep((char **) &buf, "\n"); - if (!strcmp(tmp, "no")) { - type = QETH_LARGE_SEND_NO; - } else if (!strcmp(tmp, "TSO")) { - type = QETH_LARGE_SEND_TSO; + if (!card) { + rc = -EINVAL; + goto out; + } + + /* check for unknown, too, in case we do not yet know who we are */ + if (card->info.type != QETH_CARD_TYPE_OSAE && + card->info.type != QETH_CARD_TYPE_UNKNOWN) { + rc = -EOPNOTSUPP; + dev_err(&card->gdev->dev, "Adapter does not " + "support QDIO data connection isolation\n"); + goto out; + } + + /* parse input into isolation mode */ + tmp = strsep(&curtoken, "\n"); + if (!strcmp(tmp, ATTR_QETH_ISOLATION_NONE)) { + isolation = ISOLATION_MODE_NONE; + } else if (!strcmp(tmp, ATTR_QETH_ISOLATION_FWD)) { + isolation = ISOLATION_MODE_FWD; + } else if (!strcmp(tmp, ATTR_QETH_ISOLATION_DROP)) { + isolation = ISOLATION_MODE_DROP; } else { - return -EINVAL; + rc = -EINVAL; + goto out; } - if (card->options.large_send == type) - return count; - rc = qeth_set_large_send(card, type); - if (rc) - return rc; - return count; + rc = count; + + /* defer IP assist if device is offline (until discipline->set_online)*/ + card->options.isolation = isolation; + if (card->state == CARD_STATE_SOFTSETUP || + card->state == CARD_STATE_UP) { + int ipa_rc = qeth_set_access_ctrl_online(card); + if (ipa_rc != 0) + rc = ipa_rc; + } +out: + return rc; } -static DEVICE_ATTR(large_send, 0644, qeth_dev_large_send_show, - qeth_dev_large_send_store); +static DEVICE_ATTR(isolation, 0644, qeth_dev_isolation_show, + qeth_dev_isolation_store); static ssize_t qeth_dev_blkt_show(char *buf, struct qeth_card *card, int value) { @@ -582,7 +611,7 @@ static struct attribute *qeth_device_attrs[] = { &dev_attr_recover.attr, &dev_attr_performance_stats.attr, &dev_attr_layer2.attr, - &dev_attr_large_send.attr, + &dev_attr_isolation.attr, NULL, }; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index f4f3ca1..0b76339 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -866,7 +866,7 @@ static const struct ethtool_ops qeth_l2_ethtool_ops = { .get_link = ethtool_op_get_link, .get_strings = qeth_core_get_strings, .get_ethtool_stats = qeth_core_get_ethtool_stats, - .get_stats_count = qeth_core_get_stats_count, + .get_sset_count = qeth_core_get_sset_count, .get_drvinfo = qeth_core_get_drvinfo, .get_settings = qeth_core_ethtool_get_settings, }; @@ -874,7 +874,7 @@ static const struct ethtool_ops qeth_l2_ethtool_ops = { static const struct ethtool_ops qeth_l2_osn_ops = { .get_strings = qeth_core_get_strings, .get_ethtool_stats = qeth_core_get_ethtool_stats, - .get_stats_count = qeth_core_get_stats_count, + .get_sset_count = qeth_core_get_sset_count, .get_drvinfo = qeth_core_get_drvinfo, }; @@ -940,30 +940,17 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 1); recover_flag = card->state; - rc = ccw_device_set_online(CARD_RDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = ccw_device_set_online(CARD_WDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = ccw_device_set_online(CARD_DDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = qeth_core_hardsetup_card(card); if (rc) { QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); + rc = -ENODEV; goto out_remove; } - if (!card->dev && qeth_l2_setup_netdev(card)) + if (!card->dev && qeth_l2_setup_netdev(card)) { + rc = -ENODEV; goto out_remove; + } if (card->info.type != QETH_CARD_TYPE_OSN) qeth_l2_send_setmac(card, &card->dev->dev_addr[0]); @@ -983,12 +970,14 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) card->lan_online = 0; return 0; } + rc = -ENODEV; goto out_remove; } else card->lan_online = 1; if (card->info.type != QETH_CARD_TYPE_OSN) { - qeth_set_large_send(card, card->options.large_send); + /* configure isolation level */ + qeth_set_access_ctrl_online(card); qeth_l2_process_vlans(card, 0); } @@ -997,6 +986,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) rc = qeth_init_qdio_queues(card); if (rc) { QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); + rc = -ENODEV; goto out_remove; } card->state = CARD_STATE_SOFTSETUP; @@ -1018,6 +1008,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) /* let user_space know that device is online */ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); return 0; + out_remove: card->use_hard_stop = 1; qeth_l2_stop_card(card, 0); @@ -1028,7 +1019,7 @@ out_remove: card->state = CARD_STATE_RECOVER; else card->state = CARD_STATE_DOWN; - return -ENODEV; + return rc; } static int qeth_l2_set_online(struct ccwgroup_device *gdev) diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 9f143c8..321988f 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -60,5 +60,7 @@ void qeth_l3_del_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *); int qeth_l3_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *); void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions, const u8 *); +int qeth_l3_set_large_send(struct qeth_card *, enum qeth_large_send_types); +int qeth_l3_set_rx_csum(struct qeth_card *, enum qeth_checksum_types); #endif /* __QETH_L3_H__ */ diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 073b6d3..fd1b6ed 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -41,6 +41,32 @@ static int qeth_l3_deregister_addr_entry(struct qeth_card *, static int __qeth_l3_set_online(struct ccwgroup_device *, int); static int __qeth_l3_set_offline(struct ccwgroup_device *, int); +int qeth_l3_set_large_send(struct qeth_card *card, + enum qeth_large_send_types type) +{ + int rc = 0; + + card->options.large_send = type; + if (card->dev == NULL) + return 0; + + if (card->options.large_send == QETH_LARGE_SEND_TSO) { + if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) { + card->dev->features |= NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM; + } else { + card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM); + card->options.large_send = QETH_LARGE_SEND_NO; + rc = -EOPNOTSUPP; + } + } else { + card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM); + card->options.large_send = QETH_LARGE_SEND_NO; + } + return rc; +} static int qeth_l3_isxdigit(char *buf) { @@ -1439,6 +1465,35 @@ static int qeth_l3_send_checksum_command(struct qeth_card *card) return 0; } +int qeth_l3_set_rx_csum(struct qeth_card *card, + enum qeth_checksum_types csum_type) +{ + int rc = 0; + + if (card->options.checksum_type == HW_CHECKSUMMING) { + if ((csum_type != HW_CHECKSUMMING) && + (card->state != CARD_STATE_DOWN)) { + rc = qeth_l3_send_simple_setassparms(card, + IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0); + if (rc) + return -EIO; + } + } else { + if (csum_type == HW_CHECKSUMMING) { + if (card->state != CARD_STATE_DOWN) { + if (!qeth_is_supported(card, + IPA_INBOUND_CHECKSUM)) + return -EPERM; + rc = qeth_l3_send_checksum_command(card); + if (rc) + return -EIO; + } + } + } + card->options.checksum_type = csum_type; + return rc; +} + static int qeth_l3_start_ipa_checksum(struct qeth_card *card) { int rc = 0; @@ -1506,6 +1561,8 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card) static int qeth_l3_start_ipassists(struct qeth_card *card) { QETH_DBF_TEXT(TRACE, 3, "strtipas"); + + qeth_set_access_ctrl_online(card); /* go on*/ qeth_l3_start_ipa_arp_processing(card); /* go on*/ qeth_l3_start_ipa_ip_fragmentation(card); /* go on*/ qeth_l3_start_ipa_source_mac(card); /* go on*/ @@ -2684,6 +2741,24 @@ static void qeth_tx_csum(struct sk_buff *skb) *(__sum16 *)(skb->data + offset) = csum_fold(csum); } +static inline int qeth_l3_tso_elements(struct sk_buff *skb) +{ + unsigned long tcpd = (unsigned long)tcp_hdr(skb) + + tcp_hdr(skb)->doff * 4; + int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data); + int elements = PFN_UP(tcpd + tcpd_len) - PFN_DOWN(tcpd); + elements += skb_shinfo(skb)->nr_frags; + return elements; +} + +static inline int qeth_l3_tso_check(struct sk_buff *skb) +{ + int len = ((unsigned long)tcp_hdr(skb) + tcp_hdr(skb)->doff * 4) - + (unsigned long)skb->data; + return (((unsigned long)skb->data & PAGE_MASK) != + (((unsigned long)skb->data + len) & PAGE_MASK)); +} + static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { int rc; @@ -2777,16 +2852,21 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) /* fix hardware limitation: as long as we do not have sbal * chaining we can not send long frag lists */ - if ((large_send == QETH_LARGE_SEND_TSO) && - ((skb_shinfo(new_skb)->nr_frags + 2) > 16)) { - if (skb_linearize(new_skb)) - goto tx_drop; + if (large_send == QETH_LARGE_SEND_TSO) { + if (qeth_l3_tso_elements(new_skb) + 1 > 16) { + if (skb_linearize(new_skb)) + goto tx_drop; + if (card->options.performance_stats) + card->perf_stats.tx_lin++; + } } if ((large_send == QETH_LARGE_SEND_TSO) && (cast_type == RTN_UNSPEC)) { hdr = (struct qeth_hdr *)skb_push(new_skb, sizeof(struct qeth_hdr_tso)); + if (qeth_l3_tso_check(new_skb)) + QETH_DBF_MESSAGE(2, "tso skb misaligned\n"); memset(hdr, 0, sizeof(struct qeth_hdr_tso)); qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type); qeth_tso_fill_header(card, hdr, new_skb); @@ -2903,46 +2983,28 @@ static u32 qeth_l3_ethtool_get_rx_csum(struct net_device *dev) static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data) { struct qeth_card *card = dev->ml_priv; - enum qeth_card_states old_state; enum qeth_checksum_types csum_type; - if ((card->state != CARD_STATE_UP) && - (card->state != CARD_STATE_DOWN)) - return -EPERM; - if (data) csum_type = HW_CHECKSUMMING; else csum_type = SW_CHECKSUMMING; - if (card->options.checksum_type != csum_type) { - old_state = card->state; - if (card->state == CARD_STATE_UP) - __qeth_l3_set_offline(card->gdev, 1); - card->options.checksum_type = csum_type; - if (old_state == CARD_STATE_UP) - __qeth_l3_set_online(card->gdev, 1); - } - return 0; + return qeth_l3_set_rx_csum(card, csum_type); } static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data) { struct qeth_card *card = dev->ml_priv; + int rc = 0; if (data) { - if (card->options.large_send == QETH_LARGE_SEND_NO) { - if (card->info.type == QETH_CARD_TYPE_IQD) - return -EPERM; - else - card->options.large_send = QETH_LARGE_SEND_TSO; - dev->features |= NETIF_F_TSO; - } + rc = qeth_l3_set_large_send(card, QETH_LARGE_SEND_TSO); } else { dev->features &= ~NETIF_F_TSO; card->options.large_send = QETH_LARGE_SEND_NO; } - return 0; + return rc; } static const struct ethtool_ops qeth_l3_ethtool_ops = { @@ -2957,7 +3019,7 @@ static const struct ethtool_ops qeth_l3_ethtool_ops = { .set_tso = qeth_l3_ethtool_set_tso, .get_strings = qeth_core_get_strings, .get_ethtool_stats = qeth_core_get_ethtool_stats, - .get_stats_count = qeth_core_get_stats_count, + .get_sset_count = qeth_core_get_sset_count, .get_drvinfo = qeth_core_get_drvinfo, .get_settings = qeth_core_ethtool_get_settings, }; @@ -3058,6 +3120,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; card->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; + card->dev->gso_max_size = 15 * PAGE_SIZE; SET_NETDEV_DEV(card->dev, &card->gdev->dev); return register_netdev(card->dev); @@ -3154,32 +3217,19 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 1); recover_flag = card->state; - rc = ccw_device_set_online(CARD_RDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = ccw_device_set_online(CARD_WDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = ccw_device_set_online(CARD_DDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = qeth_core_hardsetup_card(card); if (rc) { QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); + rc = -ENODEV; goto out_remove; } qeth_l3_query_ipassists(card, QETH_PROT_IPV4); - if (!card->dev && qeth_l3_setup_netdev(card)) + if (!card->dev && qeth_l3_setup_netdev(card)) { + rc = -ENODEV; goto out_remove; + } card->state = CARD_STATE_HARDSETUP; qeth_print_status_message(card); @@ -3196,10 +3246,11 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) card->lan_online = 0; return 0; } + rc = -ENODEV; goto out_remove; } else card->lan_online = 1; - qeth_set_large_send(card, card->options.large_send); + qeth_l3_set_large_send(card, card->options.large_send); rc = qeth_l3_setadapter_parms(card); if (rc) @@ -3218,6 +3269,7 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) rc = qeth_init_qdio_queues(card); if (rc) { QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); + rc = -ENODEV; goto out_remove; } card->state = CARD_STATE_SOFTSETUP; @@ -3248,7 +3300,7 @@ out_remove: card->state = CARD_STATE_RECOVER; else card->state = CARD_STATE_DOWN; - return -ENODEV; + return rc; } static int qeth_l3_set_online(struct ccwgroup_device *gdev) diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index c144b99..3360b09 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -293,31 +293,79 @@ static ssize_t qeth_l3_dev_checksum_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); + enum qeth_checksum_types csum_type; char *tmp; + int rc; if (!card) return -EINVAL; - if ((card->state != CARD_STATE_DOWN) && - (card->state != CARD_STATE_RECOVER)) - return -EPERM; - tmp = strsep((char **) &buf, "\n"); if (!strcmp(tmp, "sw_checksumming")) - card->options.checksum_type = SW_CHECKSUMMING; + csum_type = SW_CHECKSUMMING; else if (!strcmp(tmp, "hw_checksumming")) - card->options.checksum_type = HW_CHECKSUMMING; + csum_type = HW_CHECKSUMMING; else if (!strcmp(tmp, "no_checksumming")) - card->options.checksum_type = NO_CHECKSUMMING; - else { + csum_type = NO_CHECKSUMMING; + else return -EINVAL; - } + + rc = qeth_l3_set_rx_csum(card, csum_type); + if (rc) + return rc; return count; } static DEVICE_ATTR(checksumming, 0644, qeth_l3_dev_checksum_show, qeth_l3_dev_checksum_store); +static ssize_t qeth_l3_dev_large_send_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qeth_card *card = dev_get_drvdata(dev); + + if (!card) + return -EINVAL; + + switch (card->options.large_send) { + case QETH_LARGE_SEND_NO: + return sprintf(buf, "%s\n", "no"); + case QETH_LARGE_SEND_TSO: + return sprintf(buf, "%s\n", "TSO"); + default: + return sprintf(buf, "%s\n", "N/A"); + } +} + +static ssize_t qeth_l3_dev_large_send_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct qeth_card *card = dev_get_drvdata(dev); + enum qeth_large_send_types type; + int rc = 0; + char *tmp; + + if (!card) + return -EINVAL; + tmp = strsep((char **) &buf, "\n"); + if (!strcmp(tmp, "no")) + type = QETH_LARGE_SEND_NO; + else if (!strcmp(tmp, "TSO")) + type = QETH_LARGE_SEND_TSO; + else + return -EINVAL; + + if (card->options.large_send == type) + return count; + rc = qeth_l3_set_large_send(card, type); + if (rc) + return rc; + return count; +} + +static DEVICE_ATTR(large_send, 0644, qeth_l3_dev_large_send_show, + qeth_l3_dev_large_send_store); + static struct attribute *qeth_l3_device_attrs[] = { &dev_attr_route4.attr, &dev_attr_route6.attr, @@ -325,6 +373,7 @@ static struct attribute *qeth_l3_device_attrs[] = { &dev_attr_broadcast_mode.attr, &dev_attr_canonical_macaddr.attr, &dev_attr_checksumming.attr, + &dev_attr_large_send.attr, NULL, }; |