📄 netiucv.c
字号:
break; }}/** * Called from connection statemachine * when a connection has been shutdown. * * @param fi An instance of an interface statemachine. * @param event The event, just happened. * @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 intnetiucv_transmit_skb(struct iucv_connection *conn, struct sk_buff *skb) { unsigned long saveflags; ll_header header; int rc = 0; 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; } 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 + 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 = xtime; rc = iucv_send(conn->pathid, NULL, 0, 0, 1 /* single_flag */, 0, nskb->data, nskb->len); /* Shut up, gcc! nskb is always below 2G. */ 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 = (struct netiucv_priv *)conn->netdev->priv; 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 intnetiucv_open(struct net_device *dev) { fsm_event(((struct netiucv_priv *)dev->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 intnetiucv_close(struct net_device *dev) { fsm_event(((struct netiucv_priv *)dev->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){ int rc = 0; struct netiucv_priv *privptr = dev->priv; 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) { 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; if (netiucv_transmit_skb(privptr->conn, skb)) rc = 1; netiucv_clear_busy(dev); return rc;}/** * Returns interface statistics of a device. * * @param dev Pointer to interface struct. * * @return Pointer to stats struct of this interface. */static struct net_device_stats *netiucv_stats (struct net_device * dev){ IUCV_DBF_TEXT(trace, 5, __FUNCTION__); return &((struct netiucv_priv *)dev->priv)->stats;}/** * Sets MTU of an interface. * * @param dev Pointer to interface struct. * @param new_mtu The new MTU to use for this interface. * * @return 0 on success, -EINVAL if MTU is out of valid range. * (valid range is 576 .. NETIUCV_MTU_MAX). */static intnetiucv_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_tuser_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_tuser_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[10]; int i; 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]= *p; else if (*p == '\n') { /* trailing lf, grr */ break; } else { 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<9) username[i++] = ' '; username[9] = '\0'; if (memcmp(username, priv->conn->userid, 8)) { /* username changed */ if (ndev->flags & (IFF_UP | IFF_RUNNING)) { 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; } } memcpy(priv->conn->userid, username, 9); return count;}static DEVICE_ATTR(user, 0644, user_show, user_write);static ssize_tbuffer_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_tbuffer_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_tdev_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_tconn_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_tmaxmulti_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_tmaxmulti_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_tmaxcq_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_tmaxcq_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_tsdoio_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_tsdoio_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_tmdoio_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_tmdoio_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_ttxlen_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_ttxlen_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;}static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -