netiucv.c

来自「linux 内核源代码」· C语言 代码 · 共 2,166 行 · 第 1/4 页

C
2,166
字号
		/* Found a matching connection for this path. */		conn->path = path;		ev.conn = conn;		ev.data = path;		fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);		rc = 0;	}	read_unlock_bh(&iucv_connection_rwlock);	return rc;}static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16]){	struct iucv_connection *conn = path->private;	fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);}static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16]){	struct iucv_connection *conn = path->private;	fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);}static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16]){	struct iucv_connection *conn = path->private;	fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn);}/** * Dummy NOP action for all statemachines */static void fsm_action_nop(fsm_instance *fi, int event, void *arg){}/* * Actions of the connection statemachine *//** * netiucv_unpack_skb * @conn: The connection where this skb has been received. * @pskb: The received skb. * * Unpack a just received skb and hand it over to upper layers. * Helper function for conn_action_rx. */static void netiucv_unpack_skb(struct iucv_connection *conn,			       struct sk_buff *pskb){	struct net_device     *dev = conn->netdev;	struct netiucv_priv   *privptr = netdev_priv(dev);	u16 offset = 0;	skb_put(pskb, NETIUCV_HDRLEN);	pskb->dev = dev;	pskb->ip_summed = CHECKSUM_NONE;	pskb->protocol = ntohs(ETH_P_IP);	while (1) {		struct sk_buff *skb;		struct ll_header *header = (struct ll_header *) pskb->data;		if (!header->next)			break;		skb_pull(pskb, NETIUCV_HDRLEN);		header->next -= offset;		offset += header->next;		header->next -= NETIUCV_HDRLEN;		if (skb_tailroom(pskb) < header->next) {			PRINT_WARN("%s: Illegal next field in iucv header: "			       "%d > %d\n",			       dev->name, header->next, skb_tailroom(pskb));			IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",				header->next, skb_tailroom(pskb));			return;		}		skb_put(pskb, header->next);		skb_reset_mac_header(pskb);		skb = dev_alloc_skb(pskb->len);		if (!skb) {			PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n",			       dev->name);			IUCV_DBF_TEXT(data, 2,				"Out of memory in netiucv_unpack_skb\n");			privptr->stats.rx_dropped++;			return;		}		skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),					  pskb->len);		skb_reset_mac_header(skb);		skb->dev = pskb->dev;		skb->protocol = pskb->protocol;		pskb->ip_summed = CHECKSUM_UNNECESSARY;		privptr->stats.rx_packets++;		privptr->stats.rx_bytes += skb->len;		/*		 * Since receiving is always initiated from a tasklet (in iucv.c),		 * we must use netif_rx_ni() instead of netif_rx()		 */		netif_rx_ni(skb);		dev->last_rx = jiffies;		skb_pull(pskb, header->next);		skb_put(pskb, NETIUCV_HDRLEN);	}}static void conn_action_rx(fsm_instance *fi, int event, void *arg){	struct iucv_event *ev = arg;	struct iucv_connection *conn = ev->conn;	struct iucv_message *msg = ev->data;	struct netiucv_priv *privptr = netdev_priv(conn->netdev);	int rc;	IUCV_DBF_TEXT(trace, 4, __FUNCTION__);	if (!conn->netdev) {		iucv_message_reject(conn->path, msg);		PRINT_WARN("Received data for unlinked connection\n");		IUCV_DBF_TEXT(data, 2,			      "Received data for unlinked connection\n");		return;	}	if (msg->length > conn->max_buffsize) {		iucv_message_reject(conn->path, msg);		privptr->stats.rx_dropped++;		PRINT_WARN("msglen %d > max_buffsize %d\n",			   msg->length, conn->max_buffsize);		IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",			       msg->length, conn->max_buffsize);		return;	}	conn->rx_buff->data = conn->rx_buff->head;	skb_reset_tail_pointer(conn->rx_buff);	conn->rx_buff->len = 0;	rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,				  msg->length, NULL);	if (rc || msg->length < 5) {		privptr->stats.rx_errors++;		PRINT_WARN("iucv_receive returned %08x\n", rc);		IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);		return;	}	netiucv_unpack_skb(conn, conn->rx_buff);}static void conn_action_txdone(fsm_instance *fi, int event, void *arg){	struct iucv_event *ev = arg;	struct iucv_connection *conn = ev->conn;	struct iucv_message *msg = ev->data;	struct iucv_message txmsg;	struct netiucv_priv *privptr = NULL;	u32 single_flag = msg->tag;	u32 txbytes = 0;	u32 txpackets = 0;	u32 stat_maxcq = 0;	struct sk_buff *skb;	unsigned long saveflags;	struct ll_header header;	int rc;	IUCV_DBF_TEXT(trace, 4, __FUNCTION__);	if (conn && conn->netdev)		privptr = netdev_priv(conn->netdev);	conn->prof.tx_pending--;	if (single_flag) {		if ((skb = skb_dequeue(&conn->commit_queue))) {			atomic_dec(&skb->users);			dev_kfree_skb_any(skb);			if (privptr) {				privptr->stats.tx_packets++;				privptr->stats.tx_bytes +=					(skb->len - NETIUCV_HDRLEN					 	  - NETIUCV_HDRLEN);			}		}	}	conn->tx_buff->data = conn->tx_buff->head;	skb_reset_tail_pointer(conn->tx_buff);	conn->tx_buff->len = 0;	spin_lock_irqsave(&conn->collect_lock, saveflags);	while ((skb = skb_dequeue(&conn->collect_queue))) {		header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;		memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,		       NETIUCV_HDRLEN);		skb_copy_from_linear_data(skb,					  skb_put(conn->tx_buff, skb->len),					  skb->len);		txbytes += skb->len;		txpackets++;		stat_maxcq++;		atomic_dec(&skb->users);		dev_kfree_skb_any(skb);	}	if (conn->collect_len > conn->prof.maxmulti)		conn->prof.maxmulti = conn->collect_len;	conn->collect_len = 0;	spin_unlock_irqrestore(&conn->collect_lock, saveflags);	if (conn->tx_buff->len == 0) {		fsm_newstate(fi, CONN_STATE_IDLE);		return;	}	header.next = 0;	memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);	conn->prof.send_stamp = current_kernel_time();	txmsg.class = 0;	txmsg.tag = 0;	rc = iucv_message_send(conn->path, &txmsg, 0, 0,			       conn->tx_buff->data, conn->tx_buff->len);	conn->prof.doios_multi++;	conn->prof.txlen += conn->tx_buff->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) {		conn->prof.tx_pending--;		fsm_newstate(fi, CONN_STATE_IDLE);		if (privptr)			privptr->stats.tx_errors += txpackets;		PRINT_WARN("iucv_send returned %08x\n",	rc);		IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);	} else {		if (privptr) {			privptr->stats.tx_packets += txpackets;			privptr->stats.tx_bytes += txbytes;		}		if (stat_maxcq > conn->prof.maxcqueue)			conn->prof.maxcqueue = stat_maxcq;	}}static void conn_action_connaccept(fsm_instance *fi, int event, void *arg){	struct iucv_event *ev = arg;	struct iucv_connection *conn = ev->conn;	struct iucv_path *path = ev->data;	struct net_device *netdev = conn->netdev;	struct netiucv_priv *privptr = netdev_priv(netdev);	int rc;	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	conn->path = path;	path->msglim = NETIUCV_QUEUELEN_DEFAULT;	path->flags = 0;	rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);	if (rc) {		PRINT_WARN("%s: IUCV accept failed with error %d\n",		       netdev->name, rc);		IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);		return;	}	fsm_newstate(fi, CONN_STATE_IDLE);	netdev->tx_queue_len = conn->path->msglim;	fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);}static void conn_action_connreject(fsm_instance *fi, int event, void *arg){	struct iucv_event *ev = arg;	struct iucv_path *path = ev->data;	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	iucv_path_sever(path, NULL);}static void conn_action_connack(fsm_instance *fi, int event, void *arg){	struct iucv_connection *conn = arg;	struct net_device *netdev = conn->netdev;	struct netiucv_priv *privptr = netdev_priv(netdev);	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	fsm_deltimer(&conn->timer);	fsm_newstate(fi, CONN_STATE_IDLE);	netdev->tx_queue_len = conn->path->msglim;	fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev);}static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg){	struct iucv_connection *conn = arg;	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	fsm_deltimer(&conn->timer);	iucv_path_sever(conn->path, NULL);	fsm_newstate(fi, CONN_STATE_STARTWAIT);}static void conn_action_connsever(fsm_instance *fi, int event, void *arg){	struct iucv_connection *conn = arg;	struct net_device *netdev = conn->netdev;	struct netiucv_priv *privptr = netdev_priv(netdev);	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	fsm_deltimer(&conn->timer);	iucv_path_sever(conn->path, NULL);	PRINT_INFO("%s: Remote dropped connection\n", netdev->name);	IUCV_DBF_TEXT(data, 2,		      "conn_action_connsever: Remote dropped connection\n");	fsm_newstate(fi, CONN_STATE_STARTWAIT);	fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);}static void conn_action_start(fsm_instance *fi, int event, void *arg){	struct iucv_connection *conn = arg;	int rc;	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	fsm_newstate(fi, CONN_STATE_STARTWAIT);	PRINT_DEBUG("%s('%s'): connecting ...\n",		    conn->netdev->name, conn->userid);	/*	 * We must set the state before calling iucv_connect because the	 * callback handler could be called at any point after the connection	 * request is sent	 */	fsm_newstate(fi, CONN_STATE_SETUPWAIT);	conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);	rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,			       NULL, iucvMagic, conn);	switch (rc) {	case 0:		conn->netdev->tx_queue_len = conn->path->msglim;		fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,			     CONN_EVENT_TIMER, conn);		return;	case 11:		PRINT_INFO("%s: User %s is currently not available.\n",			   conn->netdev->name,			   netiucv_printname(conn->userid));		fsm_newstate(fi, CONN_STATE_STARTWAIT);		break;	case 12:		PRINT_INFO("%s: User %s is currently not ready.\n",			   conn->netdev->name,			   netiucv_printname(conn->userid));		fsm_newstate(fi, CONN_STATE_STARTWAIT);		break;	case 13:		PRINT_WARN("%s: Too many IUCV connections.\n",			   conn->netdev->name);		fsm_newstate(fi, CONN_STATE_CONNERR);		break;	case 14:		PRINT_WARN("%s: User %s has too many IUCV connections.\n",			   conn->netdev->name,			   netiucv_printname(conn->userid));		fsm_newstate(fi, CONN_STATE_CONNERR);		break;	case 15:		PRINT_WARN("%s: No IUCV authorization in CP directory.\n",			   conn->netdev->name);		fsm_newstate(fi, CONN_STATE_CONNERR);		break;	default:		PRINT_WARN("%s: iucv_connect returned error %d\n",			   conn->netdev->name, rc);		fsm_newstate(fi, CONN_STATE_CONNERR);		break;	}	IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc);	kfree(conn->path);	conn->path = NULL;}static void netiucv_purge_skb_queue(struct sk_buff_head *q){	struct sk_buff *skb;	while ((skb = skb_dequeue(q))) {		atomic_dec(&skb->users);		dev_kfree_skb_any(skb);	}}static void conn_action_stop(fsm_instance *fi, int event, void *arg){	struct iucv_event *ev = arg;	struct iucv_connection *conn = ev->conn;	struct net_device *netdev = conn->netdev;	struct netiucv_priv *privptr = netdev_priv(netdev);	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	fsm_deltimer(&conn->timer);	fsm_newstate(fi, CONN_STATE_STOPPED);	netiucv_purge_skb_queue(&conn->collect_queue);	if (conn->path) {		IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");		iucv_path_sever(conn->path, iucvMagic);		kfree(conn->path);		conn->path = NULL;	}	netiucv_purge_skb_queue(&conn->commit_queue);	fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);}static void conn_action_inval(fsm_instance *fi, int event, void *arg){	struct iucv_connection *conn = arg;	struct net_device *netdev = conn->netdev;	PRINT_WARN("%s: Cannot connect without username\n", netdev->name);	IUCV_DBF_TEXT(data, 2, "conn_action_inval called\n");}static const fsm_node conn_fsm[] = {	{ CONN_STATE_INVALID,   CONN_EVENT_START,    conn_action_inval      },	{ CONN_STATE_STOPPED,   CONN_EVENT_START,    conn_action_start      },	{ CONN_STATE_STOPPED,   CONN_EVENT_STOP,     conn_action_stop       },	{ CONN_STATE_STARTWAIT, CONN_EVENT_STOP,     conn_action_stop       },	{ CONN_STATE_SETUPWAIT, CONN_EVENT_STOP,     conn_action_stop       },	{ CONN_STATE_IDLE,      CONN_EVENT_STOP,     conn_action_stop       },	{ CONN_STATE_TX,        CONN_EVENT_STOP,     conn_action_stop       },	{ CONN_STATE_REGERR,    CONN_EVENT_STOP,     conn_action_stop       },	{ CONN_STATE_CONNERR,   CONN_EVENT_STOP,     conn_action_stop       },	{ CONN_STATE_STOPPED,   CONN_EVENT_CONN_REQ, conn_action_connreject },        { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },	{ CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept },	{ CONN_STATE_IDLE,      CONN_EVENT_CONN_REQ, conn_action_connreject },	{ CONN_STATE_TX,        CONN_EVENT_CONN_REQ, conn_action_connreject },	{ CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack    },	{ CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER,    conn_action_conntimsev },	{ CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever  },	{ CONN_STATE_IDLE,      CONN_EVENT_CONN_REJ, conn_action_connsever  },	{ CONN_STATE_TX,        CONN_EVENT_CONN_REJ, conn_action_connsever  },	{ CONN_STATE_IDLE,      CONN_EVENT_RX,       conn_action_rx         },	{ CONN_STATE_TX,        CONN_EVENT_RX,       conn_action_rx         },	{ CONN_STATE_TX,        CONN_EVENT_TXDONE,   conn_action_txdone     },	{ CONN_STATE_IDLE,      CONN_EVENT_TXDONE,   conn_action_txdone     },};static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node);/* * Actions for interface - statemachine. *//** * dev_action_start * @fi: An instance of an interface statemachine. * @event: The event, just happened. * @arg: Generic pointer, casted from struct net_device * upon call. * * Startup connection by sending CONN_EVENT_START to it. */static void dev_action_start(fsm_instance *fi, int event, void *arg){	struct net_device   *dev = arg;	struct netiucv_priv *privptr = netdev_priv(dev);	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	fsm_newstate(fi, DEV_STATE_STARTWAIT);	fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);}/** * Shutdown connection by sending CONN_EVENT_STOP to it. * * @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_stop(fsm_instance *fi, int event, void *arg){	struct net_device   *dev = arg;	struct netiucv_priv *privptr = netdev_priv(dev);	struct iucv_event   ev;	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	ev.conn = privptr->conn;	fsm_newstate(fi, DEV_STATE_STOPWAIT);	fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);}/** * Called from connection statemachine * when a connection is up and running. * * @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_connup(fsm_instance *fi, int event, void *arg){	struct net_device   *dev = arg;	struct netiucv_priv *privptr = netdev_priv(dev);	IUCV_DBF_TEXT(trace, 3, __FUNCTION__);	switch (fsm_getstate(fi)) {		case DEV_STATE_STARTWAIT:			fsm_newstate(fi, DEV_STATE_RUNNING);			PRINT_INFO("%s: connected with remote side %s\n",			       dev->name, privptr->conn->userid);			IUCV_DBF_TEXT(setup, 3,				"connection is up and running\n");			break;		case DEV_STATE_STOPWAIT:			PRINT_INFO(			       "%s: got connection UP event during shutdown!\n",			       dev->name);			IUCV_DBF_TEXT(data, 2,				"dev_action_connup: in DEV_STATE_STOPWAIT\n");			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.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?