diff options
Diffstat (limited to 'drivers/s390/net')
31 files changed, 4072 insertions, 1999 deletions
diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig index 456b1874339..8b3f5599180 100644 --- a/drivers/s390/net/Kconfig +++ b/drivers/s390/net/Kconfig @@ -2,17 +2,18 @@ menu "S/390 network device drivers"  	depends on NETDEVICES && S390  config LCS -	tristate "Lan Channel Station Interface" -	depends on CCW && NETDEVICES && (NET_ETHERNET || TR || FDDI) +	def_tristate m +	prompt "Lan Channel Station Interface" +	depends on CCW && NETDEVICES && (ETHERNET || FDDI)  	help  	   Select this option if you want to use LCS networking on IBM System z. -	   This device driver supports Token Ring (IEEE 802.5), -	   FDDI (IEEE 802.7) and Ethernet. +	   This device driver supports FDDI (IEEE 802.7) and Ethernet.  	   To compile as a module, choose M. The module name is lcs.  	   If you do not know what it is, it's safe to choose Y.  config CTCM -	tristate "CTC and MPC SNA device support" +	def_tristate m +	prompt "CTC and MPC SNA device support"  	depends on CCW && NETDEVICES  	help  	  Select this option if you want to use channel-to-channel @@ -26,7 +27,8 @@ config CTCM  	  If you do not need any channel-to-channel connection, choose N.  config NETIUCV -	tristate "IUCV network device support (VM only)" +	def_tristate m +	prompt "IUCV network device support (VM only)"  	depends on IUCV && NETDEVICES  	help  	  Select this option if you want to use inter-user communication @@ -37,14 +39,16 @@ config NETIUCV  	  The module name is netiucv. If unsure, choose Y.  config SMSGIUCV -	tristate "IUCV special message support (VM only)" +	def_tristate m +	prompt "IUCV special message support (VM only)"  	depends on IUCV  	help  	  Select this option if you want to be able to receive SMSG messages  	  from other VM guest systems.  config SMSGIUCV_EVENT -	tristate "Deliver IUCV special messages as uevents (VM only)" +	def_tristate m +	prompt "Deliver IUCV special messages as uevents (VM only)"  	depends on SMSGIUCV  	help  	  Select this option to deliver CP special messages (SMSGs) as @@ -54,7 +58,8 @@ config SMSGIUCV_EVENT  	  To compile as a module, choose M. The module name is "smsgiucv_app".  config CLAW -	tristate "CLAW device support" +	def_tristate m +	prompt "CLAW device support"  	depends on CCW && NETDEVICES  	help  	  This driver supports channel attached CLAW devices. @@ -64,12 +69,13 @@ config CLAW  	  To compile into the kernel, choose Y.  config QETH -	tristate "Gigabit Ethernet device support" +	def_tristate y +	prompt "Gigabit Ethernet device support"  	depends on CCW && NETDEVICES && IP_MULTICAST && QDIO  	help  	  This driver supports the IBM System z OSA Express adapters -	  in QDIO mode (all media types), HiperSockets interfaces and VM GuestLAN -	  interfaces in QDIO and HIPER mode. +	  in QDIO mode (all media types), HiperSockets interfaces and z/VM +	  virtual NICs for Guest LAN and VSWITCH.  	  For details please refer to the documentation provided by IBM at  	  <http://www.ibm.com/developerworks/linux/linux390> @@ -78,25 +84,25 @@ config QETH  	  The module name is qeth.  config QETH_L2 -        tristate "qeth layer 2 device support" -        depends on QETH -        help -          Select this option to be able to run qeth devices in layer 2 mode. -          To compile as a module, choose M. The module name is qeth_l2. -          If unsure, choose y. +	def_tristate y +	prompt "qeth layer 2 device support" +	depends on QETH +	help +	  Select this option to be able to run qeth devices in layer 2 mode. +	  To compile as a module, choose M. The module name is qeth_l2. +	  If unsure, choose y.  config QETH_L3 -        tristate "qeth layer 3 device support" -        depends on QETH -        help -          Select this option to be able to run qeth devices in layer 3 mode. -          To compile as a module choose M. The module name is qeth_l3. -          If unsure, choose Y. +	def_tristate y +	prompt "qeth layer 3 device support" +	depends on QETH +	help +	  Select this option to be able to run qeth devices in layer 3 mode. +	  To compile as a module choose M. The module name is qeth_l3. +	  If unsure, choose Y.  config QETH_IPV6 -        bool -        depends on (QETH_L3 = IPV6) || (QETH_L3 && IPV6 = 'y') -        default y +	def_bool y if (QETH_L3 = IPV6) || (QETH_L3 && IPV6 = 'y')  config CCWGROUP  	tristate diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile index 4dfe8c1092d..d28f05d0c75 100644 --- a/drivers/s390/net/Makefile +++ b/drivers/s390/net/Makefile @@ -11,7 +11,7 @@ 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 +qeth_l2-y += qeth_l2_main.o qeth_l2_sys.o  obj-$(CONFIG_QETH_L2) += qeth_l2.o  qeth_l3-y += qeth_l3_main.o qeth_l3_sys.o  obj-$(CONFIG_QETH_L3) += qeth_l3.o diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 8e4153d740f..d837c3c5330 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -1,5 +1,4 @@  /* - *  drivers/s390/net/claw.c   *    ESCON CLAW network driver   *   *  Linux for zSeries version @@ -136,7 +135,6 @@ static inline void  claw_set_busy(struct net_device *dev)  {   ((struct claw_privbk *)dev->ml_priv)->tbusy = 1; - eieio();  }  static inline void @@ -144,13 +142,11 @@ claw_clear_busy(struct net_device *dev)  {  	clear_bit(0, &(((struct claw_privbk *) dev->ml_priv)->tbusy));  	netif_wake_queue(dev); -	eieio();  }  static inline int  claw_check_busy(struct net_device *dev)  { -	eieio();  	return ((struct claw_privbk *) dev->ml_priv)->tbusy;  } @@ -233,8 +229,6 @@ static ssize_t claw_rbuff_show(struct device *dev,  static ssize_t claw_rbuff_write(struct device *dev,  	struct device_attribute *attr,  	const char *buf, size_t count); -static int claw_add_files(struct device *dev); -static void claw_remove_files(struct device *dev);  /*   Functions for System Validate  */  static int claw_process_control( struct net_device *dev, struct ccwbk * p_ccw); @@ -263,14 +257,14 @@ static struct device *claw_root_dev;  /* ccwgroup table  */  static struct ccwgroup_driver claw_group_driver = { -        .owner       = THIS_MODULE, -        .name        = "claw", -        .max_slaves  = 2, -        .driver_id   = 0xC3D3C1E6, -        .probe       = claw_probe, -        .remove      = claw_remove_device, -        .set_online  = claw_new_device, -        .set_offline = claw_shutdown_device, +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "claw", +	}, +	.setup	     = claw_probe, +	.remove      = claw_remove_device, +	.set_online  = claw_new_device, +	.set_offline = claw_shutdown_device,  	.prepare     = claw_pm_prepare,  }; @@ -281,37 +275,34 @@ static struct ccw_device_id claw_ids[] = {  MODULE_DEVICE_TABLE(ccw, claw_ids);  static struct ccw_driver claw_ccw_driver = { -	.owner	= THIS_MODULE, -	.name	= "claw", +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "claw", +	},  	.ids	= claw_ids,  	.probe	= ccwgroup_probe_ccwdev,  	.remove	= ccwgroup_remove_ccwdev, +	.int_class = IRQIO_CLW,  }; -static ssize_t -claw_driver_group_store(struct device_driver *ddrv, const char *buf, -			size_t count) +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, 2, buf); +	err = ccwgroup_create_dev(claw_root_dev, &claw_group_driver, 2, buf);  	return err ? err : count;  } -  static DRIVER_ATTR(group, 0200, NULL, claw_driver_group_store); -static struct attribute *claw_group_attrs[] = { +static struct attribute *claw_drv_attrs[] = {  	&driver_attr_group.attr,  	NULL,  }; - -static struct attribute_group claw_group_attr_group = { -	.attrs = claw_group_attrs, +static struct attribute_group claw_drv_attr_group = { +	.attrs = claw_drv_attrs,  }; - -static const struct attribute_group *claw_group_attr_groups[] = { -	&claw_group_attr_group, +static const struct attribute_group *claw_drv_attr_groups[] = { +	&claw_drv_attr_group,  	NULL,  }; @@ -319,60 +310,6 @@ static const struct attribute_group *claw_group_attr_groups[] = {  *       Key functions  */ -/*----------------------------------------------------------------* - *   claw_probe                                                   * - *      this function is called for each CLAW device.             * - *----------------------------------------------------------------*/ -static int -claw_probe(struct ccwgroup_device *cgdev) -{ -	int  		rc; -	struct claw_privbk *privptr=NULL; - -	CLAW_DBF_TEXT(2, setup, "probe"); -	if (!get_device(&cgdev->dev)) -		return -ENODEV; -	privptr = kzalloc(sizeof(struct claw_privbk), GFP_KERNEL); -	dev_set_drvdata(&cgdev->dev, privptr); -	if (privptr == NULL) { -		probe_error(cgdev); -		put_device(&cgdev->dev); -		CLAW_DBF_TEXT_(2, setup, "probex%d", -ENOMEM); -		return -ENOMEM; -	} -	privptr->p_mtc_envelope= kzalloc( MAX_ENVELOPE_SIZE, GFP_KERNEL); -	privptr->p_env = kzalloc(sizeof(struct claw_env), GFP_KERNEL); -        if ((privptr->p_mtc_envelope==NULL) || (privptr->p_env==NULL)) { -                probe_error(cgdev); -		put_device(&cgdev->dev); -		CLAW_DBF_TEXT_(2, setup, "probex%d", -ENOMEM); -                return -ENOMEM; -        } -	memcpy(privptr->p_env->adapter_name,WS_NAME_NOT_DEF,8); -	memcpy(privptr->p_env->host_name,WS_NAME_NOT_DEF,8); -	memcpy(privptr->p_env->api_type,WS_NAME_NOT_DEF,8); -	privptr->p_env->packing = 0; -	privptr->p_env->write_buffers = 5; -	privptr->p_env->read_buffers = 5; -	privptr->p_env->read_size = CLAW_FRAME_SIZE; -	privptr->p_env->write_size = CLAW_FRAME_SIZE; -	rc = claw_add_files(&cgdev->dev); -	if (rc) { -		probe_error(cgdev); -		put_device(&cgdev->dev); -		dev_err(&cgdev->dev, "Creating the /proc files for a new" -		" CLAW device failed\n"); -		CLAW_DBF_TEXT_(2, setup, "probex%d", rc); -		return rc; -	} -	privptr->p_env->p_priv = privptr; -        cgdev->cdev[0]->handler = claw_irq_handler; -	cgdev->cdev[1]->handler = claw_irq_handler; -	CLAW_DBF_TEXT(2, setup, "prbext 0"); - -        return 0; -}  /*  end of claw_probe       */ -  /*-------------------------------------------------------------------*   *   claw_tx                                                         *   *-------------------------------------------------------------------*/ @@ -773,7 +710,7 @@ claw_irq_handler(struct ccw_device *cdev,  	case CLAW_START_WRITE:  		if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {  			dev_info(&cdev->dev, -				"%s: Unit Check Occured in " +				"%s: Unit Check Occurred in "  				"write channel\n", dev->name);  			clear_bit(0, (void *)&p_ch->IO_active);  			if (p_ch->irb->ecw[0] & 0x80) { @@ -839,12 +776,10 @@ claw_irq_tasklet ( unsigned long data )  {  	struct chbk * p_ch;          struct net_device  *dev; -        struct claw_privbk *       privptr;  	p_ch = (struct chbk *) data;          dev = (struct net_device *)p_ch->ndev;  	CLAW_DBF_TEXT(4, trace, "IRQtask"); -	privptr = (struct claw_privbk *)dev->ml_priv;          unpack_read(dev);          clear_bit(CLAW_BH_ACTIVE, (void *)&p_ch->flag_a);  	CLAW_DBF_TEXT(4, trace, "TskletXt"); @@ -1020,7 +955,6 @@ claw_write_next ( struct chbk * p_ch )          struct net_device  *dev;          struct claw_privbk *privptr=NULL;  	struct sk_buff *pk_skb; -	int	rc;  	CLAW_DBF_TEXT(4, trace, "claw_wrt");          if (p_ch->claw_state == CLAW_STOP) @@ -1032,7 +966,7 @@ claw_write_next ( struct chbk * p_ch )  	    !skb_queue_empty(&p_ch->collect_queue)) {  	  	pk_skb = claw_pack_skb(privptr);  		while (pk_skb != NULL) { -			rc = claw_hw_tx( pk_skb, dev,1); +			claw_hw_tx(pk_skb, dev, 1);  			if (privptr->write_free_count > 0) {  	   			pk_skb = claw_pack_skb(privptr);  			} else @@ -1316,15 +1250,12 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)          unsigned char                   *pDataAddress;          struct endccw                   *pEnd;          struct ccw1                     tempCCW; -        struct chbk                     *p_ch;  	struct claw_env			*p_env; -        int                             lock;  	struct clawph			*pk_head;  	struct chbk			*ch;  	CLAW_DBF_TEXT(4, trace, "hw_tx");  	privptr = (struct claw_privbk *)(dev->ml_priv); -	p_ch = (struct chbk *)&privptr->channel[WRITE_CHANNEL];  	p_env =privptr->p_env;  	claw_free_wrt_buf(dev);	/* Clean up free chain if posible */          /*  scan the write queue to free any completed write packets   */ @@ -1505,12 +1436,6 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)          } /* endif (p_first_ccw!=NULL)  */          dev_kfree_skb_any(skb); -	if (linkid==0) { -        	lock=LOCK_NO; -        } -        else  { -                lock=LOCK_YES; -        }          claw_strt_out_IO(dev );          /*      if write free count is zero , set NOBUFFER       */  	if (privptr->write_free_count==0) { @@ -2815,15 +2740,11 @@ claw_free_wrt_buf( struct net_device *dev )  {  	struct claw_privbk *privptr = (struct claw_privbk *)dev->ml_priv; -        struct ccwbk*p_first_ccw; -	struct ccwbk*p_last_ccw;  	struct ccwbk*p_this_ccw;  	struct ccwbk*p_next_ccw;  	CLAW_DBF_TEXT(4, trace, "freewrtb");          /*  scan the write queue to free any completed write packets   */ -        p_first_ccw=NULL; -        p_last_ccw=NULL;          p_this_ccw=privptr->p_write_active_first;          while ( (p_this_ccw!=NULL) && (p_this_ccw->header.flag!=CLAW_PENDING))          { @@ -3066,7 +2987,7 @@ claw_shutdown_device(struct ccwgroup_device *cgdev)  {  	struct claw_privbk *priv;  	struct net_device *ndev; -	int	ret; +	int ret = 0;  	CLAW_DBF_TEXT_(2, setup, "%s", dev_name(&cgdev->dev));  	priv = dev_get_drvdata(&cgdev->dev); @@ -3089,7 +3010,7 @@ claw_shutdown_device(struct ccwgroup_device *cgdev)  	}  	ccw_device_set_offline(cgdev->cdev[1]);  	ccw_device_set_offline(cgdev->cdev[0]); -	return 0; +	return ret;  }  static void @@ -3097,14 +3018,11 @@ claw_remove_device(struct ccwgroup_device *cgdev)  {  	struct claw_privbk *priv; -	BUG_ON(!cgdev);  	CLAW_DBF_TEXT_(2, setup, "%s", dev_name(&cgdev->dev));  	priv = dev_get_drvdata(&cgdev->dev); -	BUG_ON(!priv);  	dev_info(&cgdev->dev, " will be removed.\n");  	if (cgdev->state == CCWGROUP_ONLINE)  		claw_shutdown_device(cgdev); -	claw_remove_files(&cgdev->dev);  	kfree(priv->p_mtc_envelope);  	priv->p_mtc_envelope=NULL;  	kfree(priv->p_env); @@ -3332,7 +3250,6 @@ claw_rbuff_write(struct device *dev, struct device_attribute *attr,  	CLAW_DBF_TEXT_(2, setup, "RB=%d", p_env->read_buffers);  	return count;  } -  static DEVICE_ATTR(read_buffer, 0644, claw_rbuff_show, claw_rbuff_write);  static struct attribute *claw_attr[] = { @@ -3343,40 +3260,73 @@ static struct attribute *claw_attr[] = {  	&dev_attr_host_name.attr,  	NULL,  }; -  static struct attribute_group claw_attr_group = {  	.attrs = claw_attr,  }; +static const struct attribute_group *claw_attr_groups[] = { +	&claw_attr_group, +	NULL, +}; +static const struct device_type claw_devtype = { +	.name = "claw", +	.groups = claw_attr_groups, +}; -static int -claw_add_files(struct device *dev) +/*----------------------------------------------------------------* + *   claw_probe 						  * + *	this function is called for each CLAW device.		  * + *----------------------------------------------------------------*/ +static int claw_probe(struct ccwgroup_device *cgdev)  { -	CLAW_DBF_TEXT(2, setup, "add_file"); -	return sysfs_create_group(&dev->kobj, &claw_attr_group); -} +	struct claw_privbk *privptr = NULL; -static void -claw_remove_files(struct device *dev) -{ -	CLAW_DBF_TEXT(2, setup, "rem_file"); -	sysfs_remove_group(&dev->kobj, &claw_attr_group); -} +	CLAW_DBF_TEXT(2, setup, "probe"); +	if (!get_device(&cgdev->dev)) +		return -ENODEV; +	privptr = kzalloc(sizeof(struct claw_privbk), GFP_KERNEL); +	dev_set_drvdata(&cgdev->dev, privptr); +	if (privptr == NULL) { +		probe_error(cgdev); +		put_device(&cgdev->dev); +		CLAW_DBF_TEXT_(2, setup, "probex%d", -ENOMEM); +		return -ENOMEM; +	} +	privptr->p_mtc_envelope = kzalloc(MAX_ENVELOPE_SIZE, GFP_KERNEL); +	privptr->p_env = kzalloc(sizeof(struct claw_env), GFP_KERNEL); +	if ((privptr->p_mtc_envelope == NULL) || (privptr->p_env == NULL)) { +		probe_error(cgdev); +		put_device(&cgdev->dev); +		CLAW_DBF_TEXT_(2, setup, "probex%d", -ENOMEM); +		return -ENOMEM; +	} +	memcpy(privptr->p_env->adapter_name, WS_NAME_NOT_DEF, 8); +	memcpy(privptr->p_env->host_name, WS_NAME_NOT_DEF, 8); +	memcpy(privptr->p_env->api_type, WS_NAME_NOT_DEF, 8); +	privptr->p_env->packing = 0; +	privptr->p_env->write_buffers = 5; +	privptr->p_env->read_buffers = 5; +	privptr->p_env->read_size = CLAW_FRAME_SIZE; +	privptr->p_env->write_size = CLAW_FRAME_SIZE; +	privptr->p_env->p_priv = privptr; +	cgdev->cdev[0]->handler = claw_irq_handler; +	cgdev->cdev[1]->handler = claw_irq_handler; +	cgdev->dev.type = &claw_devtype; +	CLAW_DBF_TEXT(2, setup, "prbext 0"); + +	return 0; +}  /*  end of claw_probe       */  /*--------------------------------------------------------------------*  *    claw_init  and cleanup                                           *  *---------------------------------------------------------------------*/ -static void __exit -claw_cleanup(void) +static void __exit claw_cleanup(void)  { -	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"); -  }  /** @@ -3385,8 +3335,7 @@ claw_cleanup(void)   *   * @return 0 on success, !0 on error.   */ -static int __init -claw_init(void) +static int __init claw_init(void)  {  	int ret = 0; @@ -3399,13 +3348,13 @@ claw_init(void)  	}  	CLAW_DBF_TEXT(2, setup, "init_mod");  	claw_root_dev = root_device_register("claw"); -	ret = IS_ERR(claw_root_dev) ? PTR_ERR(claw_root_dev) : 0; +	ret = PTR_ERR_OR_ZERO(claw_root_dev);  	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; +	claw_group_driver.driver.groups = claw_drv_attr_groups;  	ret = ccwgroup_driver_register(&claw_group_driver);  	if (ret)  		goto ccwgroup_err; @@ -3428,5 +3377,5 @@ module_exit(claw_cleanup);  MODULE_AUTHOR("Andy Richter <richtera@us.ibm.com>");  MODULE_DESCRIPTION("Linux for System z CLAW Driver\n" \ -			"Copyright 2000,2008 IBM Corporation\n"); +			"Copyright IBM Corp. 2000, 2008\n");  MODULE_LICENSE("GPL"); diff --git a/drivers/s390/net/claw.h b/drivers/s390/net/claw.h index 1bc5904df19..3339b9b607b 100644 --- a/drivers/s390/net/claw.h +++ b/drivers/s390/net/claw.h @@ -114,15 +114,9 @@ do { \  	debug_event(claw_dbf_##name,level,(void*)(addr),len); \  } while (0) -/* Allow to sort out low debug levels early to avoid wasted sprints */ -static inline int claw_dbf_passes(debug_info_t *dbf_grp, int level) -{ -	return (level <= dbf_grp->level); -} -  #define CLAW_DBF_TEXT_(level,name,text...) \  	do { \ -		if (claw_dbf_passes(claw_dbf_##name, level)) { \ +		if (debug_level_enabled(claw_dbf_##name, level)) { \  			sprintf(debug_buffer, text); \  			debug_text_event(claw_dbf_##name, level, \  						debug_buffer); \ diff --git a/drivers/s390/net/ctcm_dbug.c b/drivers/s390/net/ctcm_dbug.c index d962fd741a2..8363f1c966e 100644 --- a/drivers/s390/net/ctcm_dbug.c +++ b/drivers/s390/net/ctcm_dbug.c @@ -1,6 +1,4 @@  /* - *	drivers/s390/net/ctcm_dbug.c - *   *	Copyright IBM Corp. 2001, 2007   *	Authors:	Peter Tiedemann (ptiedem@de.ibm.com)   * @@ -68,7 +66,7 @@ void ctcm_dbf_longtext(enum ctcm_dbf_names dbf_nix, int level, char *fmt, ...)  	char dbf_txt_buf[64];  	va_list args; -	if (level > (ctcm_dbf[dbf_nix].id)->level) +	if (!debug_level_enabled(ctcm_dbf[dbf_nix].id, level))  		return;  	va_start(args, fmt);  	vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args); diff --git a/drivers/s390/net/ctcm_dbug.h b/drivers/s390/net/ctcm_dbug.h index 26966d0b9ab..47bf0501995 100644 --- a/drivers/s390/net/ctcm_dbug.h +++ b/drivers/s390/net/ctcm_dbug.h @@ -1,6 +1,4 @@  /* - *	drivers/s390/net/ctcm_dbug.h - *   *	Copyright IBM Corp. 2001, 2007   *	Authors:	Peter Tiedemann (ptiedem@de.ibm.com)   * diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index 8c921fc3511..fb92524d24e 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -1,6 +1,4 @@  /* - * drivers/s390/net/ctcm_fsms.c - *   * Copyright IBM Corp. 2001, 2007   * Authors:	Fritz Elfert (felfert@millenux.com)   * 		Peter Tiedemann (ptiedem@de.ibm.com) @@ -184,7 +182,7 @@ static void ctcmpc_chx_resend(fsm_instance *, int, void *);  static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg);  /** - * Check return code of a preceeding ccw_device call, halt_IO etc... + * Check return code of a preceding ccw_device call, halt_IO etc...   *   * ch	:	The channel, the error belongs to.   * Returns the error code (!= 0) to inspect. @@ -1341,6 +1339,12 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)  	spin_unlock(&ch->collect_lock);  	clear_normalized_cda(&ch->ccw[1]); + +	CTCM_PR_DBGDATA("ccwcda=0x%p data=0x%p\n", +			(void *)(unsigned long)ch->ccw[1].cda, +			ch->trans_skb->data); +	ch->ccw[1].count = ch->max_bufsize; +  	if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {  		dev_kfree_skb_any(ch->trans_skb);  		ch->trans_skb = NULL; @@ -1350,6 +1354,11 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)  		fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);  		return;  	} + +	CTCM_PR_DBGDATA("ccwcda=0x%p data=0x%p\n", +			(void *)(unsigned long)ch->ccw[1].cda, +			ch->trans_skb->data); +  	ch->ccw[1].count = ch->trans_skb->len;  	fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);  	ch->prof.send_stamp = current_kernel_time(); /* xtime */ @@ -1514,7 +1523,7 @@ static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg)  				goto done;  	default:  		break; -	}; +	}  	fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)  		     ? CTC_STATE_RXINIT : CTC_STATE_TXINIT); diff --git a/drivers/s390/net/ctcm_fsms.h b/drivers/s390/net/ctcm_fsms.h index 046d077fabb..c963d04799c 100644 --- a/drivers/s390/net/ctcm_fsms.h +++ b/drivers/s390/net/ctcm_fsms.h @@ -1,6 +1,4 @@  /* - * drivers/s390/net/ctcm_fsms.h - *   * Copyright IBM Corp. 2001, 2007   * Authors: 	Fritz Elfert (felfert@millenux.com)   * 		Peter Tiedemann (ptiedem@de.ibm.com) diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 2c7d2d9be4d..03b6ad03557 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1,6 +1,4 @@  /* - * drivers/s390/net/ctcm_main.c - *   * Copyright IBM Corp. 2001, 2009   * Author(s):   *	Original CTC driver(s): @@ -562,6 +560,9 @@ static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb)  		skb_queue_tail(&ch->io_queue, skb);  		ccw_idx = 3;  	} +	if (do_debug_ccw) +		ctcmpc_dumpit((char *)&ch->ccw[ccw_idx], +					sizeof(struct ccw1) * 3);  	ch->retry = 0;  	fsm_newstate(ch->fsm, CTC_STATE_TX);  	fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch); @@ -671,7 +672,6 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)  	int ccw_idx;  	unsigned long hi;  	unsigned long saveflags = 0;	/* avoids compiler warning */ -	__u16 block_len;  	CTCM_PR_DEBUG("Enter %s: %s, cp=%i ch=0x%p id=%s state=%s\n",  			__func__, dev->name, smp_processor_id(), ch, @@ -718,7 +718,6 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)  	 */  	atomic_inc(&skb->users); -	block_len = skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH;  	/*  	 * IDAL support in CTCM is broken, so we have to  	 * care about skb's above 2G ourselves. @@ -1295,6 +1294,11 @@ static void ctcm_irq_handler(struct ccw_device *cdev,  } +static const struct device_type ctcm_devtype = { +	.name = "ctcm", +	.groups = ctcm_attr_groups, +}; +  /**   * Add ctcm specific attributes.   * Add ctcm private data. @@ -1306,7 +1310,6 @@ static void ctcm_irq_handler(struct ccw_device *cdev,  static int ctcm_probe_device(struct ccwgroup_device *cgdev)  {  	struct ctcm_priv *priv; -	int rc;  	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,  			"%s %p", @@ -1323,17 +1326,11 @@ static int ctcm_probe_device(struct ccwgroup_device *cgdev)  		put_device(&cgdev->dev);  		return -ENOMEM;  	} - -	rc = ctcm_add_files(&cgdev->dev); -	if (rc) { -		kfree(priv); -		put_device(&cgdev->dev); -		return rc; -	}  	priv->buffer_size = CTCM_BUFSIZE_DEFAULT;  	cgdev->cdev[0]->handler = ctcm_irq_handler;  	cgdev->cdev[1]->handler = ctcm_irq_handler;  	dev_set_drvdata(&cgdev->dev, priv); +	cgdev->dev.type = &ctcm_devtype;  	return 0;  } @@ -1457,7 +1454,7 @@ static int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type,  				ch_fsm_len, GFP_KERNEL);  	}  	if (ch->fsm == NULL) -				goto free_return; +				goto nomem_return;  	fsm_newstate(ch->fsm, CTC_STATE_IDLE); @@ -1610,11 +1607,6 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)  		goto out_dev;  	} -	if (ctcm_add_attributes(&cgdev->dev)) { -		result = -ENODEV; -		goto out_unregister; -	} -  	strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name));  	dev_info(&dev->dev, @@ -1628,8 +1620,6 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)  			priv->channel[CTCM_WRITE]->id, priv->protocol);  	return 0; -out_unregister: -	unregister_netdev(dev);  out_dev:  	ctcm_free_netdevice(dev);  out_ccw2: @@ -1668,7 +1658,6 @@ static int ctcm_shutdown_device(struct ccwgroup_device *cgdev)  		/* Close the device */  		ctcm_close(dev);  		dev->flags &= ~IFF_RUNNING; -		ctcm_remove_attributes(&cgdev->dev);  		channel_free(priv->channel[CTCM_READ]);  	} else  		dev = NULL; @@ -1702,15 +1691,12 @@ static void ctcm_remove_device(struct ccwgroup_device *cgdev)  {  	struct ctcm_priv *priv = dev_get_drvdata(&cgdev->dev); -	BUG_ON(priv == NULL); -  	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,  			"removing device %p, proto : %d",  			cgdev, priv->protocol);  	if (cgdev->state == CCWGROUP_ONLINE)  		ctcm_shutdown_device(cgdev); -	ctcm_remove_files(&cgdev->dev);  	dev_set_drvdata(&cgdev->dev, NULL);  	kfree(priv);  	put_device(&cgdev->dev); @@ -1762,19 +1748,22 @@ static struct ccw_device_id ctcm_ids[] = {  MODULE_DEVICE_TABLE(ccw, ctcm_ids);  static struct ccw_driver ctcm_ccw_driver = { -	.owner	= THIS_MODULE, -	.name	= "ctcm", +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "ctcm", +	},  	.ids	= ctcm_ids,  	.probe	= ccwgroup_probe_ccwdev,  	.remove	= ccwgroup_remove_ccwdev, +	.int_class = IRQIO_CTC,  };  static struct ccwgroup_driver ctcm_group_driver = { -	.owner       = THIS_MODULE, -	.name        = CTC_DRIVER_NAME, -	.max_slaves  = 2, -	.driver_id   = 0xC3E3C3D4,	/* CTCM */ -	.probe       = ctcm_probe_device, +	.driver = { +		.owner	= THIS_MODULE, +		.name	= CTC_DRIVER_NAME, +	}, +	.setup	     = ctcm_probe_device,  	.remove      = ctcm_remove_device,  	.set_online  = ctcm_new_device,  	.set_offline = ctcm_shutdown_device, @@ -1783,31 +1772,25 @@ 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) +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); +	err = ccwgroup_create_dev(ctcm_root_dev, &ctcm_group_driver, 2, buf);  	return err ? err : count;  } -  static DRIVER_ATTR(group, 0200, NULL, ctcm_driver_group_store); -static struct attribute *ctcm_group_attrs[] = { +static struct attribute *ctcm_drv_attrs[] = {  	&driver_attr_group.attr,  	NULL,  }; - -static struct attribute_group ctcm_group_attr_group = { -	.attrs = ctcm_group_attrs, +static struct attribute_group ctcm_drv_attr_group = { +	.attrs = ctcm_drv_attrs,  }; - -static const struct attribute_group *ctcm_group_attr_groups[] = { -	&ctcm_group_attr_group, +static const struct attribute_group *ctcm_drv_attr_groups[] = { +	&ctcm_drv_attr_group,  	NULL,  }; @@ -1823,7 +1806,6 @@ static const struct attribute_group *ctcm_group_attr_groups[] = {   */  static void __exit ctcm_exit(void)  { -	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); @@ -1855,13 +1837,13 @@ static int __init ctcm_init(void)  	if (ret)  		goto out_err;  	ctcm_root_dev = root_device_register("ctcm"); -	ret = IS_ERR(ctcm_root_dev) ? PTR_ERR(ctcm_root_dev) : 0; +	ret = PTR_ERR_OR_ZERO(ctcm_root_dev);  	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; +	ctcm_group_driver.driver.groups = ctcm_drv_attr_groups;  	ret = ccwgroup_driver_register(&ctcm_group_driver);  	if (ret)  		goto ccwgroup_err; diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h index 24d5215eb0c..477c933685f 100644 --- a/drivers/s390/net/ctcm_main.h +++ b/drivers/s390/net/ctcm_main.h @@ -1,6 +1,4 @@  /* - *	drivers/s390/net/ctcm_main.h - *   *	Copyright IBM Corp. 2001, 2007   *	Authors:	Fritz Elfert (felfert@millenux.com)   *			Peter Tiedemann (ptiedem@de.ibm.com) @@ -225,13 +223,7 @@ struct ctcm_priv {  int ctcm_open(struct net_device *dev);  int ctcm_close(struct net_device *dev); -/* - * prototypes for non-static sysfs functions - */ -int ctcm_add_attributes(struct device *dev); -void ctcm_remove_attributes(struct device *dev); -int ctcm_add_files(struct device *dev); -void ctcm_remove_files(struct device *dev); +extern const struct attribute_group *ctcm_attr_groups[];  /*   * Compatibility macros for busy handling diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index b64881f33f2..2dbc77b5137 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -1,6 +1,4 @@  /* - *	drivers/s390/net/ctcm_mpc.c - *   *	Copyright IBM Corp. 2004, 2007   *	Authors:	Belinda Thompson (belindat@us.ibm.com)   *			Andy Richter (richtera@us.ibm.com) @@ -53,8 +51,8 @@  #include <linux/moduleparam.h>  #include <asm/idals.h> -#include "ctcm_mpc.h"  #include "ctcm_main.h" +#include "ctcm_mpc.h"  #include "ctcm_fsms.h"  static const struct xid2 init_xid = { @@ -132,7 +130,7 @@ void ctcmpc_dumpit(char *buf, int len)  	__u32	ct, sw, rm, dup;  	char	*ptr, *rptr;  	char	tbuf[82], tdup[82]; -	#if (UTS_MACHINE == s390x) +	#ifdef CONFIG_64BIT  	char	addr[22];  	#else  	char	addr[12]; @@ -149,8 +147,8 @@ void ctcmpc_dumpit(char *buf, int len)  	for (ct = 0; ct < len; ct++, ptr++, rptr++) {  		if (sw == 0) { -			#if (UTS_MACHINE == s390x) -			sprintf(addr, "%16.16lx", (__u64)rptr); +			#ifdef CONFIG_64BIT +			sprintf(addr, "%16.16llx", (__u64)rptr);  			#else  			sprintf(addr, "%8.8X", (__u32)rptr);  			#endif @@ -164,8 +162,8 @@ void ctcmpc_dumpit(char *buf, int len)  		if (sw == 8)  			strcat(bhex, "	"); -		#if (UTS_MACHINE == s390x) -		sprintf(tbuf, "%2.2lX", (__u64)*ptr); +		#if CONFIG_64BIT +		sprintf(tbuf, "%2.2llX", (__u64)*ptr);  		#else  		sprintf(tbuf, "%2.2X", (__u32)*ptr);  		#endif @@ -653,7 +651,6 @@ static void ctcmpc_send_sweep_resp(struct channel *rch)  	struct net_device *dev = rch->netdev;  	struct ctcm_priv *priv = dev->ml_priv;  	struct mpc_group *grp = priv->mpcg; -	int rc = 0;  	struct th_sweep *header;  	struct sk_buff *sweep_skb;  	struct channel *ch  = priv->channel[CTCM_WRITE]; @@ -665,16 +662,14 @@ static void ctcmpc_send_sweep_resp(struct channel *rch)  		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,  			"%s(%s): sweep_skb allocation ERROR\n",  			CTCM_FUNTAIL, rch->id); -		rc = -ENOMEM; -				goto done; +		goto done;  	}  	header = kmalloc(sizeof(struct th_sweep), gfp_type());  	if (!header) {  		dev_kfree_skb_any(sweep_skb); -		rc = -ENOMEM; -				goto done; +		goto done;  	}  	header->th.th_seg	= 0x00 ; @@ -1370,10 +1365,8 @@ static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg)  	struct net_device  *dev = arg;  	struct ctcm_priv    *priv;  	struct mpc_group *grp; -	int rc = 0; -	struct channel *wch, *rch; +	struct channel *wch; -	BUG_ON(dev == NULL);  	CTCM_PR_DEBUG("Enter %s: %s\n",	__func__, dev->name);  	priv  = dev->ml_priv; @@ -1396,7 +1389,6 @@ static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg)  		fsm_deltimer(&priv->restart_timer);  	wch = priv->channel[CTCM_WRITE]; -	rch = priv->channel[CTCM_READ];  	switch (grp->saved_state) {  	case MPCG_STATE_RESET: @@ -1435,7 +1427,7 @@ static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg)  	if (grp->send_qllc_disc == 1) {  		grp->send_qllc_disc = 0; -		rc = mpc_send_qllc_discontact(dev); +		mpc_send_qllc_discontact(dev);  	}  	/* DO NOT issue DEV_EVENT_STOP directly out of this code */ @@ -1479,8 +1471,6 @@ static void mpc_action_timeout(fsm_instance *fi, int event, void *arg)  	struct channel *wch;  	struct channel *rch; -	BUG_ON(dev == NULL); -  	priv = dev->ml_priv;  	grp = priv->mpcg;  	wch = priv->channel[CTCM_WRITE]; diff --git a/drivers/s390/net/ctcm_mpc.h b/drivers/s390/net/ctcm_mpc.h index 5336120cddf..bd1b1cc54ff 100644 --- a/drivers/s390/net/ctcm_mpc.h +++ b/drivers/s390/net/ctcm_mpc.h @@ -1,6 +1,4 @@  /* - * drivers/s390/net/ctcm_mpc.h - *   * Copyright IBM Corp. 2007   * Authors:	Peter Tiedemann (ptiedem@de.ibm.com)   * @@ -12,6 +10,7 @@  #ifndef _CTC_MPC_H_  #define _CTC_MPC_H_ +#include <linux/interrupt.h>  #include <linux/skbuff.h>  #include "fsm.h" diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c index 8305319b2a8..6bcfbbb20f0 100644 --- a/drivers/s390/net/ctcm_sysfs.c +++ b/drivers/s390/net/ctcm_sysfs.c @@ -1,6 +1,4 @@  /* - * drivers/s390/net/ctcm_sysfs.c - *   * Copyright IBM Corp. 2007, 2007   * Authors:	Peter Tiedemann (ptiedem@de.ibm.com)   * @@ -13,6 +11,7 @@  #define KMSG_COMPONENT "ctcm"  #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/device.h>  #include <linux/sysfs.h>  #include <linux/slab.h>  #include "ctcm_main.h" @@ -35,8 +34,9 @@ static ssize_t ctcm_buffer_write(struct device *dev,  		struct device_attribute *attr, const char *buf, size_t count)  {  	struct net_device *ndev; -	int bs1; +	unsigned int bs1;  	struct ctcm_priv *priv = dev_get_drvdata(dev); +	int rc;  	ndev = priv->channel[CTCM_READ]->netdev;  	if (!(priv && priv->channel[CTCM_READ] && ndev)) { @@ -44,7 +44,9 @@ static ssize_t ctcm_buffer_write(struct device *dev,  		return -ENODEV;  	} -	sscanf(buf, "%u", &bs1); +	rc = sscanf(buf, "%u", &bs1); +	if (rc != 1) +		goto einval;  	if (bs1 > CTCM_BUFSIZE_LIMIT)  					goto einval;  	if (bs1 < (576 + LL_HEADER_LENGTH + 2)) @@ -108,10 +110,12 @@ static void ctcm_print_statistics(struct ctcm_priv *priv)  }  static ssize_t stats_show(struct device *dev, -				struct device_attribute *attr, char *buf) +			  struct device_attribute *attr, char *buf)  { +	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);  	struct ctcm_priv *priv = dev_get_drvdata(dev); -	if (!priv) + +	if (!priv || gdev->state != CCWGROUP_ONLINE)  		return -ENODEV;  	ctcm_print_statistics(priv);  	return sprintf(buf, "0\n"); @@ -142,13 +146,14 @@ static ssize_t ctcm_proto_show(struct device *dev,  static ssize_t ctcm_proto_store(struct device *dev,  		struct device_attribute *attr, const char *buf, size_t count)  { -	int value; +	int value, rc;  	struct ctcm_priv *priv = dev_get_drvdata(dev);  	if (!priv)  		return -ENODEV; -	sscanf(buf, "%u", &value); -	if (!((value == CTCM_PROTO_S390)  || +	rc = sscanf(buf, "%d", &value); +	if ((rc != 1) || +	    !((value == CTCM_PROTO_S390)  ||  	      (value == CTCM_PROTO_LINUX) ||  	      (value == CTCM_PROTO_MPC) ||  	      (value == CTCM_PROTO_OS390))) @@ -159,7 +164,7 @@ static ssize_t ctcm_proto_store(struct device *dev,  	return count;  } -const char *ctcm_type[] = { +static const char *ctcm_type[] = {  	"not a channel",  	"CTC/A",  	"FICON channel", @@ -190,34 +195,14 @@ static struct attribute *ctcm_attr[] = {  	&dev_attr_protocol.attr,  	&dev_attr_type.attr,  	&dev_attr_buffer.attr, +	&dev_attr_stats.attr,  	NULL,  };  static struct attribute_group ctcm_attr_group = {  	.attrs = ctcm_attr,  }; - -int ctcm_add_attributes(struct device *dev) -{ -	int rc; - -	rc = device_create_file(dev, &dev_attr_stats); - -	return rc; -} - -void ctcm_remove_attributes(struct device *dev) -{ -	device_remove_file(dev, &dev_attr_stats); -} - -int ctcm_add_files(struct device *dev) -{ -	return sysfs_create_group(&dev->kobj, &ctcm_attr_group); -} - -void ctcm_remove_files(struct device *dev) -{ -	sysfs_remove_group(&dev->kobj, &ctcm_attr_group); -} - +const struct attribute_group *ctcm_attr_groups[] = { +	&ctcm_attr_group, +	NULL, +}; diff --git a/drivers/s390/net/fsm.h b/drivers/s390/net/fsm.h index 1e8b235d95b..a4510cf5903 100644 --- a/drivers/s390/net/fsm.h +++ b/drivers/s390/net/fsm.h @@ -8,7 +8,7 @@  #include <linux/slab.h>  #include <linux/sched.h>  #include <linux/string.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  /**   * Define this to get debugging messages. diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index c9f13b9ea33..0a7d87c372b 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -30,7 +30,6 @@  #include <linux/if.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h> -#include <linux/trdevice.h>  #include <linux/fddidevice.h>  #include <linux/inetdevice.h>  #include <linux/in.h> @@ -50,8 +49,7 @@  #include "lcs.h" -#if !defined(CONFIG_NET_ETHERNET) && \ -    !defined(CONFIG_TR) && !defined(CONFIG_FDDI) +#if !defined(CONFIG_ETHERNET) && !defined(CONFIG_FDDI)  #error Cannot compile lcs.c without some net devices switched on.  #endif @@ -284,7 +282,7 @@ lcs_setup_write_ccws(struct lcs_card *card)  	LCS_DBF_TEXT(3, setup, "iwritccw");  	/* Setup write ccws. */ -	memset(card->write.ccws, 0, sizeof(struct ccw1) * LCS_NUM_BUFFS + 1); +	memset(card->write.ccws, 0, sizeof(struct ccw1) * (LCS_NUM_BUFFS + 1));  	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {  		card->write.ccws[cnt].cmd_code = LCS_CCW_WRITE;  		card->write.ccws[cnt].count = 0; @@ -840,7 +838,7 @@ lcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd)  }  /** - * Emit buffer of a lan comand. + * Emit buffer of a lan command.   */  static void  lcs_lancmd_timeout(unsigned long data) @@ -901,6 +899,7 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer,  	add_timer(&timer);  	wait_event(reply->wait_q, reply->received);  	del_timer_sync(&timer); +	destroy_timer_on_stack(&timer);  	LCS_DBF_TEXT_(4, trace, "rc:%d",reply->rc);  	rc = reply->rc;  	lcs_put_reply(reply); @@ -1122,7 +1121,7 @@ list_modified:  	list_for_each_entry_safe(ipm, tmp, &card->ipm_list, list){  		switch (ipm->ipm_state) {  		case LCS_IPM_STATE_SET_REQUIRED: -			/* del from ipm_list so noone else can tamper with +			/* del from ipm_list so no one else can tamper with  			 * this entry */  			list_del_init(&ipm->list);  			spin_unlock_irqrestore(&card->ipm_lock, flags); @@ -1166,10 +1165,7 @@ static void  lcs_get_mac_for_ipm(__be32 ipm, char *mac, struct net_device *dev)  {  	LCS_DBF_TEXT(4,trace, "getmac"); -	if (dev->type == ARPHRD_IEEE802_TR) -		ip_tr_mc_map(ipm, mac); -	else -		ip_eth_mc_map(ipm, mac); +	ip_eth_mc_map(ipm, mac);  }  /** @@ -1481,7 +1477,6 @@ lcs_tasklet(unsigned long data)  	struct lcs_channel *channel;  	struct lcs_buffer *iob;  	int buf_idx; -	int rc;  	channel = (struct lcs_channel *) data;  	LCS_DBF_TEXT_(5, trace, "tlet%s", dev_name(&channel->ccwdev->dev)); @@ -1498,14 +1493,11 @@ lcs_tasklet(unsigned long data)  	channel->buf_idx = buf_idx;  	if (channel->state == LCS_CH_STATE_STOPPED) -		// FIXME: what if rc != 0 ?? -		rc = lcs_start_channel(channel); +		lcs_start_channel(channel);  	spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);  	if (channel->state == LCS_CH_STATE_SUSPENDED && -	    channel->iob[channel->io_idx].state == LCS_BUF_STATE_READY) { -		// FIXME: what if rc != 0 ?? -		rc = __lcs_resume_channel(channel); -	} +	    channel->iob[channel->io_idx].state == LCS_BUF_STATE_READY) +		__lcs_resume_channel(channel);  	spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);  	/* Something happened on the channel. Wake up waiters. */ @@ -1638,19 +1630,13 @@ lcs_startlan_auto(struct lcs_card *card)  	int rc;  	LCS_DBF_TEXT(2, trace, "strtauto"); -#ifdef CONFIG_NET_ETHERNET +#ifdef CONFIG_ETHERNET  	card->lan_type = LCS_FRAME_TYPE_ENET;  	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);  	if (rc == 0)  		return 0;  #endif -#ifdef CONFIG_TR -	card->lan_type = LCS_FRAME_TYPE_TR; -	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); -	if (rc == 0) -		return 0; -#endif  #ifdef CONFIG_FDDI  	card->lan_type = LCS_FRAME_TYPE_FDDI;  	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); @@ -1957,14 +1943,16 @@ static ssize_t  lcs_portno_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)  {          struct lcs_card *card; -        int value; +	int value, rc;  	card = dev_get_drvdata(dev);          if (!card)                  return 0; -        sscanf(buf, "%u", &value); +	rc = sscanf(buf, "%d", &value); +	if (rc != 1) +		return -EINVAL;          /* TODO: sanity checks */          card->portno = value; @@ -1974,7 +1962,7 @@ 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[] = { +static const char *lcs_type[] = {  	"not a channel",  	"2216 parallel",  	"2216 channel", @@ -2011,14 +1999,17 @@ static ssize_t  lcs_timeout_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)  {          struct lcs_card *card; -        int value; +	unsigned int value; +	int rc;  	card = dev_get_drvdata(dev);          if (!card)                  return 0; -        sscanf(buf, "%u", &value); +	rc = sscanf(buf, "%u", &value); +	if (rc != 1) +		return -EINVAL;          /* TODO: sanity checks */          card->lancmd_timeout = value; @@ -2055,10 +2046,17 @@ static struct attribute * lcs_attrs[] = {  	&dev_attr_recover.attr,  	NULL,  }; -  static struct attribute_group lcs_attr_group = {  	.attrs = lcs_attrs,  }; +static const struct attribute_group *lcs_attr_groups[] = { +	&lcs_attr_group, +	NULL, +}; +static const struct device_type lcs_devtype = { +	.name = "lcs", +	.groups = lcs_attr_groups, +};  /**   * lcs_probe_device is called on establishing a new ccwgroup_device. @@ -2067,7 +2065,6 @@ static int  lcs_probe_device(struct ccwgroup_device *ccwgdev)  {  	struct lcs_card *card; -	int ret;  	if (!get_device(&ccwgdev->dev))  		return -ENODEV; @@ -2079,12 +2076,6 @@ lcs_probe_device(struct ccwgroup_device *ccwgdev)  		put_device(&ccwgdev->dev);                  return -ENOMEM;          } -	ret = sysfs_create_group(&ccwgdev->dev.kobj, &lcs_attr_group); -	if (ret) { -		lcs_free_card(card); -		put_device(&ccwgdev->dev); -		return ret; -        }  	dev_set_drvdata(&ccwgdev->dev, card);  	ccwgdev->cdev[0]->handler = lcs_irq;  	ccwgdev->cdev[1]->handler = lcs_irq; @@ -2093,7 +2084,9 @@ lcs_probe_device(struct ccwgroup_device *ccwgdev)  	card->thread_start_mask = 0;  	card->thread_allowed_mask = 0;  	card->thread_running_mask = 0; -        return 0; +	ccwgdev->dev.type = &lcs_devtype; + +	return 0;  }  static int @@ -2124,7 +2117,7 @@ static const struct net_device_ops lcs_mc_netdev_ops = {  	.ndo_stop		= lcs_stop_device,  	.ndo_get_stats		= lcs_getstats,  	.ndo_start_xmit		= lcs_start_xmit, -	.ndo_set_multicast_list = lcs_set_multicast_list, +	.ndo_set_rx_mode	= lcs_set_multicast_list,  };  static int @@ -2170,18 +2163,12 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)  		goto netdev_out;  	}  	switch (card->lan_type) { -#ifdef CONFIG_NET_ETHERNET +#ifdef CONFIG_ETHERNET  	case LCS_FRAME_TYPE_ENET:  		card->lan_type_trans = eth_type_trans;  		dev = alloc_etherdev(0);  		break;  #endif -#ifdef CONFIG_TR -	case LCS_FRAME_TYPE_TR: -		card->lan_type_trans = tr_type_trans; -		dev = alloc_trdev(0); -		break; -#endif  #ifdef CONFIG_FDDI  	case LCS_FRAME_TYPE_FDDI:  		card->lan_type_trans = fddi_type_trans; @@ -2244,7 +2231,7 @@ __lcs_shutdown_device(struct ccwgroup_device *ccwgdev, int recovery_mode)  {  	struct lcs_card *card;  	enum lcs_dev_states recover_state; -	int ret; +	int ret = 0, ret2 = 0, ret3 = 0;  	LCS_DBF_TEXT(3, setup, "shtdndev");  	card = dev_get_drvdata(&ccwgdev->dev); @@ -2259,13 +2246,15 @@ __lcs_shutdown_device(struct ccwgroup_device *ccwgdev, int recovery_mode)  	recover_state = card->state;  	ret = lcs_stop_device(card->dev); -	ret = ccw_device_set_offline(card->read.ccwdev); -	ret = ccw_device_set_offline(card->write.ccwdev); +	ret2 = ccw_device_set_offline(card->read.ccwdev); +	ret3 = ccw_device_set_offline(card->write.ccwdev); +	if (!ret) +		ret = (ret2) ? ret2 : ret3; +	if (ret) +		LCS_DBF_TEXT_(3, setup, "1err:%d", ret);  	if (recover_state == DEV_STATE_UP) {  		card->state = DEV_STATE_RECOVER;  	} -	if (ret) -		return ret;  	return 0;  } @@ -2325,9 +2314,9 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev)  	}  	if (card->dev)  		unregister_netdev(card->dev); -	sysfs_remove_group(&ccwgdev->dev.kobj, &lcs_attr_group);  	lcs_cleanup_card(card);  	lcs_free_card(card); +	dev_set_drvdata(&ccwgdev->dev, NULL);  	put_device(&ccwgdev->dev);  } @@ -2394,22 +2383,25 @@ static struct ccw_device_id lcs_ids[] = {  MODULE_DEVICE_TABLE(ccw, lcs_ids);  static struct ccw_driver lcs_ccw_driver = { -	.owner	= THIS_MODULE, -	.name	= "lcs", +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "lcs", +	},  	.ids	= lcs_ids,  	.probe	= ccwgroup_probe_ccwdev,  	.remove	= ccwgroup_remove_ccwdev, +	.int_class = IRQIO_LCS,  };  /**   * LCS ccwgroup driver registration   */  static struct ccwgroup_driver lcs_group_driver = { -	.owner       = THIS_MODULE, -	.name        = "lcs", -	.max_slaves  = 2, -	.driver_id   = 0xD3C3E2, -	.probe       = lcs_probe_device, +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "lcs", +	}, +	.setup	     = lcs_probe_device,  	.remove      = lcs_remove_device,  	.set_online  = lcs_new_device,  	.set_offline = lcs_shutdown_device, @@ -2420,30 +2412,24 @@ 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) +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); +	err = ccwgroup_create_dev(lcs_root_dev, &lcs_group_driver, 2, buf);  	return err ? err : count;  } -  static DRIVER_ATTR(group, 0200, NULL, lcs_driver_group_store); -static struct attribute *lcs_group_attrs[] = { +static struct attribute *lcs_drv_attrs[] = {  	&driver_attr_group.attr,  	NULL,  }; - -static struct attribute_group lcs_group_attr_group = { -	.attrs = lcs_group_attrs, +static struct attribute_group lcs_drv_attr_group = { +	.attrs = lcs_drv_attrs,  }; - -static const struct attribute_group *lcs_group_attr_groups[] = { -	&lcs_group_attr_group, +static const struct attribute_group *lcs_drv_attr_groups[] = { +	&lcs_drv_attr_group,  	NULL,  }; @@ -2461,13 +2447,13 @@ __init lcs_init_module(void)  	if (rc)  		goto out_err;  	lcs_root_dev = root_device_register("lcs"); -	rc = IS_ERR(lcs_root_dev) ? PTR_ERR(lcs_root_dev) : 0; +	rc = PTR_ERR_OR_ZERO(lcs_root_dev);  	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; +	lcs_group_driver.driver.groups = lcs_drv_attr_groups;  	rc = ccwgroup_driver_register(&lcs_group_driver);  	if (rc)  		goto ccwgroup_err; @@ -2493,8 +2479,6 @@ __exit lcs_cleanup_module(void)  {  	pr_info("Terminating lcs module.\n");  	LCS_DBF_TEXT(0, trace, "cleanup"); -	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); diff --git a/drivers/s390/net/lcs.h b/drivers/s390/net/lcs.h index 8c03392ac83..150fcb4cebc 100644 --- a/drivers/s390/net/lcs.h +++ b/drivers/s390/net/lcs.h @@ -16,15 +16,9 @@ do { \  	debug_event(lcs_dbf_##name,level,(void*)(addr),len); \  } while (0) -/* Allow to sort out low debug levels early to avoid wasted sprints */ -static inline int lcs_dbf_passes(debug_info_t *dbf_grp, int level) -{ -	return (level <= dbf_grp->level); -} -  #define LCS_DBF_TEXT_(level,name,text...) \  	do { \ -		if (lcs_dbf_passes(lcs_dbf_##name, level)) { \ +		if (debug_level_enabled(lcs_dbf_##name, level)) { \  			sprintf(debug_buffer, text); \  			debug_text_event(lcs_dbf_##name, level, debug_buffer); \  		} \ diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 65ebee0a326..ce16d1bdb20 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -63,6 +63,7 @@  #include <asm/io.h>  #include <asm/uaccess.h> +#include <asm/ebcdic.h>  #include <net/iucv/iucv.h>  #include "fsm.h" @@ -75,7 +76,7 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");   * Debug Facility stuff   */  #define IUCV_DBF_SETUP_NAME "iucv_setup" -#define IUCV_DBF_SETUP_LEN 32 +#define IUCV_DBF_SETUP_LEN 64  #define IUCV_DBF_SETUP_PAGES 2  #define IUCV_DBF_SETUP_NR_AREAS 1  #define IUCV_DBF_SETUP_LEVEL 3 @@ -104,15 +105,9 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");  DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf); -/* Allow to sort out low debug levels early to avoid wasted sprints */ -static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level) -{ -	return (level <= dbf_grp->level); -} -  #define IUCV_DBF_TEXT_(name, level, text...) \  	do { \ -		if (iucv_dbf_passes(iucv_dbf_##name, level)) { \ +		if (debug_level_enabled(iucv_dbf_##name, level)) { \  			char* __buf = get_cpu_var(iucv_dbf_txt_buf); \  			sprintf(__buf, text); \  			debug_text_event(iucv_dbf_##name, level, __buf); \ @@ -129,26 +124,6 @@ static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)  /**   * some more debug stuff   */ -#define IUCV_HEXDUMP16(importance,header,ptr) \ -PRINT_##importance(header "%02x %02x %02x %02x  %02x %02x %02x %02x  " \ -		   "%02x %02x %02x %02x  %02x %02x %02x %02x\n", \ -		   *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \ -		   *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \ -		   *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \ -		   *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \ -		   *(((char*)ptr)+12),*(((char*)ptr)+13), \ -		   *(((char*)ptr)+14),*(((char*)ptr)+15)); \ -PRINT_##importance(header "%02x %02x %02x %02x  %02x %02x %02x %02x  " \ -		   "%02x %02x %02x %02x  %02x %02x %02x %02x\n", \ -		   *(((char*)ptr)+16),*(((char*)ptr)+17), \ -		   *(((char*)ptr)+18),*(((char*)ptr)+19), \ -		   *(((char*)ptr)+20),*(((char*)ptr)+21), \ -		   *(((char*)ptr)+22),*(((char*)ptr)+23), \ -		   *(((char*)ptr)+24),*(((char*)ptr)+25), \ -		   *(((char*)ptr)+26),*(((char*)ptr)+27), \ -		   *(((char*)ptr)+28),*(((char*)ptr)+29), \ -		   *(((char*)ptr)+30),*(((char*)ptr)+31)); -  #define PRINTK_HEADER " iucv: "       /* for debugging */  /* dummy device to make sure netiucv_pm functions are called */ @@ -226,6 +201,7 @@ struct iucv_connection {  	struct net_device         *netdev;  	struct connection_profile prof;  	char                      userid[9]; +	char			  userdata[17];  };  /** @@ -263,7 +239,7 @@ struct ll_header {  };  #define NETIUCV_HDRLEN		 (sizeof(struct ll_header)) -#define NETIUCV_BUFSIZE_MAX      32768 +#define NETIUCV_BUFSIZE_MAX	 65537  #define NETIUCV_BUFSIZE_DEFAULT  NETIUCV_BUFSIZE_MAX  #define NETIUCV_MTU_MAX          (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)  #define NETIUCV_MTU_DEFAULT      9216 @@ -288,7 +264,12 @@ static inline int netiucv_test_and_set_busy(struct net_device *dev)  	return test_and_set_bit(0, &priv->tbusy);  } -static u8 iucvMagic[16] = { +static u8 iucvMagic_ascii[16] = { +	0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +	0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 +}; + +static u8 iucvMagic_ebcdic[16] = {  	0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  	0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40  }; @@ -301,18 +282,38 @@ static u8 iucvMagic[16] = {   *   * @returns The printable string (static data!!)   */ -static char *netiucv_printname(char *name) +static char *netiucv_printname(char *name, int len)  { -	static char tmp[9]; +	static char tmp[17];  	char *p = tmp; -	memcpy(tmp, name, 8); -	tmp[8] = '\0'; -	while (*p && (!isspace(*p))) +	memcpy(tmp, name, len); +	tmp[len] = '\0'; +	while (*p && ((p - tmp) < len) && (!isspace(*p)))  		p++;  	*p = '\0';  	return tmp;  } +static char *netiucv_printuser(struct iucv_connection *conn) +{ +	static char tmp_uid[9]; +	static char tmp_udat[17]; +	static char buf[100]; + +	if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) { +		tmp_uid[8] = '\0'; +		tmp_udat[16] = '\0'; +		memcpy(tmp_uid, conn->userid, 8); +		memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8); +		memcpy(tmp_udat, conn->userdata, 16); +		EBCASC(tmp_udat, 16); +		memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16); +		sprintf(buf, "%s.%s", tmp_uid, tmp_udat); +		return buf; +	} else +		return netiucv_printname(conn->userid, 8); +} +  /**   * States of the interface statemachine.   */ @@ -563,15 +564,18 @@ static int netiucv_callback_connreq(struct iucv_path *path,  {  	struct iucv_connection *conn = path->private;  	struct iucv_event ev; +	static char tmp_user[9]; +	static char tmp_udat[17];  	int rc; -	if (memcmp(iucvMagic, ipuser, sizeof(ipuser))) -		/* ipuser must match iucvMagic. */ -		return -EINVAL;  	rc = -EINVAL; +	memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8); +	memcpy(tmp_udat, ipuser, 16); +	EBCASC(tmp_udat, 16);  	read_lock_bh(&iucv_connection_rwlock);  	list_for_each_entry(conn, &iucv_connection_list, list) { -		if (strncmp(ipvmid, conn->userid, 8)) +		if (strncmp(ipvmid, conn->userid, 8) || +		    strncmp(ipuser, conn->userdata, 16))  			continue;  		/* Found a matching connection for this path. */  		conn->path = path; @@ -580,6 +584,8 @@ static int netiucv_callback_connreq(struct iucv_path *path,  		fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);  		rc = 0;  	} +	IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n", +		       tmp_user, netiucv_printname(tmp_udat, 16));  	read_unlock_bh(&iucv_connection_rwlock);  	return rc;  } @@ -733,8 +739,12 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg)  	IUCV_DBF_TEXT(trace, 4, __func__); -	if (conn && conn->netdev) -		privptr = netdev_priv(conn->netdev); +	if (!conn || !conn->netdev) { +		IUCV_DBF_TEXT(data, 2, +			      "Send confirmation for unlinked connection\n"); +		return; +	} +	privptr = netdev_priv(conn->netdev);  	conn->prof.tx_pending--;  	if (single_flag) {  		if ((skb = skb_dequeue(&conn->commit_queue))) { @@ -816,7 +826,7 @@ static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)  	conn->path = path;  	path->msglim = NETIUCV_QUEUELEN_DEFAULT;  	path->flags = 0; -	rc = iucv_path_accept(path, &netiucv_handler, NULL, conn); +	rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);  	if (rc) {  		IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);  		return; @@ -854,7 +864,7 @@ static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)  	IUCV_DBF_TEXT(trace, 3, __func__);  	fsm_deltimer(&conn->timer); -	iucv_path_sever(conn->path, NULL); +	iucv_path_sever(conn->path, conn->userdata);  	fsm_newstate(fi, CONN_STATE_STARTWAIT);  } @@ -867,9 +877,9 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg)  	IUCV_DBF_TEXT(trace, 3, __func__);  	fsm_deltimer(&conn->timer); -	iucv_path_sever(conn->path, NULL); -	dev_info(privptr->dev, "The peer interface of the IUCV device" -		" has closed the connection\n"); +	iucv_path_sever(conn->path, conn->userdata); +	dev_info(privptr->dev, "The peer z/VM guest %s has closed the " +			       "connection\n", netiucv_printuser(conn));  	IUCV_DBF_TEXT(data, 2,  		      "conn_action_connsever: Remote dropped connection\n");  	fsm_newstate(fi, CONN_STATE_STARTWAIT); @@ -886,8 +896,6 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)  	IUCV_DBF_TEXT(trace, 3, __func__);  	fsm_newstate(fi, CONN_STATE_STARTWAIT); -	IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n", -		netdev->name, conn->userid);  	/*  	 * We must set the state before calling iucv_connect because the @@ -897,8 +905,11 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)  	fsm_newstate(fi, CONN_STATE_SETUPWAIT);  	conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL); +	IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n", +		netdev->name, netiucv_printuser(conn)); +  	rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid, -			       NULL, iucvMagic, conn); +			       NULL, conn->userdata, conn);  	switch (rc) {  	case 0:  		netdev->tx_queue_len = conn->path->msglim; @@ -908,13 +919,13 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)  	case 11:  		dev_warn(privptr->dev,  			"The IUCV device failed to connect to z/VM guest %s\n", -			netiucv_printname(conn->userid)); +			netiucv_printname(conn->userid, 8));  		fsm_newstate(fi, CONN_STATE_STARTWAIT);  		break;  	case 12:  		dev_warn(privptr->dev,  			"The IUCV device failed to connect to the peer on z/VM" -			" guest %s\n", netiucv_printname(conn->userid)); +			" guest %s\n", netiucv_printname(conn->userid, 8));  		fsm_newstate(fi, CONN_STATE_STARTWAIT);  		break;  	case 13: @@ -927,7 +938,7 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)  		dev_err(privptr->dev,  			"z/VM guest %s has too many IUCV connections"  			" to connect with the IUCV device\n", -			netiucv_printname(conn->userid)); +			netiucv_printname(conn->userid, 8));  		fsm_newstate(fi, CONN_STATE_CONNERR);  		break;  	case 15: @@ -972,7 +983,7 @@ static void conn_action_stop(fsm_instance *fi, int event, void *arg)  	netiucv_purge_skb_queue(&conn->collect_queue);  	if (conn->path) {  		IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n"); -		iucv_path_sever(conn->path, iucvMagic); +		iucv_path_sever(conn->path, conn->userdata);  		kfree(conn->path);  		conn->path = NULL;  	} @@ -1090,7 +1101,8 @@ dev_action_connup(fsm_instance *fi, int event, void *arg)  			fsm_newstate(fi, DEV_STATE_RUNNING);  			dev_info(privptr->dev,  				"The IUCV device has been connected" -				" successfully to %s\n", privptr->conn->userid); +				" successfully to %s\n", +				netiucv_printuser(privptr->conn));  			IUCV_DBF_TEXT(setup, 3,  				"connection is up and running\n");  			break; @@ -1452,45 +1464,72 @@ static ssize_t user_show(struct device *dev, struct device_attribute *attr,  	struct netiucv_priv *priv = dev_get_drvdata(dev);  	IUCV_DBF_TEXT(trace, 5, __func__); -	return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid)); +	return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));  } -static ssize_t user_write(struct device *dev, struct device_attribute *attr, -			  const char *buf, size_t count) +static int netiucv_check_user(const char *buf, size_t count, char *username, +			      char *userdata)  { -	struct netiucv_priv *priv = dev_get_drvdata(dev); -	struct net_device *ndev = priv->conn->netdev; -	char    *p; -	char    *tmp; -	char 	username[9]; -	int 	i; -	struct iucv_connection *cp; +	const char *p; +	int i; -	IUCV_DBF_TEXT(trace, 3, __func__); -	if (count > 9) { -		IUCV_DBF_TEXT_(setup, 2, -			       "%d is length of username\n", (int) count); +	p = strchr(buf, '.'); +	if ((p && ((count > 26) || +		   ((p - buf) > 8) || +		   (buf + count - p > 18))) || +	    (!p && (count > 9))) { +		IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");  		return -EINVAL;  	} -	tmp = strsep((char **) &buf, "\n"); -	for (i = 0, p = tmp; i < 8 && *p; i++, p++) { -		if (isalnum(*p) || (*p == '$')) { -			username[i]= toupper(*p); +	for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) { +		if (isalnum(*p) || *p == '$') { +			username[i] = toupper(*p);  			continue;  		} -		if (*p == '\n') { +		if (*p == '\n')  			/* trailing lf, grr */  			break; -		}  		IUCV_DBF_TEXT_(setup, 2, -			       "username: invalid character %c\n", *p); +			       "conn_write: invalid character %02x\n", *p);  		return -EINVAL;  	}  	while (i < 8)  		username[i++] = ' ';  	username[8] = '\0'; +	if (*p == '.') { +		p++; +		for (i = 0; i < 16 && *p; i++, p++) { +			if (*p == '\n') +				break; +			userdata[i] = toupper(*p); +		} +		while (i > 0 && i < 16) +			userdata[i++] = ' '; +	} else +		memcpy(userdata, iucvMagic_ascii, 16); +	userdata[16] = '\0'; +	ASCEBC(userdata, 16); + +	return 0; +} + +static ssize_t user_write(struct device *dev, struct device_attribute *attr, +			  const char *buf, size_t count) +{ +	struct netiucv_priv *priv = dev_get_drvdata(dev); +	struct net_device *ndev = priv->conn->netdev; +	char	username[9]; +	char	userdata[17]; +	int	rc; +	struct iucv_connection *cp; + +	IUCV_DBF_TEXT(trace, 3, __func__); +	rc = netiucv_check_user(buf, count, username, userdata); +	if (rc) +		return rc; +  	if (memcmp(username, priv->conn->userid, 9) &&  	    (ndev->flags & (IFF_UP | IFF_RUNNING))) {  		/* username changed while the interface is active. */ @@ -1499,15 +1538,17 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr,  	}  	read_lock_bh(&iucv_connection_rwlock);  	list_for_each_entry(cp, &iucv_connection_list, list) { -		if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) { +		if (!strncmp(username, cp->userid, 9) && +		   !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {  			read_unlock_bh(&iucv_connection_rwlock); -			IUCV_DBF_TEXT_(setup, 2, "user_write: Connection " -				"to %s already exists\n", username); +			IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s " +				"already exists\n", netiucv_printuser(cp));  			return -EEXIST;  		}  	}  	read_unlock_bh(&iucv_connection_rwlock);  	memcpy(priv->conn->userid, username, 9); +	memcpy(priv->conn->userdata, userdata, 17);  	return count;  } @@ -1537,7 +1578,8 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,  	bs1 = simple_strtoul(buf, &e, 0);  	if (e && (!isspace(*e))) { -		IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e); +		IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n", +			*e);  		return -EINVAL;  	}  	if (bs1 > NETIUCV_BUFSIZE_MAX) { @@ -1790,26 +1832,11 @@ static struct attribute_group netiucv_stat_attr_group = {  	.attrs = netiucv_stat_attrs,  }; -static int netiucv_add_files(struct device *dev) -{ -	int ret; - -	IUCV_DBF_TEXT(trace, 3, __func__); -	ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group); -	if (ret) -		return ret; -	ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group); -	if (ret) -		sysfs_remove_group(&dev->kobj, &netiucv_attr_group); -	return ret; -} - -static void netiucv_remove_files(struct device *dev) -{ -	IUCV_DBF_TEXT(trace, 3, __func__); -	sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group); -	sysfs_remove_group(&dev->kobj, &netiucv_attr_group); -} +static const struct attribute_group *netiucv_attr_groups[] = { +	&netiucv_stat_attr_group, +	&netiucv_attr_group, +	NULL, +};  static int netiucv_register_device(struct net_device *ndev)  { @@ -1823,6 +1850,7 @@ static int netiucv_register_device(struct net_device *ndev)  		dev_set_name(dev, "net%s", ndev->name);  		dev->bus = &iucv_bus;  		dev->parent = iucv_root; +		dev->groups = netiucv_attr_groups;  		/*  		 * The release function could be called after the  		 * module has been unloaded. It's _only_ task is to @@ -1840,22 +1868,14 @@ static int netiucv_register_device(struct net_device *ndev)  		put_device(dev);  		return ret;  	} -	ret = netiucv_add_files(dev); -	if (ret) -		goto out_unreg;  	priv->dev = dev;  	dev_set_drvdata(dev, priv);  	return 0; - -out_unreg: -	device_unregister(dev); -	return ret;  }  static void netiucv_unregister_device(struct device *dev)  {  	IUCV_DBF_TEXT(trace, 3, __func__); -	netiucv_remove_files(dev);  	device_unregister(dev);  } @@ -1864,7 +1884,8 @@ static void netiucv_unregister_device(struct device *dev)   * Add it to the list of netiucv connections;   */  static struct iucv_connection *netiucv_new_connection(struct net_device *dev, -						      char *username) +						      char *username, +						      char *userdata)  {  	struct iucv_connection *conn; @@ -1893,6 +1914,8 @@ static struct iucv_connection *netiucv_new_connection(struct net_device *dev,  	fsm_settimer(conn->fsm, &conn->timer);  	fsm_newstate(conn->fsm, CONN_STATE_INVALID); +	if (userdata) +		memcpy(conn->userdata, userdata, 17);  	if (username) {  		memcpy(conn->userid, username, 9);  		fsm_newstate(conn->fsm, CONN_STATE_STOPPED); @@ -1919,6 +1942,7 @@ out:   */  static void netiucv_remove_connection(struct iucv_connection *conn)  { +  	IUCV_DBF_TEXT(trace, 3, __func__);  	write_lock_bh(&iucv_connection_rwlock);  	list_del_init(&conn->list); @@ -1926,7 +1950,7 @@ static void netiucv_remove_connection(struct iucv_connection *conn)  	fsm_deltimer(&conn->timer);  	netiucv_purge_skb_queue(&conn->collect_queue);  	if (conn->path) { -		iucv_path_sever(conn->path, iucvMagic); +		iucv_path_sever(conn->path, conn->userdata);  		kfree(conn->path);  		conn->path = NULL;  	} @@ -1985,7 +2009,7 @@ static void netiucv_setup_netdevice(struct net_device *dev)  /**   * Allocate and initialize everything of a net device.   */ -static struct net_device *netiucv_init_netdevice(char *username) +static struct net_device *netiucv_init_netdevice(char *username, char *userdata)  {  	struct netiucv_priv *privptr;  	struct net_device *dev; @@ -1994,6 +2018,7 @@ static struct net_device *netiucv_init_netdevice(char *username)  			   netiucv_setup_netdevice);  	if (!dev)  		return NULL; +	rtnl_lock();  	if (dev_alloc_name(dev, dev->name) < 0)  		goto out_netdev; @@ -2004,7 +2029,7 @@ static struct net_device *netiucv_init_netdevice(char *username)  	if (!privptr->fsm)  		goto out_netdev; -	privptr->conn = netiucv_new_connection(dev, username); +	privptr->conn = netiucv_new_connection(dev, username, userdata);  	if (!privptr->conn) {  		IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");  		goto out_fsm; @@ -2015,6 +2040,7 @@ static struct net_device *netiucv_init_netdevice(char *username)  out_fsm:  	kfree_fsm(privptr->fsm);  out_netdev: +	rtnl_unlock();  	free_netdev(dev);  	return NULL;  } @@ -2022,47 +2048,31 @@ out_netdev:  static ssize_t conn_write(struct device_driver *drv,  			  const char *buf, size_t count)  { -	const char *p;  	char username[9]; -	int i, rc; +	char userdata[17]; +	int rc;  	struct net_device *dev;  	struct netiucv_priv *priv;  	struct iucv_connection *cp;  	IUCV_DBF_TEXT(trace, 3, __func__); -	if (count>9) { -		IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n"); -		return -EINVAL; -	} - -	for (i = 0, p = buf; i < 8 && *p; i++, p++) { -		if (isalnum(*p) || *p == '$') { -			username[i] = toupper(*p); -			continue; -		} -		if (*p == '\n') -			/* trailing lf, grr */ -			break; -		IUCV_DBF_TEXT_(setup, 2, -			       "conn_write: invalid character %c\n", *p); -		return -EINVAL; -	} -	while (i < 8) -		username[i++] = ' '; -	username[8] = '\0'; +	rc = netiucv_check_user(buf, count, username, userdata); +	if (rc) +		return rc;  	read_lock_bh(&iucv_connection_rwlock);  	list_for_each_entry(cp, &iucv_connection_list, list) { -		if (!strncmp(username, cp->userid, 9)) { +		if (!strncmp(username, cp->userid, 9) && +		    !strncmp(userdata, cp->userdata, 17)) {  			read_unlock_bh(&iucv_connection_rwlock); -			IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection " -				"to %s already exists\n", username); +			IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s " +				"already exists\n", netiucv_printuser(cp));  			return -EEXIST;  		}  	}  	read_unlock_bh(&iucv_connection_rwlock); -	dev = netiucv_init_netdevice(username); +	dev = netiucv_init_netdevice(username, userdata);  	if (!dev) {  		IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");  		return -ENODEV; @@ -2070,6 +2080,7 @@ static ssize_t conn_write(struct device_driver *drv,  	rc = netiucv_register_device(dev);  	if (rc) { +		rtnl_unlock();  		IUCV_DBF_TEXT_(setup, 2,  			"ret %d from netiucv_register_device\n", rc);  		goto out_free_ndev; @@ -2079,12 +2090,14 @@ static ssize_t conn_write(struct device_driver *drv,  	priv = netdev_priv(dev);  	SET_NETDEV_DEV(dev, priv->dev); -	rc = register_netdev(dev); +	rc = register_netdevice(dev); +	rtnl_unlock();  	if (rc)  		goto out_unreg; -	dev_info(priv->dev, "The IUCV interface to %s has been" -		" established successfully\n", netiucv_printname(username)); +	dev_info(priv->dev, "The IUCV interface to %s has been established " +			    "successfully\n", +		netiucv_printuser(priv->conn));  	return count; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index f47a714538d..a2088af51cc 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -1,6 +1,4 @@  /* - *  drivers/s390/net/qeth_core.h - *   *    Copyright IBM Corp. 2007   *    Author(s): Utz Bacher <utz.bacher@de.ibm.com>,   *		 Frank Pavlic <fpavlic@de.ibm.com>, @@ -13,8 +11,6 @@  #include <linux/if.h>  #include <linux/if_arp.h> -#include <linux/if_tr.h> -#include <linux/trdevice.h>  #include <linux/etherdevice.h>  #include <linux/if_vlan.h>  #include <linux/ctype.h> @@ -110,6 +106,10 @@ struct qeth_perf_stats {  	unsigned int sc_dp_p;  	unsigned int sc_p_dp; +	/* qdio_cq_handler: number of times called, time spent in */ +	__u64 cq_start_time; +	unsigned int cq_cnt; +	unsigned int cq_time;  	/* qdio_input_handler: number of times called, time spent in */  	__u64 inbound_start_time;  	unsigned int inbound_cnt; @@ -156,6 +156,27 @@ struct qeth_ipa_info {  	__u32 enabled_funcs;  }; +/* SETBRIDGEPORT stuff */ +enum qeth_sbp_roles { +	QETH_SBP_ROLE_NONE	= 0, +	QETH_SBP_ROLE_PRIMARY	= 1, +	QETH_SBP_ROLE_SECONDARY	= 2, +}; + +enum qeth_sbp_states { +	QETH_SBP_STATE_INACTIVE	= 0, +	QETH_SBP_STATE_STANDBY	= 1, +	QETH_SBP_STATE_ACTIVE	= 2, +}; + +#define QETH_SBP_HOST_NOTIFICATION 1 + +struct qeth_sbp_info { +	__u32 supported_funcs; +	enum qeth_sbp_roles role; +	__u32 hostnotification:1; +}; +  static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa,  		enum qeth_ipa_funcs func)  { @@ -213,6 +234,7 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,   */  #define QETH_TX_TIMEOUT		100 * HZ  #define QETH_RCD_TIMEOUT	60 * HZ +#define QETH_RECLAIM_WORK_TIME	HZ  #define QETH_HEADER_SIZE	32  #define QETH_MAX_PORTNO		15 @@ -225,12 +247,13 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,  /*****************************************************************************/  #define QETH_MAX_QUEUES 4  #define QETH_IN_BUF_SIZE_DEFAULT 65536 -#define QETH_IN_BUF_COUNT_DEFAULT 16 +#define QETH_IN_BUF_COUNT_DEFAULT 64 +#define QETH_IN_BUF_COUNT_HSDEFAULT 128  #define QETH_IN_BUF_COUNT_MIN 8  #define QETH_IN_BUF_COUNT_MAX 128  #define QETH_MAX_BUFFER_ELEMENTS(card) ((card)->qdio.in_buf_size >> 12)  #define QETH_IN_BUF_REQUEUE_THRESHOLD(card) \ -		((card)->qdio.in_buf_pool.buf_count / 2) +		 ((card)->qdio.in_buf_pool.buf_count / 2)  /* buffers we have to be behind before we get a PCI */  #define QETH_PCI_THRESHOLD_A(card) ((card)->qdio.in_buf_pool.buf_count+1) @@ -245,10 +268,8 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,  #define QETH_NO_PRIO_QUEUEING 0  #define QETH_PRIO_Q_ING_PREC  1  #define QETH_PRIO_Q_ING_TOS   2 -#define IP_TOS_LOWDELAY 0x10 -#define IP_TOS_HIGHTHROUGHPUT 0x08 -#define IP_TOS_HIGHRELIABILITY 0x04 -#define IP_TOS_NOTIMPORTANT 0x02 +#define QETH_PRIO_Q_ING_SKB   3 +#define QETH_PRIO_Q_ING_VLAN  4  /* Packing */  #define QETH_LOW_WATERMARK_PACK  2 @@ -259,6 +280,7 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,  /* large receive scatter gather copy break */  #define QETH_RX_SG_CB (PAGE_SIZE >> 1) +#define QETH_RX_PULL_LEN 256  struct qeth_hdr_layer3 {  	__u8  id; @@ -360,7 +382,7 @@ enum qeth_header_ids {  static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale)  { -	return (sbale->flags & SBAL_FLAGS_LAST_ENTRY); +	return (sbale->eflags & SBAL_EFLAGS_LAST_ENTRY);  }  enum qeth_qdio_buffer_states { @@ -374,6 +396,21 @@ enum qeth_qdio_buffer_states {  	 * outbound: filled by driver; owned by hardware in order to be sent  	 */  	QETH_QDIO_BUF_PRIMED, +	/* +	 * inbound: not applicable +	 * outbound: identified to be pending in TPQ +	 */ +	QETH_QDIO_BUF_PENDING, +	/* +	 * inbound: not applicable +	 * outbound: found in completion queue +	 */ +	QETH_QDIO_BUF_IN_CQ, +	/* +	 * inbound: not applicable +	 * outbound: handled via transfer pending / completion queue +	 */ +	QETH_QDIO_BUF_HANDLED_DELAYED,  };  enum qeth_qdio_info_states { @@ -398,6 +435,7 @@ struct qeth_qdio_buffer {  	struct qdio_buffer *buffer;  	/* the buffer pool entry currently associated to this buffer */  	struct qeth_buffer_pool_entry *pool_entry; +	struct sk_buff *rx_skb;  };  struct qeth_qdio_q { @@ -406,19 +444,16 @@ struct qeth_qdio_q {  	int next_buf_to_init;  } __attribute__ ((aligned(256))); -/* possible types of qeth large_send support */ -enum qeth_large_send_types { -	QETH_LARGE_SEND_NO, -	QETH_LARGE_SEND_TSO, -}; -  struct qeth_qdio_out_buffer {  	struct qdio_buffer *buffer;  	atomic_t state;  	int next_element_to_fill;  	struct sk_buff_head skb_list; -	struct list_head ctx_list;  	int is_header[16]; + +	struct qaob *aob; +	struct qeth_qdio_out_q *q; +	struct qeth_qdio_out_buffer *next_pending;  };  struct qeth_card; @@ -431,7 +466,8 @@ enum qeth_out_q_states {  struct qeth_qdio_out_q {  	struct qdio_buffer qdio_bufs[QDIO_MAX_BUFFERS_PER_Q]; -	struct qeth_qdio_out_buffer bufs[QDIO_MAX_BUFFERS_PER_Q]; +	struct qeth_qdio_out_buffer *bufs[QDIO_MAX_BUFFERS_PER_Q]; +	struct qdio_outbuf_state *bufstates; /* convenience pointer */  	int queue_no;  	struct qeth_card *card;  	atomic_t state; @@ -452,7 +488,9 @@ struct qeth_qdio_out_q {  struct qeth_qdio_info {  	atomic_t state;  	/* input */ +	int no_in_queues;  	struct qeth_qdio_q *in_q; +	struct qeth_qdio_q *c_q;  	struct qeth_qdio_buffer_pool in_buf_pool;  	struct qeth_qdio_buffer_pool init_pool;  	int in_buf_size; @@ -460,6 +498,7 @@ struct qeth_qdio_info {  	/* output */  	int no_out_queues;  	struct qeth_qdio_out_q **out_qs; +	struct qdio_outbuf_state *out_bufstates;  	/* priority queueing */  	int do_prio_queueing; @@ -531,6 +570,12 @@ enum qeth_cmd_buffer_state {  	BUF_STATE_PROCESSED,  }; +enum qeth_cq { +	QETH_CQ_DISABLED = 0, +	QETH_CQ_ENABLED = 1, +	QETH_CQ_NOTAVAILABLE = 2, +}; +  struct qeth_ipato {  	int enabled;  	int invert4; @@ -636,6 +681,8 @@ struct qeth_card_info {  	__u32 csum_mask;  	__u32 tx_csum_mask;  	enum qeth_ipa_promisc_modes promisc_mode; +	__u32 diagass_support; +	__u32 hwtrap;  };  struct qeth_card_options { @@ -644,17 +691,17 @@ struct qeth_card_options {  	struct qeth_ipa_info adp; /*Adapter parameters*/  	struct qeth_routing_info route6;  	struct qeth_ipa_info ipa6; -	enum qeth_checksum_types checksum_type; -	int broadcast_mode; -	int macaddr_mode; +	struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */  	int fake_broadcast;  	int add_hhlen;  	int layer2; -	enum qeth_large_send_types large_send;  	int performance_stats;  	int rx_sg_cb;  	enum qeth_ipa_isolation_modes isolation; +	enum qeth_ipa_isolation_modes prev_isolation;  	int sniffer; +	enum qeth_cq cq; +	char hsuid[9];  };  /* @@ -679,7 +726,18 @@ struct qeth_discipline {  	qdio_handler_t *input_handler;  	qdio_handler_t *output_handler;  	int (*recover)(void *ptr); -	struct ccwgroup_driver *ccwgdriver; +	int (*setup) (struct ccwgroup_device *); +	void (*remove) (struct ccwgroup_device *); +	int (*set_online) (struct ccwgroup_device *); +	int (*set_offline) (struct ccwgroup_device *); +	void (*shutdown)(struct ccwgroup_device *); +	int (*prepare) (struct ccwgroup_device *); +	void (*complete) (struct ccwgroup_device *); +	int (*freeze)(struct ccwgroup_device *); +	int (*thaw) (struct ccwgroup_device *); +	int (*restore)(struct ccwgroup_device *); +	int (*control_event_handler)(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd);  };  struct qeth_vlan_vid { @@ -702,7 +760,13 @@ struct qeth_rx {  	int qdio_err;  }; -#define QETH_NAPI_WEIGHT 128 +struct carrier_info { +	__u8  card_type; +	__u16 port_mode; +	__u32 port_speed; +}; + +#define QETH_NAPI_WEIGHT NAPI_POLL_WEIGHT  struct qeth_card {  	struct list_head list; @@ -725,7 +789,7 @@ struct qeth_card {  	wait_queue_head_t wait_q;  	spinlock_t vlanlock;  	spinlock_t mclock; -	struct vlan_group *vlangrp; +	unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];  	struct list_head vid_list;  	struct list_head mc_list;  	struct work_struct kernel_thread_starter; @@ -733,6 +797,7 @@ struct qeth_card {  	unsigned long thread_start_mask;  	unsigned long thread_allowed_mask;  	unsigned long thread_running_mask; +	struct task_struct *recovery_task;  	spinlock_t ip_lock;  	struct list_head ip_list;  	struct list_head *ip_tbd_list; @@ -741,10 +806,9 @@ struct qeth_card {  	/* QDIO buffer handling */  	struct qeth_qdio_info qdio;  	struct qeth_perf_stats perf_stats; -	int use_hard_stop;  	int read_or_write_problem;  	struct qeth_osn_info osn_info; -	struct qeth_discipline discipline; +	struct qeth_discipline *discipline;  	atomic_t force_alloc_skb;  	struct service_level qeth_service_level;  	struct qdio_ssqd_desc ssqd; @@ -753,6 +817,9 @@ struct qeth_card {  	struct mutex discipline_mutex;  	struct napi_struct napi;  	struct qeth_rx rx; +	struct delayed_work buffer_reclaim_work; +	int reclaim_index; +	struct work_struct close_dev_work;  };  struct qeth_card_list_struct { @@ -760,6 +827,14 @@ struct qeth_card_list_struct {  	rwlock_t rwlock;  }; +struct qeth_trap_id { +	__u16 lparnr; +	char vmname[8]; +	__u8 chpid; +	__u8 ssid; +	__u16 devno; +} __packed; +  /*some helper functions*/  #define QETH_CARD_IFNAME(card) (((card)->dev)? (card)->dev->name : "") @@ -772,13 +847,16 @@ static inline struct qeth_card *CARD_FROM_CDEV(struct ccw_device *cdev)  static inline int qeth_get_micros(void)  { -	return (int) (get_clock() >> 12); +	return (int) (get_tod_clock() >> 12);  }  static inline int qeth_get_ip_version(struct sk_buff *skb)  { -	struct ethhdr *ehdr = (struct ethhdr *)skb->data; -	switch (ehdr->h_proto) { +	__be16 *p = &((struct ethhdr *)skb->data)->h_proto; + +	if (*p == ETH_P_8021Q) +		p += 2; +	switch (*p) {  	case ETH_P_IPV6:  		return 6;  	case ETH_P_IP: @@ -794,22 +872,31 @@ static inline void qeth_put_buffer_pool_entry(struct qeth_card *card,  	list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list);  } -extern struct ccwgroup_driver qeth_l2_ccwgroup_driver; -extern struct ccwgroup_driver qeth_l3_ccwgroup_driver; +static inline int qeth_is_diagass_supported(struct qeth_card *card, +		enum qeth_diags_cmds cmd) +{ +	return card->info.diagass_support & (__u32)cmd; +} + +extern struct qeth_discipline qeth_l2_discipline; +extern struct qeth_discipline qeth_l3_discipline; +extern const struct attribute_group *qeth_generic_attr_groups[]; +extern const struct attribute_group *qeth_osn_attr_groups[]; +extern struct workqueue_struct *qeth_wq; +  const char *qeth_get_cardname_short(struct qeth_card *);  int qeth_realloc_buffer_pool(struct qeth_card *, int);  int qeth_core_load_discipline(struct qeth_card *, enum qeth_discipline_id);  void qeth_core_free_discipline(struct qeth_card *); -int qeth_core_create_device_attributes(struct device *); -void qeth_core_remove_device_attributes(struct device *); -int qeth_core_create_osn_attributes(struct device *); -void qeth_core_remove_osn_attributes(struct device *); +void qeth_buffer_reclaim_work(struct work_struct *);  /* exports for qeth discipline device drivers */  extern struct qeth_card_list_struct qeth_core_card_list;  extern struct kmem_cache *qeth_core_header_cache;  extern struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS]; +void qeth_set_recovery_task(struct qeth_card *); +void qeth_clear_recovery_task(struct qeth_card *);  void qeth_set_allowed_threads(struct qeth_card *, unsigned long , int);  int qeth_threads_running(struct qeth_card *, unsigned long);  int qeth_wait_for_threads(struct qeth_card *, unsigned long); @@ -832,7 +919,7 @@ int qeth_check_qdio_errors(struct qeth_card *, struct qdio_buffer *,  		unsigned int, const char *);  void qeth_queue_input_buffer(struct qeth_card *, int);  struct sk_buff *qeth_core_get_next_skb(struct qeth_card *, -		struct qdio_buffer *, struct qdio_buffer_element **, int *, +		struct qeth_qdio_buffer *, struct qdio_buffer_element **, int *,  		struct qeth_hdr **);  void qeth_schedule_recovery(struct qeth_card *);  void qeth_qdio_start_poll(struct ccw_device *, int, unsigned long); @@ -858,14 +945,19 @@ 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 *); -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); +int qeth_query_oat_command(struct qeth_card *, char __user *); +int qeth_query_card_info(struct qeth_card *card, +	struct carrier_info *carrier_info);  int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,  	int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long),  	void *reply_param); +int qeth_bridgeport_query_ports(struct qeth_card *card, +	enum qeth_sbp_roles *role, enum qeth_sbp_states *state); +int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); +int qeth_bridgeport_an_set(struct qeth_card *card, int enable);  int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); -int qeth_get_elements_no(struct qeth_card *, void *, struct sk_buff *, int); +int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); +int qeth_get_elements_for_frags(struct sk_buff *);  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 *, @@ -877,8 +969,13 @@ 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(debug_info_t *id, 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); -int qeth_hdr_chk_and_bounce(struct sk_buff *, int); +int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback); +int qeth_hdr_chk_and_bounce(struct sk_buff *, struct qeth_hdr **, int); +int qeth_configure_cq(struct qeth_card *, enum qeth_cq); +int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action); +int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot); +void qeth_trace_features(struct qeth_card *); +void qeth_close_dev(struct qeth_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 e6b2df0e73f..f54bec54d67 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1,6 +1,4 @@  /* - *  drivers/s390/net/qeth_core_main.c - *   *    Copyright IBM Corp. 2007, 2009   *    Author(s): Utz Bacher <utz.bacher@de.ibm.com>,   *		 Frank Pavlic <fpavlic@de.ibm.com>, @@ -21,9 +19,14 @@  #include <linux/mii.h>  #include <linux/kthread.h>  #include <linux/slab.h> +#include <net/iucv/af_iucv.h> +#include <net/dsfield.h>  #include <asm/ebcdic.h> +#include <asm/chpid.h>  #include <asm/io.h> +#include <asm/sysinfo.h> +#include <asm/compat.h>  #include "qeth_core.h" @@ -32,8 +35,8 @@ struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = {  	/*                   N  P  A    M  L  V                      H  */  	[QETH_DBF_SETUP] = {"qeth_setup",  				8, 1,   8, 5, &debug_hex_ascii_view, NULL}, -	[QETH_DBF_MSG]   = {"qeth_msg", -				8, 1, 128, 3, &debug_sprintf_view,   NULL}, +	[QETH_DBF_MSG]	 = {"qeth_msg", 8, 1, 11 * sizeof(long), 3, +			    &debug_sprintf_view, NULL},  	[QETH_DBF_CTRL]  = {"qeth_control",  		8, 1, QETH_DBF_CTRL_LEN, 5, &debug_hex_ascii_view, NULL},  }; @@ -43,10 +46,12 @@ struct qeth_card_list_struct qeth_core_card_list;  EXPORT_SYMBOL_GPL(qeth_core_card_list);  struct kmem_cache *qeth_core_header_cache;  EXPORT_SYMBOL_GPL(qeth_core_header_cache); +static struct kmem_cache *qeth_qdio_outbuf_cache;  static struct device *qeth_core_root_dev;  static unsigned int known_devices[][6] = QETH_MODELLIST_ARRAY;  static struct lock_class_key qdio_out_skb_queue_key; +static struct mutex qeth_mod_mutex;  static void qeth_send_control_data_cb(struct qeth_channel *,  			struct qeth_cmd_buffer *); @@ -55,20 +60,50 @@ static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *);  static void qeth_setup_ccw(struct qeth_channel *, unsigned char *, __u32);  static void qeth_free_buffer_pool(struct qeth_card *);  static int qeth_qdio_establish(struct qeth_card *); +static void qeth_free_qdio_buffers(struct qeth_card *); +static void qeth_notify_skbs(struct qeth_qdio_out_q *queue, +		struct qeth_qdio_out_buffer *buf, +		enum iucv_tx_notify notification); +static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf); +static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, +		struct qeth_qdio_out_buffer *buf, +		enum qeth_qdio_buffer_states newbufstate); +static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); + +struct workqueue_struct *qeth_wq; +EXPORT_SYMBOL_GPL(qeth_wq); + +static void qeth_close_dev_handler(struct work_struct *work) +{ +	struct qeth_card *card; + +	card = container_of(work, struct qeth_card, close_dev_work); +	QETH_CARD_TEXT(card, 2, "cldevhdl"); +	rtnl_lock(); +	dev_close(card->dev); +	rtnl_unlock(); +	ccwgroup_set_offline(card->gdev); +} +void qeth_close_dev(struct qeth_card *card) +{ +	QETH_CARD_TEXT(card, 2, "cldevsubm"); +	queue_work(qeth_wq, &card->close_dev_work); +} +EXPORT_SYMBOL_GPL(qeth_close_dev);  static inline const char *qeth_get_cardname(struct qeth_card *card)  {  	if (card->info.guestlan) {  		switch (card->info.type) {  		case QETH_CARD_TYPE_OSD: -			return " Guest LAN QDIO"; +			return " Virtual NIC QDIO";  		case QETH_CARD_TYPE_IQD: -			return " Guest LAN Hiper"; +			return " Virtual NIC Hiper";  		case QETH_CARD_TYPE_OSM: -			return " Guest LAN QDIO - OSM"; +			return " Virtual NIC QDIO - OSM";  		case QETH_CARD_TYPE_OSX: -			return " Guest LAN QDIO - OSX"; +			return " Virtual NIC QDIO - OSX";  		default:  			return " unknown";  		} @@ -97,13 +132,13 @@ const char *qeth_get_cardname_short(struct qeth_card *card)  	if (card->info.guestlan) {  		switch (card->info.type) {  		case QETH_CARD_TYPE_OSD: -			return "GuestLAN QDIO"; +			return "Virt.NIC QDIO";  		case QETH_CARD_TYPE_IQD: -			return "GuestLAN Hiper"; +			return "Virt.NIC Hiper";  		case QETH_CARD_TYPE_OSM: -			return "GuestLAN OSM"; +			return "Virt.NIC OSM";  		case QETH_CARD_TYPE_OSX: -			return "GuestLAN OSX"; +			return "Virt.NIC OSX";  		default:  			return "unknown";  		} @@ -145,6 +180,23 @@ const char *qeth_get_cardname_short(struct qeth_card *card)  	return "n/a";  } +void qeth_set_recovery_task(struct qeth_card *card) +{ +	card->recovery_task = current; +} +EXPORT_SYMBOL_GPL(qeth_set_recovery_task); + +void qeth_clear_recovery_task(struct qeth_card *card) +{ +	card->recovery_task = NULL; +} +EXPORT_SYMBOL_GPL(qeth_clear_recovery_task); + +static bool qeth_is_recovery_task(const struct qeth_card *card) +{ +	return card->recovery_task == current; +} +  void qeth_set_allowed_threads(struct qeth_card *card, unsigned long threads,  			 int clear_start_mask)  { @@ -173,6 +225,8 @@ EXPORT_SYMBOL_GPL(qeth_threads_running);  int qeth_wait_for_threads(struct qeth_card *card, unsigned long threads)  { +	if (qeth_is_recovery_task(card)) +		return 0;  	return wait_event_interruptible(card->wait_q,  			qeth_threads_running(card, threads) == 0);  } @@ -198,7 +252,7 @@ static int qeth_alloc_buffer_pool(struct qeth_card *card)  	QETH_CARD_TEXT(card, 5, "alocpool");  	for (i = 0; i < card->qdio.init_pool.buf_count; ++i) { -		pool_entry = kmalloc(sizeof(*pool_entry), GFP_KERNEL); +		pool_entry = kzalloc(sizeof(*pool_entry), GFP_KERNEL);  		if (!pool_entry) {  			qeth_free_buffer_pool(card);  			return -ENOMEM; @@ -238,6 +292,205 @@ int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt)  }  EXPORT_SYMBOL_GPL(qeth_realloc_buffer_pool); +static inline int qeth_cq_init(struct qeth_card *card) +{ +	int rc; + +	if (card->options.cq == QETH_CQ_ENABLED) { +		QETH_DBF_TEXT(SETUP, 2, "cqinit"); +		memset(card->qdio.c_q->qdio_bufs, 0, +		       QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer)); +		card->qdio.c_q->next_buf_to_init = 127; +		rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, +			     card->qdio.no_in_queues - 1, 0, +			     127); +		if (rc) { +			QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); +			goto out; +		} +	} +	rc = 0; +out: +	return rc; +} + +static inline int qeth_alloc_cq(struct qeth_card *card) +{ +	int rc; + +	if (card->options.cq == QETH_CQ_ENABLED) { +		int i; +		struct qdio_outbuf_state *outbuf_states; + +		QETH_DBF_TEXT(SETUP, 2, "cqon"); +		card->qdio.c_q = kzalloc(sizeof(struct qeth_qdio_q), +					 GFP_KERNEL); +		if (!card->qdio.c_q) { +			rc = -1; +			goto kmsg_out; +		} +		QETH_DBF_HEX(SETUP, 2, &card->qdio.c_q, sizeof(void *)); + +		for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { +			card->qdio.c_q->bufs[i].buffer = +				&card->qdio.c_q->qdio_bufs[i]; +		} + +		card->qdio.no_in_queues = 2; + +		card->qdio.out_bufstates = +			kzalloc(card->qdio.no_out_queues * +				QDIO_MAX_BUFFERS_PER_Q * +				sizeof(struct qdio_outbuf_state), GFP_KERNEL); +		outbuf_states = card->qdio.out_bufstates; +		if (outbuf_states == NULL) { +			rc = -1; +			goto free_cq_out; +		} +		for (i = 0; i < card->qdio.no_out_queues; ++i) { +			card->qdio.out_qs[i]->bufstates = outbuf_states; +			outbuf_states += QDIO_MAX_BUFFERS_PER_Q; +		} +	} else { +		QETH_DBF_TEXT(SETUP, 2, "nocq"); +		card->qdio.c_q = NULL; +		card->qdio.no_in_queues = 1; +	} +	QETH_DBF_TEXT_(SETUP, 2, "iqc%d", card->qdio.no_in_queues); +	rc = 0; +out: +	return rc; +free_cq_out: +	kfree(card->qdio.c_q); +	card->qdio.c_q = NULL; +kmsg_out: +	dev_err(&card->gdev->dev, "Failed to create completion queue\n"); +	goto out; +} + +static inline void qeth_free_cq(struct qeth_card *card) +{ +	if (card->qdio.c_q) { +		--card->qdio.no_in_queues; +		kfree(card->qdio.c_q); +		card->qdio.c_q = NULL; +	} +	kfree(card->qdio.out_bufstates); +	card->qdio.out_bufstates = NULL; +} + +static inline enum iucv_tx_notify qeth_compute_cq_notification(int sbalf15, +	int delayed) { +	enum iucv_tx_notify n; + +	switch (sbalf15) { +	case 0: +		n = delayed ? TX_NOTIFY_DELAYED_OK : TX_NOTIFY_OK; +		break; +	case 4: +	case 16: +	case 17: +	case 18: +		n = delayed ? TX_NOTIFY_DELAYED_UNREACHABLE : +			TX_NOTIFY_UNREACHABLE; +		break; +	default: +		n = delayed ? TX_NOTIFY_DELAYED_GENERALERROR : +			TX_NOTIFY_GENERALERROR; +		break; +	} + +	return n; +} + +static inline void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, +	int bidx, int forced_cleanup) +{ +	if (q->card->options.cq != QETH_CQ_ENABLED) +		return; + +	if (q->bufs[bidx]->next_pending != NULL) { +		struct qeth_qdio_out_buffer *head = q->bufs[bidx]; +		struct qeth_qdio_out_buffer *c = q->bufs[bidx]->next_pending; + +		while (c) { +			if (forced_cleanup || +			    atomic_read(&c->state) == +			      QETH_QDIO_BUF_HANDLED_DELAYED) { +				struct qeth_qdio_out_buffer *f = c; +				QETH_CARD_TEXT(f->q->card, 5, "fp"); +				QETH_CARD_TEXT_(f->q->card, 5, "%lx", (long) f); +				/* release here to avoid interleaving between +				   outbound tasklet and inbound tasklet +				   regarding notifications and lifecycle */ +				qeth_release_skbs(c); + +				c = f->next_pending; +				WARN_ON_ONCE(head->next_pending != f); +				head->next_pending = c; +				kmem_cache_free(qeth_qdio_outbuf_cache, f); +			} else { +				head = c; +				c = c->next_pending; +			} + +		} +	} +	if (forced_cleanup && (atomic_read(&(q->bufs[bidx]->state)) == +					QETH_QDIO_BUF_HANDLED_DELAYED)) { +		/* for recovery situations */ +		q->bufs[bidx]->aob = q->bufstates[bidx].aob; +		qeth_init_qdio_out_buf(q, bidx); +		QETH_CARD_TEXT(q->card, 2, "clprecov"); +	} +} + + +static inline void qeth_qdio_handle_aob(struct qeth_card *card, +		unsigned long phys_aob_addr) { +	struct qaob *aob; +	struct qeth_qdio_out_buffer *buffer; +	enum iucv_tx_notify notification; + +	aob = (struct qaob *) phys_to_virt(phys_aob_addr); +	QETH_CARD_TEXT(card, 5, "haob"); +	QETH_CARD_TEXT_(card, 5, "%lx", phys_aob_addr); +	buffer = (struct qeth_qdio_out_buffer *) aob->user1; +	QETH_CARD_TEXT_(card, 5, "%lx", aob->user1); + +	if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED, +			   QETH_QDIO_BUF_IN_CQ) == QETH_QDIO_BUF_PRIMED) { +		notification = TX_NOTIFY_OK; +	} else { +		WARN_ON_ONCE(atomic_read(&buffer->state) != +							QETH_QDIO_BUF_PENDING); +		atomic_set(&buffer->state, QETH_QDIO_BUF_IN_CQ); +		notification = TX_NOTIFY_DELAYED_OK; +	} + +	if (aob->aorc != 0)  { +		QETH_CARD_TEXT_(card, 2, "aorc%02X", aob->aorc); +		notification = qeth_compute_cq_notification(aob->aorc, 1); +	} +	qeth_notify_skbs(buffer->q, buffer, notification); + +	buffer->aob = NULL; +	qeth_clear_output_buffer(buffer->q, buffer, +				 QETH_QDIO_BUF_HANDLED_DELAYED); + +	/* from here on: do not touch buffer anymore */ +	qdio_release_aob(aob); +} + +static inline int qeth_is_cq(struct qeth_card *card, unsigned int queue) +{ +	return card->options.cq == QETH_CQ_ENABLED && +	    card->qdio.c_q != NULL && +	    queue != 0 && +	    queue == card->qdio.no_in_queues - 1; +} + +  static int qeth_issue_next_read(struct qeth_card *card)  {  	int rc; @@ -278,7 +531,7 @@ static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card)  		atomic_set(&reply->refcnt, 1);  		atomic_set(&reply->received, 0);  		reply->card = card; -	}; +	}  	return reply;  } @@ -302,12 +555,15 @@ static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc,  	int com = cmd->hdr.command;  	ipa_name = qeth_get_ipa_cmd_name(com);  	if (rc) -		QETH_DBF_MESSAGE(2, "IPA: %s(x%X) for %s returned x%X \"%s\"\n", -				ipa_name, com, QETH_CARD_IFNAME(card), -					rc, qeth_get_ipa_msg(rc)); +		QETH_DBF_MESSAGE(2, "IPA: %s(x%X) for %s/%s returned " +				"x%X \"%s\"\n", +				ipa_name, com, dev_name(&card->gdev->dev), +				QETH_CARD_IFNAME(card), rc, +				qeth_get_ipa_msg(rc));  	else -		QETH_DBF_MESSAGE(5, "IPA: %s(x%X) for %s succeeded\n", -				ipa_name, com, QETH_CARD_IFNAME(card)); +		QETH_DBF_MESSAGE(5, "IPA: %s(x%X) for %s/%s succeeded\n", +				ipa_name, com, dev_name(&card->gdev->dev), +				QETH_CARD_IFNAME(card));  }  static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, @@ -329,11 +585,23 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,  		} else {  			switch (cmd->hdr.command) {  			case IPA_CMD_STOPLAN: -				dev_warn(&card->gdev->dev, +				if (cmd->hdr.return_code == +						IPA_RC_VEPA_TO_VEB_TRANSITION) { +					dev_err(&card->gdev->dev, +					   "Interface %s is down because the " +					   "adjacent port is no longer in " +					   "reflective relay mode\n", +					   QETH_CARD_IFNAME(card)); +					qeth_close_dev(card); +				} else { +					dev_warn(&card->gdev->dev,  					   "The link for interface %s on CHPID"  					   " 0x%X failed\n",  					   QETH_CARD_IFNAME(card),  					   card->info.chpid); +					qeth_issue_ipa_msg(cmd, +						cmd->hdr.return_code, card); +				}  				card->lan_online = 0;  				if (card->dev && netif_carrier_ok(card->dev))  					netif_carrier_off(card->dev); @@ -346,8 +614,17 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,  					   card->info.chpid);  				netif_carrier_on(card->dev);  				card->lan_online = 1; +				if (card->info.hwtrap) +					card->info.hwtrap = 2;  				qeth_schedule_recovery(card);  				return NULL; +			case IPA_CMD_SETBRIDGEPORT: +			case IPA_CMD_ADDRESS_CHANGE_NOTIF: +				if (card->discipline->control_event_handler +								(card, cmd)) +					return cmd; +				else +					return NULL;  			case IPA_CMD_MODCCID:  				return cmd;  			case IPA_CMD_REGISTER_LOCAL_ADDR: @@ -461,6 +738,7 @@ void qeth_release_buffer(struct qeth_channel *channel,  	iob->callback = qeth_send_control_data_cb;  	iob->rc = 0;  	spin_unlock_irqrestore(&channel->iob_lock, flags); +	wake_up(&channel->wait_q);  }  EXPORT_SYMBOL_GPL(qeth_release_buffer); @@ -583,7 +861,7 @@ static int qeth_setup_channel(struct qeth_channel *channel)  	QETH_DBF_TEXT(SETUP, 2, "setupch");  	for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) {  		channel->iob[cnt].data = -			kmalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL); +			kzalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL);  		if (channel->iob[cnt].data == NULL)  			break;  		channel->iob[cnt].state = BUF_STATE_FREE; @@ -736,7 +1014,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,  	card = CARD_FROM_CDEV(cdev); -	if (!IS_ERR(irb)) +	if (!card || !IS_ERR(irb))  		return 0;  	switch (PTR_ERR(irb)) { @@ -752,7 +1030,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,  		QETH_CARD_TEXT(card, 2, "ckirberr");  		QETH_CARD_TEXT_(card, 2, "  rc%d", -ETIMEDOUT);  		if (intparm == QETH_RCD_PARM) { -			if (card && (card->data.ccwdev == cdev)) { +			if (card->data.ccwdev == cdev) {  				card->data.state = CH_STATE_DOWN;  				wake_up(&card->wait_q);  			} @@ -877,22 +1155,74 @@ out:  	return;  } -static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, -		struct qeth_qdio_out_buffer *buf) +static void qeth_notify_skbs(struct qeth_qdio_out_q *q, +		struct qeth_qdio_out_buffer *buf, +		enum iucv_tx_notify notification)  { -	int i;  	struct sk_buff *skb; -	/* is PCI flag set on buffer? */ -	if (buf->buffer->element[0].flags & 0x40) -		atomic_dec(&queue->set_pci_flags_count); +	if (skb_queue_empty(&buf->skb_list)) +		goto out; +	skb = skb_peek(&buf->skb_list); +	while (skb) { +		QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification); +		QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb); +		if (skb->protocol == ETH_P_AF_IUCV) { +			if (skb->sk) { +				struct iucv_sock *iucv = iucv_sk(skb->sk); +				iucv->sk_txnotify(skb, notification); +			} +		} +		if (skb_queue_is_last(&buf->skb_list, skb)) +			skb = NULL; +		else +			skb = skb_queue_next(&buf->skb_list, skb); +	} +out: +	return; +} + +static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf) +{ +	struct sk_buff *skb; +	struct iucv_sock *iucv; +	int notify_general_error = 0; + +	if (atomic_read(&buf->state) == QETH_QDIO_BUF_PENDING) +		notify_general_error = 1; + +	/* release may never happen from within CQ tasklet scope */ +	WARN_ON_ONCE(atomic_read(&buf->state) == QETH_QDIO_BUF_IN_CQ);  	skb = skb_dequeue(&buf->skb_list);  	while (skb) { +		QETH_CARD_TEXT(buf->q->card, 5, "skbr"); +		QETH_CARD_TEXT_(buf->q->card, 5, "%lx", (long) skb); +		if (notify_general_error && skb->protocol == ETH_P_AF_IUCV) { +			if (skb->sk) { +				iucv = iucv_sk(skb->sk); +				iucv->sk_txnotify(skb, TX_NOTIFY_GENERALERROR); +			} +		}  		atomic_dec(&skb->users);  		dev_kfree_skb_any(skb);  		skb = skb_dequeue(&buf->skb_list);  	} +} + +static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, +		struct qeth_qdio_out_buffer *buf, +		enum qeth_qdio_buffer_states newbufstate) +{ +	int i; + +	/* is PCI flag set on buffer? */ +	if (buf->buffer->element[0].sflags & SBAL_SFLAGS0_PCI_REQ) +		atomic_dec(&queue->set_pci_flags_count); + +	if (newbufstate == QETH_QDIO_BUF_EMPTY) { +		qeth_release_skbs(buf); +	}  	for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) {  		if (buf->buffer->element[i].addr && buf->is_header[i])  			kmem_cache_free(qeth_core_header_cache, @@ -900,25 +1230,42 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,  		buf->is_header[i] = 0;  		buf->buffer->element[i].length = 0;  		buf->buffer->element[i].addr = NULL; -		buf->buffer->element[i].flags = 0; +		buf->buffer->element[i].eflags = 0; +		buf->buffer->element[i].sflags = 0;  	} -	buf->buffer->element[15].flags = 0; +	buf->buffer->element[15].eflags = 0; +	buf->buffer->element[15].sflags = 0;  	buf->next_element_to_fill = 0; -	atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY); +	atomic_set(&buf->state, newbufstate); +} + +static void qeth_clear_outq_buffers(struct qeth_qdio_out_q *q, int free) +{ +	int j; + +	for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { +		if (!q->bufs[j]) +			continue; +		qeth_cleanup_handled_pending(q, j, 1); +		qeth_clear_output_buffer(q, q->bufs[j], QETH_QDIO_BUF_EMPTY); +		if (free) { +			kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[j]); +			q->bufs[j] = NULL; +		} +	}  }  void qeth_clear_qdio_buffers(struct qeth_card *card)  { -	int i, j; +	int i;  	QETH_CARD_TEXT(card, 2, "clearqdbf");  	/* clear outbound buffers to free skbs */ -	for (i = 0; i < card->qdio.no_out_queues; ++i) +	for (i = 0; i < card->qdio.no_out_queues; ++i) {  		if (card->qdio.out_qs[i]) { -			for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) -				qeth_clear_output_buffer(card->qdio.out_qs[i], -						&card->qdio.out_qs[i]->bufs[j]); +			qeth_clear_outq_buffers(card->qdio.out_qs[i], 0);  		} +	}  }  EXPORT_SYMBOL_GPL(qeth_clear_qdio_buffers); @@ -942,6 +1289,13 @@ static void qeth_free_qdio_buffers(struct qeth_card *card)  	if (atomic_xchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED) ==  		QETH_QDIO_UNINITIALIZED)  		return; + +	qeth_free_cq(card); +	cancel_delayed_work_sync(&card->buffer_reclaim_work); +	for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { +		if (card->qdio.in_q->bufs[j].rx_skb) +			dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb); +	}  	kfree(card->qdio.in_q);  	card->qdio.in_q = NULL;  	/* inbound buffer pool */ @@ -949,9 +1303,7 @@ static void qeth_free_qdio_buffers(struct qeth_card *card)  	/* free outbound qdio_qs */  	if (card->qdio.out_qs) {  		for (i = 0; i < card->qdio.no_out_queues; ++i) { -			for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) -				qeth_clear_output_buffer(card->qdio.out_qs[i], -						&card->qdio.out_qs[i]->bufs[j]); +			qeth_clear_outq_buffers(card->qdio.out_qs[i], 1);  			kfree(card->qdio.out_qs[i]);  		}  		kfree(card->qdio.out_qs); @@ -968,39 +1320,54 @@ static void qeth_clean_channel(struct qeth_channel *channel)  		kfree(channel->iob[cnt].data);  } -static void qeth_get_channel_path_desc(struct qeth_card *card) +static void qeth_set_single_write_queues(struct qeth_card *card) +{ +	if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) && +	    (card->qdio.no_out_queues == 4)) +		qeth_free_qdio_buffers(card); + +	card->qdio.no_out_queues = 1; +	if (card->qdio.default_out_queue != 0) +		dev_info(&card->gdev->dev, "Priority Queueing not supported\n"); + +	card->qdio.default_out_queue = 0; +} + +static void qeth_set_multiple_write_queues(struct qeth_card *card) +{ +	if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) && +	    (card->qdio.no_out_queues == 1)) { +		qeth_free_qdio_buffers(card); +		card->qdio.default_out_queue = 2; +	} +	card->qdio.no_out_queues = 4; +} + +static void qeth_update_from_chp_desc(struct qeth_card *card)  {  	struct ccw_device *ccwdev; -	struct channelPath_dsc { -		u8 flags; -		u8 lsn; -		u8 desc; -		u8 chpid; -		u8 swla; -		u8 zeroes; -		u8 chla; -		u8 chpp; -	} *chp_dsc; +	struct channel_path_desc *chp_dsc;  	QETH_DBF_TEXT(SETUP, 2, "chp_desc");  	ccwdev = card->data.ccwdev; -	chp_dsc = (struct channelPath_dsc *)ccw_device_get_chp_desc(ccwdev, 0); -	if (chp_dsc != NULL) { -		/* CHPP field bit 6 == 1 -> single queue */ -		if ((chp_dsc->chpp & 0x02) == 0x02) -			card->qdio.no_out_queues = 1; -		card->info.func_level = 0x4100 + chp_dsc->desc; -		kfree(chp_dsc); -	} -	if (card->qdio.no_out_queues == 1) { -		card->qdio.default_out_queue = 0; -		dev_info(&card->gdev->dev, -			"Priority Queueing not supported\n"); -	} +	chp_dsc = ccw_device_get_chp_desc(ccwdev, 0); +	if (!chp_dsc) +		goto out; + +	card->info.func_level = 0x4100 + chp_dsc->desc; +	if (card->info.type == QETH_CARD_TYPE_IQD) +		goto out; + +	/* CHPP field bit 6 == 1 -> single queue */ +	if ((chp_dsc->chpp & 0x02) == 0x02) +		qeth_set_single_write_queues(card); +	else +		qeth_set_multiple_write_queues(card); +out: +	kfree(chp_dsc);  	QETH_DBF_TEXT_(SETUP, 2, "nr:%x", card->qdio.no_out_queues);  	QETH_DBF_TEXT_(SETUP, 2, "lvl:%02x", card->info.func_level); -	return;  }  static void qeth_init_qdio_info(struct qeth_card *card) @@ -1009,7 +1376,10 @@ static void qeth_init_qdio_info(struct qeth_card *card)  	atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);  	/* inbound */  	card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; -	card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT; +	if (card->info.type == QETH_CARD_TYPE_IQD) +		card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_HSDEFAULT; +	else +		card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT;  	card->qdio.in_buf_pool.buf_count = card->qdio.init_pool.buf_count;  	INIT_LIST_HEAD(&card->qdio.in_buf_pool.entry_list);  	INIT_LIST_HEAD(&card->qdio.init_pool.entry_list); @@ -1019,14 +1389,12 @@ static void qeth_set_intial_options(struct qeth_card *card)  {  	card->options.route4.type = NO_ROUTER;  	card->options.route6.type = NO_ROUTER; -	card->options.checksum_type = QETH_CHECKSUM_DEFAULT; -	card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS; -	card->options.macaddr_mode = QETH_TR_MACADDR_NONCANONICAL;  	card->options.fake_broadcast = 0;  	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; +	card->options.cq = QETH_CQ_DISABLED;  }  static int qeth_do_start_thread(struct qeth_card *card, unsigned long thread) @@ -1046,6 +1414,7 @@ static int qeth_do_start_thread(struct qeth_card *card, unsigned long thread)  static void qeth_start_kernel_thread(struct work_struct *work)  { +	struct task_struct *ts;  	struct qeth_card *card = container_of(work, struct qeth_card,  					kernel_thread_starter);  	QETH_CARD_TEXT(card , 2, "strthrd"); @@ -1053,9 +1422,15 @@ static void qeth_start_kernel_thread(struct work_struct *work)  	if (card->read.state != CH_STATE_UP &&  	    card->write.state != CH_STATE_UP)  		return; -	if (qeth_do_start_thread(card, QETH_RECOVER_THREAD)) -		kthread_run(card->discipline.recover, (void *) card, +	if (qeth_do_start_thread(card, QETH_RECOVER_THREAD)) { +		ts = kthread_run(card->discipline->recover, (void *)card,  				"qeth_recover"); +		if (IS_ERR(ts)) { +			qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); +			qeth_clear_thread_running_bit(card, +				QETH_RECOVER_THREAD); +		} +	}  }  static int qeth_setup_card(struct qeth_card *card) @@ -1069,12 +1444,10 @@ static int qeth_setup_card(struct qeth_card *card)  	card->data.state  = CH_STATE_DOWN;  	card->state = CARD_STATE_DOWN;  	card->lan_online = 0; -	card->use_hard_stop = 0;  	card->read_or_write_problem = 0;  	card->dev = NULL;  	spin_lock_init(&card->vlanlock);  	spin_lock_init(&card->mclock); -	card->vlangrp = NULL;  	spin_lock_init(&card->lock);  	spin_lock_init(&card->ip_lock);  	spin_lock_init(&card->thread_mask_lock); @@ -1088,7 +1461,7 @@ static int qeth_setup_card(struct qeth_card *card)  	INIT_LIST_HEAD(card->ip_tbd_list);  	INIT_LIST_HEAD(&card->cmd_waiter_list);  	init_waitqueue_head(&card->wait_q); -	/* intial options */ +	/* initial options */  	qeth_set_intial_options(card);  	/* IP address takeover */  	INIT_LIST_HEAD(&card->ipato.entries); @@ -1097,6 +1470,8 @@ static int qeth_setup_card(struct qeth_card *card)  	card->ipato.invert6 = 0;  	/* init QDIO stuff */  	qeth_init_qdio_info(card); +	INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work); +	INIT_WORK(&card->close_dev_work, qeth_close_dev_handler);  	return 0;  } @@ -1118,7 +1493,7 @@ static struct qeth_card *qeth_alloc_card(void)  	if (!card)  		goto out;  	QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); -	card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL); +	card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);  	if (!card->ip_tbd_list) {  		QETH_DBF_TEXT(SETUP, 0, "iptbdnom");  		goto out_card; @@ -1158,9 +1533,10 @@ static int qeth_determine_card_type(struct qeth_card *card)  			card->info.type = known_devices[i][QETH_DEV_MODEL_IND];  			card->qdio.no_out_queues =  				known_devices[i][QETH_QUEUE_NO_IND]; +			card->qdio.no_in_queues = 1;  			card->info.is_multicast_different =  				known_devices[i][QETH_MULTICAST_IND]; -			qeth_get_channel_path_desc(card); +			qeth_update_from_chp_desc(card);  			return 0;  		}  		i++; @@ -1277,7 +1653,6 @@ int qeth_qdio_clear_card(struct qeth_card *card, int use_halt)  				QDIO_FLAG_CLEANUP_USING_CLEAR);  		if (rc)  			QETH_CARD_TEXT_(card, 3, "1err%d", rc); -		qdio_free(CARD_DDEV(card));  		atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);  		break;  	case QETH_QDIO_CLEANING: @@ -1355,14 +1730,15 @@ static void qeth_configure_blkt_default(struct qeth_card *card, char *prcd)  {  	QETH_DBF_TEXT(SETUP, 2, "cfgblkt"); -	if (prcd[74] == 0xF0 && prcd[75] == 0xF0 && prcd[76] == 0xF5) { -		card->info.blkt.time_total = 250; -		card->info.blkt.inter_packet = 5; -		card->info.blkt.inter_packet_jumbo = 15; -	} else { +	if (prcd[74] == 0xF0 && prcd[75] == 0xF0 && +	    prcd[76] >= 0xF1 && prcd[76] <= 0xF4) {  		card->info.blkt.time_total = 0;  		card->info.blkt.inter_packet = 0;  		card->info.blkt.inter_packet_jumbo = 0; +	} else { +		card->info.blkt.time_total = 250; +		card->info.blkt.inter_packet = 5; +		card->info.blkt.inter_packet_jumbo = 15;  	}  } @@ -1715,23 +2091,25 @@ int qeth_send_control_data(struct qeth_card *card, int len,  			if (time_after(jiffies, timeout))  				goto time_err;  			cpu_relax(); -		}; +		}  	} +	if (reply->rc == -EIO) +		goto error;  	rc = reply->rc;  	qeth_put_reply(reply);  	return rc;  time_err: +	reply->rc = -ETIME;  	spin_lock_irqsave(&reply->card->lock, flags);  	list_del_init(&reply->list);  	spin_unlock_irqrestore(&reply->card->lock, flags); -	reply->rc = -ETIME;  	atomic_inc(&reply->received); +error:  	atomic_set(&card->write.irq_pending, 0);  	qeth_release_buffer(iob->channel, iob);  	card->write.buf_no = (card->write.buf_no + 1) % QETH_CMD_BUFFER_NO; -	wake_up(&reply->wait_q);  	rc = reply->rc;  	qeth_put_reply(reply);  	return rc; @@ -1822,43 +2200,16 @@ static inline int qeth_get_initial_mtu_for_card(struct qeth_card *card)  		case QETH_LINK_TYPE_LANE_TR:  			return 2000;  		default: -			return 1492; +			return card->options.layer2 ? 1500 : 1492;  		}  	case QETH_CARD_TYPE_OSM:  	case QETH_CARD_TYPE_OSX: -		return 1492; +		return card->options.layer2 ? 1500 : 1492;  	default:  		return 1500;  	}  } -static inline int qeth_get_max_mtu_for_card(int cardtype) -{ -	switch (cardtype) { - -	case QETH_CARD_TYPE_UNKNOWN: -	case QETH_CARD_TYPE_OSD: -	case QETH_CARD_TYPE_OSN: -	case QETH_CARD_TYPE_OSM: -	case QETH_CARD_TYPE_OSX: -		return 61440; -	case QETH_CARD_TYPE_IQD: -		return 57344; -	default: -		return 1500; -	} -} - -static inline int qeth_get_mtu_out_of_mpc(int cardtype) -{ -	switch (cardtype) { -	case QETH_CARD_TYPE_IQD: -		return 1; -	default: -		return 0; -	} -} -  static inline int qeth_get_mtu_outof_framesize(int framesize)  {  	switch (framesize) { @@ -1881,10 +2232,9 @@ static inline int qeth_mtu_is_valid(struct qeth_card *card, int mtu)  	case QETH_CARD_TYPE_OSD:  	case QETH_CARD_TYPE_OSM:  	case QETH_CARD_TYPE_OSX: -		return ((mtu >= 576) && (mtu <= 61440));  	case QETH_CARD_TYPE_IQD:  		return ((mtu >= 576) && -			(mtu <= card->info.max_mtu + 4096 - 32)); +			(mtu <= card->info.max_mtu));  	case QETH_CARD_TYPE_OSN:  	case QETH_CARD_TYPE_UNKNOWN:  	default: @@ -1907,7 +2257,7 @@ static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply,  	memcpy(&card->token.ulp_filter_r,  	       QETH_ULP_ENABLE_RESP_FILTER_TOKEN(iob->data),  	       QETH_MPC_TOKEN_LENGTH); -	if (qeth_get_mtu_out_of_mpc(card->info.type)) { +	if (card->info.type == QETH_CARD_TYPE_IQD) {  		memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data), 2);  		mtu = qeth_get_mtu_outof_framesize(framesize);  		if (!mtu) { @@ -1915,12 +2265,22 @@ static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply,  			QETH_DBF_TEXT_(SETUP, 2, "  rc%d", iob->rc);  			return 0;  		} -		card->info.max_mtu = mtu; +		if (card->info.initial_mtu && (card->info.initial_mtu != mtu)) { +			/* frame size has changed */ +			if (card->dev && +			    ((card->dev->mtu == card->info.initial_mtu) || +			     (card->dev->mtu > mtu))) +				card->dev->mtu = mtu; +			qeth_free_qdio_buffers(card); +		}  		card->info.initial_mtu = mtu; +		card->info.max_mtu = mtu;  		card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE;  	} else { -		card->info.initial_mtu = qeth_get_initial_mtu_for_card(card); -		card->info.max_mtu = qeth_get_max_mtu_for_card(card->info.type); +		card->info.max_mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU( +			iob->data); +		card->info.initial_mtu = min(card->info.max_mtu, +					qeth_get_initial_mtu_for_card(card));  		card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;  	} @@ -1975,7 +2335,6 @@ static int qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply,  		unsigned long data)  {  	struct qeth_cmd_buffer *iob; -	int rc = 0;  	QETH_DBF_TEXT(SETUP, 2, "ulpstpcb"); @@ -1991,7 +2350,7 @@ static int qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply,  		iob->rc = -EMLINK;  	}  	QETH_DBF_TEXT_(SETUP, 2, "  rc%d", iob->rc); -	return rc; +	return 0;  }  static int qeth_ulp_setup(struct qeth_card *card) @@ -2022,6 +2381,37 @@ static int qeth_ulp_setup(struct qeth_card *card)  	return rc;  } +static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *q, int bidx) +{ +	int rc; +	struct qeth_qdio_out_buffer *newbuf; + +	rc = 0; +	newbuf = kmem_cache_zalloc(qeth_qdio_outbuf_cache, GFP_ATOMIC); +	if (!newbuf) { +		rc = -ENOMEM; +		goto out; +	} +	newbuf->buffer = &q->qdio_bufs[bidx]; +	skb_queue_head_init(&newbuf->skb_list); +	lockdep_set_class(&newbuf->skb_list.lock, &qdio_out_skb_queue_key); +	newbuf->q = q; +	newbuf->aob = NULL; +	newbuf->next_pending = q->bufs[bidx]; +	atomic_set(&newbuf->state, QETH_QDIO_BUF_EMPTY); +	q->bufs[bidx] = newbuf; +	if (q->bufstates) { +		q->bufstates[bidx].user = newbuf; +		QETH_CARD_TEXT_(q->card, 2, "nbs%d", bidx); +		QETH_CARD_TEXT_(q->card, 2, "%lx", (long) newbuf); +		QETH_CARD_TEXT_(q->card, 2, "%lx", +				(long) newbuf->next_pending); +	} +out: +	return rc; +} + +  static int qeth_alloc_qdio_buffers(struct qeth_card *card)  {  	int i, j; @@ -2032,52 +2422,63 @@ static int qeth_alloc_qdio_buffers(struct qeth_card *card)  		QETH_QDIO_ALLOCATED) != QETH_QDIO_UNINITIALIZED)  		return 0; -	card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q), -				  GFP_KERNEL); +	card->qdio.in_q = kzalloc(sizeof(struct qeth_qdio_q), +				   GFP_KERNEL);  	if (!card->qdio.in_q)  		goto out_nomem;  	QETH_DBF_TEXT(SETUP, 2, "inq");  	QETH_DBF_HEX(SETUP, 2, &card->qdio.in_q, sizeof(void *));  	memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q));  	/* give inbound qeth_qdio_buffers their qdio_buffers */ -	for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) +	for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) {  		card->qdio.in_q->bufs[i].buffer =  			&card->qdio.in_q->qdio_bufs[i]; +		card->qdio.in_q->bufs[i].rx_skb = NULL; +	}  	/* inbound buffer pool */  	if (qeth_alloc_buffer_pool(card))  		goto out_freeinq; +  	/* outbound */  	card->qdio.out_qs = -		kmalloc(card->qdio.no_out_queues * +		kzalloc(card->qdio.no_out_queues *  			sizeof(struct qeth_qdio_out_q *), GFP_KERNEL);  	if (!card->qdio.out_qs)  		goto out_freepool;  	for (i = 0; i < card->qdio.no_out_queues; ++i) { -		card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q), +		card->qdio.out_qs[i] = kzalloc(sizeof(struct qeth_qdio_out_q),  					       GFP_KERNEL);  		if (!card->qdio.out_qs[i])  			goto out_freeoutq;  		QETH_DBF_TEXT_(SETUP, 2, "outq %i", i);  		QETH_DBF_HEX(SETUP, 2, &card->qdio.out_qs[i], sizeof(void *)); -		memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q));  		card->qdio.out_qs[i]->queue_no = i;  		/* give outbound qeth_qdio_buffers their qdio_buffers */  		for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { -			card->qdio.out_qs[i]->bufs[j].buffer = -				&card->qdio.out_qs[i]->qdio_bufs[j]; -			skb_queue_head_init(&card->qdio.out_qs[i]->bufs[j]. -					    skb_list); -			lockdep_set_class( -				&card->qdio.out_qs[i]->bufs[j].skb_list.lock, -				&qdio_out_skb_queue_key); -			INIT_LIST_HEAD(&card->qdio.out_qs[i]->bufs[j].ctx_list); +			WARN_ON(card->qdio.out_qs[i]->bufs[j] != NULL); +			if (qeth_init_qdio_out_buf(card->qdio.out_qs[i], j)) +				goto out_freeoutqbufs;  		}  	} + +	/* completion */ +	if (qeth_alloc_cq(card)) +		goto out_freeoutq; +  	return 0; +out_freeoutqbufs: +	while (j > 0) { +		--j; +		kmem_cache_free(qeth_qdio_outbuf_cache, +				card->qdio.out_qs[i]->bufs[j]); +		card->qdio.out_qs[i]->bufs[j] = NULL; +	}  out_freeoutq: -	while (i > 0) +	while (i > 0) {  		kfree(card->qdio.out_qs[--i]); +		qeth_clear_outq_buffers(card->qdio.out_qs[i], 1); +	}  	kfree(card->qdio.out_qs);  	card->qdio.out_qs = NULL;  out_freepool: @@ -2196,6 +2597,7 @@ static int qeth_mpc_initialize(struct qeth_card *card)  	return 0;  out_qdio:  	qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); +	qdio_free(CARD_DDEV(card));  	return rc;  } @@ -2348,6 +2750,12 @@ static int qeth_init_input_buffer(struct qeth_card *card,  	struct qeth_buffer_pool_entry *pool_entry;  	int i; +	if ((card->options.cq == QETH_CQ_ENABLED) && (!buf->rx_skb)) { +		buf->rx_skb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN); +		if (!buf->rx_skb) +			return 1; +	} +  	pool_entry = qeth_find_free_buffer_pool_entry(card);  	if (!pool_entry)  		return 1; @@ -2364,9 +2772,10 @@ static int qeth_init_input_buffer(struct qeth_card *card,  		buf->buffer->element[i].length = PAGE_SIZE;  		buf->buffer->element[i].addr =  pool_entry->elements[i];  		if (i == QETH_MAX_BUFFER_ELEMENTS(card) - 1) -			buf->buffer->element[i].flags = SBAL_FLAGS_LAST_ENTRY; +			buf->buffer->element[i].eflags = SBAL_EFLAGS_LAST_ENTRY;  		else -			buf->buffer->element[i].flags = 0; +			buf->buffer->element[i].eflags = 0; +		buf->buffer->element[i].sflags = 0;  	}  	return 0;  } @@ -2393,13 +2802,21 @@ int qeth_init_qdio_queues(struct qeth_card *card)  		QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);  		return rc;  	} + +	/* completion */ +	rc = qeth_cq_init(card); +	if (rc) { +		return rc; +	} +  	/* outbound queue */  	for (i = 0; i < card->qdio.no_out_queues; ++i) {  		memset(card->qdio.out_qs[i]->qdio_bufs, 0,  		       QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer));  		for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {  			qeth_clear_output_buffer(card->qdio.out_qs[i], -					&card->qdio.out_qs[i]->bufs[j]); +					card->qdio.out_qs[i]->bufs[j], +					QETH_QDIO_BUF_EMPTY);  		}  		card->qdio.out_qs[i]->card = card;  		card->qdio.out_qs[i]->next_buf_to_fill = 0; @@ -2495,46 +2912,20 @@ int qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,  }  EXPORT_SYMBOL_GPL(qeth_send_ipa_cmd); -static int qeth_send_startstoplan(struct qeth_card *card, -		enum qeth_ipa_cmds ipacmd, enum qeth_prot_versions prot) -{ -	int rc; -	struct qeth_cmd_buffer *iob; - -	iob = qeth_get_ipacmd_buffer(card, ipacmd, prot); -	rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); - -	return rc; -} -  int qeth_send_startlan(struct qeth_card *card)  {  	int rc; +	struct qeth_cmd_buffer *iob;  	QETH_DBF_TEXT(SETUP, 2, "strtlan"); -	rc = qeth_send_startstoplan(card, IPA_CMD_STARTLAN, 0); +	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_STARTLAN, 0); +	rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);  	return rc;  }  EXPORT_SYMBOL_GPL(qeth_send_startlan); -int qeth_send_stoplan(struct qeth_card *card) -{ -	int rc = 0; - -	/* -	 * TODO: according to the IPA format document page 14, -	 * TCP/IP (we!) never issue a STOPLAN -	 * is this right ?!? -	 */ -	QETH_DBF_TEXT(SETUP, 2, "stoplan"); - -	rc = qeth_send_startstoplan(card, IPA_CMD_STOPLAN, 0); -	return rc; -} -EXPORT_SYMBOL_GPL(qeth_send_stoplan); - -int qeth_default_setadapterparms_cb(struct qeth_card *card, +static int qeth_default_setadapterparms_cb(struct qeth_card *card,  		struct qeth_reply *reply, unsigned long data)  {  	struct qeth_ipa_cmd *cmd; @@ -2547,7 +2938,6 @@ int qeth_default_setadapterparms_cb(struct qeth_card *card,  			cmd->data.setadapterparms.hdr.return_code;  	return 0;  } -EXPORT_SYMBOL_GPL(qeth_default_setadapterparms_cb);  static int qeth_query_setadapterparms_cb(struct qeth_card *card,  		struct qeth_reply *reply, unsigned long data) @@ -2567,7 +2957,7 @@ static int qeth_query_setadapterparms_cb(struct qeth_card *card,  	return qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd);  } -struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card, +static struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card,  		__u32 command, __u32 cmdlen)  {  	struct qeth_cmd_buffer *iob; @@ -2583,7 +2973,6 @@ struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card,  	return iob;  } -EXPORT_SYMBOL_GPL(qeth_get_adapter_cmd);  int qeth_query_setadapterparms(struct qeth_card *card)  { @@ -2598,17 +2987,163 @@ int qeth_query_setadapterparms(struct qeth_card *card)  }  EXPORT_SYMBOL_GPL(qeth_query_setadapterparms); +static int qeth_query_ipassists_cb(struct qeth_card *card, +		struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd; + +	QETH_DBF_TEXT(SETUP, 2, "qipasscb"); + +	cmd = (struct qeth_ipa_cmd *) data; + +	switch (cmd->hdr.return_code) { +	case IPA_RC_NOTSUPP: +	case IPA_RC_L2_UNSUPPORTED_CMD: +		QETH_DBF_TEXT(SETUP, 2, "ipaunsup"); +		card->options.ipa4.supported_funcs |= IPA_SETADAPTERPARMS; +		card->options.ipa6.supported_funcs |= IPA_SETADAPTERPARMS; +		return -0; +	default: +		if (cmd->hdr.return_code) { +			QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Unhandled " +						"rc=%d\n", +						dev_name(&card->gdev->dev), +						cmd->hdr.return_code); +			return 0; +		} +	} + +	if (cmd->hdr.prot_version == QETH_PROT_IPV4) { +		card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported; +		card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; +	} else if (cmd->hdr.prot_version == QETH_PROT_IPV6) { +		card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported; +		card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; +	} else +		QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Flawed LIC detected" +					"\n", dev_name(&card->gdev->dev)); +	return 0; +} + +int qeth_query_ipassists(struct qeth_card *card, enum qeth_prot_versions prot) +{ +	int rc; +	struct qeth_cmd_buffer *iob; + +	QETH_DBF_TEXT_(SETUP, 2, "qipassi%i", prot); +	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_QIPASSIST, prot); +	rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL); +	return rc; +} +EXPORT_SYMBOL_GPL(qeth_query_ipassists); + +static int qeth_query_setdiagass_cb(struct qeth_card *card, +		struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd; +	__u16 rc; + +	cmd = (struct qeth_ipa_cmd *)data; +	rc = cmd->hdr.return_code; +	if (rc) +		QETH_CARD_TEXT_(card, 2, "diagq:%x", rc); +	else +		card->info.diagass_support = cmd->data.diagass.ext; +	return 0; +} + +static int qeth_query_setdiagass(struct qeth_card *card) +{ +	struct qeth_cmd_buffer *iob; +	struct qeth_ipa_cmd    *cmd; + +	QETH_DBF_TEXT(SETUP, 2, "qdiagass"); +	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); +	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); +	cmd->data.diagass.subcmd_len = 16; +	cmd->data.diagass.subcmd = QETH_DIAGS_CMD_QUERY; +	return qeth_send_ipa_cmd(card, iob, qeth_query_setdiagass_cb, NULL); +} + +static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid) +{ +	unsigned long info = get_zeroed_page(GFP_KERNEL); +	struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; +	struct sysinfo_3_2_2 *info322 = (struct sysinfo_3_2_2 *)info; +	struct ccw_dev_id ccwid; +	int level; + +	tid->chpid = card->info.chpid; +	ccw_device_get_id(CARD_RDEV(card), &ccwid); +	tid->ssid = ccwid.ssid; +	tid->devno = ccwid.devno; +	if (!info) +		return; +	level = stsi(NULL, 0, 0, 0); +	if ((level >= 2) && (stsi(info222, 2, 2, 2) == 0)) +		tid->lparnr = info222->lpar_number; +	if ((level >= 3) && (stsi(info322, 3, 2, 2) == 0)) { +		EBCASC(info322->vm[0].name, sizeof(info322->vm[0].name)); +		memcpy(tid->vmname, info322->vm[0].name, sizeof(tid->vmname)); +	} +	free_page(info); +	return; +} + +static int qeth_hw_trap_cb(struct qeth_card *card, +		struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd; +	__u16 rc; + +	cmd = (struct qeth_ipa_cmd *)data; +	rc = cmd->hdr.return_code; +	if (rc) +		QETH_CARD_TEXT_(card, 2, "trapc:%x", rc); +	return 0; +} + +int qeth_hw_trap(struct qeth_card *card, enum qeth_diags_trap_action action) +{ +	struct qeth_cmd_buffer *iob; +	struct qeth_ipa_cmd *cmd; + +	QETH_DBF_TEXT(SETUP, 2, "diagtrap"); +	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); +	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); +	cmd->data.diagass.subcmd_len = 80; +	cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRAP; +	cmd->data.diagass.type = 1; +	cmd->data.diagass.action = action; +	switch (action) { +	case QETH_DIAGS_TRAP_ARM: +		cmd->data.diagass.options = 0x0003; +		cmd->data.diagass.ext = 0x00010000 + +			sizeof(struct qeth_trap_id); +		qeth_get_trap_id(card, +			(struct qeth_trap_id *)cmd->data.diagass.cdata); +		break; +	case QETH_DIAGS_TRAP_DISARM: +		cmd->data.diagass.options = 0x0001; +		break; +	case QETH_DIAGS_TRAP_CAPTURE: +		break; +	} +	return qeth_send_ipa_cmd(card, iob, qeth_hw_trap_cb, NULL); +} +EXPORT_SYMBOL_GPL(qeth_hw_trap); +  int qeth_check_qdio_errors(struct qeth_card *card, struct qdio_buffer *buf,  		unsigned int qdio_error, const char *dbftext)  {  	if (qdio_error) {  		QETH_CARD_TEXT(card, 2, dbftext);  		QETH_CARD_TEXT_(card, 2, " F15=%02X", -			       buf->element[15].flags & 0xff); +			       buf->element[15].sflags);  		QETH_CARD_TEXT_(card, 2, " F14=%02X", -			       buf->element[14].flags & 0xff); +			       buf->element[14].sflags);  		QETH_CARD_TEXT_(card, 2, " qerr=%X", qdio_error); -		if ((buf->element[15].flags & 0xff) == 0x12) { +		if ((buf->element[15].sflags) == 0x12) {  			card->stats.rx_dropped++;  			return 0;  		} else @@ -2618,9 +3153,19 @@ int qeth_check_qdio_errors(struct qeth_card *card, struct qdio_buffer *buf,  }  EXPORT_SYMBOL_GPL(qeth_check_qdio_errors); +void qeth_buffer_reclaim_work(struct work_struct *work) +{ +	struct qeth_card *card = container_of(work, struct qeth_card, +		buffer_reclaim_work.work); + +	QETH_CARD_TEXT_(card, 2, "brw:%x", card->reclaim_index); +	qeth_queue_input_buffer(card, card->reclaim_index); +} +  void qeth_queue_input_buffer(struct qeth_card *card, int index)  {  	struct qeth_qdio_q *queue = card->qdio.in_q; +	struct list_head *lh;  	int count;  	int i;  	int rc; @@ -2652,6 +3197,20 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index)  			atomic_add_unless(&card->force_alloc_skb, -1, 0);  		} +		if (!count) { +			i = 0; +			list_for_each(lh, &card->qdio.in_buf_pool.entry_list) +				i++; +			if (i == card->qdio.in_buf_pool.buf_count) { +				QETH_CARD_TEXT(card, 2, "qsarbw"); +				card->reclaim_index = index; +				schedule_delayed_work( +					&card->buffer_reclaim_work, +					QETH_RECLAIM_WORK_TIME); +			} +			return; +		} +  		/*  		 * according to old code it should be avoided to requeue all  		 * 128 buffers in order to benefit from PCI avoidance. @@ -2671,8 +3230,6 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index)  				qeth_get_micros() -  				card->perf_stats.inbound_do_qdio_start_time;  		if (rc) { -			dev_warn(&card->gdev->dev, -				"QDIO reported an error, rc=%i\n", rc);  			QETH_CARD_TEXT(card, 2, "qinberr");  		}  		queue->next_buf_to_init = (queue->next_buf_to_init + count) % @@ -2684,7 +3241,7 @@ EXPORT_SYMBOL_GPL(qeth_queue_input_buffer);  static int qeth_handle_send_error(struct qeth_card *card,  		struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err)  { -	int sbalf15 = buffer->buffer->element[15].flags & 0xff; +	int sbalf15 = buffer->buffer->element[15].sflags;  	QETH_CARD_TEXT(card, 6, "hdsnderr");  	if (card->info.type == QETH_CARD_TYPE_IQD) { @@ -2746,12 +3303,12 @@ static int qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue)  				queue->card->perf_stats.sc_p_dp++;  			queue->do_pack = 0;  			/* flush packing buffers */ -			buffer = &queue->bufs[queue->next_buf_to_fill]; +			buffer = queue->bufs[queue->next_buf_to_fill];  			if ((atomic_read(&buffer->state) ==  						QETH_QDIO_BUF_EMPTY) &&  			    (buffer->next_element_to_fill > 0)) {  				atomic_set(&buffer->state, -						QETH_QDIO_BUF_PRIMED); +					   QETH_QDIO_BUF_PRIMED);  				flush_count++;  				queue->next_buf_to_fill =  					(queue->next_buf_to_fill + 1) % @@ -2762,6 +3319,7 @@ static int qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue)  	return flush_count;  } +  /*   * Called to flush a packing buffer if no more pci flags are on the queue.   * Checks if there is a packing buffer and prepares it to be flushed. @@ -2771,7 +3329,7 @@ static int qeth_flush_buffers_on_no_pci(struct qeth_qdio_out_q *queue)  {  	struct qeth_qdio_out_buffer *buffer; -	buffer = &queue->bufs[queue->next_buf_to_fill]; +	buffer = queue->bufs[queue->next_buf_to_fill];  	if ((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) &&  	   (buffer->next_element_to_fill > 0)) {  		/* it's a packing buffer */ @@ -2792,9 +3350,13 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,  	unsigned int qdio_flags;  	for (i = index; i < index + count; ++i) { -		buf = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; -		buf->buffer->element[buf->next_element_to_fill - 1].flags |= -				SBAL_FLAGS_LAST_ENTRY; +		int bidx = i % QDIO_MAX_BUFFERS_PER_Q; +		buf = queue->bufs[bidx]; +		buf->buffer->element[buf->next_element_to_fill - 1].eflags |= +				SBAL_EFLAGS_LAST_ENTRY; + +		if (queue->bufstates) +			queue->bufstates[bidx].user = buf;  		if (queue->card->info.type == QETH_CARD_TYPE_IQD)  			continue; @@ -2807,7 +3369,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,  				/* it's likely that we'll go to packing  				 * mode soon */  				atomic_inc(&queue->set_pci_flags_count); -				buf->buffer->element[0].flags |= 0x40; +				buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ;  			}  		} else {  			if (!atomic_read(&queue->set_pci_flags_count)) { @@ -2820,7 +3382,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,  				 * further send was requested by the stack  				 */  				atomic_inc(&queue->set_pci_flags_count); -				buf->buffer->element[0].flags |= 0x40; +				buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ;  			}  		}  	} @@ -2840,12 +3402,16 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,  		queue->card->perf_stats.outbound_do_qdio_time +=  			qeth_get_micros() -  			queue->card->perf_stats.outbound_do_qdio_start_time; +	atomic_add(count, &queue->used_buffers);  	if (rc) {  		queue->card->stats.tx_errors += count;  		/* ignore temporary SIGA errors without busy condition */ -		if (rc == QDIO_ERROR_SIGA_TARGET) +		if (rc == -ENOBUFS)  			return;  		QETH_CARD_TEXT(queue->card, 2, "flushbuf"); +		QETH_CARD_TEXT_(queue->card, 2, " q%d", queue->queue_no); +		QETH_CARD_TEXT_(queue->card, 2, " idx%d", index); +		QETH_CARD_TEXT_(queue->card, 2, " c%d", count);  		QETH_CARD_TEXT_(queue->card, 2, " err%d", rc);  		/* this must not happen under normal circumstances. if it @@ -2853,7 +3419,6 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,  		qeth_schedule_recovery(queue->card);  		return;  	} -	atomic_add(count, &queue->used_buffers);  	if (queue->card->options.performance_stats)  		queue->card->perf_stats.bufs_sent += count;  } @@ -2908,14 +3473,120 @@ void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue,  }  EXPORT_SYMBOL_GPL(qeth_qdio_start_poll); +int qeth_configure_cq(struct qeth_card *card, enum qeth_cq cq) +{ +	int rc; + +	if (card->options.cq ==  QETH_CQ_NOTAVAILABLE) { +		rc = -1; +		goto out; +	} else { +		if (card->options.cq == cq) { +			rc = 0; +			goto out; +		} + +		if (card->state != CARD_STATE_DOWN && +		    card->state != CARD_STATE_RECOVER) { +			rc = -1; +			goto out; +		} + +		qeth_free_qdio_buffers(card); +		card->options.cq = cq; +		rc = 0; +	} +out: +	return rc; + +} +EXPORT_SYMBOL_GPL(qeth_configure_cq); + + +static void qeth_qdio_cq_handler(struct qeth_card *card, +		unsigned int qdio_err, +		unsigned int queue, int first_element, int count) { +	struct qeth_qdio_q *cq = card->qdio.c_q; +	int i; +	int rc; + +	if (!qeth_is_cq(card, queue)) +		goto out; + +	QETH_CARD_TEXT_(card, 5, "qcqhe%d", first_element); +	QETH_CARD_TEXT_(card, 5, "qcqhc%d", count); +	QETH_CARD_TEXT_(card, 5, "qcqherr%d", qdio_err); + +	if (qdio_err) { +		netif_stop_queue(card->dev); +		qeth_schedule_recovery(card); +		goto out; +	} + +	if (card->options.performance_stats) { +		card->perf_stats.cq_cnt++; +		card->perf_stats.cq_start_time = qeth_get_micros(); +	} + +	for (i = first_element; i < first_element + count; ++i) { +		int bidx = i % QDIO_MAX_BUFFERS_PER_Q; +		struct qdio_buffer *buffer = &cq->qdio_bufs[bidx]; +		int e; + +		e = 0; +		while (buffer->element[e].addr) { +			unsigned long phys_aob_addr; + +			phys_aob_addr = (unsigned long) buffer->element[e].addr; +			qeth_qdio_handle_aob(card, phys_aob_addr); +			buffer->element[e].addr = NULL; +			buffer->element[e].eflags = 0; +			buffer->element[e].sflags = 0; +			buffer->element[e].length = 0; + +			++e; +		} + +		buffer->element[15].eflags = 0; +		buffer->element[15].sflags = 0; +	} +	rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, queue, +		    card->qdio.c_q->next_buf_to_init, +		    count); +	if (rc) { +		dev_warn(&card->gdev->dev, +			"QDIO reported an error, rc=%i\n", rc); +		QETH_CARD_TEXT(card, 2, "qcqherr"); +	} +	card->qdio.c_q->next_buf_to_init = (card->qdio.c_q->next_buf_to_init +				   + count) % QDIO_MAX_BUFFERS_PER_Q; + +	netif_wake_queue(card->dev); + +	if (card->options.performance_stats) { +		int delta_t = qeth_get_micros(); +		delta_t -= card->perf_stats.cq_start_time; +		card->perf_stats.cq_time += delta_t; +	} +out: +	return; +} +  void qeth_qdio_input_handler(struct ccw_device *ccwdev, unsigned int qdio_err, -		unsigned int queue, int first_element, int count, +		unsigned int queue, int first_elem, int count,  		unsigned long card_ptr)  {  	struct qeth_card *card = (struct qeth_card *)card_ptr; -	if (qdio_err) +	QETH_CARD_TEXT_(card, 2, "qihq%d", queue); +	QETH_CARD_TEXT_(card, 2, "qiec%d", qdio_err); + +	if (qeth_is_cq(card, queue)) +		qeth_qdio_cq_handler(card, qdio_err, queue, first_elem, count); +	else if (qdio_err)  		qeth_schedule_recovery(card); + +  }  EXPORT_SYMBOL_GPL(qeth_qdio_input_handler); @@ -2929,7 +3600,7 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev,  	int i;  	QETH_CARD_TEXT(card, 6, "qdouhdl"); -	if (qdio_error & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) { +	if (qdio_error & QDIO_ERROR_FATAL) {  		QETH_CARD_TEXT(card, 2, "achkcond");  		netif_stop_queue(card->dev);  		qeth_schedule_recovery(card); @@ -2941,9 +3612,44 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev,  			qeth_get_micros();  	}  	for (i = first_element; i < (first_element + count); ++i) { -		buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; +		int bidx = i % QDIO_MAX_BUFFERS_PER_Q; +		buffer = queue->bufs[bidx];  		qeth_handle_send_error(card, buffer, qdio_error); -		qeth_clear_output_buffer(queue, buffer); + +		if (queue->bufstates && +		    (queue->bufstates[bidx].flags & +		     QDIO_OUTBUF_STATE_FLAG_PENDING) != 0) { +			WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED); + +			if (atomic_cmpxchg(&buffer->state, +					   QETH_QDIO_BUF_PRIMED, +					   QETH_QDIO_BUF_PENDING) == +				QETH_QDIO_BUF_PRIMED) { +				qeth_notify_skbs(queue, buffer, +						 TX_NOTIFY_PENDING); +			} +			buffer->aob = queue->bufstates[bidx].aob; +			QETH_CARD_TEXT_(queue->card, 5, "pel%d", bidx); +			QETH_CARD_TEXT(queue->card, 5, "aob"); +			QETH_CARD_TEXT_(queue->card, 5, "%lx", +					virt_to_phys(buffer->aob)); +			if (qeth_init_qdio_out_buf(queue, bidx)) { +				QETH_CARD_TEXT(card, 2, "outofbuf"); +				qeth_schedule_recovery(card); +			} +		} else { +			if (card->options.cq == QETH_CQ_ENABLED) { +				enum iucv_tx_notify n; + +				n = qeth_compute_cq_notification( +					buffer->buffer->element[15].sflags, 0); +				qeth_notify_skbs(queue, buffer, n); +			} + +			qeth_clear_output_buffer(queue, buffer, +						QETH_QDIO_BUF_EMPTY); +		} +		qeth_cleanup_handled_pending(queue, bidx, 0);  	}  	atomic_sub(count, &queue->used_buffers);  	/* check if we need to do something on this outbound queue */ @@ -2957,53 +3663,87 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev,  }  EXPORT_SYMBOL_GPL(qeth_qdio_output_handler); +/** + * Note: Function assumes that we have 4 outbound queues. + */  int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,  			int ipv, int cast_type)  { -	if (!ipv && (card->info.type == QETH_CARD_TYPE_OSD || -		     card->info.type == QETH_CARD_TYPE_OSX)) -		return card->qdio.default_out_queue; -	switch (card->qdio.no_out_queues) { -	case 4: -		if (cast_type && card->info.is_multicast_different) -			return card->info.is_multicast_different & -				(card->qdio.no_out_queues - 1); -		if (card->qdio.do_prio_queueing && (ipv == 4)) { -			const u8 tos = ip_hdr(skb)->tos; - -			if (card->qdio.do_prio_queueing == -				QETH_PRIO_Q_ING_TOS) { -				if (tos & IP_TOS_NOTIMPORTANT) -					return 3; -				if (tos & IP_TOS_HIGHRELIABILITY) -					return 2; -				if (tos & IP_TOS_HIGHTHROUGHPUT) -					return 1; -				if (tos & IP_TOS_LOWDELAY) -					return 0; -			} -			if (card->qdio.do_prio_queueing == -				QETH_PRIO_Q_ING_PREC) -				return 3 - (tos >> 6); -		} else if (card->qdio.do_prio_queueing && (ipv == 6)) { -			/* TODO: IPv6!!! */ +	__be16 *tci; +	u8 tos; + +	if (cast_type && card->info.is_multicast_different) +		return card->info.is_multicast_different & +			(card->qdio.no_out_queues - 1); + +	switch (card->qdio.do_prio_queueing) { +	case QETH_PRIO_Q_ING_TOS: +	case QETH_PRIO_Q_ING_PREC: +		switch (ipv) { +		case 4: +			tos = ipv4_get_dsfield(ip_hdr(skb)); +			break; +		case 6: +			tos = ipv6_get_dsfield(ipv6_hdr(skb)); +			break; +		default: +			return card->qdio.default_out_queue;  		} -		return card->qdio.default_out_queue; -	case 1: /* fallthrough for single-out-queue 1920-device */ +		if (card->qdio.do_prio_queueing == QETH_PRIO_Q_ING_PREC) +			return ~tos >> 6 & 3; +		if (tos & IPTOS_MINCOST) +			return 3; +		if (tos & IPTOS_RELIABILITY) +			return 2; +		if (tos & IPTOS_THROUGHPUT) +			return 1; +		if (tos & IPTOS_LOWDELAY) +			return 0; +		break; +	case QETH_PRIO_Q_ING_SKB: +		if (skb->priority > 5) +			return 0; +		return ~skb->priority >> 1 & 3; +	case QETH_PRIO_Q_ING_VLAN: +		tci = &((struct ethhdr *)skb->data)->h_proto; +		if (*tci == ETH_P_8021Q) +			return ~*(tci + 1) >> (VLAN_PRIO_SHIFT + 1) & 3; +		break;  	default: -		return card->qdio.default_out_queue; +		break;  	} +	return card->qdio.default_out_queue;  }  EXPORT_SYMBOL_GPL(qeth_get_priority_queue); -int qeth_get_elements_no(struct qeth_card *card, void *hdr, +int qeth_get_elements_for_frags(struct sk_buff *skb) +{ +	int cnt, length, e, elements = 0; +	struct skb_frag_struct *frag; +	char *data; + +	for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { +		frag = &skb_shinfo(skb)->frags[cnt]; +		data = (char *)page_to_phys(skb_frag_page(frag)) + +			frag->page_offset; +		length = frag->size; +		e = PFN_UP((unsigned long)data + length - 1) - +			PFN_DOWN((unsigned long)data); +		elements += e; +	} +	return elements; +} +EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); + +int qeth_get_elements_no(struct qeth_card *card,  		     struct sk_buff *skb, int elems)  {  	int dlen = skb->len - skb->data_len;  	int elements_needed = PFN_UP((unsigned long)skb->data + dlen - 1) -  		PFN_DOWN((unsigned long)skb->data); -	elements_needed += skb_shinfo(skb)->nr_frags; +	elements_needed += qeth_get_elements_for_frags(skb); +  	if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {  		QETH_DBF_MESSAGE(2, "Invalid size of IP packet "  			"(Number=%d / Length=%d). Discarded.\n", @@ -3014,7 +3754,7 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr,  }  EXPORT_SYMBOL_GPL(qeth_get_elements_no); -int qeth_hdr_chk_and_bounce(struct sk_buff *skb, int len) +int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len)  {  	int hroom, inpage, rest; @@ -3027,6 +3767,8 @@ int qeth_hdr_chk_and_bounce(struct sk_buff *skb, int len)  			return 1;  		memmove(skb->data - rest, skb->data, skb->len - skb->data_len);  		skb->data -= rest; +		skb->tail -= rest; +		*hdr = (struct qeth_hdr *)skb->data;  		QETH_DBF_MESSAGE(2, "skb bounce len: %d rest: %d\n", len, rest);  	}  	return 0; @@ -3066,20 +3808,20 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb,  		if (!length) {  			if (first_lap)  				if (skb_shinfo(skb)->nr_frags) -					buffer->element[element].flags = -						SBAL_FLAGS_FIRST_FRAG; +					buffer->element[element].eflags = +						SBAL_EFLAGS_FIRST_FRAG;  				else -					buffer->element[element].flags = 0; +					buffer->element[element].eflags = 0;  			else -				buffer->element[element].flags = -				    SBAL_FLAGS_MIDDLE_FRAG; +				buffer->element[element].eflags = +				    SBAL_EFLAGS_MIDDLE_FRAG;  		} else {  			if (first_lap) -				buffer->element[element].flags = -				    SBAL_FLAGS_FIRST_FRAG; +				buffer->element[element].eflags = +				    SBAL_EFLAGS_FIRST_FRAG;  			else -				buffer->element[element].flags = -				    SBAL_FLAGS_MIDDLE_FRAG; +				buffer->element[element].eflags = +				    SBAL_EFLAGS_MIDDLE_FRAG;  		}  		data += length_here;  		element++; @@ -3088,15 +3830,27 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb,  	for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) {  		frag = &skb_shinfo(skb)->frags[cnt]; -		buffer->element[element].addr = (char *)page_to_phys(frag->page) -			+ frag->page_offset; -		buffer->element[element].length = frag->size; -		buffer->element[element].flags = SBAL_FLAGS_MIDDLE_FRAG; -		element++; +		data = (char *)page_to_phys(skb_frag_page(frag)) + +			frag->page_offset; +		length = frag->size; +		while (length > 0) { +			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; +			buffer->element[element].eflags = +				SBAL_EFLAGS_MIDDLE_FRAG; +			length -= length_here; +			data += length_here; +			element++; +		}  	} -	if (buffer->element[element - 1].flags) -		buffer->element[element - 1].flags = SBAL_FLAGS_LAST_FRAG; +	if (buffer->element[element - 1].eflags) +		buffer->element[element - 1].eflags = SBAL_EFLAGS_LAST_FRAG;  	*next_element_to_fill = element;  } @@ -3120,7 +3874,7 @@ static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue,  		/*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; +		buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;  		buf->next_element_to_fill++;  		skb->data += hdr_len;  		skb->len  -= hdr_len; @@ -3132,7 +3886,7 @@ static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue,  		buffer->element[element].addr = hdr;  		buffer->element[element].length = sizeof(struct qeth_hdr) +  							hd_len; -		buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG; +		buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;  		buf->is_header[element] = 1;  		buf->next_element_to_fill++;  	} @@ -3175,7 +3929,7 @@ int qeth_do_send_packet_fast(struct qeth_card *card,  			      QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);  	/* ... now we've got the queue */  	index = queue->next_buf_to_fill; -	buffer = &queue->bufs[queue->next_buf_to_fill]; +	buffer = queue->bufs[queue->next_buf_to_fill];  	/*  	 * check if buffer is empty to make sure that we do not 'overtake'  	 * ourselves and try to fill a buffer that is already primed @@ -3209,7 +3963,7 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,  	while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,  			      QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);  	start_index = queue->next_buf_to_fill; -	buffer = &queue->bufs[queue->next_buf_to_fill]; +	buffer = queue->bufs[queue->next_buf_to_fill];  	/*  	 * check if buffer is empty to make sure that we do not 'overtake'  	 * ourselves and try to fill a buffer that is already primed @@ -3231,7 +3985,7 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,  			queue->next_buf_to_fill =  				(queue->next_buf_to_fill + 1) %  				QDIO_MAX_BUFFERS_PER_Q; -			buffer = &queue->bufs[queue->next_buf_to_fill]; +			buffer = queue->bufs[queue->next_buf_to_fill];  			/* we did a step forward, so check buffer state  			 * again */  			if (atomic_read(&buffer->state) != @@ -3407,6 +4161,7 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,  {  	struct qeth_ipa_cmd *cmd;  	struct qeth_set_access_ctrl *access_ctrl_req; +	int fallback = *(int *)reply->param;  	QETH_CARD_TEXT(card, 4, "setaccb"); @@ -3416,12 +4171,14 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,  	QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name);  	QETH_DBF_TEXT_(SETUP, 2, "rc=%d",  		cmd->data.setadapterparms.hdr.return_code); +	if (cmd->data.setadapterparms.hdr.return_code != +						SET_ACCESS_CTRL_RC_SUCCESS) +		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);  	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"); @@ -3429,72 +4186,64 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,  			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);  		break; -	} +	case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED: +		QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already " +				"deactivated\n", dev_name(&card->gdev->dev)); +		if (fallback) +			card->options.isolation = card->options.prev_isolation; +		break; +	case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED: +		QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already" +				" activated\n", dev_name(&card->gdev->dev)); +		if (fallback) +			card->options.isolation = card->options.prev_isolation; +		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;  		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; +		if (fallback) +			card->options.isolation = card->options.prev_isolation;  		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; +		if (fallback) +			card->options.isolation = card->options.prev_isolation; +		break; +	case SET_ACCESS_CTRL_RC_REFLREL_UNSUPPORTED: +		dev_err(&card->gdev->dev, "The adjacent switch port does not " +			"support reflective relay mode\n"); +		if (fallback) +			card->options.isolation = card->options.prev_isolation; +		break; +	case SET_ACCESS_CTRL_RC_REFLREL_FAILED: +		dev_err(&card->gdev->dev, "The reflective relay mode cannot be " +					"enabled at the adjacent switch port"); +		if (fallback) +			card->options.isolation = card->options.prev_isolation; +		break; +	case SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED: +		dev_warn(&card->gdev->dev, "Turning off reflective relay mode " +					"at the adjacent switch failed\n");  		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; +		if (fallback) +			card->options.isolation = card->options.prev_isolation;  		break;  	} -	}  	qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);  	return 0;  }  static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card, -		enum qeth_ipa_isolation_modes isolation) +		enum qeth_ipa_isolation_modes isolation, int fallback)  {  	int rc;  	struct qeth_cmd_buffer *iob; @@ -3514,12 +4263,12 @@ static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card,  	access_ctrl_req->subcmd_code = isolation;  	rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb, -			       NULL); +			       &fallback);  	QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc);  	return rc;  } -int qeth_set_access_ctrl_online(struct qeth_card *card) +int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback)  {  	int rc = 0; @@ -3529,12 +4278,13 @@ int qeth_set_access_ctrl_online(struct qeth_card *card)  	     card->info.type == QETH_CARD_TYPE_OSX) &&  	     qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) {  		rc = qeth_setadpparms_set_access_ctrl(card, -			card->options.isolation); +			card->options.isolation, fallback);  		if (rc) {  			QETH_DBF_MESSAGE(3,  				"IPA(SET_ACCESS_CTRL,%s,%d) sent failed\n",  				card->gdev->dev.kobj.name,  				rc); +			rc = -EOPNOTSUPP;  		}  	} else if (card->options.isolation != ISOLATION_MODE_NONE) {  		card->options.isolation = ISOLATION_MODE_NONE; @@ -3682,7 +4432,7 @@ static int qeth_snmp_command_cb(struct qeth_card *card,  	/* check if there is enough room in userspace */  	if ((qinfo->udata_len - qinfo->udata_offset) < data_len) {  		QETH_CARD_TEXT_(card, 4, "scer3%i", -ENOMEM); -		cmd->hdr.return_code = -ENOMEM; +		cmd->hdr.return_code = IPA_RC_ENOMEM;  		return 0;  	}  	QETH_CARD_TEXT_(card, 4, "snore%i", @@ -3716,7 +4466,7 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata)  	struct qeth_cmd_buffer *iob;  	struct qeth_ipa_cmd *cmd;  	struct qeth_snmp_ureq *ureq; -	int req_len; +	unsigned int req_len;  	struct qeth_arp_query_info qinfo = {0, };  	int rc = 0; @@ -3732,6 +4482,10 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata)  	/* skip 4 bytes (data_len struct member) to get req_len */  	if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int)))  		return -EFAULT; +	if (req_len > (QETH_BUFSIZE - IPA_PDU_HEADER_SIZE - +		       sizeof(struct qeth_ipacmd_hdr) - +		       sizeof(struct qeth_ipacmd_setadpparms_hdr))) +		return -EINVAL;  	ureq = memdup_user(udata, req_len + sizeof(struct qeth_snmp_ureq_hdr));  	if (IS_ERR(ureq)) {  		QETH_CARD_TEXT(card, 2, "snmpnome"); @@ -3765,6 +4519,140 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata)  }  EXPORT_SYMBOL_GPL(qeth_snmp_command); +static int qeth_setadpparms_query_oat_cb(struct qeth_card *card, +		struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd; +	struct qeth_qoat_priv *priv; +	char *resdata; +	int resdatalen; + +	QETH_CARD_TEXT(card, 3, "qoatcb"); + +	cmd = (struct qeth_ipa_cmd *)data; +	priv = (struct qeth_qoat_priv *)reply->param; +	resdatalen = cmd->data.setadapterparms.hdr.cmdlength; +	resdata = (char *)data + 28; + +	if (resdatalen > (priv->buffer_len - priv->response_len)) { +		cmd->hdr.return_code = IPA_RC_FFFF; +		return 0; +	} + +	memcpy((priv->buffer + priv->response_len), resdata, +		resdatalen); +	priv->response_len += resdatalen; + +	if (cmd->data.setadapterparms.hdr.seq_no < +	    cmd->data.setadapterparms.hdr.used_total) +		return 1; +	return 0; +} + +int qeth_query_oat_command(struct qeth_card *card, char __user *udata) +{ +	int rc = 0; +	struct qeth_cmd_buffer *iob; +	struct qeth_ipa_cmd *cmd; +	struct qeth_query_oat *oat_req; +	struct qeth_query_oat_data oat_data; +	struct qeth_qoat_priv priv; +	void __user *tmp; + +	QETH_CARD_TEXT(card, 3, "qoatcmd"); + +	if (!qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) { +		rc = -EOPNOTSUPP; +		goto out; +	} + +	if (copy_from_user(&oat_data, udata, +	    sizeof(struct qeth_query_oat_data))) { +			rc = -EFAULT; +			goto out; +	} + +	priv.buffer_len = oat_data.buffer_len; +	priv.response_len = 0; +	priv.buffer =  kzalloc(oat_data.buffer_len, GFP_KERNEL); +	if (!priv.buffer) { +		rc = -ENOMEM; +		goto out; +	} + +	iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_OAT, +				   sizeof(struct qeth_ipacmd_setadpparms_hdr) + +				   sizeof(struct qeth_query_oat)); +	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); +	oat_req = &cmd->data.setadapterparms.data.query_oat; +	oat_req->subcmd_code = oat_data.command; + +	rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_query_oat_cb, +			       &priv); +	if (!rc) { +		if (is_compat_task()) +			tmp = compat_ptr(oat_data.ptr); +		else +			tmp = (void __user *)(unsigned long)oat_data.ptr; + +		if (copy_to_user(tmp, priv.buffer, +		    priv.response_len)) { +			rc = -EFAULT; +			goto out_free; +		} + +		oat_data.response_len = priv.response_len; + +		if (copy_to_user(udata, &oat_data, +		    sizeof(struct qeth_query_oat_data))) +			rc = -EFAULT; +	} else +		if (rc == IPA_RC_FFFF) +			rc = -EFAULT; + +out_free: +	kfree(priv.buffer); +out: +	return rc; +} +EXPORT_SYMBOL_GPL(qeth_query_oat_command); + +static int qeth_query_card_info_cb(struct qeth_card *card, +				   struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd; +	struct qeth_query_card_info *card_info; +	struct carrier_info *carrier_info; + +	QETH_CARD_TEXT(card, 2, "qcrdincb"); +	carrier_info = (struct carrier_info *)reply->param; +	cmd = (struct qeth_ipa_cmd *)data; +	card_info = &cmd->data.setadapterparms.data.card_info; +	if (cmd->data.setadapterparms.hdr.return_code == 0) { +		carrier_info->card_type = card_info->card_type; +		carrier_info->port_mode = card_info->port_mode; +		carrier_info->port_speed = card_info->port_speed; +	} + +	qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); +	return 0; +} + +int qeth_query_card_info(struct qeth_card *card, +				struct carrier_info *carrier_info) +{ +	struct qeth_cmd_buffer *iob; + +	QETH_CARD_TEXT(card, 2, "qcrdinfo"); +	if (!qeth_adp_supported(card, IPA_SETADP_QUERY_CARD_INFO)) +		return -EOPNOTSUPP; +	iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO, +		sizeof(struct qeth_ipacmd_setadpparms_hdr)); +	return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb, +					(void *)carrier_info); +} +EXPORT_SYMBOL_GPL(qeth_query_card_info); +  static inline int qeth_get_qdio_q_format(struct qeth_card *card)  {  	switch (card->info.type) { @@ -3775,11 +4663,86 @@ static inline int qeth_get_qdio_q_format(struct qeth_card *card)  	}  } +static void qeth_determine_capabilities(struct qeth_card *card) +{ +	int rc; +	int length; +	char *prcd; +	struct ccw_device *ddev; +	int ddev_offline = 0; + +	QETH_DBF_TEXT(SETUP, 2, "detcapab"); +	ddev = CARD_DDEV(card); +	if (!ddev->online) { +		ddev_offline = 1; +		rc = ccw_device_set_online(ddev); +		if (rc) { +			QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); +			goto out; +		} +	} + +	rc = qeth_read_conf_data(card, (void **) &prcd, &length); +	if (rc) { +		QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n", +			dev_name(&card->gdev->dev), rc); +		QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); +		goto out_offline; +	} +	qeth_configure_unitaddr(card, prcd); +	if (ddev_offline) +		qeth_configure_blkt_default(card, prcd); +	kfree(prcd); + +	rc = qdio_get_ssqd_desc(ddev, &card->ssqd); +	if (rc) +		QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); + +	QETH_DBF_TEXT_(SETUP, 2, "qfmt%d", card->ssqd.qfmt); +	QETH_DBF_TEXT_(SETUP, 2, "%d", card->ssqd.qdioac1); +	QETH_DBF_TEXT_(SETUP, 2, "%d", card->ssqd.qdioac3); +	QETH_DBF_TEXT_(SETUP, 2, "icnt%d", card->ssqd.icnt); +	if (!((card->ssqd.qfmt != QDIO_IQDIO_QFMT) || +	    ((card->ssqd.qdioac1 & CHSC_AC1_INITIATE_INPUTQ) == 0) || +	    ((card->ssqd.qdioac3 & CHSC_AC3_FORMAT2_CQ_AVAILABLE) == 0))) { +		dev_info(&card->gdev->dev, +			"Completion Queueing supported\n"); +	} else { +		card->options.cq = QETH_CQ_NOTAVAILABLE; +	} + + +out_offline: +	if (ddev_offline == 1) +		ccw_device_set_offline(ddev); +out: +	return; +} + +static inline void qeth_qdio_establish_cq(struct qeth_card *card, +	struct qdio_buffer **in_sbal_ptrs, +	void (**queue_start_poll) (struct ccw_device *, int, unsigned long)) { +	int i; + +	if (card->options.cq == QETH_CQ_ENABLED) { +		int offset = QDIO_MAX_BUFFERS_PER_Q * +			     (card->qdio.no_in_queues - 1); +		i = QDIO_MAX_BUFFERS_PER_Q * (card->qdio.no_in_queues - 1); +		for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { +			in_sbal_ptrs[offset + i] = (struct qdio_buffer *) +				virt_to_phys(card->qdio.c_q->bufs[i].buffer); +		} + +		queue_start_poll[card->qdio.no_in_queues - 1] = NULL; +	} +} +  static int qeth_qdio_establish(struct qeth_card *card)  {  	struct qdio_initialize init_data;  	char *qib_param_field;  	struct qdio_buffer **in_sbal_ptrs; +	void (**queue_start_poll) (struct ccw_device *, int, unsigned long);  	struct qdio_buffer **out_sbal_ptrs;  	int i, j, k;  	int rc = 0; @@ -3788,34 +4751,48 @@ static int qeth_qdio_establish(struct qeth_card *card)  	qib_param_field = kzalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char),  			      GFP_KERNEL); -	if (!qib_param_field) -		return -ENOMEM; +	if (!qib_param_field) { +		rc =  -ENOMEM; +		goto out_free_nothing; +	}  	qeth_create_qib_param_field(card, qib_param_field);  	qeth_create_qib_param_field_blkt(card, qib_param_field); -	in_sbal_ptrs = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(void *), +	in_sbal_ptrs = kzalloc(card->qdio.no_in_queues * +			       QDIO_MAX_BUFFERS_PER_Q * sizeof(void *),  			       GFP_KERNEL);  	if (!in_sbal_ptrs) { -		kfree(qib_param_field); -		return -ENOMEM; +		rc = -ENOMEM; +		goto out_free_qib_param;  	} -	for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) +	for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) {  		in_sbal_ptrs[i] = (struct qdio_buffer *)  			virt_to_phys(card->qdio.in_q->bufs[i].buffer); +	} + +	queue_start_poll = kzalloc(sizeof(void *) * card->qdio.no_in_queues, +				   GFP_KERNEL); +	if (!queue_start_poll) { +		rc = -ENOMEM; +		goto out_free_in_sbals; +	} +	for (i = 0; i < card->qdio.no_in_queues; ++i) +		queue_start_poll[i] = card->discipline->start_poll; + +	qeth_qdio_establish_cq(card, in_sbal_ptrs, queue_start_poll);  	out_sbal_ptrs = -		kmalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q * +		kzalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q *  			sizeof(void *), GFP_KERNEL);  	if (!out_sbal_ptrs) { -		kfree(in_sbal_ptrs); -		kfree(qib_param_field); -		return -ENOMEM; +		rc = -ENOMEM; +		goto out_free_queue_start_poll;  	}  	for (i = 0, k = 0; i < card->qdio.no_out_queues; ++i)  		for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j, ++k) {  			out_sbal_ptrs[k] = (struct qdio_buffer *)virt_to_phys( -				card->qdio.out_qs[i]->bufs[j].buffer); +				card->qdio.out_qs[i]->bufs[j]->buffer);  		}  	memset(&init_data, 0, sizeof(struct qdio_initialize)); @@ -3823,14 +4800,17 @@ static int qeth_qdio_establish(struct qeth_card *card)  	init_data.q_format               = qeth_get_qdio_q_format(card);  	init_data.qib_param_field_format = 0;  	init_data.qib_param_field        = qib_param_field; -	init_data.no_input_qs            = 1; +	init_data.no_input_qs            = card->qdio.no_in_queues;  	init_data.no_output_qs           = card->qdio.no_out_queues; -	init_data.input_handler          = card->discipline.input_handler; -	init_data.output_handler         = card->discipline.output_handler; -	init_data.queue_start_poll	 = card->discipline.start_poll; +	init_data.input_handler 	 = card->discipline->input_handler; +	init_data.output_handler	 = card->discipline->output_handler; +	init_data.queue_start_poll_array = queue_start_poll;  	init_data.int_parm               = (unsigned long) card;  	init_data.input_sbal_addr_array  = (void **) in_sbal_ptrs;  	init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; +	init_data.output_sbal_state_array = card->qdio.out_bufstates; +	init_data.scan_threshold = +		(card->info.type == QETH_CARD_TYPE_IQD) ? 1 : 32;  	if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED,  		QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) { @@ -3845,10 +4825,26 @@ static int qeth_qdio_establish(struct qeth_card *card)  			qdio_free(CARD_DDEV(card));  		}  	} + +	switch (card->options.cq) { +	case QETH_CQ_ENABLED: +		dev_info(&card->gdev->dev, "Completion Queue support enabled"); +		break; +	case QETH_CQ_DISABLED: +		dev_info(&card->gdev->dev, "Completion Queue support disabled"); +		break; +	default: +		break; +	}  out:  	kfree(out_sbal_ptrs); +out_free_queue_start_poll: +	kfree(queue_start_poll); +out_free_in_sbals:  	kfree(in_sbal_ptrs); +out_free_qib_param:  	kfree(qib_param_field); +out_free_nothing:  	return rc;  } @@ -3867,6 +4863,19 @@ static void qeth_core_free_card(struct qeth_card *card)  	kfree(card);  } +void qeth_trace_features(struct qeth_card *card) +{ +	QETH_CARD_TEXT(card, 2, "features"); +	QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.supported_funcs); +	QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.enabled_funcs); +	QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.supported_funcs); +	QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.enabled_funcs); +	QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.supported_funcs); +	QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.enabled_funcs); +	QETH_CARD_TEXT_(card, 2, "%x", card->info.diagass_support); +} +EXPORT_SYMBOL_GPL(qeth_trace_features); +  static struct ccw_device_id qeth_ids[] = {  	{CCW_DEVICE_DEVTYPE(0x1731, 0x01, 0x1732, 0x01),  					.driver_info = QETH_CARD_TYPE_OSD}, @@ -3883,33 +4892,32 @@ static struct ccw_device_id qeth_ids[] = {  MODULE_DEVICE_TABLE(ccw, qeth_ids);  static struct ccw_driver qeth_ccw_driver = { -	.name = "qeth", +	.driver = { +		.owner = THIS_MODULE, +		.name = "qeth", +	},  	.ids = qeth_ids,  	.probe = ccwgroup_probe_ccwdev,  	.remove = ccwgroup_remove_ccwdev,  }; -static int qeth_core_driver_group(const char *buf, struct device *root_dev, -				unsigned long driver_id) -{ -	return ccwgroup_create_from_string(root_dev, driver_id, -					   &qeth_ccw_driver, 3, buf); -} -  int qeth_core_hardsetup_card(struct qeth_card *card)  { -	int retries = 0; +	int retries = 3;  	int rc;  	QETH_DBF_TEXT(SETUP, 2, "hrdsetup");  	atomic_set(&card->force_alloc_skb, 0); +	qeth_update_from_chp_desc(card);  retry: -	if (retries) +	if (retries < 3)  		QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n",  			dev_name(&card->gdev->dev)); +	rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD);  	ccw_device_set_offline(CARD_DDEV(card));  	ccw_device_set_offline(CARD_WDEV(card));  	ccw_device_set_offline(CARD_RDEV(card)); +	qdio_free(CARD_DDEV(card));  	rc = ccw_device_set_online(CARD_RDEV(card));  	if (rc)  		goto retriable; @@ -3919,18 +4927,18 @@ retry:  	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 > 3) +		if (--retries < 0)  			goto out;  		else  			goto retry;  	} +	qeth_determine_capabilities(card);  	qeth_init_tokens(card);  	qeth_init_func_level(card);  	rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb); @@ -3961,6 +4969,16 @@ retriable:  		QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);  		goto out;  	} + +	card->options.ipa4.supported_funcs = 0; +	card->options.adp.supported_funcs = 0; +	card->options.sbp.supported_funcs = 0; +	card->info.diagass_support = 0; +	qeth_query_ipassists(card, QETH_PROT_IPV4); +	if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) +		qeth_query_setadapterparms(card); +	if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) +		qeth_query_setdiagass(card);  	return 0;  out:  	dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " @@ -3971,29 +4989,36 @@ out:  }  EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card); -static inline int qeth_create_skb_frag(struct qdio_buffer_element *element, +static inline int qeth_create_skb_frag(struct qeth_qdio_buffer *qethbuffer, +		struct qdio_buffer_element *element,  		struct sk_buff **pskb, int offset, int *pfrag, int data_len)  {  	struct page *page = virt_to_page(element->addr);  	if (*pskb == NULL) { -		/* the upper protocol layers assume that there is data in the -		 * skb itself. Copy a small amount (64 bytes) to make them -		 * happy. */ -		*pskb = dev_alloc_skb(64 + ETH_HLEN); -		if (!(*pskb)) -			return -ENOMEM; +		if (qethbuffer->rx_skb) { +			/* only if qeth_card.options.cq == QETH_CQ_ENABLED */ +			*pskb = qethbuffer->rx_skb; +			qethbuffer->rx_skb = NULL; +		} else { +			*pskb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN); +			if (!(*pskb)) +				return -ENOMEM; +		} +  		skb_reserve(*pskb, ETH_HLEN); -		if (data_len <= 64) { +		if (data_len <= QETH_RX_PULL_LEN) {  			memcpy(skb_put(*pskb, data_len), element->addr + offset,  				data_len);  		} else {  			get_page(page); -			memcpy(skb_put(*pskb, 64), element->addr + offset, 64); -			skb_fill_page_desc(*pskb, *pfrag, page, offset + 64, -				data_len - 64); -			(*pskb)->data_len += data_len - 64; -			(*pskb)->len      += data_len - 64; -			(*pskb)->truesize += data_len - 64; +			memcpy(skb_put(*pskb, QETH_RX_PULL_LEN), +			       element->addr + offset, QETH_RX_PULL_LEN); +			skb_fill_page_desc(*pskb, *pfrag, page, +				offset + QETH_RX_PULL_LEN, +				data_len - QETH_RX_PULL_LEN); +			(*pskb)->data_len += data_len - QETH_RX_PULL_LEN; +			(*pskb)->len      += data_len - QETH_RX_PULL_LEN; +			(*pskb)->truesize += data_len - QETH_RX_PULL_LEN;  			(*pfrag)++;  		}  	} else { @@ -4004,15 +5029,18 @@ static inline int qeth_create_skb_frag(struct qdio_buffer_element *element,  		(*pskb)->truesize += data_len;  		(*pfrag)++;  	} + +  	return 0;  }  struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, -		struct qdio_buffer *buffer, +		struct qeth_qdio_buffer *qethbuffer,  		struct qdio_buffer_element **__element, int *__offset,  		struct qeth_hdr **hdr)  {  	struct qdio_buffer_element *element = *__element; +	struct qdio_buffer *buffer = qethbuffer->buffer;  	int offset = *__offset;  	struct sk_buff *skb = NULL;  	int skb_len = 0; @@ -4040,11 +5068,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,  		break;  	case QETH_HEADER_TYPE_LAYER3:  		skb_len = (*hdr)->hdr.l3.length; -		if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) || -		    (card->info.link_type == QETH_LINK_TYPE_HSTR)) -			headroom = TR_HLEN; -		else -			headroom = ETH_HLEN; +		headroom = ETH_HLEN;  		break;  	case QETH_HEADER_TYPE_OSN:  		skb_len = (*hdr)->hdr.osn.pdu_length; @@ -4057,9 +5081,10 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,  	if (!skb_len)  		return NULL; -	if ((skb_len >= card->options.rx_sg_cb) && -	    (!(card->info.type == QETH_CARD_TYPE_OSN)) && -	    (!atomic_read(&card->force_alloc_skb))) { +	if (((skb_len >= card->options.rx_sg_cb) && +	     (!(card->info.type == QETH_CARD_TYPE_OSN)) && +	     (!atomic_read(&card->force_alloc_skb))) || +	    (card->options.cq == QETH_CQ_ENABLED)) {  		use_rx_sg = 1;  	} else {  		skb = dev_alloc_skb(skb_len + headroom); @@ -4074,8 +5099,8 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,  		data_len = min(skb_len, (int)(element->length - offset));  		if (data_len) {  			if (use_rx_sg) { -				if (qeth_create_skb_frag(element, &skb, offset, -				    &frag, data_len)) +				if (qeth_create_skb_frag(qethbuffer, element, +				    &skb, offset, &frag, data_len))  					goto no_mem;  			} else {  				memcpy(skb_put(skb, data_len), data_ptr, @@ -4128,7 +5153,7 @@ void qeth_dbf_longtext(debug_info_t *id, int level, char *fmt, ...)  	char dbf_txt_buf[32];  	va_list args; -	if (level > id->level) +	if (!debug_level_enabled(id, level))  		return;  	va_start(args, fmt);  	vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args); @@ -4171,68 +5196,110 @@ int qeth_core_load_discipline(struct qeth_card *card,  		enum qeth_discipline_id discipline)  {  	int rc = 0; +	mutex_lock(&qeth_mod_mutex);  	switch (discipline) {  	case QETH_DISCIPLINE_LAYER3: -		card->discipline.ccwgdriver = try_then_request_module( -			symbol_get(qeth_l3_ccwgroup_driver), -			"qeth_l3"); +		card->discipline = try_then_request_module( +			symbol_get(qeth_l3_discipline), "qeth_l3");  		break;  	case QETH_DISCIPLINE_LAYER2: -		card->discipline.ccwgdriver = try_then_request_module( -			symbol_get(qeth_l2_ccwgroup_driver), -			"qeth_l2"); +		card->discipline = try_then_request_module( +			symbol_get(qeth_l2_discipline), "qeth_l2");  		break;  	} -	if (!card->discipline.ccwgdriver) { +	if (!card->discipline) {  		dev_err(&card->gdev->dev, "There is no kernel module to "  			"support discipline %d\n", discipline);  		rc = -EINVAL;  	} +	mutex_unlock(&qeth_mod_mutex);  	return rc;  }  void qeth_core_free_discipline(struct qeth_card *card)  {  	if (card->options.layer2) -		symbol_put(qeth_l2_ccwgroup_driver); +		symbol_put(qeth_l2_discipline);  	else -		symbol_put(qeth_l3_ccwgroup_driver); -	card->discipline.ccwgdriver = NULL; +		symbol_put(qeth_l3_discipline); +	card->discipline = NULL;  } -static void qeth_determine_capabilities(struct qeth_card *card) +static const struct device_type qeth_generic_devtype = { +	.name = "qeth_generic", +	.groups = qeth_generic_attr_groups, +}; +static const struct device_type qeth_osn_devtype = { +	.name = "qeth_osn", +	.groups = qeth_osn_attr_groups, +}; + +#define DBF_NAME_LEN	20 + +struct qeth_dbf_entry { +	char dbf_name[DBF_NAME_LEN]; +	debug_info_t *dbf_info; +	struct list_head dbf_list; +}; + +static LIST_HEAD(qeth_dbf_list); +static DEFINE_MUTEX(qeth_dbf_list_mutex); + +static debug_info_t *qeth_get_dbf_entry(char *name)  { -	int rc; -	int length; -	char *prcd; +	struct qeth_dbf_entry *entry; +	debug_info_t *rc = NULL; -	QETH_DBF_TEXT(SETUP, 2, "detcapab"); -	rc = ccw_device_set_online(CARD_DDEV(card)); -	if (rc) { -		QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); -		goto out; +	mutex_lock(&qeth_dbf_list_mutex); +	list_for_each_entry(entry, &qeth_dbf_list, dbf_list) { +		if (strcmp(entry->dbf_name, name) == 0) { +			rc = entry->dbf_info; +			break; +		}  	} +	mutex_unlock(&qeth_dbf_list_mutex); +	return rc; +} +static int qeth_add_dbf_entry(struct qeth_card *card, char *name) +{ +	struct qeth_dbf_entry *new_entry; -	rc = qeth_read_conf_data(card, (void **) &prcd, &length); -	if (rc) { -		QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n", -			dev_name(&card->gdev->dev), rc); -		QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); -		goto out_offline; -	} -	qeth_configure_unitaddr(card, prcd); -	qeth_configure_blkt_default(card, prcd); -	kfree(prcd); +	card->debug = debug_register(name, 2, 1, 8); +	if (!card->debug) { +		QETH_DBF_TEXT_(SETUP, 2, "%s", "qcdbf"); +		goto err; +	} +	if (debug_register_view(card->debug, &debug_hex_ascii_view)) +		goto err_dbg; +	new_entry = kzalloc(sizeof(struct qeth_dbf_entry), GFP_KERNEL); +	if (!new_entry) +		goto err_dbg; +	strncpy(new_entry->dbf_name, name, DBF_NAME_LEN); +	new_entry->dbf_info = card->debug; +	mutex_lock(&qeth_dbf_list_mutex); +	list_add(&new_entry->dbf_list, &qeth_dbf_list); +	mutex_unlock(&qeth_dbf_list_mutex); -	rc = qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd); -	if (rc) -		QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); +	return 0; -out_offline: -	ccw_device_set_offline(CARD_DDEV(card)); -out: -	return; +err_dbg: +	debug_unregister(card->debug); +err: +	return -ENOMEM; +} + +static void qeth_clear_dbf_list(void) +{ +	struct qeth_dbf_entry *entry, *tmp; + +	mutex_lock(&qeth_dbf_list_mutex); +	list_for_each_entry_safe(entry, tmp, &qeth_dbf_list, dbf_list) { +		list_del(&entry->dbf_list); +		debug_unregister(entry->dbf_info); +		kfree(entry); +	} +	mutex_unlock(&qeth_dbf_list_mutex);  }  static int qeth_core_probe_device(struct ccwgroup_device *gdev) @@ -4241,7 +5308,7 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)  	struct device *dev;  	int rc;  	unsigned long flags; -	char dbf_name[20]; +	char dbf_name[DBF_NAME_LEN];  	QETH_DBF_TEXT(SETUP, 2, "probedev"); @@ -4260,13 +5327,12 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)  	snprintf(dbf_name, sizeof(dbf_name), "qeth_card_%s",  		dev_name(&gdev->dev)); -	card->debug = debug_register(dbf_name, 2, 1, 8); +	card->debug = qeth_get_dbf_entry(dbf_name);  	if (!card->debug) { -		QETH_DBF_TEXT_(SETUP, 2, "%s", "qcdbf"); -		rc = -ENOMEM; -		goto err_card; +		rc = qeth_add_dbf_entry(card, dbf_name); +		if (rc) +			goto err_card;  	} -	debug_register_view(card->debug, &debug_hex_ascii_view);  	card->read.ccwdev  = gdev->cdev[0];  	card->write.ccwdev = gdev->cdev[1]; @@ -4280,27 +5346,26 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)  	rc = qeth_determine_card_type(card);  	if (rc) {  		QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); -		goto err_dbf; +		goto err_card;  	}  	rc = qeth_setup_card(card);  	if (rc) {  		QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); -		goto err_dbf; +		goto err_card;  	}  	if (card->info.type == QETH_CARD_TYPE_OSN) -		rc = qeth_core_create_osn_attributes(dev); +		gdev->dev.type = &qeth_osn_devtype;  	else -		rc = qeth_core_create_device_attributes(dev); -	if (rc) -		goto err_dbf; +		gdev->dev.type = &qeth_generic_devtype; +  	switch (card->info.type) {  	case QETH_CARD_TYPE_OSN:  	case QETH_CARD_TYPE_OSM:  		rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2);  		if (rc) -			goto err_attr; -		rc = card->discipline.ccwgdriver->probe(card->gdev); +			goto err_card; +		rc = card->discipline->setup(card->gdev);  		if (rc)  			goto err_disc;  	case QETH_CARD_TYPE_OSD: @@ -4318,13 +5383,6 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)  err_disc:  	qeth_core_free_discipline(card); -err_attr: -	if (card->info.type == QETH_CARD_TYPE_OSN) -		qeth_core_remove_osn_attributes(dev); -	else -		qeth_core_remove_device_attributes(dev); -err_dbf: -	debug_unregister(card->debug);  err_card:  	qeth_core_free_card(card);  err_dev: @@ -4339,18 +5397,11 @@ static void qeth_core_remove_device(struct ccwgroup_device *gdev)  	QETH_DBF_TEXT(SETUP, 2, "removedv"); -	if (card->info.type == QETH_CARD_TYPE_OSN) { -		qeth_core_remove_osn_attributes(&gdev->dev); -	} else { -		qeth_core_remove_device_attributes(&gdev->dev); -	} - -	if (card->discipline.ccwgdriver) { -		card->discipline.ccwgdriver->remove(gdev); +	if (card->discipline) { +		card->discipline->remove(gdev);  		qeth_core_free_discipline(card);  	} -	debug_unregister(card->debug);  	write_lock_irqsave(&qeth_core_card_list.rwlock, flags);  	list_del(&card->list);  	write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); @@ -4366,7 +5417,7 @@ static int qeth_core_set_online(struct ccwgroup_device *gdev)  	int rc = 0;  	int def_discipline; -	if (!card->discipline.ccwgdriver) { +	if (!card->discipline) {  		if (card->info.type == QETH_CARD_TYPE_IQD)  			def_discipline = QETH_DISCIPLINE_LAYER3;  		else @@ -4374,11 +5425,11 @@ static int qeth_core_set_online(struct ccwgroup_device *gdev)  		rc = qeth_core_load_discipline(card, def_discipline);  		if (rc)  			goto err; -		rc = card->discipline.ccwgdriver->probe(card->gdev); +		rc = card->discipline->setup(card->gdev);  		if (rc)  			goto err;  	} -	rc = card->discipline.ccwgdriver->set_online(gdev); +	rc = card->discipline->set_online(gdev);  err:  	return rc;  } @@ -4386,66 +5437,61 @@ err:  static int qeth_core_set_offline(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); -	return card->discipline.ccwgdriver->set_offline(gdev); +	return card->discipline->set_offline(gdev);  }  static void qeth_core_shutdown(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); -	if (card->discipline.ccwgdriver && -	    card->discipline.ccwgdriver->shutdown) -		card->discipline.ccwgdriver->shutdown(gdev); +	if (card->discipline && card->discipline->shutdown) +		card->discipline->shutdown(gdev);  }  static int qeth_core_prepare(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); -	if (card->discipline.ccwgdriver && -	    card->discipline.ccwgdriver->prepare) -		return card->discipline.ccwgdriver->prepare(gdev); +	if (card->discipline && card->discipline->prepare) +		return card->discipline->prepare(gdev);  	return 0;  }  static void qeth_core_complete(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); -	if (card->discipline.ccwgdriver && -	    card->discipline.ccwgdriver->complete) -		card->discipline.ccwgdriver->complete(gdev); +	if (card->discipline && card->discipline->complete) +		card->discipline->complete(gdev);  }  static int qeth_core_freeze(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); -	if (card->discipline.ccwgdriver && -	    card->discipline.ccwgdriver->freeze) -		return card->discipline.ccwgdriver->freeze(gdev); +	if (card->discipline && card->discipline->freeze) +		return card->discipline->freeze(gdev);  	return 0;  }  static int qeth_core_thaw(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); -	if (card->discipline.ccwgdriver && -	    card->discipline.ccwgdriver->thaw) -		return card->discipline.ccwgdriver->thaw(gdev); +	if (card->discipline && card->discipline->thaw) +		return card->discipline->thaw(gdev);  	return 0;  }  static int qeth_core_restore(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); -	if (card->discipline.ccwgdriver && -	    card->discipline.ccwgdriver->restore) -		return card->discipline.ccwgdriver->restore(gdev); +	if (card->discipline && card->discipline->restore) +		return card->discipline->restore(gdev);  	return 0;  }  static struct ccwgroup_driver qeth_core_ccwgroup_driver = { -	.owner = THIS_MODULE, -	.name = "qeth", -	.driver_id = 0xD8C5E3C8, -	.probe = qeth_core_probe_device, +	.driver = { +		.owner = THIS_MODULE, +		.name = "qeth", +	}, +	.setup = qeth_core_probe_device,  	.remove = qeth_core_remove_device,  	.set_online = qeth_core_set_online,  	.set_offline = qeth_core_set_offline, @@ -4457,21 +5503,30 @@ static struct ccwgroup_driver qeth_core_ccwgroup_driver = {  	.restore = qeth_core_restore,  }; -static ssize_t -qeth_core_driver_group_store(struct device_driver *ddrv, const char *buf, -			   size_t count) +static ssize_t qeth_core_driver_group_store(struct device_driver *ddrv, +					    const char *buf, size_t count)  {  	int err; -	err = qeth_core_driver_group(buf, qeth_core_root_dev, -					qeth_core_ccwgroup_driver.driver_id); -	if (err) -		return err; -	else -		return count; -} +	err = ccwgroup_create_dev(qeth_core_root_dev, +				  &qeth_core_ccwgroup_driver, 3, buf); + +	return err ? err : count; +}  static DRIVER_ATTR(group, 0200, NULL, qeth_core_driver_group_store); +static struct attribute *qeth_drv_attrs[] = { +	&driver_attr_group.attr, +	NULL, +}; +static struct attribute_group qeth_drv_attr_group = { +	.attrs = qeth_drv_attrs, +}; +static const struct attribute_group *qeth_drv_attr_groups[] = { +	&qeth_drv_attr_group, +	NULL, +}; +  static struct {  	const char str[ETH_GSTRING_LEN];  } qeth_ethtool_stats_keys[] = { @@ -4510,6 +5565,8 @@ static struct {  	{"tx do_QDIO count"},  	{"tx csum"},  	{"tx lin"}, +	{"cq handler count"}, +	{"cq handler time"}  };  int qeth_core_get_sset_count(struct net_device *dev, int stringset) @@ -4568,6 +5625,8 @@ void qeth_core_get_ethtool_stats(struct net_device *dev,  	data[32] = card->perf_stats.outbound_do_qdio_cnt;  	data[33] = card->perf_stats.tx_csum;  	data[34] = card->perf_stats.tx_lin; +	data[35] = card->perf_stats.cq_cnt; +	data[36] = card->perf_stats.cq_time;  }  EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats); @@ -4589,25 +5648,77 @@ void qeth_core_get_drvinfo(struct net_device *dev,  		struct ethtool_drvinfo *info)  {  	struct qeth_card *card = dev->ml_priv; -	if (card->options.layer2) -		strcpy(info->driver, "qeth_l2"); -	else -		strcpy(info->driver, "qeth_l3"); -	strcpy(info->version, "1.0"); -	strcpy(info->fw_version, card->info.mcl_level); -	sprintf(info->bus_info, "%s/%s/%s", -			CARD_RDEV_ID(card), -			CARD_WDEV_ID(card), -			CARD_DDEV_ID(card)); +	strlcpy(info->driver, card->options.layer2 ? "qeth_l2" : "qeth_l3", +		sizeof(info->driver)); +	strlcpy(info->version, "1.0", sizeof(info->version)); +	strlcpy(info->fw_version, card->info.mcl_level, +		sizeof(info->fw_version)); +	snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s", +		 CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card));  }  EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); +/* Helper function to fill 'advertizing' and 'supported' which are the same. */ +/* Autoneg and full-duplex are supported and advertized uncondionally.	     */ +/* Always advertize and support all speeds up to specified, and only one     */ +/* specified port type.							     */ +static void qeth_set_ecmd_adv_sup(struct ethtool_cmd *ecmd, +				int maxspeed, int porttype) +{ +	int port_sup, port_adv, spd_sup, spd_adv; + +	switch (porttype) { +	case PORT_TP: +		port_sup = SUPPORTED_TP; +		port_adv = ADVERTISED_TP; +		break; +	case PORT_FIBRE: +		port_sup = SUPPORTED_FIBRE; +		port_adv = ADVERTISED_FIBRE; +		break; +	default: +		port_sup = SUPPORTED_TP; +		port_adv = ADVERTISED_TP; +		WARN_ON_ONCE(1); +	} + +	/* "Fallthrough" case'es ordered from high to low result in setting  */ +	/* flags cumulatively, starting from the specified speed and down to */ +	/* the lowest possible.						     */ +	spd_sup = 0; +	spd_adv = 0; +	switch (maxspeed) { +	case SPEED_10000: +		spd_sup |= SUPPORTED_10000baseT_Full; +		spd_adv |= ADVERTISED_10000baseT_Full; +	case SPEED_1000: +		spd_sup |= SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; +		spd_adv |= ADVERTISED_1000baseT_Half | +						ADVERTISED_1000baseT_Full; +	case SPEED_100: +		spd_sup |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; +		spd_adv |= ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; +	case SPEED_10: +		spd_sup |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; +		spd_adv |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; +	break; +	default: +		spd_sup = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; +		spd_adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; +		WARN_ON_ONCE(1); +	} +	ecmd->advertising = ADVERTISED_Autoneg | port_adv | spd_adv; +	ecmd->supported = SUPPORTED_Autoneg | port_sup | spd_sup; +} +  int qeth_core_ethtool_get_settings(struct net_device *netdev,  					struct ethtool_cmd *ecmd)  {  	struct qeth_card *card = netdev->ml_priv;  	enum qeth_link_types link_type; +	struct carrier_info carrier_info; +	u32 speed;  	if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))  		link_type = QETH_LINK_TYPE_10GBIT_ETH; @@ -4615,79 +5726,93 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,  		link_type = card->info.link_type;  	ecmd->transceiver = XCVR_INTERNAL; -	ecmd->supported = SUPPORTED_Autoneg; -	ecmd->advertising = ADVERTISED_Autoneg;  	ecmd->duplex = DUPLEX_FULL;  	ecmd->autoneg = AUTONEG_ENABLE;  	switch (link_type) {  	case QETH_LINK_TYPE_FAST_ETH:  	case QETH_LINK_TYPE_LANE_ETH100: -		ecmd->supported |= SUPPORTED_10baseT_Half | -					SUPPORTED_10baseT_Full | -					SUPPORTED_100baseT_Half | -					SUPPORTED_100baseT_Full | -					SUPPORTED_TP; -		ecmd->advertising |= ADVERTISED_10baseT_Half | -					ADVERTISED_10baseT_Full | -					ADVERTISED_100baseT_Half | -					ADVERTISED_100baseT_Full | -					ADVERTISED_TP; -		ecmd->speed = SPEED_100; +		qeth_set_ecmd_adv_sup(ecmd, SPEED_100, PORT_TP); +		speed = SPEED_100;  		ecmd->port = PORT_TP;  		break;  	case QETH_LINK_TYPE_GBIT_ETH:  	case QETH_LINK_TYPE_LANE_ETH1000: -		ecmd->supported |= SUPPORTED_10baseT_Half | -					SUPPORTED_10baseT_Full | -					SUPPORTED_100baseT_Half | -					SUPPORTED_100baseT_Full | -					SUPPORTED_1000baseT_Half | -					SUPPORTED_1000baseT_Full | -					SUPPORTED_FIBRE; -		ecmd->advertising |= ADVERTISED_10baseT_Half | -					ADVERTISED_10baseT_Full | -					ADVERTISED_100baseT_Half | -					ADVERTISED_100baseT_Full | -					ADVERTISED_1000baseT_Half | -					ADVERTISED_1000baseT_Full | -					ADVERTISED_FIBRE; -		ecmd->speed = SPEED_1000; +		qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE); +		speed = SPEED_1000;  		ecmd->port = PORT_FIBRE;  		break;  	case QETH_LINK_TYPE_10GBIT_ETH: -		ecmd->supported |= SUPPORTED_10baseT_Half | -					SUPPORTED_10baseT_Full | -					SUPPORTED_100baseT_Half | -					SUPPORTED_100baseT_Full | -					SUPPORTED_1000baseT_Half | -					SUPPORTED_1000baseT_Full | -					SUPPORTED_10000baseT_Full | -					SUPPORTED_FIBRE; -		ecmd->advertising |= ADVERTISED_10baseT_Half | -					ADVERTISED_10baseT_Full | -					ADVERTISED_100baseT_Half | -					ADVERTISED_100baseT_Full | -					ADVERTISED_1000baseT_Half | -					ADVERTISED_1000baseT_Full | -					ADVERTISED_10000baseT_Full | -					ADVERTISED_FIBRE; -		ecmd->speed = SPEED_10000; +		qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE); +		speed = SPEED_10000;  		ecmd->port = PORT_FIBRE;  		break;  	default: -		ecmd->supported |= SUPPORTED_10baseT_Half | -					SUPPORTED_10baseT_Full | -					SUPPORTED_TP; -		ecmd->advertising |= ADVERTISED_10baseT_Half | -					ADVERTISED_10baseT_Full | -					ADVERTISED_TP; -		ecmd->speed = SPEED_10; +		qeth_set_ecmd_adv_sup(ecmd, SPEED_10, PORT_TP); +		speed = SPEED_10;  		ecmd->port = PORT_TP;  	} +	ethtool_cmd_speed_set(ecmd, speed); + +	/* Check if we can obtain more accurate information.	 */ +	/* If QUERY_CARD_INFO command is not supported or fails, */ +	/* just return the heuristics that was filled above.	 */ +	if (qeth_query_card_info(card, &carrier_info) != 0) +		return 0; + +	netdev_dbg(netdev, +	"card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n", +			carrier_info.card_type, +			carrier_info.port_mode, +			carrier_info.port_speed); + +	/* Update attributes for which we've obtained more authoritative */ +	/* information, leave the rest the way they where filled above.  */ +	switch (carrier_info.card_type) { +	case CARD_INFO_TYPE_1G_COPPER_A: +	case CARD_INFO_TYPE_1G_COPPER_B: +		qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_TP); +		ecmd->port = PORT_TP; +		break; +	case CARD_INFO_TYPE_1G_FIBRE_A: +	case CARD_INFO_TYPE_1G_FIBRE_B: +		qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE); +		ecmd->port = PORT_FIBRE; +		break; +	case CARD_INFO_TYPE_10G_FIBRE_A: +	case CARD_INFO_TYPE_10G_FIBRE_B: +		qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE); +		ecmd->port = PORT_FIBRE; +		break; +	} + +	switch (carrier_info.port_mode) { +	case CARD_INFO_PORTM_FULLDUPLEX: +		ecmd->duplex = DUPLEX_FULL; +		break; +	case CARD_INFO_PORTM_HALFDUPLEX: +		ecmd->duplex = DUPLEX_HALF; +		break; +	} + +	switch (carrier_info.port_speed) { +	case CARD_INFO_PORTS_10M: +		speed = SPEED_10; +		break; +	case CARD_INFO_PORTS_100M: +		speed = SPEED_100; +		break; +	case CARD_INFO_PORTS_1G: +		speed = SPEED_1000; +		break; +	case CARD_INFO_PORTS_10G: +		speed = SPEED_10000; +		break; +	} +	ethtool_cmd_speed_set(ecmd, speed);  	return 0;  } @@ -4699,45 +5824,50 @@ static int __init qeth_core_init(void)  	pr_info("loading core functions\n");  	INIT_LIST_HEAD(&qeth_core_card_list.list); +	INIT_LIST_HEAD(&qeth_dbf_list);  	rwlock_init(&qeth_core_card_list.rwlock); +	mutex_init(&qeth_mod_mutex); + +	qeth_wq = create_singlethread_workqueue("qeth_wq");  	rc = qeth_register_dbf_views();  	if (rc)  		goto out_err; -	rc = ccw_driver_register(&qeth_ccw_driver); -	if (rc) -		goto ccw_err; -	rc = ccwgroup_driver_register(&qeth_core_ccwgroup_driver); -	if (rc) -		goto ccwgroup_err; -	rc = driver_create_file(&qeth_core_ccwgroup_driver.driver, -				&driver_attr_group); -	if (rc) -		goto driver_err;  	qeth_core_root_dev = root_device_register("qeth"); -	rc = IS_ERR(qeth_core_root_dev) ? PTR_ERR(qeth_core_root_dev) : 0; +	rc = PTR_ERR_OR_ZERO(qeth_core_root_dev);  	if (rc)  		goto register_err; -  	qeth_core_header_cache = kmem_cache_create("qeth_hdr",  			sizeof(struct qeth_hdr) + ETH_HLEN, 64, 0, NULL);  	if (!qeth_core_header_cache) {  		rc = -ENOMEM;  		goto slab_err;  	} +	qeth_qdio_outbuf_cache = kmem_cache_create("qeth_buf", +			sizeof(struct qeth_qdio_out_buffer), 0, 0, NULL); +	if (!qeth_qdio_outbuf_cache) { +		rc = -ENOMEM; +		goto cqslab_err; +	} +	rc = ccw_driver_register(&qeth_ccw_driver); +	if (rc) +		goto ccw_err; +	qeth_core_ccwgroup_driver.driver.groups = qeth_drv_attr_groups; +	rc = ccwgroup_driver_register(&qeth_core_ccwgroup_driver); +	if (rc) +		goto ccwgroup_err;  	return 0; -slab_err: -	root_device_unregister(qeth_core_root_dev); -register_err: -	driver_remove_file(&qeth_core_ccwgroup_driver.driver, -			   &driver_attr_group); -driver_err: -	ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver); +  ccwgroup_err:  	ccw_driver_unregister(&qeth_ccw_driver);  ccw_err: -	QETH_DBF_MESSAGE(2, "Initialization failed with code %d\n", rc); +	kmem_cache_destroy(qeth_qdio_outbuf_cache); +cqslab_err: +	kmem_cache_destroy(qeth_core_header_cache); +slab_err: +	root_device_unregister(qeth_core_root_dev); +register_err:  	qeth_unregister_dbf_views();  out_err:  	pr_err("Initializing the qeth device driver failed\n"); @@ -4746,12 +5876,13 @@ out_err:  static void __exit qeth_core_exit(void)  { -	root_device_unregister(qeth_core_root_dev); -	driver_remove_file(&qeth_core_ccwgroup_driver.driver, -			   &driver_attr_group); +	qeth_clear_dbf_list(); +	destroy_workqueue(qeth_wq);  	ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver);  	ccw_driver_unregister(&qeth_ccw_driver); +	kmem_cache_destroy(qeth_qdio_outbuf_cache);  	kmem_cache_destroy(qeth_core_header_cache); +	root_device_unregister(qeth_core_root_dev);  	qeth_unregister_dbf_views();  	pr_info("core functions removed\n");  } diff --git a/drivers/s390/net/qeth_core_mpc.c b/drivers/s390/net/qeth_core_mpc.c index ec24901c802..7b55768a959 100644 --- a/drivers/s390/net/qeth_core_mpc.c +++ b/drivers/s390/net/qeth_core_mpc.c @@ -1,6 +1,4 @@  /* - *  drivers/s390/net/qeth_core_mpc.c - *   *    Copyright IBM Corp. 2007   *    Author(s): Frank Pavlic <fpavlic@de.ibm.com>,   *		 Thomas Spatzier <tspat@de.ibm.com>, @@ -206,7 +204,9 @@ static struct ipa_rc_msg qeth_ipa_rc_msg[] = {  	{IPA_RC_INVALID_SETRTG_INDICATOR, "Invalid SETRTG indicator"},  	{IPA_RC_MC_ADDR_ALREADY_DEFINED, "Multicast address already defined"},  	{IPA_RC_LAN_OFFLINE,		"STRTLAN_LAN_DISABLED - LAN offline"}, +	{IPA_RC_VEPA_TO_VEB_TRANSITION,	"Adj. switch disabled port mode RR"},  	{IPA_RC_INVALID_IP_VERSION2,	"Invalid IP version"}, +	{IPA_RC_ENOMEM,			"Memory problem"},  	{IPA_RC_FFFF,			"Unknown Error"}  }; @@ -249,10 +249,12 @@ static struct ipa_cmd_names qeth_ipa_cmd_names[] = {  	{IPA_CMD_DELIP,		"delip"},  	{IPA_CMD_SETADAPTERPARMS, "setadapterparms"},  	{IPA_CMD_SET_DIAG_ASS,	"set_diag_ass"}, +	{IPA_CMD_SETBRIDGEPORT,	"set_bridge_port"},  	{IPA_CMD_CREATE_ADDR,	"create_addr"},  	{IPA_CMD_DESTROY_ADDR,	"destroy_addr"},  	{IPA_CMD_REGISTER_LOCAL_ADDR,	"register_local_addr"},  	{IPA_CMD_UNREGISTER_LOCAL_ADDR,	"unregister_local_addr"}, +	{IPA_CMD_ADDRESS_CHANGE_NOTIF, "address_change_notification"},  	{IPA_CMD_UNKNOWN,	"unknown"},  }; diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index e37dd8c4bf4..cf6a90ed42a 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -1,6 +1,4 @@  /* - *  drivers/s390/net/qeth_core_mpc.h - *   *    Copyright IBM Corp. 2007   *    Author(s): Frank Pavlic <fpavlic@de.ibm.com>,   *		 Thomas Spatzier <tspat@de.ibm.com>, @@ -70,24 +68,6 @@ enum qeth_link_types {  	QETH_LINK_TYPE_ATM_NATIVE   = 0x90,  }; -enum qeth_tr_macaddr_modes { -	QETH_TR_MACADDR_NONCANONICAL = 0, -	QETH_TR_MACADDR_CANONICAL    = 1, -}; - -enum qeth_tr_broadcast_modes { -	QETH_TR_BROADCAST_ALLRINGS = 0, -	QETH_TR_BROADCAST_LOCAL    = 1, -}; - -/* these values match CHECKSUM_* in include/linux/skbuff.h */ -enum qeth_checksum_types { -	SW_CHECKSUMMING = 0, /* TODO: set to bit flag used in IPA Command */ -	HW_CHECKSUMMING = 1, -	NO_CHECKSUMMING = 2, -}; -#define QETH_CHECKSUM_DEFAULT SW_CHECKSUMMING -  /*   * Routing stuff   */ @@ -124,10 +104,12 @@ enum qeth_ipa_cmds {  	IPA_CMD_DELIP			= 0xb7,  	IPA_CMD_SETADAPTERPARMS		= 0xb8,  	IPA_CMD_SET_DIAG_ASS		= 0xb9, +	IPA_CMD_SETBRIDGEPORT		= 0xbe,  	IPA_CMD_CREATE_ADDR		= 0xc3,  	IPA_CMD_DESTROY_ADDR		= 0xc4,  	IPA_CMD_REGISTER_LOCAL_ADDR	= 0xd1,  	IPA_CMD_UNREGISTER_LOCAL_ADDR	= 0xd2, +	IPA_CMD_ADDRESS_CHANGE_NOTIF	= 0xd3,  	IPA_CMD_UNKNOWN			= 0x00  }; @@ -197,7 +179,9 @@ enum qeth_ipa_return_codes {  	IPA_RC_INVALID_SETRTG_INDICATOR	= 0xe012,  	IPA_RC_MC_ADDR_ALREADY_DEFINED	= 0xe013,  	IPA_RC_LAN_OFFLINE		= 0xe080, +	IPA_RC_VEPA_TO_VEB_TRANSITION	= 0xe090,  	IPA_RC_INVALID_IP_VERSION2	= 0xf001, +	IPA_RC_ENOMEM			= 0xfffe,  	IPA_RC_FFFF			= 0xffff  };  /* for DELIP */ @@ -257,6 +241,7 @@ enum qeth_ipa_setadp_cmd {  	IPA_SETADP_SET_PROMISC_MODE		= 0x00000800L,  	IPA_SETADP_SET_DIAG_ASSIST		= 0x00002000L,  	IPA_SETADP_SET_ACCESS_CONTROL		= 0x00010000L, +	IPA_SETADP_QUERY_OAT			= 0x00080000L,  };  enum qeth_ipa_mac_ops {  	CHANGE_ADDR_READ_MAC		= 0, @@ -287,8 +272,28 @@ enum qeth_ipa_set_access_mode_rc {  	SET_ACCESS_CTRL_RC_ALREADY_ISOLATED	= 0x0010,  	SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER	= 0x0014,  	SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF	= 0x0018, +	SET_ACCESS_CTRL_RC_REFLREL_UNSUPPORTED	= 0x0022, +	SET_ACCESS_CTRL_RC_REFLREL_FAILED	= 0x0024, +	SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED	= 0x0028, +}; +enum qeth_card_info_card_type { +	CARD_INFO_TYPE_1G_COPPER_A	= 0x61, +	CARD_INFO_TYPE_1G_FIBRE_A	= 0x71, +	CARD_INFO_TYPE_10G_FIBRE_A	= 0x91, +	CARD_INFO_TYPE_1G_COPPER_B	= 0xb1, +	CARD_INFO_TYPE_1G_FIBRE_B	= 0xa1, +	CARD_INFO_TYPE_10G_FIBRE_B	= 0xc1, +}; +enum qeth_card_info_port_mode { +	CARD_INFO_PORTM_HALFDUPLEX	= 0x0002, +	CARD_INFO_PORTM_FULLDUPLEX	= 0x0003, +}; +enum qeth_card_info_port_speed { +	CARD_INFO_PORTS_10M		= 0x00000005, +	CARD_INFO_PORTS_100M		= 0x00000006, +	CARD_INFO_PORTS_1G		= 0x00000007, +	CARD_INFO_PORTS_10G		= 0x00000008,  }; -  /* (SET)DELIP(M) IPA stuff ***************************************************/  struct qeth_ipacmd_setdelip4 { @@ -333,7 +338,7 @@ struct qeth_arp_query_data {  	__u16 request_bits;  	__u16 reply_bits;  	__u32 no_entries; -	char data; +	char data; /* only for replies */  } __attribute__((packed));  /* used as parameter for arp_query reply */ @@ -404,8 +409,28 @@ struct qeth_snmp_ureq {  /* SET_ACCESS_CONTROL: same format for request and reply */  struct qeth_set_access_ctrl {  	__u32 subcmd_code; +	__u8 reserved[8];  } __attribute__((packed)); +struct qeth_query_oat { +	__u32 subcmd_code; +	__u8 reserved[12]; +} __packed; + +struct qeth_qoat_priv { +	__u32 buffer_len; +	__u32 response_len; +	char *buffer; +}; + +struct qeth_query_card_info { +	__u8	card_type; +	__u8	reserved1; +	__u16	port_mode; +	__u32	port_speed; +	__u32	reserved2; +}; +  struct qeth_ipacmd_setadpparms_hdr {  	__u32 supp_hw_cmds;  	__u32 reserved1; @@ -425,6 +450,8 @@ struct qeth_ipacmd_setadpparms {  		struct qeth_change_addr change_addr;  		struct qeth_snmp_cmd snmp;  		struct qeth_set_access_ctrl set_access_ctrl; +		struct qeth_query_oat query_oat; +		struct qeth_query_card_info card_info;  		__u32 mode;  	} data;  } __attribute__ ((packed)); @@ -456,6 +483,12 @@ enum qeth_diags_trace_cmds {  	QETH_DIAGS_CMD_TRACE_QUERY	= 0x0010,  }; +enum qeth_diags_trap_action { +	QETH_DIAGS_TRAP_ARM	= 0x01, +	QETH_DIAGS_TRAP_DISARM	= 0x02, +	QETH_DIAGS_TRAP_CAPTURE = 0x04, +}; +  struct qeth_ipacmd_diagass {  	__u32  host_tod2;  	__u32:32; @@ -465,9 +498,128 @@ struct qeth_ipacmd_diagass {  	__u8   type;  	__u8   action;  	__u16  options; -	__u32:32; +	__u32  ext; +	__u8   cdata[64];  } __attribute__ ((packed)); +/* SETBRIDGEPORT IPA Command:	 *********************************************/ +enum qeth_ipa_sbp_cmd { +	IPA_SBP_QUERY_COMMANDS_SUPPORTED	= 0x00000000L, +	IPA_SBP_RESET_BRIDGE_PORT_ROLE		= 0x00000001L, +	IPA_SBP_SET_PRIMARY_BRIDGE_PORT		= 0x00000002L, +	IPA_SBP_SET_SECONDARY_BRIDGE_PORT	= 0x00000004L, +	IPA_SBP_QUERY_BRIDGE_PORTS		= 0x00000008L, +	IPA_SBP_BRIDGE_PORT_STATE_CHANGE	= 0x00000010L, +}; + +struct net_if_token { +	__u16 devnum; +	__u8 cssid; +	__u8 iid; +	__u8 ssid; +	__u8 chpid; +	__u16 chid; +} __packed; + +struct mac_addr_lnid { +	__u8 mac[6]; +	__u16 lnid; +} __packed; + +struct qeth_ipacmd_sbp_hdr { +	__u32 supported_sbp_cmds; +	__u32 enabled_sbp_cmds; +	__u16 cmdlength; +	__u16 reserved1; +	__u32 command_code; +	__u16 return_code; +	__u8  used_total; +	__u8  seq_no; +	__u32 reserved2; +} __packed; + +struct qeth_sbp_query_cmds_supp { +	__u32 supported_cmds; +	__u32 reserved; +} __packed; + +struct qeth_sbp_reset_role { +} __packed; + +struct qeth_sbp_set_primary { +	struct net_if_token token; +} __packed; + +struct qeth_sbp_set_secondary { +} __packed; + +struct qeth_sbp_port_entry { +		__u8 role; +		__u8 state; +		__u8 reserved1; +		__u8 reserved2; +		struct net_if_token token; +} __packed; + +struct qeth_sbp_query_ports { +	__u8 primary_bp_supported; +	__u8 secondary_bp_supported; +	__u8 num_entries; +	__u8 entry_length; +	struct qeth_sbp_port_entry entry[]; +} __packed; + +struct qeth_sbp_state_change { +	__u8 primary_bp_supported; +	__u8 secondary_bp_supported; +	__u8 num_entries; +	__u8 entry_length; +	struct qeth_sbp_port_entry entry[]; +} __packed; + +struct qeth_ipacmd_setbridgeport { +	struct qeth_ipacmd_sbp_hdr hdr; +	union { +		struct qeth_sbp_query_cmds_supp query_cmds_supp; +		struct qeth_sbp_reset_role reset_role; +		struct qeth_sbp_set_primary set_primary; +		struct qeth_sbp_set_secondary set_secondary; +		struct qeth_sbp_query_ports query_ports; +		struct qeth_sbp_state_change state_change; +	} data; +} __packed; + +/* ADDRESS_CHANGE_NOTIFICATION adapter-initiated "command" *******************/ +/* Bitmask for entry->change_code. Both bits may be raised.		     */ +enum qeth_ipa_addr_change_code { +	IPA_ADDR_CHANGE_CODE_VLANID		= 0x01, +	IPA_ADDR_CHANGE_CODE_MACADDR		= 0x02, +	IPA_ADDR_CHANGE_CODE_REMOVAL		= 0x80,	/* else addition */ +}; +enum qeth_ipa_addr_change_retcode { +	IPA_ADDR_CHANGE_RETCODE_OK		= 0x0000, +	IPA_ADDR_CHANGE_RETCODE_LOSTEVENTS	= 0x0010, +}; +enum qeth_ipa_addr_change_lostmask { +	IPA_ADDR_CHANGE_MASK_OVERFLOW		= 0x01, +	IPA_ADDR_CHANGE_MASK_STATECHANGE	= 0x02, +}; + +struct qeth_ipacmd_addr_change_entry { +	struct net_if_token token; +	struct mac_addr_lnid addr_lnid; +	__u8 change_code; +	__u8 reserved1; +	__u16 reserved2; +} __packed; + +struct qeth_ipacmd_addr_change { +	__u8 lost_event_mask; +	__u8 reserved; +	__u16 num_entries; +	struct qeth_ipacmd_addr_change_entry entry[]; +} __packed; +  /* Header for each IPA command */  struct qeth_ipacmd_hdr {  	__u8   command; @@ -497,6 +649,8 @@ struct qeth_ipa_cmd {  		struct qeth_ipacmd_setadpparms		setadapterparms;  		struct qeth_set_routing			setrtg;  		struct qeth_ipacmd_diagass		diagass; +		struct qeth_ipacmd_setbridgeport	sbp; +		struct qeth_ipacmd_addr_change		addrchange;  	} data;  } __attribute__ ((packed)); diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index b5e967cf7e2..8a25a2be989 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -1,6 +1,4 @@  /* - *  drivers/s390/net/qeth_core_sys.c - *   *    Copyright IBM Corp. 2007   *    Author(s): Utz Bacher <utz.bacher@de.ibm.com>,   *		 Frank Pavlic <fpavlic@de.ibm.com>, @@ -219,6 +217,10 @@ static ssize_t qeth_dev_prioqing_show(struct device *dev,  		return sprintf(buf, "%s\n", "by precedence");  	case QETH_PRIO_Q_ING_TOS:  		return sprintf(buf, "%s\n", "by type of service"); +	case QETH_PRIO_Q_ING_SKB: +		return sprintf(buf, "%s\n", "by skb-priority"); +	case QETH_PRIO_Q_ING_VLAN: +		return sprintf(buf, "%s\n", "by VLAN headers");  	default:  		return sprintf(buf, "always queue %i\n",  			       card->qdio.default_out_queue); @@ -252,11 +254,23 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev,  	}  	tmp = strsep((char **) &buf, "\n"); -	if (!strcmp(tmp, "prio_queueing_prec")) +	if (!strcmp(tmp, "prio_queueing_prec")) {  		card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_PREC; -	else if (!strcmp(tmp, "prio_queueing_tos")) +		card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; +	} else if (!strcmp(tmp, "prio_queueing_skb")) { +		card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_SKB; +		card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; +	} else if (!strcmp(tmp, "prio_queueing_tos")) {  		card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_TOS; -	else if (!strcmp(tmp, "no_prio_queueing:0")) { +		card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; +	} else if (!strcmp(tmp, "prio_queueing_vlan")) { +		if (!card->options.layer2) { +			rc = -ENOTSUPP; +			goto out; +		} +		card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_VLAN; +		card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; +	} else if (!strcmp(tmp, "no_prio_queueing:0")) {  		card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING;  		card->qdio.default_out_queue = 0;  	} else if (!strcmp(tmp, "no_prio_queueing:1")) { @@ -434,8 +448,8 @@ static ssize_t qeth_dev_layer2_store(struct device *dev,  		goto out;  	else {  		card->info.mac_bits  = 0; -		if (card->discipline.ccwgdriver) { -			card->discipline.ccwgdriver->remove(card->gdev); +		if (card->discipline) { +			card->discipline->remove(card->gdev);  			qeth_core_free_discipline(card);  		}  	} @@ -444,7 +458,7 @@ static ssize_t qeth_dev_layer2_store(struct device *dev,  	if (rc)  		goto out; -	rc = card->discipline.ccwgdriver->probe(card->gdev); +	rc = card->discipline->setup(card->gdev);  out:  	mutex_unlock(&card->discipline_mutex);  	return rc ? rc : count; @@ -515,10 +529,11 @@ static ssize_t qeth_dev_isolation_store(struct device *dev,  	rc = count;  	/* defer IP assist if device is offline (until discipline->set_online)*/ +	card->options.prev_isolation = card->options.isolation;  	card->options.isolation = isolation;  	if (card->state == CARD_STATE_SOFTSETUP ||  	    card->state == CARD_STATE_UP) { -		int ipa_rc = qeth_set_access_ctrl_online(card); +		int ipa_rc = qeth_set_access_ctrl_online(card, 1);  		if (ipa_rc != 0)  			rc = ipa_rc;  	} @@ -530,6 +545,66 @@ out:  static DEVICE_ATTR(isolation, 0644, qeth_dev_isolation_show,  		   qeth_dev_isolation_store); +static ssize_t qeth_hw_trap_show(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	struct qeth_card *card = dev_get_drvdata(dev); + +	if (!card) +		return -EINVAL; +	if (card->info.hwtrap) +		return snprintf(buf, 5, "arm\n"); +	else +		return snprintf(buf, 8, "disarm\n"); +} + +static ssize_t qeth_hw_trap_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	struct qeth_card *card = dev_get_drvdata(dev); +	int rc = 0; +	char *tmp, *curtoken; +	int state = 0; +	curtoken = (char *)buf; + +	if (!card) +		return -EINVAL; + +	mutex_lock(&card->conf_mutex); +	if (card->state == CARD_STATE_SOFTSETUP || card->state == CARD_STATE_UP) +		state = 1; +	tmp = strsep(&curtoken, "\n"); + +	if (!strcmp(tmp, "arm") && !card->info.hwtrap) { +		if (state) { +			if (qeth_is_diagass_supported(card, +			    QETH_DIAGS_CMD_TRAP)) { +				rc = qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM); +				if (!rc) +					card->info.hwtrap = 1; +			} else +				rc = -EINVAL; +		} else +			card->info.hwtrap = 1; +	} else if (!strcmp(tmp, "disarm") && card->info.hwtrap) { +		if (state) { +			rc = qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); +			if (!rc) +				card->info.hwtrap = 0; +		} else +			card->info.hwtrap = 0; +	} else if (!strcmp(tmp, "trap") && state && card->info.hwtrap) +		rc = qeth_hw_trap(card, QETH_DIAGS_TRAP_CAPTURE); +	else +		rc = -EINVAL; + +	mutex_unlock(&card->conf_mutex); +	return rc ? rc : count; +} + +static DEVICE_ATTR(hw_trap, 0644, qeth_hw_trap_show, +		   qeth_hw_trap_store); +  static ssize_t qeth_dev_blkt_show(char *buf, struct qeth_card *card, int value)  { @@ -633,7 +708,6 @@ static struct attribute *qeth_blkt_device_attrs[] = {  	&dev_attr_inter_jumbo.attr,  	NULL,  }; -  static struct attribute_group qeth_device_blkt_group = {  	.name = "blkt",  	.attrs = qeth_blkt_device_attrs, @@ -653,13 +727,19 @@ static struct attribute *qeth_device_attrs[] = {  	&dev_attr_performance_stats.attr,  	&dev_attr_layer2.attr,  	&dev_attr_isolation.attr, +	&dev_attr_hw_trap.attr,  	NULL,  }; -  static struct attribute_group qeth_device_attr_group = {  	.attrs = qeth_device_attrs,  }; +const struct attribute_group *qeth_generic_attr_groups[] = { +	&qeth_device_attr_group, +	&qeth_device_blkt_group, +	NULL, +}; +  static struct attribute *qeth_osn_device_attrs[] = {  	&dev_attr_state.attr,  	&dev_attr_chpid.attr, @@ -669,37 +749,10 @@ static struct attribute *qeth_osn_device_attrs[] = {  	&dev_attr_recover.attr,  	NULL,  }; -  static struct attribute_group qeth_osn_device_attr_group = {  	.attrs = qeth_osn_device_attrs,  }; - -int qeth_core_create_device_attributes(struct device *dev) -{ -	int ret; -	ret = sysfs_create_group(&dev->kobj, &qeth_device_attr_group); -	if (ret) -		return ret; -	ret = sysfs_create_group(&dev->kobj, &qeth_device_blkt_group); -	if (ret) -		sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); - -	return 0; -} - -void qeth_core_remove_device_attributes(struct device *dev) -{ -	sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); -	sysfs_remove_group(&dev->kobj, &qeth_device_blkt_group); -} - -int qeth_core_create_osn_attributes(struct device *dev) -{ -	return sysfs_create_group(&dev->kobj, &qeth_osn_device_attr_group); -} - -void qeth_core_remove_osn_attributes(struct device *dev) -{ -	sysfs_remove_group(&dev->kobj, &qeth_osn_device_attr_group); -	return; -} +const struct attribute_group *qeth_osn_attr_groups[] = { +	&qeth_osn_device_attr_group, +	NULL, +}; diff --git a/drivers/s390/net/qeth_l2.h b/drivers/s390/net/qeth_l2.h new file mode 100644 index 00000000000..0767556404b --- /dev/null +++ b/drivers/s390/net/qeth_l2.h @@ -0,0 +1,15 @@ +/* + *    Copyright IBM Corp. 2013 + *    Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> + */ + +#ifndef __QETH_L2_H__ +#define __QETH_L2_H__ + +#include "qeth_core.h" + +int qeth_l2_create_device_attributes(struct device *); +void qeth_l2_remove_device_attributes(struct device *); +void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); + +#endif /* __QETH_L2_H__ */ diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 7a7a1b66478..5ef5b4f4575 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1,6 +1,4 @@  /* - *  drivers/s390/net/qeth_l2_main.c - *   *    Copyright IBM Corp. 2007, 2009   *    Author(s): Utz Bacher <utz.bacher@de.ibm.com>,   *		 Frank Pavlic <fpavlic@de.ibm.com>, @@ -23,6 +21,7 @@  #include <linux/list.h>  #include "qeth_core.h" +#include "qeth_l2.h"  static int qeth_l2_set_offline(struct ccwgroup_device *);  static int qeth_l2_stop(struct net_device *); @@ -34,6 +33,11 @@ static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *,  					    unsigned long));  static void qeth_l2_set_multicast_list(struct net_device *);  static int qeth_l2_recover(void *); +static void qeth_bridgeport_query_support(struct qeth_card *card); +static void qeth_bridge_state_change(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd); +static void qeth_bridge_host_event(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd);  static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)  { @@ -75,6 +79,9 @@ static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)  			mii_data->val_out = qeth_mdio_read(dev,  				mii_data->phy_id, mii_data->reg_num);  		break; +	case SIOC_QETH_QUERY_OAT: +		rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data); +		break;  	default:  		rc = -EOPNOTSUPP;  	} @@ -202,17 +209,19 @@ static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac)  		kfree(mc);  } -static void qeth_l2_del_all_mc(struct qeth_card *card) +static void qeth_l2_del_all_mc(struct qeth_card *card, int del)  {  	struct qeth_mc_mac *mc, *tmp;  	spin_lock_bh(&card->mclock);  	list_for_each_entry_safe(mc, tmp, &card->mc_list, list) { -		if (mc->is_vmac) -			qeth_l2_send_setdelmac(card, mc->mc_addr, +		if (del) { +			if (mc->is_vmac) +				qeth_l2_send_setdelmac(card, mc->mc_addr,  					IPA_CMD_DELVMAC, NULL); -		else -			qeth_l2_send_delgroupmac(card, mc->mc_addr); +			else +				qeth_l2_send_delgroupmac(card, mc->mc_addr); +		}  		list_del(&mc->list);  		kfree(mc);  	} @@ -232,7 +241,7 @@ static inline int qeth_l2_get_cast_type(struct qeth_card *card,  }  static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, -			struct sk_buff *skb, int ipv, int cast_type) +			struct sk_buff *skb, int cast_type)  {  	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb); @@ -288,37 +297,33 @@ static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i,  				 qeth_l2_send_setdelvlan_cb, NULL);  } -static void qeth_l2_process_vlans(struct qeth_card *card, int clear) +static void qeth_l2_process_vlans(struct qeth_card *card)  {  	struct qeth_vlan_vid *id;  	QETH_CARD_TEXT(card, 3, "L2prcvln");  	spin_lock_bh(&card->vlanlock);  	list_for_each_entry(id, &card->vid_list, list) { -		if (clear) -			qeth_l2_send_setdelvlan(card, id->vid, -				IPA_CMD_DELVLAN); -		else -			qeth_l2_send_setdelvlan(card, id->vid, -				IPA_CMD_SETVLAN); +		qeth_l2_send_setdelvlan(card, id->vid, IPA_CMD_SETVLAN);  	}  	spin_unlock_bh(&card->vlanlock);  } -static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) +static int qeth_l2_vlan_rx_add_vid(struct net_device *dev, +				   __be16 proto, u16 vid)  {  	struct qeth_card *card = dev->ml_priv;  	struct qeth_vlan_vid *id;  	QETH_CARD_TEXT_(card, 4, "aid:%d", vid);  	if (!vid) -		return; +		return 0;  	if (card->info.type == QETH_CARD_TYPE_OSM) {  		QETH_CARD_TEXT(card, 3, "aidOSM"); -		return; +		return 0;  	}  	if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {  		QETH_CARD_TEXT(card, 3, "aidREC"); -		return; +		return 0;  	}  	id = kmalloc(sizeof(struct qeth_vlan_vid), GFP_ATOMIC);  	if (id) { @@ -327,10 +332,14 @@ static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)  		spin_lock_bh(&card->vlanlock);  		list_add_tail(&id->list, &card->vid_list);  		spin_unlock_bh(&card->vlanlock); +	} else { +		return -ENOMEM;  	} +	return 0;  } -static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, +				    __be16 proto, u16 vid)  {  	struct qeth_vlan_vid *id, *tmpid = NULL;  	struct qeth_card *card = dev->ml_priv; @@ -338,11 +347,11 @@ static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)  	QETH_CARD_TEXT_(card, 4, "kid:%d", vid);  	if (card->info.type == QETH_CARD_TYPE_OSM) {  		QETH_CARD_TEXT(card, 3, "kidOSM"); -		return; +		return 0;  	}  	if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {  		QETH_CARD_TEXT(card, 3, "kidREC"); -		return; +		return 0;  	}  	spin_lock_bh(&card->vlanlock);  	list_for_each_entry(id, &card->vid_list, list) { @@ -358,6 +367,7 @@ static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)  		kfree(tmpid);  	}  	qeth_l2_set_multicast_list(card->dev); +	return 0;  }  static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) @@ -379,19 +389,11 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)  			dev_close(card->dev);  			rtnl_unlock();  		} -		if (!card->use_hard_stop || -			recovery_mode) { -			__u8 *mac = &card->dev->dev_addr[0]; -			rc = qeth_l2_send_delmac(card, mac); -			QETH_DBF_TEXT_(SETUP, 2, "Lerr%d", rc); -		} +		card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;  		card->state = CARD_STATE_SOFTSETUP;  	}  	if (card->state == CARD_STATE_SOFTSETUP) { -		qeth_l2_process_vlans(card, 1); -		if (!card->use_hard_stop || -			recovery_mode) -			qeth_l2_del_all_mc(card); +		qeth_l2_del_all_mc(card, 0);  		qeth_clear_ipacmd_list(card);  		card->state = CARD_STATE_HARDSETUP;  	} @@ -405,7 +407,6 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)  		qeth_clear_cmd_buffers(&card->read);  		qeth_clear_cmd_buffers(&card->write);  	} -	card->use_hard_stop = 0;  	return rc;  } @@ -418,10 +419,10 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card,  	unsigned int len;  	*done = 0; -	BUG_ON(!budget); +	WARN_ON_ONCE(!budget);  	while (budget) {  		skb = qeth_core_get_next_skb(card, -			card->qdio.in_q->bufs[card->rx.b_index].buffer, +			&card->qdio.in_q->bufs[card->rx.b_index],  			&card->rx.b_element, &card->rx.e_offset, &hdr);  		if (!skb) {  			*done = 1; @@ -432,10 +433,7 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card,  		case QETH_HEADER_TYPE_LAYER2:  			skb->pkt_type = PACKET_HOST;  			skb->protocol = eth_type_trans(skb, skb->dev); -			if (card->options.checksum_type == NO_CHECKSUMMING) -				skb->ip_summed = CHECKSUM_UNNECESSARY; -			else -				skb->ip_summed = CHECKSUM_NONE; +			skb->ip_summed = CHECKSUM_NONE;  			if (skb->protocol == htons(ETH_P_802_2))  				*((__u32 *)skb->cb) = ++card->seqno.pkt_seqno;  			len = skb->len; @@ -573,18 +571,17 @@ static int qeth_l2_send_setmac_cb(struct qeth_card *card,  		case IPA_RC_L2_DUP_LAYER3_MAC:  			dev_warn(&card->gdev->dev,  				"MAC address %pM already exists\n", -				card->dev->dev_addr); +				cmd->data.setdelmac.mac);  			break;  		case IPA_RC_L2_MAC_NOT_AUTH_BY_HYP:  		case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP:  			dev_warn(&card->gdev->dev,  				"MAC address %pM is not authorized\n", -				card->dev->dev_addr); +				cmd->data.setdelmac.mac);  			break;  		default:  			break;  		} -		cmd->hdr.return_code = -EIO;  	} else {  		card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;  		memcpy(card->dev->dev_addr, cmd->data.setdelmac.mac, @@ -613,7 +610,6 @@ static int qeth_l2_send_delmac_cb(struct qeth_card *card,  	cmd = (struct qeth_ipa_cmd *) data;  	if (cmd->hdr.return_code) {  		QETH_CARD_TEXT_(card, 2, "err%d", cmd->hdr.return_code); -		cmd->hdr.return_code = -EIO;  		return 0;  	}  	card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; @@ -638,10 +634,13 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)  	QETH_DBF_TEXT(SETUP, 2, "doL2init");  	QETH_DBF_TEXT_(SETUP, 2, "doL2%s", CARD_BUS_ID(card)); -	rc = qeth_query_setadapterparms(card); -	if (rc) { -		QETH_DBF_MESSAGE(2, "could not query adapter parameters on " -			"device %s: x%x\n", CARD_BUS_ID(card), rc); +	if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) { +		rc = qeth_query_setadapterparms(card); +		if (rc) { +			QETH_DBF_MESSAGE(2, "could not query adapter " +				"parameters on device %s: x%x\n", +				CARD_BUS_ID(card), rc); +		}  	}  	if (card->info.type == QETH_CARD_TYPE_IQD || @@ -657,7 +656,7 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)  		}  		QETH_DBF_HEX(SETUP, 2, card->dev->dev_addr, OSA_ADDR_LEN);  	} else { -		random_ether_addr(card->dev->dev_addr); +		eth_random_addr(card->dev->dev_addr);  		memcpy(card->dev->dev_addr, vendor_pre, 3);  	}  	return 0; @@ -688,9 +687,9 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)  		return -ERESTARTSYS;  	}  	rc = qeth_l2_send_delmac(card, &card->dev->dev_addr[0]); -	if (!rc) +	if (!rc || (rc == IPA_RC_L2_MAC_NOT_FOUND))  		rc = qeth_l2_send_setmac(card, addr->sa_data); -	return rc; +	return rc ? -EINVAL : 0;  }  static void qeth_l2_set_multicast_list(struct net_device *dev) @@ -705,7 +704,7 @@ static void qeth_l2_set_multicast_list(struct net_device *dev)  	if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&  	    (card->state != CARD_STATE_UP))  		return; -	qeth_l2_del_all_mc(card); +	qeth_l2_del_all_mc(card, 1);  	spin_lock_bh(&card->mclock);  	netdev_for_each_mc_addr(ha, dev)  		qeth_l2_add_mc(card, ha->addr, 0); @@ -726,15 +725,20 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  	int elements = 0;  	struct qeth_card *card = dev->ml_priv;  	struct sk_buff *new_skb = skb; -	int ipv = qeth_get_ip_version(skb);  	int cast_type = qeth_l2_get_cast_type(card, skb); -	struct qeth_qdio_out_q *queue = card->qdio.out_qs -		[qeth_get_priority_queue(card, skb, ipv, cast_type)]; +	struct qeth_qdio_out_q *queue;  	int tx_bytes = skb->len;  	int data_offset = -1;  	int elements_needed = 0;  	int hd_len = 0; +	if (card->qdio.do_prio_queueing || (cast_type && +					card->info.is_multicast_different)) +		queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb, +					qeth_get_ip_version(skb), cast_type)]; +	else +		queue = card->qdio.out_qs[card->qdio.default_out_queue]; +  	if ((card->state != CARD_STATE_UP) || !card->lan_online) {  		card->stats.tx_carrier_errors++;  		goto tx_drop; @@ -763,7 +767,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  				goto tx_drop;  			elements_needed++;  			skb_reset_mac_header(new_skb); -			qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); +			qeth_l2_fill_header(card, hdr, new_skb, cast_type);  			hdr->hdr.l2.pkt_length = new_skb->len;  			memcpy(((char *)hdr) + sizeof(struct qeth_hdr),  				skb_mac_header(new_skb), ETH_HLEN); @@ -776,12 +780,11 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  			hdr = (struct qeth_hdr *)skb_push(new_skb,  						sizeof(struct qeth_hdr));  			skb_set_mac_header(new_skb, sizeof(struct qeth_hdr)); -			qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); +			qeth_l2_fill_header(card, hdr, new_skb, cast_type);  		}  	} -	elements = qeth_get_elements_no(card, (void *)hdr, new_skb, -						elements_needed); +	elements = qeth_get_elements_no(card, new_skb, elements_needed);  	if (!elements) {  		if (data_offset >= 0)  			kmem_cache_free(qeth_core_header_cache, hdr); @@ -789,7 +792,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  	}  	if (card->info.type != QETH_CARD_TYPE_IQD) { -		if (qeth_hdr_chk_and_bounce(new_skb, +		if (qeth_hdr_chk_and_bounce(new_skb, &hdr,  		    sizeof(struct qeth_hdr_layer2)))  			goto tx_drop;  		rc = qeth_do_send_packet(card, queue, new_skb, hdr, @@ -831,12 +834,14 @@ tx_drop:  	return NETDEV_TX_OK;  } -static int qeth_l2_open(struct net_device *dev) +static int __qeth_l2_open(struct net_device *dev)  {  	struct qeth_card *card = dev->ml_priv;  	int rc = 0;  	QETH_CARD_TEXT(card, 4, "qethopen"); +	if (card->state == CARD_STATE_UP) +		return rc;  	if (card->state != CARD_STATE_SOFTSETUP)  		return -ENODEV; @@ -857,6 +862,18 @@ static int qeth_l2_open(struct net_device *dev)  	return rc;  } +static int qeth_l2_open(struct net_device *dev) +{ +	struct qeth_card *card = dev->ml_priv; + +	QETH_CARD_TEXT(card, 5, "qethope_"); +	if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) { +		QETH_CARD_TEXT(card, 3, "openREC"); +		return -ERESTARTSYS; +	} +	return __qeth_l2_open(dev); +} +  static int qeth_l2_stop(struct net_device *dev)  {  	struct qeth_card *card = dev->ml_priv; @@ -874,15 +891,11 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); +	qeth_l2_create_device_attributes(&gdev->dev);  	INIT_LIST_HEAD(&card->vid_list);  	INIT_LIST_HEAD(&card->mc_list);  	card->options.layer2 = 1; -	card->discipline.start_poll = qeth_qdio_start_poll; -	card->discipline.input_handler = (qdio_handler_t *) -		qeth_qdio_input_handler; -	card->discipline.output_handler = (qdio_handler_t *) -		qeth_qdio_output_handler; -	card->discipline.recover = qeth_l2_recover; +	card->info.hwtrap = 0;  	return 0;  } @@ -890,13 +903,12 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)  {  	struct qeth_card *card = dev_get_drvdata(&cgdev->dev); +	qeth_l2_remove_device_attributes(&cgdev->dev);  	qeth_set_allowed_threads(card, 0, 1);  	wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); -	if (cgdev->state == CCWGROUP_ONLINE) { -		card->use_hard_stop = 1; +	if (cgdev->state == CCWGROUP_ONLINE)  		qeth_l2_set_offline(cgdev); -	}  	if (card->dev) {  		unregister_netdev(card->dev); @@ -927,7 +939,7 @@ static const struct net_device_ops qeth_l2_netdev_ops = {  	.ndo_get_stats		= qeth_get_stats,  	.ndo_start_xmit		= qeth_l2_hard_start_xmit,  	.ndo_validate_addr	= eth_validate_addr, -	.ndo_set_multicast_list = qeth_l2_set_multicast_list, +	.ndo_set_rx_mode	= qeth_l2_set_multicast_list,  	.ndo_do_ioctl	   	= qeth_l2_do_ioctl,  	.ndo_set_mac_address    = qeth_l2_set_mac_address,  	.ndo_change_mtu	   	= qeth_change_mtu, @@ -957,11 +969,10 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)  	card->dev->watchdog_timeo = QETH_TX_TIMEOUT;  	card->dev->mtu = card->info.initial_mtu;  	card->dev->netdev_ops = &qeth_l2_netdev_ops; -	if (card->info.type != QETH_CARD_TYPE_OSN) -		SET_ETHTOOL_OPS(card->dev, &qeth_l2_ethtool_ops); -	else -		SET_ETHTOOL_OPS(card->dev, &qeth_l2_osn_ops); -	card->dev->features |= NETIF_F_HW_VLAN_FILTER; +	card->dev->ethtool_ops = +		(card->info.type != QETH_CARD_TYPE_OSN) ? +		&qeth_l2_ethtool_ops : &qeth_l2_osn_ops; +	card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;  	card->info.broadcast_capable = 1;  	qeth_l2_request_initial_mac(card);  	SET_NETDEV_DEV(card->dev, &card->gdev->dev); @@ -975,7 +986,6 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)  	int rc = 0;  	enum qeth_card_states recover_flag; -	BUG_ON(!card);  	mutex_lock(&card->discipline_mutex);  	mutex_lock(&card->conf_mutex);  	QETH_DBF_TEXT(SETUP, 2, "setonlin"); @@ -988,6 +998,11 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)  		rc = -ENODEV;  		goto out_remove;  	} +	qeth_bridgeport_query_support(card); +	if (card->options.sbp.supported_funcs) +		dev_info(&card->gdev->dev, +		"The device represents a HiperSockets Bridge Capable Port\n"); +	qeth_trace_features(card);  	if (!card->dev && qeth_l2_setup_netdev(card)) {  		rc = -ENODEV; @@ -997,6 +1012,15 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)  	if (card->info.type != QETH_CARD_TYPE_OSN)  		qeth_l2_send_setmac(card, &card->dev->dev_addr[0]); +	if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { +		if (card->info.hwtrap && +		    qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM)) +			card->info.hwtrap = 0; +	} else +		card->info.hwtrap = 0; + +	qeth_l2_setup_bridgeport_attrs(card); +  	card->state = CARD_STATE_HARDSETUP;  	memset(&card->rx, 0, sizeof(struct qeth_rx));  	qeth_print_status_message(card); @@ -1020,13 +1044,18 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)  contin:  	if ((card->info.type == QETH_CARD_TYPE_OSD) || -	    (card->info.type == QETH_CARD_TYPE_OSX)) +	    (card->info.type == QETH_CARD_TYPE_OSX)) {  		/* configure isolation level */ -		qeth_set_access_ctrl_online(card); +		rc = qeth_set_access_ctrl_online(card, 0); +		if (rc) { +			rc = -ENODEV; +			goto out_remove; +		} +	}  	if (card->info.type != QETH_CARD_TYPE_OSN &&  	    card->info.type != QETH_CARD_TYPE_OSM) -		qeth_l2_process_vlans(card, 0); +		qeth_l2_process_vlans(card);  	netif_tx_disable(card->dev); @@ -1046,7 +1075,7 @@ contin:  	if (recover_flag == CARD_STATE_RECOVER) {  		if (recovery_mode &&  		    card->info.type != QETH_CARD_TYPE_OSN) { -			qeth_l2_open(card->dev); +			__qeth_l2_open(card->dev);  		} else {  			rtnl_lock();  			dev_open(card->dev); @@ -1062,11 +1091,11 @@ contin:  	return 0;  out_remove: -	card->use_hard_stop = 1;  	qeth_l2_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)); +	qdio_free(CARD_DDEV(card));  	if (recover_flag == CARD_STATE_RECOVER)  		card->state = CARD_STATE_RECOVER;  	else @@ -1096,6 +1125,10 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev,  	if (card->dev && netif_carrier_ok(card->dev))  		netif_carrier_off(card->dev);  	recover_flag = card->state; +	if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) { +		qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); +		card->info.hwtrap = 1; +	}  	qeth_l2_stop_card(card, recovery_mode);  	rc  = ccw_device_set_offline(CARD_DDEV(card));  	rc2 = ccw_device_set_offline(CARD_WDEV(card)); @@ -1104,6 +1137,7 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev,  		rc = (rc2) ? rc2 : rc3;  	if (rc)  		QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); +	qdio_free(CARD_DDEV(card));  	if (recover_flag == CARD_STATE_UP)  		card->state = CARD_STATE_RECOVER;  	/* let user_space know that device is offline */ @@ -1130,19 +1164,18 @@ static int qeth_l2_recover(void *ptr)  	QETH_CARD_TEXT(card, 2, "recover2");  	dev_warn(&card->gdev->dev,  		"A recovery process has been started for the device\n"); -	card->use_hard_stop = 1; +	qeth_set_recovery_task(card);  	__qeth_l2_set_offline(card->gdev, 1);  	rc = __qeth_l2_set_online(card->gdev, 1);  	if (!rc)  		dev_info(&card->gdev->dev,  			"Device successfully recovered!\n");  	else { -		rtnl_lock(); -		dev_close(card->dev); -		rtnl_unlock(); +		qeth_close_dev(card);  		dev_warn(&card->gdev->dev, "The qeth device driver " -			"failed to recover an error on the device\n"); +				"failed to recover an error on the device\n");  	} +	qeth_clear_recovery_task(card);  	qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);  	qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);  	return 0; @@ -1162,8 +1195,12 @@ static void __exit qeth_l2_exit(void)  static void qeth_l2_shutdown(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); +	qeth_set_allowed_threads(card, 0, 1); +	if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap) +		qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);  	qeth_qdio_clear_card(card, 0);  	qeth_clear_qdio_buffers(card); +	qdio_free(CARD_DDEV(card));  }  static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev) @@ -1177,7 +1214,8 @@ static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev)  	if (gdev->state == CCWGROUP_OFFLINE)  		return 0;  	if (card->state == CARD_STATE_UP) { -		card->use_hard_stop = 1; +		if (card->info.hwtrap) +			qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);  		__qeth_l2_set_offline(card->gdev, 1);  	} else  		__qeth_l2_set_offline(card->gdev, 0); @@ -1211,8 +1249,32 @@ out:  	return rc;  } -struct ccwgroup_driver qeth_l2_ccwgroup_driver = { -	.probe = qeth_l2_probe_device, +/* Returns zero if the command is successfully "consumed" */ +static int qeth_l2_control_event(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd) +{ +	switch (cmd->hdr.command) { +	case IPA_CMD_SETBRIDGEPORT: +		if (cmd->data.sbp.hdr.command_code == +				IPA_SBP_BRIDGE_PORT_STATE_CHANGE) { +			qeth_bridge_state_change(card, cmd); +			return 0; +		} else +			return 1; +	case IPA_CMD_ADDRESS_CHANGE_NOTIF: +		qeth_bridge_host_event(card, cmd); +		return 0; +	default: +		return 1; +	} +} + +struct qeth_discipline qeth_l2_discipline = { +	.start_poll = qeth_qdio_start_poll, +	.input_handler = (qdio_handler_t *) qeth_qdio_input_handler, +	.output_handler = (qdio_handler_t *) qeth_qdio_output_handler, +	.recover = qeth_l2_recover, +	.setup = qeth_l2_probe_device,  	.remove = qeth_l2_remove_device,  	.set_online = qeth_l2_set_online,  	.set_offline = qeth_l2_set_offline, @@ -1220,8 +1282,9 @@ struct ccwgroup_driver qeth_l2_ccwgroup_driver = {  	.freeze = qeth_l2_pm_suspend,  	.thaw = qeth_l2_pm_resume,  	.restore = qeth_l2_pm_resume, +	.control_event_handler = qeth_l2_control_event,  }; -EXPORT_SYMBOL_GPL(qeth_l2_ccwgroup_driver); +EXPORT_SYMBOL_GPL(qeth_l2_discipline);  static int qeth_osn_send_control_data(struct qeth_card *card, int len,  			   struct qeth_cmd_buffer *iob) @@ -1326,6 +1389,594 @@ void qeth_osn_deregister(struct net_device *dev)  }  EXPORT_SYMBOL(qeth_osn_deregister); +/* SETBRIDGEPORT support, async notifications */ + +enum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset}; + +/** + * qeth_bridge_emit_host_event() - bridgeport address change notification + * @card:  qeth_card structure pointer, for udev events. + * @evtype:  "normal" register/unregister, or abort, or reset. For abort + *	      and reset token and addr_lnid are unused and may be NULL. + * @code:  event bitmask: high order bit 0x80 value 1 means removal of an + *			  object, 0 - addition of an object. + *			  0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC. + * @token: "network token" structure identifying physical address of the port. + * @addr_lnid: pointer to structure with MAC address and VLAN ID. + * + * This function is called when registrations and deregistrations are + * reported by the hardware, and also when notifications are enabled - + * for all currently registered addresses. + */ +static void qeth_bridge_emit_host_event(struct qeth_card *card, +	enum qeth_an_event_type evtype, +	u8 code, struct net_if_token *token, struct mac_addr_lnid *addr_lnid) +{ +	char str[7][32]; +	char *env[8]; +	int i = 0; + +	switch (evtype) { +	case anev_reg_unreg: +		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=%s", +				(code & IPA_ADDR_CHANGE_CODE_REMOVAL) +				? "deregister" : "register"); +		env[i] = str[i]; i++; +		if (code & IPA_ADDR_CHANGE_CODE_VLANID) { +			snprintf(str[i], sizeof(str[i]), "VLAN=%d", +				addr_lnid->lnid); +			env[i] = str[i]; i++; +		} +		if (code & IPA_ADDR_CHANGE_CODE_MACADDR) { +			snprintf(str[i], sizeof(str[i]), "MAC=%pM6", +				&addr_lnid->mac); +			env[i] = str[i]; i++; +		} +		snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x", +			token->cssid, token->ssid, token->devnum); +		env[i] = str[i]; i++; +		snprintf(str[i], sizeof(str[i]), "NTOK_IID=%02x", token->iid); +		env[i] = str[i]; i++; +		snprintf(str[i], sizeof(str[i]), "NTOK_CHPID=%02x", +				token->chpid); +		env[i] = str[i]; i++; +		snprintf(str[i], sizeof(str[i]), "NTOK_CHID=%04x", token->chid); +		env[i] = str[i]; i++; +		break; +	case anev_abort: +		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=abort"); +		env[i] = str[i]; i++; +		break; +	case anev_reset: +		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=reset"); +		env[i] = str[i]; i++; +		break; +	} +	env[i] = NULL; +	kobject_uevent_env(&card->gdev->dev.kobj, KOBJ_CHANGE, env); +} + +struct qeth_bridge_state_data { +	struct work_struct worker; +	struct qeth_card *card; +	struct qeth_sbp_state_change qports; +}; + +static void qeth_bridge_state_change_worker(struct work_struct *work) +{ +	struct qeth_bridge_state_data *data = +		container_of(work, struct qeth_bridge_state_data, worker); +	/* We are only interested in the first entry - local port */ +	struct qeth_sbp_port_entry *entry = &data->qports.entry[0]; +	char env_locrem[32]; +	char env_role[32]; +	char env_state[32]; +	char *env[] = { +		env_locrem, +		env_role, +		env_state, +		NULL +	}; + +	/* Role should not change by itself, but if it did, */ +	/* information from the hardware is authoritative.  */ +	mutex_lock(&data->card->conf_mutex); +	data->card->options.sbp.role = entry->role; +	mutex_unlock(&data->card->conf_mutex); + +	snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange"); +	snprintf(env_role, sizeof(env_role), "ROLE=%s", +		(entry->role == QETH_SBP_ROLE_NONE) ? "none" : +		(entry->role == QETH_SBP_ROLE_PRIMARY) ? "primary" : +		(entry->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" : +		"<INVALID>"); +	snprintf(env_state, sizeof(env_state), "STATE=%s", +		(entry->state == QETH_SBP_STATE_INACTIVE) ? "inactive" : +		(entry->state == QETH_SBP_STATE_STANDBY) ? "standby" : +		(entry->state == QETH_SBP_STATE_ACTIVE) ? "active" : +		"<INVALID>"); +	kobject_uevent_env(&data->card->gdev->dev.kobj, +				KOBJ_CHANGE, env); +	kfree(data); +} + +static void qeth_bridge_state_change(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd) +{ +	struct qeth_sbp_state_change *qports = +		 &cmd->data.sbp.data.state_change; +	struct qeth_bridge_state_data *data; +	int extrasize; + +	QETH_CARD_TEXT(card, 2, "brstchng"); +	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { +		QETH_CARD_TEXT_(card, 2, "BPsz%.8d", qports->entry_length); +		return; +	} +	extrasize = sizeof(struct qeth_sbp_port_entry) * qports->num_entries; +	data = kzalloc(sizeof(struct qeth_bridge_state_data) + extrasize, +		GFP_ATOMIC); +	if (!data) { +		QETH_CARD_TEXT(card, 2, "BPSalloc"); +		return; +	} +	INIT_WORK(&data->worker, qeth_bridge_state_change_worker); +	data->card = card; +	memcpy(&data->qports, qports, +			sizeof(struct qeth_sbp_state_change) + extrasize); +	queue_work(qeth_wq, &data->worker); +} + +struct qeth_bridge_host_data { +	struct work_struct worker; +	struct qeth_card *card; +	struct qeth_ipacmd_addr_change hostevs; +}; + +static void qeth_bridge_host_event_worker(struct work_struct *work) +{ +	struct qeth_bridge_host_data *data = +		container_of(work, struct qeth_bridge_host_data, worker); +	int i; + +	if (data->hostevs.lost_event_mask) { +		dev_info(&data->card->gdev->dev, +"Address notification from the HiperSockets Bridge Port stopped %s (%s)\n", +			data->card->dev->name, +			(data->hostevs.lost_event_mask == 0x01) +			? "Overflow" +			: (data->hostevs.lost_event_mask == 0x02) +			? "Bridge port state change" +			: "Unknown reason"); +		mutex_lock(&data->card->conf_mutex); +		data->card->options.sbp.hostnotification = 0; +		mutex_unlock(&data->card->conf_mutex); +		qeth_bridge_emit_host_event(data->card, anev_abort, +			0, NULL, NULL); +	} else +		for (i = 0; i < data->hostevs.num_entries; i++) { +			struct qeth_ipacmd_addr_change_entry *entry = +					&data->hostevs.entry[i]; +			qeth_bridge_emit_host_event(data->card, +					anev_reg_unreg, +					entry->change_code, +					&entry->token, &entry->addr_lnid); +		} +	kfree(data); +} + +static void qeth_bridge_host_event(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd) +{ +	struct qeth_ipacmd_addr_change *hostevs = +		 &cmd->data.addrchange; +	struct qeth_bridge_host_data *data; +	int extrasize; + +	QETH_CARD_TEXT(card, 2, "brhostev"); +	if (cmd->hdr.return_code != 0x0000) { +		if (cmd->hdr.return_code == 0x0010) { +			if (hostevs->lost_event_mask == 0x00) +				hostevs->lost_event_mask = 0xff; +		} else { +			QETH_CARD_TEXT_(card, 2, "BPHe%04x", +				cmd->hdr.return_code); +			return; +		} +	} +	extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) * +						hostevs->num_entries; +	data = kzalloc(sizeof(struct qeth_bridge_host_data) + extrasize, +		GFP_ATOMIC); +	if (!data) { +		QETH_CARD_TEXT(card, 2, "BPHalloc"); +		return; +	} +	INIT_WORK(&data->worker, qeth_bridge_host_event_worker); +	data->card = card; +	memcpy(&data->hostevs, hostevs, +			sizeof(struct qeth_ipacmd_addr_change) + extrasize); +	queue_work(qeth_wq, &data->worker); +} + +/* SETBRIDGEPORT support; sending commands */ + +struct _qeth_sbp_cbctl { +	u16 ipa_rc; +	u16 cmd_rc; +	union { +		u32 supported; +		struct { +			enum qeth_sbp_roles *role; +			enum qeth_sbp_states *state; +		} qports; +	} data; +}; + +/** + * qeth_bridgeport_makerc() - derive "traditional" error from hardware codes. + * @card:		      qeth_card structure pointer, for debug messages. + * @cbctl:		      state structure with hardware return codes. + * @setcmd:		      IPA command code + * + * Returns negative errno-compatible error indication or 0 on success. + */ +static int qeth_bridgeport_makerc(struct qeth_card *card, +	struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd) +{ +	int rc; + +	switch (cbctl->ipa_rc) { +	case IPA_RC_SUCCESS: +		switch (cbctl->cmd_rc) { +		case 0x0000: +			rc = 0; +			break; +		case 0x0004: +			rc = -ENOSYS; +			break; +		case 0x000C: /* Not configured as bridge Port */ +			rc = -ENODEV; /* maybe not the best code here? */ +			dev_err(&card->gdev->dev, +	"The HiperSockets device is not configured as a Bridge Port\n"); +			break; +		case 0x0014: /* Another device is Primary */ +			switch (setcmd) { +			case IPA_SBP_SET_PRIMARY_BRIDGE_PORT: +				rc = -EEXIST; +				dev_err(&card->gdev->dev, +	"The HiperSockets LAN already has a primary Bridge Port\n"); +				break; +			case IPA_SBP_SET_SECONDARY_BRIDGE_PORT: +				rc = -EBUSY; +				dev_err(&card->gdev->dev, +	"The HiperSockets device is already a primary Bridge Port\n"); +				break; +			default: +				rc = -EIO; +			} +			break; +		case 0x0018: /* This device is currently Secondary */ +			rc = -EBUSY; +			dev_err(&card->gdev->dev, +	"The HiperSockets device is already a secondary Bridge Port\n"); +			break; +		case 0x001C: /* Limit for Secondary devices reached */ +			rc = -EEXIST; +			dev_err(&card->gdev->dev, +	"The HiperSockets LAN cannot have more secondary Bridge Ports\n"); +			break; +		case 0x0024: /* This device is currently Primary */ +			rc = -EBUSY; +			dev_err(&card->gdev->dev, +	"The HiperSockets device is already a primary Bridge Port\n"); +			break; +		case 0x0020: /* Not authorized by zManager */ +			rc = -EACCES; +			dev_err(&card->gdev->dev, +	"The HiperSockets device is not authorized to be a Bridge Port\n"); +			break; +		default: +			rc = -EIO; +		} +		break; +	case IPA_RC_NOTSUPP: +		rc = -ENOSYS; +		break; +	case IPA_RC_UNSUPPORTED_COMMAND: +		rc = -ENOSYS; +		break; +	default: +		rc = -EIO; +	} +	if (rc) { +		QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc); +		QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc); +	} +	return rc; +} + +static int qeth_bridgeport_query_support_cb(struct qeth_card *card, +	struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; +	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; +	QETH_CARD_TEXT(card, 2, "brqsupcb"); +	cbctl->ipa_rc = cmd->hdr.return_code; +	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; +	if ((cbctl->ipa_rc == 0) && (cbctl->cmd_rc == 0)) { +		cbctl->data.supported = +			cmd->data.sbp.data.query_cmds_supp.supported_cmds; +	} else { +		cbctl->data.supported = 0; +	} +	return 0; +} + +/** + * qeth_bridgeport_query_support() - store bitmask of supported subfunctions. + * @card:			     qeth_card structure pointer. + * + * Sets bitmask of supported setbridgeport subfunctions in the qeth_card + * strucutre: card->options.sbp.supported_funcs. + */ +static void qeth_bridgeport_query_support(struct qeth_card *card) +{ +	struct qeth_cmd_buffer *iob; +	struct qeth_ipa_cmd *cmd; +	struct _qeth_sbp_cbctl cbctl; + +	QETH_CARD_TEXT(card, 2, "brqsuppo"); +	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); +	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); +	cmd->data.sbp.hdr.cmdlength = +		sizeof(struct qeth_ipacmd_sbp_hdr) + +		sizeof(struct qeth_sbp_query_cmds_supp); +	cmd->data.sbp.hdr.command_code = +		IPA_SBP_QUERY_COMMANDS_SUPPORTED; +	cmd->data.sbp.hdr.used_total = 1; +	cmd->data.sbp.hdr.seq_no = 1; +	if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb, +							(void *)&cbctl) || +	    qeth_bridgeport_makerc(card, &cbctl, +					IPA_SBP_QUERY_COMMANDS_SUPPORTED)) { +		/* non-zero makerc signifies failure, and produce messages */ +		card->options.sbp.role = QETH_SBP_ROLE_NONE; +		return; +	} +	card->options.sbp.supported_funcs = cbctl.data.supported; +} + +static int qeth_bridgeport_query_ports_cb(struct qeth_card *card, +	struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; +	struct qeth_sbp_query_ports *qports = &cmd->data.sbp.data.query_ports; +	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; + +	QETH_CARD_TEXT(card, 2, "brqprtcb"); +	cbctl->ipa_rc = cmd->hdr.return_code; +	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; +	if ((cbctl->ipa_rc != 0) || (cbctl->cmd_rc != 0)) +		return 0; +	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { +		cbctl->cmd_rc = 0xffff; +		QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length); +		return 0; +	} +	/* first entry contains the state of the local port */ +	if (qports->num_entries > 0) { +		if (cbctl->data.qports.role) +			*cbctl->data.qports.role = qports->entry[0].role; +		if (cbctl->data.qports.state) +			*cbctl->data.qports.state = qports->entry[0].state; +	} +	return 0; +} + +/** + * qeth_bridgeport_query_ports() - query local bridgeport status. + * @card:			   qeth_card structure pointer. + * @role:   Role of the port: 0-none, 1-primary, 2-secondary. + * @state:  State of the port: 0-inactive, 1-standby, 2-active. + * + * Returns negative errno-compatible error indication or 0 on success. + * + * 'role' and 'state' are not updated in case of hardware operation failure. + */ +int qeth_bridgeport_query_ports(struct qeth_card *card, +	enum qeth_sbp_roles *role, enum qeth_sbp_states *state) +{ +	int rc = 0; +	struct qeth_cmd_buffer *iob; +	struct qeth_ipa_cmd *cmd; +	struct _qeth_sbp_cbctl cbctl = { +		.data = { +			.qports = { +				.role = role, +				.state = state, +			}, +		}, +	}; + +	QETH_CARD_TEXT(card, 2, "brqports"); +	if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS)) +		return -EOPNOTSUPP; +	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); +	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); +	cmd->data.sbp.hdr.cmdlength = +		sizeof(struct qeth_ipacmd_sbp_hdr); +	cmd->data.sbp.hdr.command_code = +		IPA_SBP_QUERY_BRIDGE_PORTS; +	cmd->data.sbp.hdr.used_total = 1; +	cmd->data.sbp.hdr.seq_no = 1; +	rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb, +				(void *)&cbctl); +	if (rc) +		return rc; +	rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS); +	if (rc) +		return rc; +	return 0; +} +EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports); + +static int qeth_bridgeport_set_cb(struct qeth_card *card, +	struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; +	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; +	QETH_CARD_TEXT(card, 2, "brsetrcb"); +	cbctl->ipa_rc = cmd->hdr.return_code; +	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; +	return 0; +} + +/** + * qeth_bridgeport_setrole() - Assign primary role to the port. + * @card:		       qeth_card structure pointer. + * @role:		       Role to assign. + * + * Returns negative errno-compatible error indication or 0 on success. + */ +int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role) +{ +	int rc = 0; +	int cmdlength; +	struct qeth_cmd_buffer *iob; +	struct qeth_ipa_cmd *cmd; +	struct _qeth_sbp_cbctl cbctl; +	enum qeth_ipa_sbp_cmd setcmd; + +	QETH_CARD_TEXT(card, 2, "brsetrol"); +	switch (role) { +	case QETH_SBP_ROLE_NONE: +		setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE; +		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) + +			sizeof(struct qeth_sbp_reset_role); +		break; +	case QETH_SBP_ROLE_PRIMARY: +		setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT; +		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) + +			sizeof(struct qeth_sbp_set_primary); +		break; +	case QETH_SBP_ROLE_SECONDARY: +		setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT; +		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) + +			sizeof(struct qeth_sbp_set_secondary); +		break; +	default: +		return -EINVAL; +	} +	if (!(card->options.sbp.supported_funcs & setcmd)) +		return -EOPNOTSUPP; +	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); +	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); +	cmd->data.sbp.hdr.cmdlength = cmdlength; +	cmd->data.sbp.hdr.command_code = setcmd; +	cmd->data.sbp.hdr.used_total = 1; +	cmd->data.sbp.hdr.seq_no = 1; +	rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, +				(void *)&cbctl); +	if (rc) +		return rc; +	rc = qeth_bridgeport_makerc(card, &cbctl, setcmd); +	return rc; +} + +/** + * qeth_anset_makerc() - derive "traditional" error from hardware codes. + * @card:		      qeth_card structure pointer, for debug messages. + * + * Returns negative errno-compatible error indication or 0 on success. + */ +static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response) +{ +	int rc; + +	if (pnso_rc == 0) +		switch (response) { +		case 0x0001: +			rc = 0; +			break; +		case 0x0004: +		case 0x0100: +		case 0x0106: +			rc = -ENOSYS; +			dev_err(&card->gdev->dev, +				"Setting address notification failed\n"); +			break; +		case 0x0107: +			rc = -EAGAIN; +			break; +		default: +			rc = -EIO; +		} +	else +		rc = -EIO; + +	if (rc) { +		QETH_CARD_TEXT_(card, 2, "SBPp%04x", pnso_rc); +		QETH_CARD_TEXT_(card, 2, "SBPr%04x", response); +	} +	return rc; +} + +static void qeth_bridgeport_an_set_cb(void *priv, +		enum qdio_brinfo_entry_type type, void *entry) +{ +	struct qeth_card *card = (struct qeth_card *)priv; +	struct qdio_brinfo_entry_l2 *l2entry; +	u8 code; + +	if (type != l2_addr_lnid) { +		WARN_ON_ONCE(1); +		return; +	} + +	l2entry = (struct qdio_brinfo_entry_l2 *)entry; +	code = IPA_ADDR_CHANGE_CODE_MACADDR; +	if (l2entry->addr_lnid.lnid) +		code |= IPA_ADDR_CHANGE_CODE_VLANID; +	qeth_bridge_emit_host_event(card, anev_reg_unreg, code, +		(struct net_if_token *)&l2entry->nit, +		(struct mac_addr_lnid *)&l2entry->addr_lnid); +} + +/** + * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification + * @card:		      qeth_card structure pointer. + * @enable:		      0 - disable, non-zero - enable notifications + * + * Returns negative errno-compatible error indication or 0 on success. + * + * On enable, emits a series of address notifications udev events for all + * currently registered hosts. + */ +int qeth_bridgeport_an_set(struct qeth_card *card, int enable) +{ +	int rc; +	u16 response; +	struct ccw_device *ddev; +	struct subchannel_id schid; + +	if (!card) +		return -EINVAL; +	if (!card->options.sbp.supported_funcs) +		return -EOPNOTSUPP; +	ddev = CARD_DDEV(card); +	ccw_device_get_schid(ddev, &schid); + +	if (enable) { +		qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL); +		rc = qdio_pnso_brinfo(schid, 1, &response, +			qeth_bridgeport_an_set_cb, card); +	} else +		rc = qdio_pnso_brinfo(schid, 0, &response, NULL, NULL); +	return qeth_anset_makerc(card, rc, response); +} +EXPORT_SYMBOL_GPL(qeth_bridgeport_an_set); +  module_init(qeth_l2_init);  module_exit(qeth_l2_exit);  MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c new file mode 100644 index 00000000000..ae1bc04b865 --- /dev/null +++ b/drivers/s390/net/qeth_l2_sys.c @@ -0,0 +1,223 @@ +/* + *    Copyright IBM Corp. 2013 + *    Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> + */ + +#include <linux/slab.h> +#include <asm/ebcdic.h> +#include "qeth_l2.h" + +#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \ +struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store) + +static int qeth_card_hw_is_reachable(struct qeth_card *card) +{ +	return (card->state == CARD_STATE_SOFTSETUP) || +		(card->state == CARD_STATE_UP); +} + +static ssize_t qeth_bridge_port_role_state_show(struct device *dev, +				struct device_attribute *attr, char *buf, +				int show_state) +{ +	struct qeth_card *card = dev_get_drvdata(dev); +	enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE; +	int rc = 0; +	char *word; + +	if (!card) +		return -EINVAL; + +	mutex_lock(&card->conf_mutex); + +	if (qeth_card_hw_is_reachable(card) && +					card->options.sbp.supported_funcs) +		rc = qeth_bridgeport_query_ports(card, +			&card->options.sbp.role, &state); +	if (!rc) { +		if (show_state) +			switch (state) { +			case QETH_SBP_STATE_INACTIVE: +				word = "inactive"; break; +			case QETH_SBP_STATE_STANDBY: +				word = "standby"; break; +			case QETH_SBP_STATE_ACTIVE: +				word = "active"; break; +			default: +				rc = -EIO; +			} +		else +			switch (card->options.sbp.role) { +			case QETH_SBP_ROLE_NONE: +				word = "none"; break; +			case QETH_SBP_ROLE_PRIMARY: +				word = "primary"; break; +			case QETH_SBP_ROLE_SECONDARY: +				word = "secondary"; break; +			default: +				rc = -EIO; +			} +		if (rc) +			QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x", +				card->options.sbp.role, state); +		else +			rc = sprintf(buf, "%s\n", word); +	} + +	mutex_unlock(&card->conf_mutex); + +	return rc; +} + +static ssize_t qeth_bridge_port_role_show(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	return qeth_bridge_port_role_state_show(dev, attr, buf, 0); +} + +static ssize_t qeth_bridge_port_role_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	struct qeth_card *card = dev_get_drvdata(dev); +	int rc = 0; +	enum qeth_sbp_roles role; + +	if (!card) +		return -EINVAL; +	if (sysfs_streq(buf, "primary")) +		role = QETH_SBP_ROLE_PRIMARY; +	else if (sysfs_streq(buf, "secondary")) +		role = QETH_SBP_ROLE_SECONDARY; +	else if (sysfs_streq(buf, "none")) +		role = QETH_SBP_ROLE_NONE; +	else +		return -EINVAL; + +	mutex_lock(&card->conf_mutex); + +	if (qeth_card_hw_is_reachable(card)) { +		rc = qeth_bridgeport_setrole(card, role); +		if (!rc) +			card->options.sbp.role = role; +	} else +		card->options.sbp.role = role; + +	mutex_unlock(&card->conf_mutex); + +	return rc ? rc : count; +} + +static DEVICE_ATTR(bridge_role, 0644, qeth_bridge_port_role_show, +		   qeth_bridge_port_role_store); + +static ssize_t qeth_bridge_port_state_show(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	return qeth_bridge_port_role_state_show(dev, attr, buf, 1); +} + +static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, +		   NULL); + +static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	struct qeth_card *card = dev_get_drvdata(dev); +	int enabled; + +	if (!card) +		return -EINVAL; + +	mutex_lock(&card->conf_mutex); + +	enabled = card->options.sbp.hostnotification; + +	mutex_unlock(&card->conf_mutex); + +	return sprintf(buf, "%d\n", enabled); +} + +static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	struct qeth_card *card = dev_get_drvdata(dev); +	int rc = 0; +	int enable; + +	if (!card) +		return -EINVAL; + +	if (sysfs_streq(buf, "0")) +		enable = 0; +	else if (sysfs_streq(buf, "1")) +		enable = 1; +	else +		return -EINVAL; + +	mutex_lock(&card->conf_mutex); + +	if (qeth_card_hw_is_reachable(card)) { +		rc = qeth_bridgeport_an_set(card, enable); +		if (!rc) +			card->options.sbp.hostnotification = enable; +	} else +		card->options.sbp.hostnotification = enable; + +	mutex_unlock(&card->conf_mutex); + +	return rc ? rc : count; +} + +static DEVICE_ATTR(bridge_hostnotify, 0644, +			qeth_bridgeport_hostnotification_show, +			qeth_bridgeport_hostnotification_store); + +static struct attribute *qeth_l2_bridgeport_attrs[] = { +	&dev_attr_bridge_role.attr, +	&dev_attr_bridge_state.attr, +	&dev_attr_bridge_hostnotify.attr, +	NULL, +}; + +static struct attribute_group qeth_l2_bridgeport_attr_group = { +	.attrs = qeth_l2_bridgeport_attrs, +}; + +int qeth_l2_create_device_attributes(struct device *dev) +{ +	return sysfs_create_group(&dev->kobj, &qeth_l2_bridgeport_attr_group); +} + +void qeth_l2_remove_device_attributes(struct device *dev) +{ +	sysfs_remove_group(&dev->kobj, &qeth_l2_bridgeport_attr_group); +} + +/** + * qeth_l2_setup_bridgeport_attrs() - set/restore attrs when turning online. + * @card:			      qeth_card structure pointer + * + * Note: this function is called with conf_mutex held by the caller + */ +void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) +{ +	int rc; + +	if (!card) +		return; +	if (!card->options.sbp.supported_funcs) +		return; +	if (card->options.sbp.role != QETH_SBP_ROLE_NONE) { +		/* Conditional to avoid spurious error messages */ +		qeth_bridgeport_setrole(card, card->options.sbp.role); +		/* Let the callback function refresh the stored role value. */ +		qeth_bridgeport_query_ports(card, +			&card->options.sbp.role, NULL); +	} +	if (card->options.sbp.hostnotification) { +		rc = qeth_bridgeport_an_set(card, 1); +		if (rc) +			card->options.sbp.hostnotification = 0; +	} else +		qeth_bridgeport_an_set(card, 0); +} diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index e705b27ec7d..29c1c00e3a0 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -1,6 +1,4 @@  /* - *  drivers/s390/net/qeth_l3.h - *   *    Copyright IBM Corp. 2007   *    Author(s): Utz Bacher <utz.bacher@de.ibm.com>,   *		 Frank Pavlic <fpavlic@de.ibm.com>, @@ -62,8 +60,10 @@ 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);  int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *); +struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions); +int qeth_l3_add_ip(struct qeth_card *, struct qeth_ipaddr *); +int qeth_l3_delete_ip(struct qeth_card *, struct qeth_ipaddr *); +void qeth_l3_set_ip_addr_list(struct qeth_card *);  #endif /* __QETH_L3_H__ */ diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index a1abb37db00..14e0b5810e8 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1,6 +1,4 @@  /* - *  drivers/s390/net/qeth_l3_main.c - *   *    Copyright IBM Corp. 2007, 2009   *    Author(s): Utz Bacher <utz.bacher@de.ibm.com>,   *		 Frank Pavlic <fpavlic@de.ibm.com>, @@ -13,6 +11,7 @@  #include <linux/module.h>  #include <linux/moduleparam.h> +#include <linux/bitops.h>  #include <linux/string.h>  #include <linux/errno.h>  #include <linux/kernel.h> @@ -23,13 +22,18 @@  #include <linux/inetdevice.h>  #include <linux/igmp.h>  #include <linux/slab.h> +#include <linux/if_vlan.h>  #include <net/ip.h>  #include <net/arp.h> +#include <net/route.h> +#include <net/ip6_fib.h>  #include <net/ip6_checksum.h> +#include <net/iucv/af_iucv.h>  #include "qeth_l3.h" +  static int qeth_l3_set_offline(struct ccwgroup_device *);  static int qeth_l3_recover(void *);  static int qeth_l3_stop(struct net_device *); @@ -42,33 +46,6 @@ 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_IP_CSUM; -		} else { -			card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | -					NETIF_F_IP_CSUM); -			card->options.large_send = QETH_LARGE_SEND_NO; -			rc = -EOPNOTSUPP; -		} -	} else { -		card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | -					NETIF_F_IP_CSUM); -		card->options.large_send = QETH_LARGE_SEND_NO; -	} -	return rc; -} -  static int qeth_l3_isxdigit(char *buf)  {  	while (*buf) { @@ -86,7 +63,7 @@ void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf)  int qeth_l3_string_to_ipaddr4(const char *buf, __u8 *addr)  {  	int count = 0, rc = 0; -	int in[4]; +	unsigned int in[4];  	char c;  	rc = sscanf(buf, "%u.%u.%u.%u%c", @@ -291,7 +268,7 @@ static int __qeth_l3_insert_ip_todo(struct qeth_card *card,  	}  } -static int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr) +int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)  {  	unsigned long flags;  	int rc = 0; @@ -310,7 +287,7 @@ static int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)  	return rc;  } -static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr) +int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)  {  	unsigned long flags;  	int rc = 0; @@ -329,7 +306,7 @@ static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)  } -static struct qeth_ipaddr *qeth_l3_get_addr_buffer( +struct qeth_ipaddr *qeth_l3_get_addr_buffer(  				enum qeth_prot_versions prot)  {  	struct qeth_ipaddr *addr; @@ -445,7 +422,7 @@ again:  	list_splice(&fail_list, &card->ip_list);  } -static void qeth_l3_set_ip_addr_list(struct qeth_card *card) +void qeth_l3_set_ip_addr_list(struct qeth_card *card)  {  	struct list_head *tbd_list;  	struct qeth_ipaddr *todo, *addr; @@ -455,11 +432,14 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)  	QETH_CARD_TEXT(card, 2, "sdiplist");  	QETH_CARD_HEX(card, 2, &card, sizeof(void *)); -	if (card->options.sniffer) +	if ((card->state != CARD_STATE_UP && +	     card->state != CARD_STATE_SOFTSETUP) || card->options.sniffer) {  		return; +	} +  	spin_lock_irqsave(&card->ip_lock, flags);  	tbd_list = card->ip_tbd_list; -	card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC); +	card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_ATOMIC);  	if (!card->ip_tbd_list) {  		QETH_CARD_TEXT(card, 0, "silnomem");  		card->ip_tbd_list = tbd_list; @@ -506,8 +486,7 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)  	kfree(tbd_list);  } -static void qeth_l3_clear_ip_list(struct qeth_card *card, int clean, -					int recover) +static void qeth_l3_clear_ip_list(struct qeth_card *card, int recover)  {  	struct qeth_ipaddr *addr, *tmp;  	unsigned long flags; @@ -526,11 +505,6 @@ static void qeth_l3_clear_ip_list(struct qeth_card *card, int clean,  		addr = list_entry(card->ip_list.next,  				  struct qeth_ipaddr, entry);  		list_del_init(&addr->entry); -		if (clean) { -			spin_unlock_irqrestore(&card->ip_lock, flags); -			qeth_l3_deregister_addr_entry(card, addr); -			spin_lock_irqsave(&card->ip_lock, flags); -		}  		if (!recover || addr->is_multicast) {  			kfree(addr);  			continue; @@ -649,7 +623,7 @@ static int qeth_l3_send_setrouting(struct qeth_card *card,  	return rc;  } -static void qeth_l3_correct_routing_type(struct qeth_card *card, +static int qeth_l3_correct_routing_type(struct qeth_card *card,  		enum qeth_routing_types *type, enum qeth_prot_versions prot)  {  	if (card->info.type == QETH_CARD_TYPE_IQD) { @@ -658,7 +632,7 @@ static void qeth_l3_correct_routing_type(struct qeth_card *card,  		case PRIMARY_CONNECTOR:  		case SECONDARY_CONNECTOR:  		case MULTICAST_ROUTER: -			return; +			return 0;  		default:  			goto out_inval;  		} @@ -667,17 +641,18 @@ static void qeth_l3_correct_routing_type(struct qeth_card *card,  		case NO_ROUTER:  		case PRIMARY_ROUTER:  		case SECONDARY_ROUTER: -			return; +			return 0;  		case MULTICAST_ROUTER:  			if (qeth_is_ipafunc_supported(card, prot,  						      IPA_OSA_MC_ROUTER)) -				return; +				return 0;  		default:  			goto out_inval;  		}  	}  out_inval:  	*type = NO_ROUTER; +	return -EINVAL;  }  int qeth_l3_setrouting_v4(struct qeth_card *card) @@ -686,8 +661,10 @@ int qeth_l3_setrouting_v4(struct qeth_card *card)  	QETH_CARD_TEXT(card, 3, "setrtg4"); -	qeth_l3_correct_routing_type(card, &card->options.route4.type, +	rc = qeth_l3_correct_routing_type(card, &card->options.route4.type,  				  QETH_PROT_IPV4); +	if (rc) +		return rc;  	rc = qeth_l3_send_setrouting(card, card->options.route4.type,  				  QETH_PROT_IPV4); @@ -709,8 +686,10 @@ int qeth_l3_setrouting_v6(struct qeth_card *card)  	if (!qeth_is_supported(card, IPA_IPV6))  		return 0; -	qeth_l3_correct_routing_type(card, &card->options.route6.type, +	rc = qeth_l3_correct_routing_type(card, &card->options.route6.type,  				  QETH_PROT_IPV6); +	if (rc) +		return rc;  	rc = qeth_l3_send_setrouting(card, card->options.route6.type,  				  QETH_PROT_IPV6); @@ -820,6 +799,7 @@ int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,  		rc = -EEXIST;  	spin_unlock_irqrestore(&card->ip_lock, flags);  	if (rc) { +		kfree(ipaddr);  		return rc;  	}  	if (!qeth_l3_add_ip(card, ipaddr)) @@ -884,6 +864,7 @@ int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,  		rc = -EEXIST;  	spin_unlock_irqrestore(&card->ip_lock, flags);  	if (rc) { +		kfree(ipaddr);  		return rc;  	}  	if (!qeth_l3_add_ip(card, ipaddr)) @@ -1000,57 +981,6 @@ static inline u8 qeth_l3_get_qeth_hdr_flags6(int cast_type)  	return ct | QETH_CAST_UNICAST;  } -static int qeth_l3_send_setadp_mode(struct qeth_card *card, __u32 command, -					__u32 mode) -{ -	int rc; -	struct qeth_cmd_buffer *iob; -	struct qeth_ipa_cmd *cmd; - -	QETH_CARD_TEXT(card, 4, "adpmode"); - -	iob = qeth_get_adapter_cmd(card, command, -				   sizeof(struct qeth_ipacmd_setadpparms)); -	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); -	cmd->data.setadapterparms.data.mode = mode; -	rc = qeth_send_ipa_cmd(card, iob, qeth_default_setadapterparms_cb, -			       NULL); -	return rc; -} - -static int qeth_l3_setadapter_hstr(struct qeth_card *card) -{ -	int rc; - -	QETH_CARD_TEXT(card, 4, "adphstr"); - -	if (qeth_adp_supported(card, IPA_SETADP_SET_BROADCAST_MODE)) { -		rc = qeth_l3_send_setadp_mode(card, -					IPA_SETADP_SET_BROADCAST_MODE, -					card->options.broadcast_mode); -		if (rc) -			QETH_DBF_MESSAGE(2, "couldn't set broadcast mode on " -				   "device %s: x%x\n", -				   CARD_BUS_ID(card), rc); -		rc = qeth_l3_send_setadp_mode(card, -					IPA_SETADP_ALTER_MAC_ADDRESS, -					card->options.macaddr_mode); -		if (rc) -			QETH_DBF_MESSAGE(2, "couldn't set macaddr mode on " -				   "device %s: x%x\n", CARD_BUS_ID(card), rc); -		return rc; -	} -	if (card->options.broadcast_mode == QETH_TR_BROADCAST_LOCAL) -		QETH_DBF_MESSAGE(2, "set adapter parameters not available " -			   "to set broadcast mode, using ALLRINGS " -			   "on device %s:\n", CARD_BUS_ID(card)); -	if (card->options.macaddr_mode == QETH_TR_MACADDR_CANONICAL) -		QETH_DBF_MESSAGE(2, "set adapter parameters not available " -			   "to set macaddr mode, using NONCANONICAL " -			   "on device %s:\n", CARD_BUS_ID(card)); -	return 0; -} -  static int qeth_l3_setadapter_parms(struct qeth_card *card)  {  	int rc; @@ -1076,10 +1006,6 @@ static int qeth_l3_setadapter_parms(struct qeth_card *card)  				" address failed\n");  	} -	if ((card->info.link_type == QETH_LINK_TYPE_HSTR) || -	    (card->info.link_type == QETH_LINK_TYPE_LANE_TR)) -		rc = qeth_l3_setadapter_hstr(card); -  	return rc;  } @@ -1306,39 +1232,6 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card)  	return rc;  } -static int qeth_l3_query_ipassists_cb(struct qeth_card *card, -		struct qeth_reply *reply, unsigned long data) -{ -	struct qeth_ipa_cmd *cmd; - -	QETH_DBF_TEXT(SETUP, 2, "qipasscb"); - -	cmd = (struct qeth_ipa_cmd *) data; -	if (cmd->hdr.prot_version == QETH_PROT_IPV4) { -		card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported; -		card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; -	} else { -		card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported; -		card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; -	} -	QETH_DBF_TEXT(SETUP, 2, "suppenbl"); -	QETH_DBF_TEXT_(SETUP, 2, "%x", cmd->hdr.ipa_supported); -	QETH_DBF_TEXT_(SETUP, 2, "%x", cmd->hdr.ipa_enabled); -	return 0; -} - -static int qeth_l3_query_ipassists(struct qeth_card *card, -				enum qeth_prot_versions prot) -{ -	int rc; -	struct qeth_cmd_buffer *iob; - -	QETH_DBF_TEXT_(SETUP, 2, "qipassi%i", prot); -	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_QIPASSIST, prot); -	rc = qeth_send_ipa_cmd(card, iob, qeth_l3_query_ipassists_cb, NULL); -	return rc; -} -  #ifdef CONFIG_QETH_IPV6  static int qeth_l3_softsetup_ipv6(struct qeth_card *card)  { @@ -1349,7 +1242,7 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card)  	if (card->info.type == QETH_CARD_TYPE_IQD)  		goto out; -	rc = qeth_l3_query_ipassists(card, QETH_PROT_IPV6); +	rc = qeth_query_ipassists(card, QETH_PROT_IPV6);  	if (rc) {  		dev_err(&card->gdev->dev,  			"Activating IPv6 support for %s failed\n", @@ -1474,68 +1367,38 @@ 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) +static int qeth_l3_set_rx_csum(struct qeth_card *card, int on)  {  	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; -		} +	if (on) { +		rc = qeth_l3_send_checksum_command(card); +		if (rc) +			return -EIO; +		dev_info(&card->gdev->dev, +			"HW Checksumming (inbound) enabled\n");  	} 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; -			} -		} +		rc = qeth_l3_send_simple_setassparms(card, +			IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0); +		if (rc) +			return -EIO;  	} -	card->options.checksum_type = csum_type; -	return rc; + +	return 0;  }  static int qeth_l3_start_ipa_checksum(struct qeth_card *card)  { -	int rc = 0; -  	QETH_CARD_TEXT(card, 3, "strtcsum"); -	if (card->options.checksum_type == NO_CHECKSUMMING) { -		dev_info(&card->gdev->dev, -			"Using no checksumming on %s.\n", -			QETH_CARD_IFNAME(card)); -		return 0; -	} -	if (card->options.checksum_type == SW_CHECKSUMMING) { -		dev_info(&card->gdev->dev, -			"Using SW checksumming on %s.\n", -			QETH_CARD_IFNAME(card)); -		return 0; -	} -	if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) { -		dev_info(&card->gdev->dev, -			"Inbound HW Checksumming not " -			"supported on %s,\ncontinuing " -			"using Inbound SW Checksumming\n", -			QETH_CARD_IFNAME(card)); -		card->options.checksum_type = SW_CHECKSUMMING; -		return 0; +	if (card->dev->features & NETIF_F_RXCSUM) { +		rtnl_lock(); +		/* force set_features call */ +		card->dev->features &= ~NETIF_F_RXCSUM; +		netdev_update_features(card->dev); +		rtnl_unlock();  	} -	rc = qeth_l3_send_checksum_command(card); -	if (!rc) -		dev_info(&card->gdev->dev, -			"HW Checksumming (inbound) enabled\n"); - -	return rc; +	return 0;  }  static int qeth_l3_start_ipa_tx_checksum(struct qeth_card *card) @@ -1582,10 +1445,8 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card)  			dev_info(&card->gdev->dev,  				"Outbound TSO enabled\n");  	} -	if (rc && (card->options.large_send == QETH_LARGE_SEND_TSO)) { -		card->options.large_send = QETH_LARGE_SEND_NO; -		card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG); -	} +	if (rc) +		card->dev->features &= ~NETIF_F_TSO;  	return rc;  } @@ -1593,7 +1454,8 @@ static int qeth_l3_start_ipassists(struct qeth_card *card)  {  	QETH_CARD_TEXT(card, 3, "strtipas"); -	qeth_set_access_ctrl_online(card);	/* go on*/ +	if (qeth_set_access_ctrl_online(card, 0)) +		return -EIO;  	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*/ @@ -1607,29 +1469,6 @@ static int qeth_l3_start_ipassists(struct qeth_card *card)  	return 0;  } -static int qeth_l3_put_unique_id(struct qeth_card *card) -{ - -	int rc = 0; -	struct qeth_cmd_buffer *iob; -	struct qeth_ipa_cmd *cmd; - -	QETH_CARD_TEXT(card, 2, "puniqeid"); - -	if ((card->info.unique_id & UNIQUE_ID_NOT_BY_CARD) == -		UNIQUE_ID_NOT_BY_CARD) -		return -1; -	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_DESTROY_ADDR, -				     QETH_PROT_IPV6); -	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); -	*((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) = -				card->info.unique_id; -	memcpy(&cmd->data.create_destroy_addr.unique_id[0], -	       card->dev->dev_addr, OSA_ADDR_LEN); -	rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); -	return rc; -} -  static int qeth_l3_iqd_read_initial_mac_cb(struct qeth_card *card,  		struct qeth_reply *reply, unsigned long data)  { @@ -1640,7 +1479,7 @@ static int qeth_l3_iqd_read_initial_mac_cb(struct qeth_card *card,  		memcpy(card->dev->dev_addr,  			cmd->data.create_destroy_addr.unique_id, ETH_ALEN);  	else -		random_ether_addr(card->dev->dev_addr); +		eth_random_addr(card->dev->dev_addr);  	return 0;  } @@ -1783,10 +1622,7 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd)  static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac,  				struct net_device *dev)  { -	if (dev->type == ARPHRD_IEEE802_TR) -		ip_tr_mc_map(ipm, mac); -	else -		ip_eth_mc_map(ipm, mac); +	ip_eth_mc_map(ipm, mac);  }  static void qeth_l3_add_mc(struct qeth_card *card, struct in_device *in4_dev) @@ -1810,29 +1646,28 @@ static void qeth_l3_add_mc(struct qeth_card *card, struct in_device *in4_dev)  	}  } +/* called with rcu_read_lock */  static void qeth_l3_add_vlan_mc(struct qeth_card *card)  {  	struct in_device *in_dev; -	struct vlan_group *vg; -	int i; +	u16 vid;  	QETH_CARD_TEXT(card, 4, "addmcvl"); -	if (!qeth_is_supported(card, IPA_FULL_VLAN) || (card->vlangrp == NULL)) +	if (!qeth_is_supported(card, IPA_FULL_VLAN))  		return; -	vg = card->vlangrp; -	for (i = 0; i < VLAN_N_VID; i++) { -		struct net_device *netdev = vlan_group_get_device(vg, i); +	for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { +		struct net_device *netdev; + +		netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), +					      vid);  		if (netdev == NULL ||  		    !(netdev->flags & IFF_UP))  			continue; -		in_dev = in_dev_get(netdev); +		in_dev = __in_dev_get_rcu(netdev);  		if (!in_dev)  			continue; -		rcu_read_lock();  		qeth_l3_add_mc(card, in_dev); -		rcu_read_unlock(); -		in_dev_put(in_dev);  	}  } @@ -1841,14 +1676,14 @@ static void qeth_l3_add_multicast_ipv4(struct qeth_card *card)  	struct in_device *in4_dev;  	QETH_CARD_TEXT(card, 4, "chkmcv4"); -	in4_dev = in_dev_get(card->dev); -	if (in4_dev == NULL) -		return;  	rcu_read_lock(); +	in4_dev = __in_dev_get_rcu(card->dev); +	if (in4_dev == NULL) +		goto unlock;  	qeth_l3_add_mc(card, in4_dev);  	qeth_l3_add_vlan_mc(card); +unlock:  	rcu_read_unlock(); -	in_dev_put(in4_dev);  }  #ifdef CONFIG_QETH_IPV6 @@ -1873,19 +1708,21 @@ static void qeth_l3_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev)  	}  } +/* called with rcu_read_lock */  static void qeth_l3_add_vlan_mc6(struct qeth_card *card)  {  	struct inet6_dev *in_dev; -	struct vlan_group *vg; -	int i; +	u16 vid;  	QETH_CARD_TEXT(card, 4, "admc6vl"); -	if (!qeth_is_supported(card, IPA_FULL_VLAN) || (card->vlangrp == NULL)) +	if (!qeth_is_supported(card, IPA_FULL_VLAN))  		return; -	vg = card->vlangrp; -	for (i = 0; i < VLAN_N_VID; i++) { -		struct net_device *netdev = vlan_group_get_device(vg, i); +	for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { +		struct net_device *netdev; + +		netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), +					      vid);  		if (netdev == NULL ||  		    !(netdev->flags & IFF_UP))  			continue; @@ -1909,10 +1746,12 @@ static void qeth_l3_add_multicast_ipv6(struct qeth_card *card)  	in6_dev = in6_dev_get(card->dev);  	if (in6_dev == NULL)  		return; +	rcu_read_lock();  	read_lock_bh(&in6_dev->lock);  	qeth_l3_add_mc6(card, in6_dev);  	qeth_l3_add_vlan_mc6(card);  	read_unlock_bh(&in6_dev->lock); +	rcu_read_unlock();  	in6_dev_put(in6_dev);  }  #endif /* CONFIG_QETH_IPV6 */ @@ -1923,10 +1762,14 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card,  	struct in_device *in_dev;  	struct in_ifaddr *ifa;  	struct qeth_ipaddr *addr; +	struct net_device *netdev;  	QETH_CARD_TEXT(card, 4, "frvaddr4"); -	in_dev = in_dev_get(vlan_group_get_device(card->vlangrp, vid)); +	netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); +	if (!netdev) +		return; +	in_dev = in_dev_get(netdev);  	if (!in_dev)  		return;  	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { @@ -1949,10 +1792,14 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card,  	struct inet6_dev *in6_dev;  	struct inet6_ifaddr *ifa;  	struct qeth_ipaddr *addr; +	struct net_device *netdev;  	QETH_CARD_TEXT(card, 4, "frvaddr6"); -	in6_dev = in6_dev_get(vlan_group_get_device(card->vlangrp, vid)); +	netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); +	if (!netdev) +		return; +	in6_dev = in6_dev_get(netdev);  	if (!in6_dev)  		return;  	list_for_each_entry(ifa, &in6_dev->addr_list, if_list) { @@ -1973,30 +1820,23 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card,  static void qeth_l3_free_vlan_addresses(struct qeth_card *card,  			unsigned short vid)  { -	if (!card->vlangrp) -		return; +	rcu_read_lock();  	qeth_l3_free_vlan_addresses4(card, vid);  	qeth_l3_free_vlan_addresses6(card, vid); +	rcu_read_unlock();  } -static void qeth_l3_vlan_rx_register(struct net_device *dev, -			struct vlan_group *grp) +static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, +				   __be16 proto, u16 vid)  {  	struct qeth_card *card = dev->ml_priv; -	unsigned long flags; - -	QETH_CARD_TEXT(card, 4, "vlanreg"); -	spin_lock_irqsave(&card->vlanlock, flags); -	card->vlangrp = grp; -	spin_unlock_irqrestore(&card->vlanlock, flags); -} -static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) -{ -	return; +	set_bit(vid, card->active_vlans); +	return 0;  } -static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev, +				    __be16 proto, u16 vid)  {  	struct qeth_card *card = dev->ml_priv;  	unsigned long flags; @@ -2004,14 +1844,15 @@ static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)  	QETH_CARD_TEXT_(card, 4, "kid:%d", vid);  	if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {  		QETH_CARD_TEXT(card, 3, "kidREC"); -		return; +		return 0;  	}  	spin_lock_irqsave(&card->vlanlock, flags);  	/* unregister IP addresses of vlan device */  	qeth_l3_free_vlan_addresses(card, vid); -	vlan_group_set_device(card->vlangrp, vid, NULL); +	clear_bit(vid, card->active_vlans);  	spin_unlock_irqrestore(&card->vlanlock, flags);  	qeth_l3_set_multicast_list(card->dev); +	return 0;  }  static inline int qeth_l3_rebuild_skb(struct qeth_card *card, @@ -2038,8 +1879,6 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card,  #endif  			case __constant_htons(ETH_P_IP):  				ip_hdr = (struct iphdr *)skb->data; -				(card->dev->type == ARPHRD_IEEE802_TR) ? -				ip_tr_mc_map(ip_hdr->daddr, tg_addr):  				ip_eth_mc_map(ip_hdr->daddr, tg_addr);  				break;  			default: @@ -2075,12 +1914,7 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card,  				tg_addr, "FAKELL", card->dev->addr_len);  	} -#ifdef CONFIG_TR -	if (card->dev->type == ARPHRD_IEEE802_TR) -		skb->protocol = tr_type_trans(skb, card->dev); -	else -#endif -		skb->protocol = eth_type_trans(skb, card->dev); +	skb->protocol = eth_type_trans(skb, card->dev);  	if (hdr->hdr.l3.ext_flags &  	    (QETH_HDR_EXT_VLAN_FRAME | QETH_HDR_EXT_INCLUDE_VLAN_TAG)) { @@ -2089,14 +1923,7 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card,  		is_vlan = 1;  	} -	switch (card->options.checksum_type) { -	case SW_CHECKSUMMING: -		skb->ip_summed = CHECKSUM_NONE; -		break; -	case NO_CHECKSUMMING: -		skb->ip_summed = CHECKSUM_UNNECESSARY; -		break; -	case HW_CHECKSUMMING: +	if (card->dev->features & NETIF_F_RXCSUM) {  		if ((hdr->hdr.l3.ext_flags &  		    (QETH_HDR_EXT_CSUM_HDR_REQ |  		     QETH_HDR_EXT_CSUM_TRANSP_REQ)) == @@ -2105,7 +1932,8 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card,  			skb->ip_summed = CHECKSUM_UNNECESSARY;  		else  			skb->ip_summed = CHECKSUM_NONE; -	} +	} else +		skb->ip_summed = CHECKSUM_NONE;  	return is_vlan;  } @@ -2119,12 +1947,13 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,  	__u16 vlan_tag = 0;  	int is_vlan;  	unsigned int len; +	__u16 magic;  	*done = 0; -	BUG_ON(!budget); +	WARN_ON_ONCE(!budget);  	while (budget) {  		skb = qeth_core_get_next_skb(card, -			card->qdio.in_q->bufs[card->rx.b_index].buffer, +			&card->qdio.in_q->bufs[card->rx.b_index],  			&card->rx.b_element, &card->rx.e_offset, &hdr);  		if (!skb) {  			*done = 1; @@ -2133,14 +1962,27 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,  		skb->dev = card->dev;  		switch (hdr->hdr.l3.id) {  		case QETH_HEADER_TYPE_LAYER3: -			is_vlan = qeth_l3_rebuild_skb(card, skb, hdr, +			magic = *(__u16 *)skb->data; +			if ((card->info.type == QETH_CARD_TYPE_IQD) && +			    (magic == ETH_P_AF_IUCV)) { +				skb->protocol = ETH_P_AF_IUCV; +				skb->pkt_type = PACKET_HOST; +				skb->mac_header = NET_SKB_PAD; +				skb->dev = card->dev; +				len = skb->len; +				card->dev->header_ops->create(skb, card->dev, 0, +					card->dev->dev_addr, "FAKELL", +					card->dev->addr_len); +				netif_receive_skb(skb); +			} else { +				is_vlan = qeth_l3_rebuild_skb(card, skb, hdr,  						      &vlan_tag); -			len = skb->len; -			if (is_vlan && !card->options.sniffer) -				vlan_gro_receive(&card->napi, card->vlangrp, -					vlan_tag, skb); -			else +				len = skb->len; +				if (is_vlan && !card->options.sniffer) +					__vlan_hwaccel_put_tag(skb, +						htons(ETH_P_8021Q), vlan_tag);  				napi_gro_receive(&card->napi, skb); +			}  			break;  		case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */  			skb->pkt_type = PACKET_HOST; @@ -2241,15 +2083,16 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev,  			struct qeth_card *card)  {  	int rc = 0; -	struct vlan_group *vg; -	int i; +	u16 vid; -	vg = card->vlangrp; -	if (!vg) -		return rc; +	for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { +		struct net_device *netdev; -	for (i = 0; i < VLAN_N_VID; i++) { -		if (vlan_group_get_device(vg, i) == dev) { +		rcu_read_lock(); +		netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), +					      vid); +		rcu_read_unlock(); +		if (netdev == dev) {  			rc = QETH_VLAN_CARD;  			break;  		} @@ -2320,25 +2163,14 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)  			dev_close(card->dev);  			rtnl_unlock();  		} -		if (!card->use_hard_stop) { -			rc = qeth_send_stoplan(card); -			if (rc) -				QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); -		}  		card->state = CARD_STATE_SOFTSETUP;  	}  	if (card->state == CARD_STATE_SOFTSETUP) { -		qeth_l3_clear_ip_list(card, !card->use_hard_stop, 1); +		qeth_l3_clear_ip_list(card, 1);  		qeth_clear_ipacmd_list(card);  		card->state = CARD_STATE_HARDSETUP;  	}  	if (card->state == CARD_STATE_HARDSETUP) { -		if (!card->use_hard_stop && -		    (card->info.type != QETH_CARD_TYPE_IQD)) { -			rc = qeth_l3_put_unique_id(card); -			if (rc) -				QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); -		}  		qeth_qdio_clear_card(card, 0);  		qeth_clear_qdio_buffers(card);  		qeth_clear_working_pool_list(card); @@ -2348,7 +2180,6 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)  		qeth_clear_cmd_buffers(&card->read);  		qeth_clear_cmd_buffers(&card->write);  	} -	card->use_hard_stop = 0;  	return rc;  } @@ -2455,22 +2286,46 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)  	return rc;  } -static void qeth_l3_copy_arp_entries_stripped(struct qeth_arp_query_info *qinfo, -		struct qeth_arp_query_data *qdata, int entry_size, -		int uentry_size) +static __u32 get_arp_entry_size(struct qeth_card *card, +			struct qeth_arp_query_data *qdata, +			struct qeth_arp_entrytype *type, __u8 strip_entries)  { -	char *entry_ptr; -	char *uentry_ptr; -	int i; +	__u32 rc; +	__u8 is_hsi; -	entry_ptr = (char *)&qdata->data; -	uentry_ptr = (char *)(qinfo->udata + qinfo->udata_offset); -	for (i = 0; i < qdata->no_entries; ++i) { -		/* strip off 32 bytes "media specific information" */ -		memcpy(uentry_ptr, (entry_ptr + 32), entry_size - 32); -		entry_ptr += entry_size; -		uentry_ptr += uentry_size; +	is_hsi = qdata->reply_bits == 5; +	if (type->ip == QETHARP_IP_ADDR_V4) { +		QETH_CARD_TEXT(card, 4, "arpev4"); +		if (strip_entries) { +			rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5_short) : +				sizeof(struct qeth_arp_qi_entry7_short); +		} else { +			rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5) : +				sizeof(struct qeth_arp_qi_entry7); +		} +	} else if (type->ip == QETHARP_IP_ADDR_V6) { +		QETH_CARD_TEXT(card, 4, "arpev6"); +		if (strip_entries) { +			rc = is_hsi ? +				sizeof(struct qeth_arp_qi_entry5_short_ipv6) : +				sizeof(struct qeth_arp_qi_entry7_short_ipv6); +		} else { +			rc = is_hsi ? +				sizeof(struct qeth_arp_qi_entry5_ipv6) : +				sizeof(struct qeth_arp_qi_entry7_ipv6); +		} +	} else { +		QETH_CARD_TEXT(card, 4, "arpinv"); +		rc = 0;  	} + +	return rc; +} + +static int arpentry_matches_prot(struct qeth_arp_entrytype *type, __u16 prot) +{ +	return (type->ip == QETHARP_IP_ADDR_V4 && prot == QETH_PROT_IPV4) || +		(type->ip == QETHARP_IP_ADDR_V6 && prot == QETH_PROT_IPV6);  }  static int qeth_l3_arp_query_cb(struct qeth_card *card, @@ -2479,72 +2334,77 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card,  	struct qeth_ipa_cmd *cmd;  	struct qeth_arp_query_data *qdata;  	struct qeth_arp_query_info *qinfo; -	int entry_size; -	int uentry_size;  	int i; +	int e; +	int entrybytes_done; +	int stripped_bytes; +	__u8 do_strip_entries; -	QETH_CARD_TEXT(card, 4, "arpquecb"); +	QETH_CARD_TEXT(card, 3, "arpquecb");  	qinfo = (struct qeth_arp_query_info *) reply->param;  	cmd = (struct qeth_ipa_cmd *) data; +	QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.prot_version);  	if (cmd->hdr.return_code) { -		QETH_CARD_TEXT_(card, 4, "qaer1%i", cmd->hdr.return_code); +		QETH_CARD_TEXT(card, 4, "arpcberr"); +		QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);  		return 0;  	}  	if (cmd->data.setassparms.hdr.return_code) {  		cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; -		QETH_CARD_TEXT_(card, 4, "qaer2%i", cmd->hdr.return_code); +		QETH_CARD_TEXT(card, 4, "setaperr"); +		QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);  		return 0;  	}  	qdata = &cmd->data.setassparms.data.query_arp; -	switch (qdata->reply_bits) { -	case 5: -		uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry5); -		if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) -			uentry_size = sizeof(struct qeth_arp_qi_entry5_short); -		break; -	case 7: -		/* fall through to default */ -	default: -		/* tr is the same as eth -> entry7 */ -		uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry7); -		if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) -			uentry_size = sizeof(struct qeth_arp_qi_entry7_short); -		break; -	} -	/* check if there is enough room in userspace */ -	if ((qinfo->udata_len - qinfo->udata_offset) < -			qdata->no_entries * uentry_size){ -		QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM); -		cmd->hdr.return_code = -ENOMEM; -		goto out_error; -	} -	QETH_CARD_TEXT_(card, 4, "anore%i", -		       cmd->data.setassparms.hdr.number_of_replies); -	QETH_CARD_TEXT_(card, 4, "aseqn%i", cmd->data.setassparms.hdr.seq_no);  	QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries); -	if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) { -		/* strip off "media specific information" */ -		qeth_l3_copy_arp_entries_stripped(qinfo, qdata, entry_size, -					       uentry_size); -	} else -		/*copy entries to user buffer*/ -		memcpy(qinfo->udata + qinfo->udata_offset, -		       (char *)&qdata->data, qdata->no_entries*uentry_size); +	do_strip_entries = (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) > 0; +	stripped_bytes = do_strip_entries ? QETH_QARP_MEDIASPECIFIC_BYTES : 0; +	entrybytes_done = 0; +	for (e = 0; e < qdata->no_entries; ++e) { +		char *cur_entry; +		__u32 esize; +		struct qeth_arp_entrytype *etype; + +		cur_entry = &qdata->data + entrybytes_done; +		etype = &((struct qeth_arp_qi_entry5 *) cur_entry)->type; +		if (!arpentry_matches_prot(etype, cmd->hdr.prot_version)) { +			QETH_CARD_TEXT(card, 4, "pmis"); +			QETH_CARD_TEXT_(card, 4, "%i", etype->ip); +			break; +		} +		esize = get_arp_entry_size(card, qdata, etype, +			do_strip_entries); +		QETH_CARD_TEXT_(card, 5, "esz%i", esize); +		if (!esize) +			break; -	qinfo->no_entries += qdata->no_entries; -	qinfo->udata_offset += (qdata->no_entries*uentry_size); +		if ((qinfo->udata_len - qinfo->udata_offset) < esize) { +			QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM); +			cmd->hdr.return_code = IPA_RC_ENOMEM; +			goto out_error; +		} + +		memcpy(qinfo->udata + qinfo->udata_offset, +			&qdata->data + entrybytes_done + stripped_bytes, +			esize); +		entrybytes_done += esize + stripped_bytes; +		qinfo->udata_offset += esize; +		++qinfo->no_entries; +	}  	/* check if all replies received ... */  	if (cmd->data.setassparms.hdr.seq_no <  	    cmd->data.setassparms.hdr.number_of_replies)  		return 1; +	QETH_CARD_TEXT_(card, 4, "nove%i", qinfo->no_entries);  	memcpy(qinfo->udata, &qinfo->no_entries, 4);  	/* keep STRIP_ENTRIES flag so the user program can distinguish  	 * stripped entries from normal ones */  	if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)  		qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES;  	memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2); +	QETH_CARD_TEXT_(card, 4, "rc%i", 0);  	return 0;  out_error:  	i = 0; @@ -2567,45 +2427,86 @@ static int qeth_l3_send_ipa_arp_cmd(struct qeth_card *card,  				      reply_cb, reply_param);  } -static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) +static int qeth_l3_query_arp_cache_info(struct qeth_card *card, +	enum qeth_prot_versions prot, +	struct qeth_arp_query_info *qinfo)  {  	struct qeth_cmd_buffer *iob; -	struct qeth_arp_query_info qinfo = {0, }; +	struct qeth_ipa_cmd *cmd;  	int tmp;  	int rc; +	QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot); + +	iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, +			IPA_CMD_ASS_ARP_QUERY_INFO, +			sizeof(struct qeth_arp_query_data) - sizeof(char), +			prot); +	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); +	cmd->data.setassparms.data.query_arp.request_bits = 0x000F; +	cmd->data.setassparms.data.query_arp.reply_bits = 0; +	cmd->data.setassparms.data.query_arp.no_entries = 0; +	rc = qeth_l3_send_ipa_arp_cmd(card, iob, +			   QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN, +			   qeth_l3_arp_query_cb, (void *)qinfo); +	if (rc) { +		tmp = rc; +		QETH_DBF_MESSAGE(2, +			"Error while querying ARP cache on %s: %s " +			"(0x%x/%d)\n", QETH_CARD_IFNAME(card), +			qeth_l3_arp_get_error_cause(&rc), tmp, tmp); +	} + +	return rc; +} + +static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) +{ +	struct qeth_arp_query_info qinfo = {0, }; +	int rc; +  	QETH_CARD_TEXT(card, 3, "arpquery");  	if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/  			       IPA_ARP_PROCESSING)) { -		return -EOPNOTSUPP; +		QETH_CARD_TEXT(card, 3, "arpqnsup"); +		rc = -EOPNOTSUPP; +		goto out;  	}  	/* get size of userspace buffer and mask_bits -> 6 bytes */ -	if (copy_from_user(&qinfo, udata, 6)) -		return -EFAULT; +	if (copy_from_user(&qinfo, udata, 6)) { +		rc = -EFAULT; +		goto out; +	}  	qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); -	if (!qinfo.udata) -		return -ENOMEM; +	if (!qinfo.udata) { +		rc = -ENOMEM; +		goto out; +	}  	qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET; -	iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, -				       IPA_CMD_ASS_ARP_QUERY_INFO, -				       sizeof(int), QETH_PROT_IPV4); - -	rc = qeth_l3_send_ipa_arp_cmd(card, iob, -				   QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN, -				   qeth_l3_arp_query_cb, (void *)&qinfo); +	rc = qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV4, &qinfo);  	if (rc) { -		tmp = rc; -		QETH_DBF_MESSAGE(2, "Error while querying ARP cache on %s: %s " -			"(0x%x/%d)\n", QETH_CARD_IFNAME(card), -			qeth_l3_arp_get_error_cause(&rc), tmp, tmp);  		if (copy_to_user(udata, qinfo.udata, 4))  			rc = -EFAULT; +			goto free_and_out;  	} else { -		if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) +#ifdef CONFIG_QETH_IPV6 +		if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) { +			/* fails in case of GuestLAN QDIO mode */ +			qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6, +				&qinfo); +		} +#endif +		if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) { +			QETH_CARD_TEXT(card, 4, "qactf");  			rc = -EFAULT; +			goto free_and_out; +		} +		QETH_CARD_TEXT_(card, 4, "qacts");  	} +free_and_out:  	kfree(qinfo.udata); +out:  	return rc;  } @@ -2796,6 +2697,9 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)  							mii_data->phy_id,  							mii_data->reg_num);  		break; +	case SIOC_QETH_QUERY_OAT: +		rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data); +		break;  	default:  		rc = -EOPNOTSUPP;  	} @@ -2807,9 +2711,17 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)  int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb)  {  	int cast_type = RTN_UNSPEC; +	struct neighbour *n = NULL; +	struct dst_entry *dst; -	if (skb_dst(skb) && skb_dst(skb)->neighbour) { -		cast_type = skb_dst(skb)->neighbour->type; +	rcu_read_lock(); +	dst = skb_dst(skb); +	if (dst) +		n = dst_neigh_lookup_skb(dst, skb); +	if (n) { +		cast_type = n->type; +		rcu_read_unlock(); +		neigh_release(n);  		if ((cast_type == RTN_BROADCAST) ||  		    (cast_type == RTN_MULTICAST) ||  		    (cast_type == RTN_ANYCAST)) @@ -2817,6 +2729,8 @@ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb)  		else  			return RTN_UNSPEC;  	} +	rcu_read_unlock(); +  	/* try something else */  	if (skb->protocol == ETH_P_IPV6)  		return (skb_network_header(skb)[24] == 0xff) ? @@ -2849,9 +2763,35 @@ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb)  	return cast_type;  } +static void qeth_l3_fill_af_iucv_hdr(struct qeth_card *card, +		struct qeth_hdr *hdr, struct sk_buff *skb) +{ +	char daddr[16]; +	struct af_iucv_trans_hdr *iucv_hdr; + +	skb_pull(skb, 14); +	card->dev->header_ops->create(skb, card->dev, 0, +				      card->dev->dev_addr, card->dev->dev_addr, +				      card->dev->addr_len); +	skb_pull(skb, 14); +	iucv_hdr = (struct af_iucv_trans_hdr *)skb->data; +	memset(hdr, 0, sizeof(struct qeth_hdr)); +	hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; +	hdr->hdr.l3.ext_flags = 0; +	hdr->hdr.l3.length = skb->len; +	hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST; +	memset(daddr, 0, sizeof(daddr)); +	daddr[0] = 0xfe; +	daddr[1] = 0x80; +	memcpy(&daddr[8], iucv_hdr->destUserID, 8); +	memcpy(hdr->hdr.l3.dest_addr, daddr, 16); +} +  static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,  		struct sk_buff *skb, int ipv, int cast_type)  { +	struct dst_entry *dst; +  	memset(hdr, 0, sizeof(struct qeth_hdr));  	hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;  	hdr->hdr.l3.ext_flags = 0; @@ -2860,7 +2800,7 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,  	 * before we're going to overwrite this location with next hop ip.  	 * v6 uses passthrough, v4 sets the tag in the QDIO header.  	 */ -	if (card->vlangrp && vlan_tx_tag_present(skb)) { +	if (vlan_tx_tag_present(skb)) {  		if ((ipv == 4) || (card->info.type == QETH_CARD_TYPE_IQD))  			hdr->hdr.l3.ext_flags = QETH_HDR_EXT_VLAN_FRAME;  		else @@ -2869,39 +2809,34 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,  	}  	hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr); + +	rcu_read_lock(); +	dst = skb_dst(skb);  	if (ipv == 4) { +		struct rtable *rt = (struct rtable *) dst; +		__be32 *pkey = &ip_hdr(skb)->daddr; + +		if (rt->rt_gateway) +			pkey = &rt->rt_gateway; +  		/* IPv4 */  		hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags4(cast_type);  		memset(hdr->hdr.l3.dest_addr, 0, 12); -		if ((skb_dst(skb)) && (skb_dst(skb)->neighbour)) { -			*((u32 *) (&hdr->hdr.l3.dest_addr[12])) = -			    *((u32 *) skb_dst(skb)->neighbour->primary_key); -		} else { -			/* fill in destination address used in ip header */ -			*((u32 *) (&hdr->hdr.l3.dest_addr[12])) = -							ip_hdr(skb)->daddr; -		} +		*((__be32 *) (&hdr->hdr.l3.dest_addr[12])) = *pkey;  	} else if (ipv == 6) { +		struct rt6_info *rt = (struct rt6_info *) dst; +		struct in6_addr *pkey = &ipv6_hdr(skb)->daddr; + +		if (!ipv6_addr_any(&rt->rt6i_gateway)) +			pkey = &rt->rt6i_gateway; +  		/* IPv6 */  		hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags6(cast_type);  		if (card->info.type == QETH_CARD_TYPE_IQD)  			hdr->hdr.l3.flags &= ~QETH_HDR_PASSTHRU; -		if ((skb_dst(skb)) && (skb_dst(skb)->neighbour)) { -			memcpy(hdr->hdr.l3.dest_addr, -			       skb_dst(skb)->neighbour->primary_key, 16); -		} else { -			/* fill in destination address used in ip header */ -			memcpy(hdr->hdr.l3.dest_addr, -			       &ipv6_hdr(skb)->daddr, 16); -		} +		memcpy(hdr->hdr.l3.dest_addr, pkey, 16);  	} else { -		/* passthrough */ -		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), +		if (!memcmp(skb->data + sizeof(struct qeth_hdr),  			    skb->dev->broadcast, 6)) {  			/* broadcast? */  			hdr->hdr.l3.flags = QETH_CAST_BROADCAST | @@ -2912,6 +2847,7 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,  				QETH_CAST_UNICAST | QETH_HDR_PASSTHRU;  		}  	} +	rcu_read_unlock();  }  static inline void qeth_l3_hdr_csum(struct qeth_card *card, @@ -2924,7 +2860,9 @@ static inline void qeth_l3_hdr_csum(struct qeth_card *card,  	 */  	if (iph->protocol == IPPROTO_UDP)  		hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_UDP; -	hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ; +	hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ | +		QETH_HDR_EXT_CSUM_HDR_REQ; +	iph->check = 0;  	if (card->options.performance_stats)  		card->perf_stats.tx_csum++;  } @@ -2971,7 +2909,9 @@ static inline int qeth_l3_tso_elements(struct sk_buff *skb)  		tcp_hdr(skb)->doff * 4;  	int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data);  	int elements = PFN_UP(tcpd + tcpd_len - 1) - PFN_DOWN(tcpd); -	elements += skb_shinfo(skb)->nr_frags; + +	elements += qeth_get_elements_for_frags(skb); +  	return elements;  } @@ -2986,15 +2926,21 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  	struct sk_buff *new_skb = NULL;  	int ipv = qeth_get_ip_version(skb);  	int cast_type = qeth_l3_get_cast_type(card, skb); -	struct qeth_qdio_out_q *queue = card->qdio.out_qs -		[qeth_get_priority_queue(card, skb, ipv, cast_type)]; +	struct qeth_qdio_out_q *queue = +		card->qdio.out_qs[card->qdio.do_prio_queueing +			|| (cast_type && card->info.is_multicast_different) ? +			qeth_get_priority_queue(card, skb, ipv, cast_type) : +			card->qdio.default_out_queue];  	int tx_bytes = skb->len; -	enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO; +	bool large_send;  	int data_offset = -1;  	int nr_frags; -	if (((card->info.type == QETH_CARD_TYPE_IQD) && (!ipv)) || -	     card->options.sniffer) +	if (((card->info.type == QETH_CARD_TYPE_IQD) && +	     (((card->options.cq != QETH_CQ_ENABLED) && !ipv) || +	      ((card->options.cq == QETH_CQ_ENABLED) && +	       (skb->protocol != ETH_P_AF_IUCV)))) || +	    card->options.sniffer)  			goto tx_drop;  	if ((card->state != CARD_STATE_UP) || !card->lan_online) { @@ -3011,13 +2957,15 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  		card->perf_stats.outbound_start_time = qeth_get_micros();  	} -	if (skb_is_gso(skb)) -		large_send = card->options.large_send; +	large_send = skb_is_gso(skb);  	if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) &&  	    (skb_shinfo(skb)->nr_frags == 0)) {  		new_skb = skb; -		data_offset = ETH_HLEN; +		if (new_skb->protocol == ETH_P_AF_IUCV) +			data_offset = 0; +		else +			data_offset = ETH_HLEN;  		hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);  		if (!hdr)  			goto tx_drop; @@ -3035,14 +2983,10 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  			skb_pull(new_skb, ETH_HLEN);  	} else {  		if (ipv == 4) { -			if (card->dev->type == ARPHRD_IEEE802_TR) -				skb_pull(new_skb, TR_HLEN); -			else -				skb_pull(new_skb, ETH_HLEN); +			skb_pull(new_skb, ETH_HLEN);  		} -		if (ipv == 6 && card->vlangrp && -				vlan_tx_tag_present(new_skb)) { +		if (ipv != 4 && vlan_tx_tag_present(new_skb)) {  			skb_push(new_skb, VLAN_HLEN);  			skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4);  			skb_copy_to_linear_data_offset(new_skb, 4, @@ -3052,7 +2996,6 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  			tag = (u16 *)(new_skb->data + 12);  			*tag = __constant_htons(ETH_P_8021Q);  			*(tag + 1) = htons(vlan_tx_tag_get(new_skb)); -			new_skb->vlan_tci = 0;  		}  	} @@ -3061,7 +3004,7 @@ 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) { +	if (large_send) {  		if (qeth_l3_tso_elements(new_skb) + 1 > 16) {  			if (skb_linearize(new_skb))  				goto tx_drop; @@ -3070,8 +3013,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  		}  	} -	if ((large_send == QETH_LARGE_SEND_TSO) && -	    (cast_type == RTN_UNSPEC)) { +	if (large_send && (cast_type == RTN_UNSPEC)) {  		hdr = (struct qeth_hdr *)skb_push(new_skb,  						sizeof(struct qeth_hdr_tso));  		memset(hdr, 0, sizeof(struct qeth_hdr_tso)); @@ -3085,17 +3027,20 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  			qeth_l3_fill_header(card, hdr, new_skb, ipv,  						cast_type);  		} else { -			qeth_l3_fill_header(card, hdr, new_skb, ipv, -						cast_type); -			hdr->hdr.l3.length = new_skb->len - data_offset; +			if (new_skb->protocol == ETH_P_AF_IUCV) +				qeth_l3_fill_af_iucv_hdr(card, hdr, new_skb); +			else { +				qeth_l3_fill_header(card, hdr, new_skb, ipv, +							cast_type); +				hdr->hdr.l3.length = new_skb->len - data_offset; +			}  		}  		if (skb->ip_summed == CHECKSUM_PARTIAL)  			qeth_l3_hdr_csum(card, hdr, new_skb);  	} -	elems = qeth_get_elements_no(card, (void *)hdr, new_skb, -						 elements_needed); +	elems = qeth_get_elements_no(card, new_skb, elements_needed);  	if (!elems) {  		if (data_offset >= 0)  			kmem_cache_free(qeth_core_header_cache, hdr); @@ -3106,14 +3051,14 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  	if (card->info.type != QETH_CARD_TYPE_IQD) {  		int len; -		if (large_send == QETH_LARGE_SEND_TSO) +		if (large_send)  			len = ((unsigned long)tcp_hdr(new_skb) +  				tcp_hdr(new_skb)->doff * 4) -  				(unsigned long)new_skb->data;  		else  			len = sizeof(struct qeth_hdr_layer3); -		if (qeth_hdr_chk_and_bounce(new_skb, len)) +		if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len))  			goto tx_drop;  		rc = qeth_do_send_packet(card, queue, new_skb, hdr,  					 elements_needed); @@ -3127,7 +3072,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  		if (new_skb != skb)  			dev_kfree_skb_any(skb);  		if (card->options.performance_stats) { -			if (large_send != QETH_LARGE_SEND_NO) { +			if (large_send) {  				card->perf_stats.large_send_bytes += tx_bytes;  				card->perf_stats.large_send_cnt++;  			} @@ -3166,12 +3111,14 @@ tx_drop:  	return NETDEV_TX_OK;  } -static int qeth_l3_open(struct net_device *dev) +static int __qeth_l3_open(struct net_device *dev)  {  	struct qeth_card *card = dev->ml_priv;  	int rc = 0;  	QETH_CARD_TEXT(card, 4, "qethopen"); +	if (card->state == CARD_STATE_UP) +		return rc;  	if (card->state != CARD_STATE_SOFTSETUP)  		return -ENODEV;  	card->data.state = CH_STATE_UP; @@ -3186,6 +3133,18 @@ static int qeth_l3_open(struct net_device *dev)  	return rc;  } +static int qeth_l3_open(struct net_device *dev) +{ +	struct qeth_card *card = dev->ml_priv; + +	QETH_CARD_TEXT(card, 5, "qethope_"); +	if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) { +		QETH_CARD_TEXT(card, 3, "openREC"); +		return -ERESTARTSYS; +	} +	return __qeth_l3_open(dev); +} +  static int qeth_l3_stop(struct net_device *dev)  {  	struct qeth_card *card = dev->ml_priv; @@ -3199,65 +3158,44 @@ static int qeth_l3_stop(struct net_device *dev)  	return 0;  } -static u32 qeth_l3_ethtool_get_rx_csum(struct net_device *dev) -{ -	struct qeth_card *card = dev->ml_priv; - -	return (card->options.checksum_type == HW_CHECKSUMMING); -} - -static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data) +static netdev_features_t qeth_l3_fix_features(struct net_device *dev, +	netdev_features_t features)  {  	struct qeth_card *card = dev->ml_priv; -	enum qeth_checksum_types csum_type; -	if (data) -		csum_type = HW_CHECKSUMMING; -	else -		csum_type = SW_CHECKSUMMING; +	if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) +		features &= ~NETIF_F_IP_CSUM; +	if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) +		features &= ~NETIF_F_TSO; +	if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) +		features &= ~NETIF_F_RXCSUM; -	return qeth_l3_set_rx_csum(card, csum_type); +	return features;  } -static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data) +static int qeth_l3_set_features(struct net_device *dev, +	netdev_features_t features)  {  	struct qeth_card *card = dev->ml_priv; -	int rc = 0; +	u32 changed = dev->features ^ features; +	int err; -	if (data) { -		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 rc; -} +	if (!(changed & NETIF_F_RXCSUM)) +		return 0; -static int qeth_l3_ethtool_set_tx_csum(struct net_device *dev, u32 data) -{ -	struct qeth_card *card = dev->ml_priv; +	if (card->state == CARD_STATE_DOWN || +	    card->state == CARD_STATE_RECOVER) +		return 0; -	if (data) { -		if (qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) -			dev->features |= NETIF_F_IP_CSUM; -		else -			return -EPERM; -	} else -		dev->features &= ~NETIF_F_IP_CSUM; +	err = qeth_l3_set_rx_csum(card, features & NETIF_F_RXCSUM); +	if (err) +		dev->features = features ^ NETIF_F_RXCSUM; -	return 0; +	return err;  }  static const struct ethtool_ops qeth_l3_ethtool_ops = {  	.get_link = ethtool_op_get_link, -	.get_tx_csum = ethtool_op_get_tx_csum, -	.set_tx_csum = qeth_l3_ethtool_set_tx_csum, -	.get_rx_csum = qeth_l3_ethtool_get_rx_csum, -	.set_rx_csum = qeth_l3_ethtool_set_rx_csum, -	.get_sg      = ethtool_op_get_sg, -	.set_sg      = ethtool_op_set_sg, -	.get_tso     = ethtool_op_get_tso, -	.set_tso     = qeth_l3_ethtool_set_tso,  	.get_strings = qeth_core_get_strings,  	.get_ethtool_stats = qeth_core_get_ethtool_stats,  	.get_sset_count = qeth_core_get_sset_count, @@ -3295,13 +3233,14 @@ static const struct net_device_ops qeth_l3_netdev_ops = {  	.ndo_get_stats		= qeth_get_stats,  	.ndo_start_xmit		= qeth_l3_hard_start_xmit,  	.ndo_validate_addr	= eth_validate_addr, -	.ndo_set_multicast_list = qeth_l3_set_multicast_list, -	.ndo_do_ioctl	   	= qeth_l3_do_ioctl, -	.ndo_change_mtu	   	= qeth_change_mtu, -	.ndo_vlan_rx_register	= qeth_l3_vlan_rx_register, +	.ndo_set_rx_mode	= qeth_l3_set_multicast_list, +	.ndo_do_ioctl		= qeth_l3_do_ioctl, +	.ndo_change_mtu		= qeth_change_mtu, +	.ndo_fix_features	= qeth_l3_fix_features, +	.ndo_set_features	= qeth_l3_set_features,  	.ndo_vlan_rx_add_vid	= qeth_l3_vlan_rx_add_vid,  	.ndo_vlan_rx_kill_vid   = qeth_l3_vlan_rx_kill_vid, -	.ndo_tx_timeout	   	= qeth_tx_timeout, +	.ndo_tx_timeout		= qeth_tx_timeout,  };  static const struct net_device_ops qeth_l3_osa_netdev_ops = { @@ -3310,13 +3249,14 @@ static const struct net_device_ops qeth_l3_osa_netdev_ops = {  	.ndo_get_stats		= qeth_get_stats,  	.ndo_start_xmit		= qeth_l3_hard_start_xmit,  	.ndo_validate_addr	= eth_validate_addr, -	.ndo_set_multicast_list = qeth_l3_set_multicast_list, -	.ndo_do_ioctl	   	= qeth_l3_do_ioctl, -	.ndo_change_mtu	   	= qeth_change_mtu, -	.ndo_vlan_rx_register	= qeth_l3_vlan_rx_register, +	.ndo_set_rx_mode	= qeth_l3_set_multicast_list, +	.ndo_do_ioctl		= qeth_l3_do_ioctl, +	.ndo_change_mtu		= qeth_change_mtu, +	.ndo_fix_features	= qeth_l3_fix_features, +	.ndo_set_features	= qeth_l3_set_features,  	.ndo_vlan_rx_add_vid	= qeth_l3_vlan_rx_add_vid,  	.ndo_vlan_rx_kill_vid   = qeth_l3_vlan_rx_kill_vid, -	.ndo_tx_timeout	   	= qeth_tx_timeout, +	.ndo_tx_timeout		= qeth_tx_timeout,  	.ndo_neigh_setup	= qeth_l3_neigh_setup,  }; @@ -3326,12 +3266,8 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)  	    card->info.type == QETH_CARD_TYPE_OSX) {  		if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) ||  		    (card->info.link_type == QETH_LINK_TYPE_HSTR)) { -#ifdef CONFIG_TR -			card->dev = alloc_trdev(0); -#endif -			if (!card->dev) -				return -ENODEV; -			card->dev->netdev_ops = &qeth_l3_netdev_ops; +			pr_info("qeth_l3: ignoring TR device\n"); +			return -ENODEV;  		} else {  			card->dev = alloc_etherdev(0);  			if (!card->dev) @@ -3343,6 +3279,12 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)  			if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))  				card->dev->dev_id = card->info.unique_id &  							 0xffff; +			if (!card->info.guestlan) { +				card->dev->hw_features = NETIF_F_SG | +					NETIF_F_RXCSUM | NETIF_F_IP_CSUM | +					NETIF_F_TSO; +				card->dev->features = NETIF_F_RXCSUM; +			}  		}  	} else if (card->info.type == QETH_CARD_TYPE_IQD) {  		card->dev = alloc_netdev(0, "hsi%d", ether_setup); @@ -3351,16 +3293,18 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)  		card->dev->flags |= IFF_NOARP;  		card->dev->netdev_ops = &qeth_l3_netdev_ops;  		qeth_l3_iqd_read_initial_mac(card); +		if (card->options.hsuid[0]) +			memcpy(card->dev->perm_addr, card->options.hsuid, 9);  	} else  		return -ENODEV;  	card->dev->ml_priv = card;  	card->dev->watchdog_timeo = QETH_TX_TIMEOUT;  	card->dev->mtu = card->info.initial_mtu; -	SET_ETHTOOL_OPS(card->dev, &qeth_l3_ethtool_ops); -	card->dev->features |=	NETIF_F_HW_VLAN_TX | -				NETIF_F_HW_VLAN_RX | -				NETIF_F_HW_VLAN_FILTER; +	card->dev->ethtool_ops = &qeth_l3_ethtool_ops; +	card->dev->features |=	NETIF_F_HW_VLAN_CTAG_TX | +				NETIF_F_HW_VLAN_CTAG_RX | +				NETIF_F_HW_VLAN_CTAG_FILTER;  	card->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;  	card->dev->gso_max_size = 15 * PAGE_SIZE; @@ -3375,12 +3319,7 @@ static int qeth_l3_probe_device(struct ccwgroup_device *gdev)  	qeth_l3_create_device_attributes(&gdev->dev);  	card->options.layer2 = 0; -	card->discipline.start_poll = qeth_qdio_start_poll; -	card->discipline.input_handler = (qdio_handler_t *) -		qeth_qdio_input_handler; -	card->discipline.output_handler = (qdio_handler_t *) -		qeth_qdio_output_handler; -	card->discipline.recover = qeth_l3_recover; +	card->info.hwtrap = 0;  	return 0;  } @@ -3393,17 +3332,15 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev)  	qeth_set_allowed_threads(card, 0, 1);  	wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); -	if (cgdev->state == CCWGROUP_ONLINE) { -		card->use_hard_stop = 1; +	if (cgdev->state == CCWGROUP_ONLINE)  		qeth_l3_set_offline(cgdev); -	}  	if (card->dev) {  		unregister_netdev(card->dev);  		card->dev = NULL;  	} -	qeth_l3_clear_ip_list(card, 0, 0); +	qeth_l3_clear_ip_list(card, 0);  	qeth_l3_clear_ipato_list(card);  	return;  } @@ -3414,7 +3351,6 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)  	int rc = 0;  	enum qeth_card_states recover_flag; -	BUG_ON(!card);  	mutex_lock(&card->discipline_mutex);  	mutex_lock(&card->conf_mutex);  	QETH_DBF_TEXT(SETUP, 2, "setonlin"); @@ -3428,13 +3364,18 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)  		goto out_remove;  	} -	qeth_l3_query_ipassists(card, QETH_PROT_IPV4); -  	if (!card->dev && qeth_l3_setup_netdev(card)) {  		rc = -ENODEV;  		goto out_remove;  	} +	if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { +		if (card->info.hwtrap && +		    qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM)) +			card->info.hwtrap = 0; +	} else +		card->info.hwtrap = 0; +  	card->state = CARD_STATE_HARDSETUP;  	memset(&card->rx, 0, sizeof(struct qeth_rx));  	qeth_print_status_message(card); @@ -3462,9 +3403,10 @@ contin:  		QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);  	if (!card->options.sniffer) {  		rc = qeth_l3_start_ipassists(card); -		if (rc) +		if (rc) {  			QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); -		qeth_l3_set_large_send(card, card->options.large_send); +			goto out_remove; +		}  		rc = qeth_l3_setrouting_v4(card);  		if (rc)  			QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc); @@ -3489,26 +3431,26 @@ contin:  	else  		netif_carrier_off(card->dev);  	if (recover_flag == CARD_STATE_RECOVER) { +		rtnl_lock();  		if (recovery_mode) -			qeth_l3_open(card->dev); -		else { -			rtnl_lock(); +			__qeth_l3_open(card->dev); +		else  			dev_open(card->dev); -			rtnl_unlock(); -		}  		qeth_l3_set_multicast_list(card->dev); +		rtnl_unlock();  	} +	qeth_trace_features(card);  	/* let user_space know that device is online */  	kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);  	mutex_unlock(&card->conf_mutex);  	mutex_unlock(&card->discipline_mutex);  	return 0;  out_remove: -	card->use_hard_stop = 1;  	qeth_l3_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)); +	qdio_free(CARD_DDEV(card));  	if (recover_flag == CARD_STATE_RECOVER)  		card->state = CARD_STATE_RECOVER;  	else @@ -3538,7 +3480,16 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev,  	if (card->dev && netif_carrier_ok(card->dev))  		netif_carrier_off(card->dev);  	recover_flag = card->state; +	if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) { +		qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); +		card->info.hwtrap = 1; +	}  	qeth_l3_stop_card(card, recovery_mode); +	if ((card->options.cq == QETH_CQ_ENABLED) && card->dev) { +		rtnl_lock(); +		call_netdevice_notifiers(NETDEV_REBOOT, card->dev); +		rtnl_unlock(); +	}  	rc  = ccw_device_set_offline(CARD_DDEV(card));  	rc2 = ccw_device_set_offline(CARD_WDEV(card));  	rc3 = ccw_device_set_offline(CARD_RDEV(card)); @@ -3546,6 +3497,7 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev,  		rc = (rc2) ? rc2 : rc3;  	if (rc)  		QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); +	qdio_free(CARD_DDEV(card));  	if (recover_flag == CARD_STATE_UP)  		card->state = CARD_STATE_RECOVER;  	/* let user_space know that device is offline */ @@ -3573,19 +3525,18 @@ static int qeth_l3_recover(void *ptr)  	QETH_CARD_TEXT(card, 2, "recover2");  	dev_warn(&card->gdev->dev,  		"A recovery process has been started for the device\n"); -	card->use_hard_stop = 1; +	qeth_set_recovery_task(card);  	__qeth_l3_set_offline(card->gdev, 1);  	rc = __qeth_l3_set_online(card->gdev, 1);  	if (!rc)  		dev_info(&card->gdev->dev,  			"Device successfully recovered!\n");  	else { -		rtnl_lock(); -		dev_close(card->dev); -		rtnl_unlock(); +		qeth_close_dev(card);  		dev_warn(&card->gdev->dev, "The qeth device driver " -			"failed to recover an error on the device\n"); +				"failed to recover an error on the device\n");  	} +	qeth_clear_recovery_task(card);  	qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);  	qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);  	return 0; @@ -3594,9 +3545,12 @@ static int qeth_l3_recover(void *ptr)  static void qeth_l3_shutdown(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); -	qeth_l3_clear_ip_list(card, 0, 0); +	qeth_set_allowed_threads(card, 0, 1); +	if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap) +		qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);  	qeth_qdio_clear_card(card, 0);  	qeth_clear_qdio_buffers(card); +	qdio_free(CARD_DDEV(card));  }  static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev) @@ -3610,7 +3564,8 @@ static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev)  	if (gdev->state == CCWGROUP_OFFLINE)  		return 0;  	if (card->state == CARD_STATE_UP) { -		card->use_hard_stop = 1; +		if (card->info.hwtrap) +			qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);  		__qeth_l3_set_offline(card->gdev, 1);  	} else  		__qeth_l3_set_offline(card->gdev, 0); @@ -3644,8 +3599,19 @@ out:  	return rc;  } -struct ccwgroup_driver qeth_l3_ccwgroup_driver = { -	.probe = qeth_l3_probe_device, +/* Returns zero if the command is successfully "consumed" */ +static int qeth_l3_control_event(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd) +{ +	return 1; +} + +struct qeth_discipline qeth_l3_discipline = { +	.start_poll = qeth_qdio_start_poll, +	.input_handler = (qdio_handler_t *) qeth_qdio_input_handler, +	.output_handler = (qdio_handler_t *) qeth_qdio_output_handler, +	.recover = qeth_l3_recover, +	.setup = qeth_l3_probe_device,  	.remove = qeth_l3_remove_device,  	.set_online = qeth_l3_set_online,  	.set_offline = qeth_l3_set_offline, @@ -3653,8 +3619,9 @@ struct ccwgroup_driver qeth_l3_ccwgroup_driver = {  	.freeze = qeth_l3_pm_suspend,  	.thaw = qeth_l3_pm_resume,  	.restore = qeth_l3_pm_resume, +	.control_event_handler = qeth_l3_control_event,  }; -EXPORT_SYMBOL_GPL(qeth_l3_ccwgroup_driver); +EXPORT_SYMBOL_GPL(qeth_l3_discipline);  static int qeth_l3_ip_event(struct notifier_block *this,  			    unsigned long event, void *ptr) @@ -3668,9 +3635,9 @@ static int qeth_l3_ip_event(struct notifier_block *this,  		return NOTIFY_DONE;  	card = qeth_l3_get_card_from_dev(dev); -	QETH_CARD_TEXT(card, 3, "ipevent");  	if (!card)  		return NOTIFY_DONE; +	QETH_CARD_TEXT(card, 3, "ipevent");  	addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);  	if (addr != NULL) { @@ -3714,7 +3681,6 @@ static int qeth_l3_ip6_event(struct notifier_block *this,  	struct qeth_ipaddr *addr;  	struct qeth_card *card; -  	card = qeth_l3_get_card_from_dev(dev);  	if (!card)  		return NOTIFY_DONE; @@ -3777,9 +3743,9 @@ static void qeth_l3_unregister_notifiers(void)  {  	QETH_DBF_TEXT(SETUP, 5, "unregnot"); -	BUG_ON(unregister_inetaddr_notifier(&qeth_l3_ip_notifier)); +	WARN_ON(unregister_inetaddr_notifier(&qeth_l3_ip_notifier));  #ifdef CONFIG_QETH_IPV6 -	BUG_ON(unregister_inet6addr_notifier(&qeth_l3_ip6_notifier)); +	WARN_ON(unregister_inet6addr_notifier(&qeth_l3_ip6_notifier));  #endif /* QETH_IPV6 */  } diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index 67cfa68dcf1..adef5f5de11 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -1,6 +1,4 @@  /* - *  drivers/s390/net/qeth_l3_sys.c - *   *    Copyright IBM Corp. 2007   *    Author(s): Utz Bacher <utz.bacher@de.ibm.com>,   *		 Frank Pavlic <fpavlic@de.ibm.com>, @@ -9,22 +7,12 @@   */  #include <linux/slab.h> - +#include <asm/ebcdic.h>  #include "qeth_l3.h"  #define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \  struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store) -static const char *qeth_l3_get_checksum_str(struct qeth_card *card) -{ -	if (card->options.checksum_type == SW_CHECKSUMMING) -		return "sw"; -	else if (card->options.checksum_type == HW_CHECKSUMMING) -		return "hw"; -	else -		return "no"; -} -  static ssize_t qeth_l3_dev_route_show(struct qeth_card *card,  			struct qeth_routing_info *route, char *buf)  { @@ -99,6 +87,8 @@ static ssize_t qeth_l3_dev_route_store(struct qeth_card *card,  			rc = qeth_l3_setrouting_v6(card);  	}  out: +	if (rc) +		route->type = old_route_type;  	mutex_unlock(&card->conf_mutex);  	return rc ? rc : count;  } @@ -185,161 +175,6 @@ out:  static DEVICE_ATTR(fake_broadcast, 0644, qeth_l3_dev_fake_broadcast_show,  		   qeth_l3_dev_fake_broadcast_store); -static ssize_t qeth_l3_dev_broadcast_mode_show(struct device *dev, -				struct device_attribute *attr, char *buf) -{ -	struct qeth_card *card = dev_get_drvdata(dev); - -	if (!card) -		return -EINVAL; - -	if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || -	      (card->info.link_type == QETH_LINK_TYPE_LANE_TR))) -		return sprintf(buf, "n/a\n"); - -	return sprintf(buf, "%s\n", (card->options.broadcast_mode == -				     QETH_TR_BROADCAST_ALLRINGS)? -		       "all rings":"local"); -} - -static ssize_t qeth_l3_dev_broadcast_mode_store(struct device *dev, -		struct device_attribute *attr, const char *buf, size_t count) -{ -	struct qeth_card *card = dev_get_drvdata(dev); -	char *tmp; -	int rc = 0; - -	if (!card) -		return -EINVAL; - -	mutex_lock(&card->conf_mutex); -	if ((card->state != CARD_STATE_DOWN) && -	    (card->state != CARD_STATE_RECOVER)) { -		rc = -EPERM; -		goto out; -	} - -	if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || -	      (card->info.link_type == QETH_LINK_TYPE_LANE_TR))) { -		rc = -EINVAL; -		goto out; -	} - -	tmp = strsep((char **) &buf, "\n"); - -	if (!strcmp(tmp, "local")) -		card->options.broadcast_mode = QETH_TR_BROADCAST_LOCAL; -	else if (!strcmp(tmp, "all_rings")) -		card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS; -	else -		rc = -EINVAL; -out: -	mutex_unlock(&card->conf_mutex); -	return rc ? rc : count; -} - -static DEVICE_ATTR(broadcast_mode, 0644, qeth_l3_dev_broadcast_mode_show, -		   qeth_l3_dev_broadcast_mode_store); - -static ssize_t qeth_l3_dev_canonical_macaddr_show(struct device *dev, -				struct device_attribute *attr, char *buf) -{ -	struct qeth_card *card = dev_get_drvdata(dev); - -	if (!card) -		return -EINVAL; - -	if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || -	      (card->info.link_type == QETH_LINK_TYPE_LANE_TR))) -		return sprintf(buf, "n/a\n"); - -	return sprintf(buf, "%i\n", (card->options.macaddr_mode == -				     QETH_TR_MACADDR_CANONICAL)? 1:0); -} - -static ssize_t qeth_l3_dev_canonical_macaddr_store(struct device *dev, -		struct device_attribute *attr, const char *buf, size_t count) -{ -	struct qeth_card *card = dev_get_drvdata(dev); -	char *tmp; -	int i, rc = 0; - -	if (!card) -		return -EINVAL; - -	mutex_lock(&card->conf_mutex); -	if ((card->state != CARD_STATE_DOWN) && -	    (card->state != CARD_STATE_RECOVER)) { -		rc = -EPERM; -		goto out; -	} - -	if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || -	      (card->info.link_type == QETH_LINK_TYPE_LANE_TR))) { -		rc = -EINVAL; -		goto out; -	} - -	i = simple_strtoul(buf, &tmp, 16); -	if ((i == 0) || (i == 1)) -		card->options.macaddr_mode = i? -			QETH_TR_MACADDR_CANONICAL : -			QETH_TR_MACADDR_NONCANONICAL; -	else -		rc = -EINVAL; -out: -	mutex_unlock(&card->conf_mutex); -	return rc ? rc : count; -} - -static DEVICE_ATTR(canonical_macaddr, 0644, qeth_l3_dev_canonical_macaddr_show, -		   qeth_l3_dev_canonical_macaddr_store); - -static ssize_t qeth_l3_dev_checksum_show(struct device *dev, -			struct device_attribute *attr, char *buf) -{ -	struct qeth_card *card = dev_get_drvdata(dev); - -	if (!card) -		return -EINVAL; - -	return sprintf(buf, "%s checksumming\n", -			qeth_l3_get_checksum_str(card)); -} - -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 = 0; - -	if (!card) -		return -EINVAL; - -	mutex_lock(&card->conf_mutex); -	tmp = strsep((char **) &buf, "\n"); -	if (!strcmp(tmp, "sw_checksumming")) -		csum_type = SW_CHECKSUMMING; -	else if (!strcmp(tmp, "hw_checksumming")) -		csum_type = HW_CHECKSUMMING; -	else if (!strcmp(tmp, "no_checksumming")) -		csum_type = NO_CHECKSUMMING; -	else { -		rc = -EINVAL; -		goto out; -	} - -	rc = qeth_l3_set_rx_csum(card, csum_type); -out: -	mutex_unlock(&card->conf_mutex); -	return rc ? rc : count; -} - -static DEVICE_ATTR(checksumming, 0644, qeth_l3_dev_checksum_show, -		qeth_l3_dev_checksum_store); -  static ssize_t qeth_l3_dev_sniffer_show(struct device *dev,  		struct device_attribute *attr, char *buf)  { @@ -363,6 +198,8 @@ static ssize_t qeth_l3_dev_sniffer_store(struct device *dev,  	if (card->info.type != QETH_CARD_TYPE_IQD)  		return -EPERM; +	if (card->options.cq == QETH_CQ_ENABLED) +		return -EPERM;  	mutex_lock(&card->conf_mutex);  	if ((card->state != CARD_STATE_DOWN) && @@ -371,7 +208,7 @@ static ssize_t qeth_l3_dev_sniffer_store(struct device *dev,  		goto out;  	} -	rc = strict_strtoul(buf, 16, &i); +	rc = kstrtoul(buf, 16, &i);  	if (rc) {  		rc = -EINVAL;  		goto out; @@ -388,10 +225,10 @@ static ssize_t qeth_l3_dev_sniffer_store(struct device *dev,  					QETH_IN_BUF_COUNT_MAX)  				qeth_realloc_buffer_pool(card,  					QETH_IN_BUF_COUNT_MAX); -			break;  		} else  			rc = -EPERM; -	default:   /* fall through */ +		break; +	default:  		rc = -EINVAL;  	}  out: @@ -402,61 +239,115 @@ out:  static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show,  		qeth_l3_dev_sniffer_store); -static ssize_t qeth_l3_dev_large_send_show(struct device *dev, -				struct device_attribute *attr, char *buf) + +static ssize_t qeth_l3_dev_hsuid_show(struct device *dev, +		struct device_attribute *attr, char *buf)  {  	struct qeth_card *card = dev_get_drvdata(dev); +	char tmp_hsuid[9];  	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"); -	} +	if (card->info.type != QETH_CARD_TYPE_IQD) +		return -EPERM; + +	if (card->state == CARD_STATE_DOWN) +		return -EPERM; + +	memcpy(tmp_hsuid, card->options.hsuid, sizeof(tmp_hsuid)); +	EBCASC(tmp_hsuid, 8); +	return sprintf(buf, "%s\n", tmp_hsuid);  } -static ssize_t qeth_l3_dev_large_send_store(struct device *dev, +static ssize_t qeth_l3_dev_hsuid_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; +	struct qeth_ipaddr *addr;  	char *tmp; +	int i;  	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 + +	if (card->info.type != QETH_CARD_TYPE_IQD) +		return -EPERM; +	if (card->state != CARD_STATE_DOWN && +	    card->state != CARD_STATE_RECOVER) +		return -EPERM; +	if (card->options.sniffer) +		return -EPERM; +	if (card->options.cq == QETH_CQ_NOTAVAILABLE) +		return -EPERM; + +	tmp = strsep((char **)&buf, "\n"); +	if (strlen(tmp) > 8)  		return -EINVAL; -	mutex_lock(&card->conf_mutex); -	if (card->options.large_send != type) -		rc = qeth_l3_set_large_send(card, type); -	mutex_unlock(&card->conf_mutex); -	return rc ? rc : count; +	if (card->options.hsuid[0]) { +		/* delete old ip address */ +		addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); +		if (addr != NULL) { +			addr->u.a6.addr.s6_addr32[0] = 0xfe800000; +			addr->u.a6.addr.s6_addr32[1] = 0x00000000; +			for (i = 8; i < 16; i++) +				addr->u.a6.addr.s6_addr[i] = +					card->options.hsuid[i - 8]; +			addr->u.a6.pfxlen = 0; +			addr->type = QETH_IP_TYPE_NORMAL; +		} else +			return -ENOMEM; +		if (!qeth_l3_delete_ip(card, addr)) +			kfree(addr); +		qeth_l3_set_ip_addr_list(card); +	} + +	if (strlen(tmp) == 0) { +		/* delete ip address only */ +		card->options.hsuid[0] = '\0'; +		if (card->dev) +			memcpy(card->dev->perm_addr, card->options.hsuid, 9); +		qeth_configure_cq(card, QETH_CQ_DISABLED); +		return count; +	} + +	if (qeth_configure_cq(card, QETH_CQ_ENABLED)) +		return -EPERM; + +	snprintf(card->options.hsuid, sizeof(card->options.hsuid), +		 "%-8s", tmp); +	ASCEBC(card->options.hsuid, 8); +	if (card->dev) +		memcpy(card->dev->perm_addr, card->options.hsuid, 9); + +	addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); +	if (addr != NULL) { +		addr->u.a6.addr.s6_addr32[0] = 0xfe800000; +		addr->u.a6.addr.s6_addr32[1] = 0x00000000; +		for (i = 8; i < 16; i++) +			addr->u.a6.addr.s6_addr[i] = card->options.hsuid[i - 8]; +		addr->u.a6.pfxlen = 0; +		addr->type = QETH_IP_TYPE_NORMAL; +	} else +		return -ENOMEM; +	if (!qeth_l3_add_ip(card, addr)) +		kfree(addr); +	qeth_l3_set_ip_addr_list(card); + +	return count;  } -static DEVICE_ATTR(large_send, 0644, qeth_l3_dev_large_send_show, -		   qeth_l3_dev_large_send_store); +static DEVICE_ATTR(hsuid, 0644, qeth_l3_dev_hsuid_show, +		   qeth_l3_dev_hsuid_store); +  static struct attribute *qeth_l3_device_attrs[] = {  	&dev_attr_route4.attr,  	&dev_attr_route6.attr,  	&dev_attr_fake_broadcast.attr, -	&dev_attr_broadcast_mode.attr, -	&dev_attr_canonical_macaddr.attr, -	&dev_attr_checksumming.attr,  	&dev_attr_sniffer.attr, -	&dev_attr_large_send.attr, +	&dev_attr_hsuid.attr,  	NULL,  }; diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index 65e1cf10494..d8f990b6b33 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -60,7 +60,7 @@ static struct iucv_handler smsg_handler = {  static int smsg_path_pending(struct iucv_path *path, u8 ipvmid[8],  			     u8 ipuser[16])  { -	if (strncmp(ipvmid, "*MSG    ", sizeof(ipvmid)) != 0) +	if (strncmp(ipvmid, "*MSG    ", 8) != 0)  		return -EINVAL;  	/* Path pending from *MSG. */  	return iucv_path_accept(path, &smsg_handler, "SMSGIUCV        ", NULL); @@ -157,7 +157,7 @@ static int smsg_pm_restore_thaw(struct device *dev)  #ifdef CONFIG_PM_DEBUG  	printk(KERN_WARNING "smsg_pm_restore_thaw\n");  #endif -	if (smsg_path && iucv_path_connected) { +	if (smsg_path && !iucv_path_connected) {  		memset(smsg_path, 0, sizeof(*smsg_path));  		smsg_path->msglim = 255;  		smsg_path->flags = 0; diff --git a/drivers/s390/net/smsgiucv.h b/drivers/s390/net/smsgiucv.h index 149a1151608..45bc925928c 100644 --- a/drivers/s390/net/smsgiucv.h +++ b/drivers/s390/net/smsgiucv.h @@ -1,7 +1,7 @@  /*   * IUCV special message driver   * - * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright IBM Corp. 2003   * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)   */ diff --git a/drivers/s390/net/smsgiucv_app.c b/drivers/s390/net/smsgiucv_app.c index 4d2ea400042..32515a201bb 100644 --- a/drivers/s390/net/smsgiucv_app.c +++ b/drivers/s390/net/smsgiucv_app.c @@ -168,7 +168,7 @@ static int __init smsgiucv_app_init(void)  	rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);  	if (rc) {  		kfree(smsg_app_dev); -		goto fail_put_driver; +		goto fail;  	}  	smsg_app_dev->bus = &iucv_bus;  	smsg_app_dev->parent = iucv_root; @@ -177,7 +177,7 @@ static int __init smsgiucv_app_init(void)  	rc = device_register(smsg_app_dev);  	if (rc) {  		put_device(smsg_app_dev); -		goto fail_put_driver; +		goto fail;  	}  	/* convert sender to uppercase characters */ @@ -191,12 +191,11 @@ static int __init smsgiucv_app_init(void)  	rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);  	if (rc) {  		device_unregister(smsg_app_dev); -		goto fail_put_driver; +		goto fail;  	}  	rc = 0; -fail_put_driver: -	put_driver(smsgiucv_drv); +fail:  	return rc;  }  module_init(smsgiucv_app_init);  | 
