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