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