📄 netiucv.c
字号:
/** * 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(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; 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) { printk(KERN_WARNING "%s: Could not allocate tx_skb\n", conn->netdev->name); rc = -ENOMEM; } 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); conn->retry = 0; fsm_newstate(conn->fsm, CONN_STATE_TX); fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC, CONN_EVENT_TIMER, conn); conn->prof.send_stamp = xtime; rc = iucv_send(conn->pathid, NULL, 0, 0, /* Shut up, gcc! nskb is always below 2G. */ (__u32)(((unsigned long)nskb)&0xffffffff), 0, nskb->data, nskb->len); conn->prof.doios_single++; conn->prof.txlen += skb->len; if (rc != 0) { fsm_deltimer(&conn->timer); 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); } } else { if (copied) dev_kfree_skb(skb); } } 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(net_device *dev) { MOD_INC_USE_COUNT; SET_DEVICE_START(dev, 1); fsm_event(((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(net_device *dev) { SET_DEVICE_START(dev, 0); fsm_event(((netiucv_priv *)dev->priv)->fsm, DEV_EVENT_STOP, dev); MOD_DEC_USE_COUNT; 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, net_device *dev){ int rc = 0; netiucv_priv *privptr = (netiucv_priv *)dev->priv; /** * Some sanity checks ... */ if (skb == NULL) { printk(KERN_WARNING "%s: NULL sk_buff passed\n", dev->name); privptr->stats.tx_dropped++; return 0; } if (skb_headroom(skb) < (NETIUCV_HDRLEN)) { printk(KERN_WARNING "%s: Got sk_buff with head room < %ld bytes\n", dev->name, NETIUCV_HDRLEN); dev_kfree_skb(skb); privptr->stats.tx_dropped++; return 0; } /** * If connection is not running, try to restart it * notify anybody about a link failure and throw * away packet. */ if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) { fsm_event(privptr->fsm, DEV_EVENT_START, dev); dst_link_failure(skb); 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)) return -EBUSY; dev->trans_start = jiffies; if (netiucv_transmit_skb(privptr->conn, skb) != 0) 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 (net_device * dev){ return &((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 (net_device * dev, int new_mtu){ if ((new_mtu < 576) || (new_mtu > NETIUCV_MTU_MAX)) return -EINVAL; dev->mtu = new_mtu; return 0;}/** * procfs related structures and routines *****************************************************************************/static net_device *find_netdev_by_ino(unsigned long ino){ iucv_connection *conn = connections; net_device *dev = NULL; netiucv_priv *privptr; while (conn) { if (conn->netdev != dev) { dev = conn->netdev; privptr = (netiucv_priv *)dev->priv; if ((privptr->proc_buffer_entry->low_ino == ino) || (privptr->proc_user_entry->low_ino == ino) || (privptr->proc_stat_entry->low_ino == ino) ) return dev; } conn = conn->next; } return NULL;}#if LINUX_VERSION_CODE < 0x020363/** * Lock the module, if someone changes into * our proc directory. */static voidnetiucv_fill_inode(struct inode *inode, int fill){ if (fill) { MOD_INC_USE_COUNT; } else MOD_DEC_USE_COUNT;}#endif#define CTRL_BUFSIZE 40static intnetiucv_buffer_open(struct inode *inode, struct file *file){ file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL); if (file->private_data == NULL) return -ENOMEM; MOD_INC_USE_COUNT; return 0;}static intnetiucv_buffer_close(struct inode *inode, struct file *file){ kfree(file->private_data); MOD_DEC_USE_COUNT; return 0;}static ssize_tnetiucv_buffer_write(struct file *file, const char *buf, size_t count, loff_t *off){ unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino; net_device *dev; netiucv_priv *privptr; char *e; int bs1; char tmp[CTRL_BUFSIZE]; if (!(dev = find_netdev_by_ino(ino))) return -ENODEV; if (off != &file->f_pos) return -ESPIPE; privptr = (netiucv_priv *)dev->priv; if (count >= 39) return -EINVAL; if (copy_from_user(tmp, buf, count)) return -EFAULT; tmp[count+1] = '\0'; bs1 = simple_strtoul(tmp, &e, 0); if ((bs1 > NETIUCV_BUFSIZE_MAX) || (e && (!isspace(*e)))) return -EINVAL; if ((dev->flags & IFF_RUNNING) && (bs1 < (dev->mtu + NETIUCV_HDRLEN + 2))) return -EINVAL; if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) return -EINVAL; privptr->conn->max_buffsize = bs1; if (!(dev->flags & IFF_RUNNING)) dev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN; privptr->conn->flags |= CONN_FLAGS_BUFSIZE_CHANGED; return count;}static ssize_tnetiucv_buffer_read(struct file *file, char *buf, size_t count, loff_t *off){ unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino; char *sbuf = (char *)file->private_data; net_device *dev; netiucv_priv *privptr; ssize_t ret = 0; char *p = sbuf; int l; if (!(dev = find_netdev_by_ino(ino))) return -ENODEV; if (off != &file->f_pos) return -ESPIPE; privptr = (netiucv_priv *)dev->priv; if (file->f_pos == 0) sprintf(sbuf, "%d\n", privptr->conn->max_buffsize); l = strlen(sbuf); p = sbuf; if (file->f_pos < l) { p += file->f_pos; l = strlen(p); ret = (count > l) ? l : count; if (copy_to_user(buf, p, ret)) return -EFAULT; } file->f_pos += ret; return ret;}static intnetiucv_user_open(struct inode *inode, struct file *file){ file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL); if (file->private_data == NULL) return -ENOMEM; MOD_INC_USE_COUNT; return 0;}static intnetiucv_user_close(struct inode *inode, struct file *file){ kfree(file->private_data); MOD_DEC_USE_COUNT; return 0;}static ssize_tnetiucv_user_write(struct file *file, const char *buf, size_t count, loff_t *off){ unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino; net_device *dev; netiucv_priv *privptr; int i; char *p; char tmp[CTRL_BUFSIZE]; char user[9]; if (!(dev = find_netdev_by_ino(ino))) return -ENODEV; if (off != &file->f_pos) return -ESPIPE; privptr = (netiucv_priv *)dev->priv; if (count >= 39) return -EINVAL; if (copy_from_user(tmp, buf, count)) return -EFAULT; tmp[count+1] = '\0'; memset(user, ' ', sizeof(user)); user[8] = '\0'; for (p = tmp, i = 0; *p && (!isspace(*p)); p++) { if (i > 7) return -EINVAL; user[i++] = *p; } if (memcmp(user, privptr->conn->userid, 8) != 0) { /* username changed */ if (dev->flags & IFF_RUNNING) return -EBUSY; } memcpy(privptr->conn->userid, user, 9); return count;}static ssize_tnetiucv_user_read(struct file *file, char *buf, size_t count, loff_t *off){ unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino; char *sbuf = (char *)file->private_data; net_device *dev; netiucv_priv *privptr; ssize_t ret = 0; char *p = sbuf; int l; if (!(dev = find_netdev_by_ino(ino))) return -ENODEV; if (off != &file->f_pos) return -ESPIPE; privptr = (netiucv_priv *)dev->priv; if (file->f_pos == 0) sprintf(sbuf, "%s\n", netiucv_printname(privptr->conn->userid)); l = strlen(sbuf); p = sbuf; if (file->f_pos < l) { p += file->f_pos; l = strlen(p); ret = (count > l) ? l : count; if (copy_to_user(buf, p, ret)) return -EFAULT; } file->f_pos += ret; return ret;}#define STATS_BUFSIZE 2048static intnetiucv_stat_open(struct inode *inode, struct file *file){ file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL); if (file->private_data == NULL) return -ENOMEM; MOD_INC_USE_COUNT; return 0;}static intnetiucv_stat_close(struct inode *inode, struct file *file){ kfree(file->private_data); MOD_DEC_USE_COUNT; return 0;}static ssize_tnetiucv_stat_write(struct file *file, const char *buf, size_t count, loff_t *off){ unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino; net_device *dev; netiucv_priv *privptr; if (!(dev = find_netdev_by_ino(ino))) return -ENODEV; privptr = (netiucv_priv *)dev->priv; privptr->conn->prof.maxmulti = 0; privptr->conn->prof.maxcqueue = 0; privptr->conn->prof.doios_single = 0; privptr->conn->prof.doios_multi = 0; privptr->conn->prof.txlen = 0; privptr->conn->prof.tx_time = 0; return count;}static ssize_tnetiucv_stat_read(struct file *file, char *buf, size_t count, loff_t *off){ unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino; char *sbuf = (char *)file->private_data; net_device *dev; netiucv_priv *privptr; ssize_t ret = 0; char *p = sbuf; int l; if (!(dev = find_netdev_by_ino(ino))) return -ENODEV; if (off != &file->f_pos) return -ESPIPE; privptr = (netiucv_priv *)dev->priv; if (file->f_pos == 0) { p += sprintf(p, "Device FSM state: %s\n", fsm_getstate_str(privptr->fsm)); p += sprintf(p, "Connection FSM state: %s\n", fsm_getstate_str(privptr->conn->fsm)); p += sprintf(p, "Max. TX buffer used: %ld\n", privptr->conn->prof.maxmulti); p += sprintf(p, "Max. chained SKBs: %ld\n", privptr->conn->prof.maxcqueue); p += sprintf(p, "TX single write ops: %ld\n", privptr->conn->prof.doios_single); p += sprintf(p, "TX multi write ops: %ld\n", privptr->conn->prof.doios_multi); p += sprintf(p, "Netto bytes written: %ld\n", privptr->conn->prof.txlen); p += sprintf(p, "Max. TX IO-time: %ld\n", privptr->conn->prof.tx_time); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -