netiucv.c

来自「linux 内核源代码」· C语言 代码 · 共 2,166 行 · 第 1/4 页

C
2,166
字号
 * @param arg   Generic pointer, casted from struct net_device * upon call. */static voiddev_action_conndown(fsm_instance *fi, int event, void *arg){	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	switch (fsm_getstate(fi)) {		case DEV_STATE_RUNNING:			fsm_newstate(fi, DEV_STATE_STARTWAIT);			break;		case DEV_STATE_STOPWAIT:			fsm_newstate(fi, DEV_STATE_STOPPED);			IUCV_DBF_TEXT(setup, 3, "connection is down\n");			break;	}}static const fsm_node dev_fsm[] = {	{ DEV_STATE_STOPPED,    DEV_EVENT_START,   dev_action_start    },	{ DEV_STATE_STOPWAIT,   DEV_EVENT_START,   dev_action_start    },	{ DEV_STATE_STOPWAIT,   DEV_EVENT_CONDOWN, dev_action_conndown },	{ DEV_STATE_STARTWAIT,  DEV_EVENT_STOP,    dev_action_stop     },	{ DEV_STATE_STARTWAIT,  DEV_EVENT_CONUP,   dev_action_connup   },	{ DEV_STATE_RUNNING,    DEV_EVENT_STOP,    dev_action_stop     },	{ DEV_STATE_RUNNING,    DEV_EVENT_CONDOWN, dev_action_conndown },	{ DEV_STATE_RUNNING,    DEV_EVENT_CONUP,   fsm_action_nop      },};static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);/** * Transmit a packet. * This is a helper function for netiucv_tx(). * * @param conn Connection to be used for sending. * @param skb Pointer to struct sk_buff of packet to send. *            The linklevel header has already been set up *            by netiucv_tx(). * * @return 0 on success, -ERRNO on failure. (Never fails.) */static int netiucv_transmit_skb(struct iucv_connection *conn,				struct sk_buff *skb){	struct iucv_message msg;	unsigned long saveflags;	struct ll_header header;	int rc;	if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) {		int l = skb->len + NETIUCV_HDRLEN;		spin_lock_irqsave(&conn->collect_lock, saveflags);		if (conn->collect_len + l >		    (conn->max_buffsize - NETIUCV_HDRLEN)) {			rc = -EBUSY;			IUCV_DBF_TEXT(data, 2,				      "EBUSY from netiucv_transmit_skb\n");		} else {			atomic_inc(&skb->users);			skb_queue_tail(&conn->collect_queue, skb);			conn->collect_len += l;			rc = 0;		}		spin_unlock_irqrestore(&conn->collect_lock, saveflags);	} else {		struct sk_buff *nskb = skb;		/**		 * Copy the skb to a new allocated skb in lowmem only if the		 * data is located above 2G in memory or tailroom is < 2.		 */		unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +				    NETIUCV_HDRLEN)) >> 31;		int copied = 0;		if (hi || (skb_tailroom(skb) < 2)) {			nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +					 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);			if (!nskb) {				PRINT_WARN("%s: Could not allocate tx_skb\n",				       conn->netdev->name);				IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");				rc = -ENOMEM;				return rc;			} else {				skb_reserve(nskb, NETIUCV_HDRLEN);				memcpy(skb_put(nskb, skb->len),				       skb->data, skb->len);			}			copied = 1;		}		/**		 * skb now is below 2G and has enough room. Add headers.		 */		header.next = nskb->len + NETIUCV_HDRLEN;		memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);		header.next = 0;		memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header,  NETIUCV_HDRLEN);		fsm_newstate(conn->fsm, CONN_STATE_TX);		conn->prof.send_stamp = current_kernel_time();		msg.tag = 1;		msg.class = 0;		rc = iucv_message_send(conn->path, &msg, 0, 0,				       nskb->data, nskb->len);		conn->prof.doios_single++;		conn->prof.txlen += skb->len;		conn->prof.tx_pending++;		if (conn->prof.tx_pending > conn->prof.tx_max_pending)			conn->prof.tx_max_pending = conn->prof.tx_pending;		if (rc) {			struct netiucv_priv *privptr;			fsm_newstate(conn->fsm, CONN_STATE_IDLE);			conn->prof.tx_pending--;			privptr = netdev_priv(conn->netdev);			if (privptr)				privptr->stats.tx_errors++;			if (copied)				dev_kfree_skb(nskb);			else {				/**				 * Remove our headers. They get added				 * again on retransmit.				 */				skb_pull(skb, NETIUCV_HDRLEN);				skb_trim(skb, skb->len - NETIUCV_HDRLEN);			}			PRINT_WARN("iucv_send returned %08x\n",	rc);			IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);		} else {			if (copied)				dev_kfree_skb(skb);			atomic_inc(&nskb->users);			skb_queue_tail(&conn->commit_queue, nskb);		}	}	return rc;}/* * Interface API for upper network layers *//** * Open an interface. * Called from generic network layer when ifconfig up is run. * * @param dev Pointer to interface struct. * * @return 0 on success, -ERRNO on failure. (Never fails.) */static int netiucv_open(struct net_device *dev){	struct netiucv_priv *priv = netdev_priv(dev);	fsm_event(priv->fsm, DEV_EVENT_START, dev);	return 0;}/** * Close an interface. * Called from generic network layer when ifconfig down is run. * * @param dev Pointer to interface struct. * * @return 0 on success, -ERRNO on failure. (Never fails.) */static int netiucv_close(struct net_device *dev){	struct netiucv_priv *priv = netdev_priv(dev);	fsm_event(priv->fsm, DEV_EVENT_STOP, dev);	return 0;}/** * Start transmission of a packet. * Called from generic network device layer. * * @param skb Pointer to buffer containing the packet. * @param dev Pointer to interface struct. * * @return 0 if packet consumed, !0 if packet rejected. *         Note: If we return !0, then the packet is free'd by *               the generic network layer. */static int netiucv_tx(struct sk_buff *skb, struct net_device *dev){	struct netiucv_priv *privptr = netdev_priv(dev);	int rc;	IUCV_DBF_TEXT(trace, 4, __FUNCTION__);	/**	 * Some sanity checks ...	 */	if (skb == NULL) {		PRINT_WARN("%s: NULL sk_buff passed\n", dev->name);		IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");		privptr->stats.tx_dropped++;		return 0;	}	if (skb_headroom(skb) < NETIUCV_HDRLEN) {		PRINT_WARN("%s: Got sk_buff with head room < %ld bytes\n",		       dev->name, NETIUCV_HDRLEN);		IUCV_DBF_TEXT(data, 2,			"netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");		dev_kfree_skb(skb);		privptr->stats.tx_dropped++;		return 0;	}	/**	 * If connection is not running, try to restart it	 * and throw away packet.	 */	if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {		if (!in_atomic())			fsm_event(privptr->fsm, DEV_EVENT_START, dev);		dev_kfree_skb(skb);		privptr->stats.tx_dropped++;		privptr->stats.tx_errors++;		privptr->stats.tx_carrier_errors++;		return 0;	}	if (netiucv_test_and_set_busy(dev)) {		IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");		return -EBUSY;	}	dev->trans_start = jiffies;	rc = netiucv_transmit_skb(privptr->conn, skb) != 0;	netiucv_clear_busy(dev);	return rc;}/** * netiucv_stats * @dev: Pointer to interface struct. * * Returns interface statistics of a device. * * Returns pointer to stats struct of this interface. */static struct net_device_stats *netiucv_stats (struct net_device * dev){	struct netiucv_priv *priv = netdev_priv(dev);	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	return &priv->stats;}/** * netiucv_change_mtu * @dev: Pointer to interface struct. * @new_mtu: The new MTU to use for this interface. * * Sets MTU of an interface. * * Returns 0 on success, -EINVAL if MTU is out of valid range. *         (valid range is 576 .. NETIUCV_MTU_MAX). */static int netiucv_change_mtu(struct net_device * dev, int new_mtu){	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {		IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");		return -EINVAL;	}	dev->mtu = new_mtu;	return 0;}/* * attributes in sysfs */static ssize_t user_show(struct device *dev, struct device_attribute *attr,			 char *buf){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));}static ssize_t user_write(struct device *dev, struct device_attribute *attr,			  const char *buf, size_t count){	struct netiucv_priv *priv = dev->driver_data;	struct net_device *ndev = priv->conn->netdev;	char    *p;	char    *tmp;	char 	username[9];	int 	i;	struct iucv_connection *cp;	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	if (count > 9) {		PRINT_WARN("netiucv: username too long (%d)!\n", (int) count);		IUCV_DBF_TEXT_(setup, 2,			       "%d is length of username\n", (int) count);		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);			continue;		}		if (*p == '\n') {			/* trailing lf, grr */			break;		}		PRINT_WARN("netiucv: Invalid char %c in username!\n", *p);		IUCV_DBF_TEXT_(setup, 2,			       "username: invalid character %c\n", *p);		return -EINVAL;	}	while (i < 8)		username[i++] = ' ';	username[8] = '\0';	if (memcmp(username, priv->conn->userid, 9) &&	    (ndev->flags & (IFF_UP | IFF_RUNNING))) {		/* username changed while the interface is active. */		PRINT_WARN("netiucv: device %s active, connected to %s\n",			   dev->bus_id, priv->conn->userid);		PRINT_WARN("netiucv: user cannot be updated\n");		IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");		return -EBUSY;	}	read_lock_bh(&iucv_connection_rwlock);	list_for_each_entry(cp, &iucv_connection_list, list) {		if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {			read_unlock_bh(&iucv_connection_rwlock);			PRINT_WARN("netiucv: Connection to %s already "				   "exists\n", username);			return -EEXIST;		}	}	read_unlock_bh(&iucv_connection_rwlock);	memcpy(priv->conn->userid, username, 9);	return count;}static DEVICE_ATTR(user, 0644, user_show, user_write);static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,			    char *buf){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	return sprintf(buf, "%d\n", priv->conn->max_buffsize);}static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,			     const char *buf, size_t count){	struct netiucv_priv *priv = dev->driver_data;	struct net_device *ndev = priv->conn->netdev;	char         *e;	int          bs1;	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	if (count >= 39)		return -EINVAL;	bs1 = simple_strtoul(buf, &e, 0);	if (e && (!isspace(*e))) {		PRINT_WARN("netiucv: Invalid character in buffer!\n");		IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);		return -EINVAL;	}	if (bs1 > NETIUCV_BUFSIZE_MAX) {		PRINT_WARN("netiucv: Given buffer size %d too large.\n",			bs1);		IUCV_DBF_TEXT_(setup, 2,			"buffer_write: buffer size %d too large\n",			bs1);		return -EINVAL;	}	if ((ndev->flags & IFF_RUNNING) &&	    (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {		PRINT_WARN("netiucv: Given buffer size %d too small.\n",			bs1);		IUCV_DBF_TEXT_(setup, 2,			"buffer_write: buffer size %d too small\n",			bs1);		return -EINVAL;	}	if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {		PRINT_WARN("netiucv: Given buffer size %d too small.\n",			bs1);		IUCV_DBF_TEXT_(setup, 2,			"buffer_write: buffer size %d too small\n",			bs1);		return -EINVAL;	}	priv->conn->max_buffsize = bs1;	if (!(ndev->flags & IFF_RUNNING))		ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;	return count;}static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,			     char *buf){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));}static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);static ssize_t conn_fsm_show (struct device *dev,			      struct device_attribute *attr, char *buf){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));}static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);static ssize_t maxmulti_show (struct device *dev,			      struct device_attribute *attr, char *buf){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);}static ssize_t maxmulti_write (struct device *dev,			       struct device_attribute *attr,			       const char *buf, size_t count){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 4, __FUNCTION__);	priv->conn->prof.maxmulti = 0;	return count;}static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,			   char *buf){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);}static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,			    const char *buf, size_t count){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 4, __FUNCTION__);	priv->conn->prof.maxcqueue = 0;	return count;}static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,			   char *buf){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);}static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,			    const char *buf, size_t count){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 4, __FUNCTION__);	priv->conn->prof.doios_single = 0;	return count;}static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,			   char *buf){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);}static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,			    const char *buf, size_t count){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	priv->conn->prof.doios_multi = 0;	return count;}static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,			   char *buf){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 5, __FUNCTION__);	return sprintf(buf, "%ld\n", priv->conn->prof.txlen);}static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,			    const char *buf, size_t count){	struct netiucv_priv *priv = dev->driver_data;	IUCV_DBF_TEXT(trace, 4, __FUNCTION__);	priv->conn->prof.txlen = 0;	return count;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?