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 + -
显示快捷键?