netiucv.c
来自「linux 内核源代码」· C语言 代码 · 共 2,166 行 · 第 1/4 页
C
2,166 行
static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);static ssize_t txtime_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.tx_time);}static ssize_t txtime_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.tx_time = 0; return count;}static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);static ssize_t txpend_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.tx_pending);}static ssize_t txpend_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.tx_pending = 0; return count;}static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);static ssize_t txmpnd_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.tx_max_pending);}static ssize_t txmpnd_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.tx_max_pending = 0; return count;}static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);static struct attribute *netiucv_attrs[] = { &dev_attr_buffer.attr, &dev_attr_user.attr, NULL,};static struct attribute_group netiucv_attr_group = { .attrs = netiucv_attrs,};static struct attribute *netiucv_stat_attrs[] = { &dev_attr_device_fsm_state.attr, &dev_attr_connection_fsm_state.attr, &dev_attr_max_tx_buffer_used.attr, &dev_attr_max_chained_skbs.attr, &dev_attr_tx_single_write_ops.attr, &dev_attr_tx_multi_write_ops.attr, &dev_attr_netto_bytes.attr, &dev_attr_max_tx_io_time.attr, &dev_attr_tx_pending.attr, &dev_attr_tx_max_pending.attr, NULL,};static struct attribute_group netiucv_stat_attr_group = { .name = "stats", .attrs = netiucv_stat_attrs,};static int netiucv_add_files(struct device *dev){ int ret; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); 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, __FUNCTION__); sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group); sysfs_remove_group(&dev->kobj, &netiucv_attr_group);}static int netiucv_register_device(struct net_device *ndev){ struct netiucv_priv *priv = netdev_priv(ndev); struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL); int ret; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); if (dev) { snprintf(dev->bus_id, BUS_ID_SIZE, "net%s", ndev->name); dev->bus = &iucv_bus; dev->parent = iucv_root; /* * The release function could be called after the * module has been unloaded. It's _only_ task is to * free the struct. Therefore, we specify kfree() * directly here. (Probably a little bit obfuscating * but legitime ...). */ dev->release = (void (*)(struct device *))kfree; dev->driver = &netiucv_driver; } else return -ENOMEM; ret = device_register(dev); if (ret) return ret; ret = netiucv_add_files(dev); if (ret) goto out_unreg; priv->dev = dev; dev->driver_data = priv; return 0;out_unreg: device_unregister(dev); return ret;}static void netiucv_unregister_device(struct device *dev){ IUCV_DBF_TEXT(trace, 3, __FUNCTION__); netiucv_remove_files(dev); device_unregister(dev);}/** * Allocate and initialize a new connection structure. * Add it to the list of netiucv connections; */static struct iucv_connection *netiucv_new_connection(struct net_device *dev, char *username){ struct iucv_connection *conn; conn = kzalloc(sizeof(*conn), GFP_KERNEL); if (!conn) goto out; skb_queue_head_init(&conn->collect_queue); skb_queue_head_init(&conn->commit_queue); spin_lock_init(&conn->collect_lock); conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT; conn->netdev = dev; conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA); if (!conn->rx_buff) goto out_conn; conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA); if (!conn->tx_buff) goto out_rx; conn->fsm = init_fsm("netiucvconn", conn_state_names, conn_event_names, NR_CONN_STATES, NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN, GFP_KERNEL); if (!conn->fsm) goto out_tx; fsm_settimer(conn->fsm, &conn->timer); fsm_newstate(conn->fsm, CONN_STATE_INVALID); if (username) { memcpy(conn->userid, username, 9); fsm_newstate(conn->fsm, CONN_STATE_STOPPED); } write_lock_bh(&iucv_connection_rwlock); list_add_tail(&conn->list, &iucv_connection_list); write_unlock_bh(&iucv_connection_rwlock); return conn;out_tx: kfree_skb(conn->tx_buff);out_rx: kfree_skb(conn->rx_buff);out_conn: kfree(conn);out: return NULL;}/** * Release a connection structure and remove it from the * list of netiucv connections. */static void netiucv_remove_connection(struct iucv_connection *conn){ IUCV_DBF_TEXT(trace, 3, __FUNCTION__); write_lock_bh(&iucv_connection_rwlock); list_del_init(&conn->list); write_unlock_bh(&iucv_connection_rwlock); fsm_deltimer(&conn->timer); netiucv_purge_skb_queue(&conn->collect_queue); if (conn->path) { iucv_path_sever(conn->path, iucvMagic); kfree(conn->path); conn->path = NULL; } netiucv_purge_skb_queue(&conn->commit_queue); kfree_fsm(conn->fsm); kfree_skb(conn->rx_buff); kfree_skb(conn->tx_buff);}/** * Release everything of a net device. */static void netiucv_free_netdevice(struct net_device *dev){ struct netiucv_priv *privptr = netdev_priv(dev); IUCV_DBF_TEXT(trace, 3, __FUNCTION__); if (!dev) return; if (privptr) { if (privptr->conn) netiucv_remove_connection(privptr->conn); if (privptr->fsm) kfree_fsm(privptr->fsm); privptr->conn = NULL; privptr->fsm = NULL; /* privptr gets freed by free_netdev() */ } free_netdev(dev);}/** * Initialize a net device. (Called from kernel in alloc_netdev()) */static void netiucv_setup_netdevice(struct net_device *dev){ dev->mtu = NETIUCV_MTU_DEFAULT; dev->hard_start_xmit = netiucv_tx; dev->open = netiucv_open; dev->stop = netiucv_close; dev->get_stats = netiucv_stats; dev->change_mtu = netiucv_change_mtu; dev->destructor = netiucv_free_netdevice; dev->hard_header_len = NETIUCV_HDRLEN; dev->addr_len = 0; dev->type = ARPHRD_SLIP; dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT; dev->flags = IFF_POINTOPOINT | IFF_NOARP;}/** * Allocate and initialize everything of a net device. */static struct net_device *netiucv_init_netdevice(char *username){ struct netiucv_priv *privptr; struct net_device *dev; dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d", netiucv_setup_netdevice); if (!dev) return NULL; if (dev_alloc_name(dev, dev->name) < 0) goto out_netdev; privptr = netdev_priv(dev); privptr->fsm = init_fsm("netiucvdev", dev_state_names, dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS, dev_fsm, DEV_FSM_LEN, GFP_KERNEL); if (!privptr->fsm) goto out_netdev; privptr->conn = netiucv_new_connection(dev, username); if (!privptr->conn) { IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n"); goto out_fsm; } fsm_newstate(privptr->fsm, DEV_STATE_STOPPED); return dev;out_fsm: kfree_fsm(privptr->fsm);out_netdev: free_netdev(dev); return NULL;}static ssize_t conn_write(struct device_driver *drv, const char *buf, size_t count){ const char *p; char username[9]; int i, rc; struct net_device *dev; struct netiucv_priv *priv; 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, "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; PRINT_WARN("netiucv: Invalid character in username!\n"); IUCV_DBF_TEXT_(setup, 2, "conn_write: invalid character %c\n", *p); return -EINVAL; } while (i < 8) username[i++] = ' '; username[8] = '\0'; read_lock_bh(&iucv_connection_rwlock); list_for_each_entry(cp, &iucv_connection_list, list) { if (!strncmp(username, cp->userid, 9)) { read_unlock_bh(&iucv_connection_rwlock); PRINT_WARN("netiucv: Connection to %s already " "exists\n", username); return -EEXIST; } } read_unlock_bh(&iucv_connection_rwlock); dev = netiucv_init_netdevice(username); if (!dev) { PRINT_WARN("netiucv: Could not allocate network device " "structure for user '%s'\n", netiucv_printname(username)); IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n"); return -ENODEV; } rc = netiucv_register_device(dev); if (rc) { IUCV_DBF_TEXT_(setup, 2, "ret %d from netiucv_register_device\n", rc); goto out_free_ndev; } /* sysfs magic */ priv = netdev_priv(dev); SET_NETDEV_DEV(dev, priv->dev); rc = register_netdev(dev); if (rc) goto out_unreg; PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username)); return count;out_unreg: netiucv_unregister_device(priv->dev);out_free_ndev: PRINT_WARN("netiucv: Could not register '%s'\n", dev->name); IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n"); netiucv_free_netdevice(dev); return rc;}static DRIVER_ATTR(connection, 0200, NULL, conn_write);static ssize_t remove_write (struct device_driver *drv, const char *buf, size_t count){ struct iucv_connection *cp; struct net_device *ndev; struct netiucv_priv *priv; struct device *dev; char name[IFNAMSIZ]; const char *p; int i; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); if (count >= IFNAMSIZ) count = IFNAMSIZ - 1;; for (i = 0, p = buf; i < count && *p; i++, p++) { if (*p == '\n' || *p == ' ') /* trailing lf, grr */ break; name[i] = *p; } name[i] = '\0'; read_lock_bh(&iucv_connection_rwlock); list_for_each_entry(cp, &iucv_connection_list, list) { ndev = cp->netdev; priv = netdev_priv(ndev); dev = priv->dev; if (strncmp(name, ndev->name, count)) continue; read_unlock_bh(&iucv_connection_rwlock); if (ndev->flags & (IFF_UP | IFF_RUNNING)) { PRINT_WARN("netiucv: net device %s active with peer " "%s\n", ndev->name, priv->conn->userid); PRINT_WARN("netiucv: %s cannot be removed\n", ndev->name); IUCV_DBF_TEXT(data, 2, "remove_write: still active\n"); return -EBUSY; } unregister_netdev(ndev); netiucv_unregister_device(dev); return count; } read_unlock_bh(&iucv_connection_rwlock); PRINT_WARN("netiucv: net device %s unknown\n", name); IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n"); return -EINVAL;}static DRIVER_ATTR(remove, 0200, NULL, remove_write);static struct attribute * netiucv_drv_attrs[] = { &driver_attr_connection.attr, &driver_attr_remove.attr, NULL,};static struct attribute_group netiucv_drv_attr_group = { .attrs = netiucv_drv_attrs,};static void netiucv_banner(void){ PRINT_INFO("NETIUCV driver initialized\n");}static void __exit netiucv_exit(void){ struct iucv_connection *cp; struct net_device *ndev; struct netiucv_priv *priv; struct device *dev; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); while (!list_empty(&iucv_connection_list)) { cp = list_entry(iucv_connection_list.next, struct iucv_connection, list); ndev = cp->netdev; priv = netdev_priv(ndev); dev = priv->dev; unregister_netdev(ndev); netiucv_unregister_device(dev); } sysfs_remove_group(&netiucv_driver.kobj, &netiucv_drv_attr_group); driver_unregister(&netiucv_driver); iucv_unregister(&netiucv_handler, 1); iucv_unregister_dbf_views(); PRINT_INFO("NETIUCV driver unloaded\n"); return;}static int __init netiucv_init(void){ int rc; rc = iucv_register_dbf_views(); if (rc) goto out; rc = iucv_register(&netiucv_handler, 1); if (rc) goto out_dbf; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); rc = driver_register(&netiucv_driver); if (rc) { PRINT_ERR("NETIUCV: failed to register driver.\n"); IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc); goto out_iucv; } rc = sysfs_create_group(&netiucv_driver.kobj, &netiucv_drv_attr_group); if (rc) { PRINT_ERR("NETIUCV: failed to add driver attributes.\n"); IUCV_DBF_TEXT_(setup, 2, "ret %d - netiucv_drv_attr_group\n", rc); goto out_driver; } netiucv_banner(); return rc;out_driver: driver_unregister(&netiucv_driver);out_iucv: iucv_unregister(&netiucv_handler, 1);out_dbf: iucv_unregister_dbf_views();out: return rc;}module_init(netiucv_init);module_exit(netiucv_exit);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?