📄 ctcmain.c
字号:
(header->length > len)) {#ifndef DEBUG if (!(ch->logflags & LOG_FLAG_OVERRUN)) {#endif ctc_pr_warn( "%s Illegal packet size %d " "(beyond the end of received data), " "dropping\n", dev->name, header->length); ch->logflags |= LOG_FLAG_OVERRUN;#ifndef DEBUG }#endif#ifdef DEBUG ctc_dump_skb(pskb, -6);#endif privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; return; } skb_put(pskb, header->length); pskb->mac.raw = pskb->data; len -= header->length; skb = dev_alloc_skb(pskb->len); if (!skb) {#ifndef DEBUG if (!(ch->logflags & LOG_FLAG_NOMEM)) {#endif ctc_pr_warn( "%s Out of memory in ctc_unpack_skb\n", dev->name); ch->logflags |= LOG_FLAG_NOMEM;#ifndef DEBUG }#endif privptr->stats.rx_dropped++; return; } memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len); skb->mac.raw = skb->data; skb->dev = pskb->dev; skb->protocol = pskb->protocol; pskb->ip_summed = CHECKSUM_UNNECESSARY; if (ch->protocol == CTC_PROTO_LINUX_TTY) ctc_tty_netif_rx(skb); else netif_rx_ni(skb); /** * Successful rx; reset logflags */ ch->logflags = 0; dev->last_rx = jiffies; privptr->stats.rx_packets++; privptr->stats.rx_bytes += skb->len; if (len > 0) { skb_pull(pskb, header->length); if (skb_tailroom(pskb) < LL_HEADER_LENGTH) {#ifndef DEBUG if (!(ch->logflags & LOG_FLAG_OVERRUN)) {#endif ctc_pr_warn( "%s Overrun in ctc_unpack_skb\n", dev->name); ch->logflags |= LOG_FLAG_OVERRUN;#ifndef DEBUG }#endif return; } skb_put(pskb, LL_HEADER_LENGTH); } }}/** * Check return code of a preceeding ccw_device call, halt_IO etc... * * @param ch The channel, the error belongs to. * @param return_code The error code to inspect. */static void inlineccw_check_return_code(struct channel *ch, int return_code, char *msg){ DBF_TEXT(trace, 5, __FUNCTION__); switch (return_code) { case 0: fsm_event(ch->fsm, CH_EVENT_IO_SUCCESS, ch); break; case -EBUSY: ctc_pr_warn("%s (%s): Busy !\n", ch->id, msg); fsm_event(ch->fsm, CH_EVENT_IO_EBUSY, ch); break; case -ENODEV: ctc_pr_emerg("%s (%s): Invalid device called for IO\n", ch->id, msg); fsm_event(ch->fsm, CH_EVENT_IO_ENODEV, ch); break; case -EIO: ctc_pr_emerg("%s (%s): Status pending... \n", ch->id, msg); fsm_event(ch->fsm, CH_EVENT_IO_EIO, ch); break; default: ctc_pr_emerg("%s (%s): Unknown error in do_IO %04x\n", ch->id, msg, return_code); fsm_event(ch->fsm, CH_EVENT_IO_UNKNOWN, ch); }}/** * Check sense of a unit check. * * @param ch The channel, the sense code belongs to. * @param sense The sense code to inspect. */static void inlineccw_unit_check(struct channel *ch, unsigned char sense){ DBF_TEXT(trace, 5, __FUNCTION__); if (sense & SNS0_INTERVENTION_REQ) { if (sense & 0x01) { if (ch->protocol != CTC_PROTO_LINUX_TTY) ctc_pr_debug("%s: Interface disc. or Sel. reset " "(remote)\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_RCRESET, ch); } else { ctc_pr_debug("%s: System reset (remote)\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_RSRESET, ch); } } else if (sense & SNS0_EQUIPMENT_CHECK) { if (sense & SNS0_BUS_OUT_CHECK) { ctc_pr_warn("%s: Hardware malfunction (remote)\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_HWFAIL, ch); } else { ctc_pr_warn("%s: Read-data parity error (remote)\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_RXPARITY, ch); } } else if (sense & SNS0_BUS_OUT_CHECK) { if (sense & 0x04) { ctc_pr_warn("%s: Data-streaming timeout)\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_TXTIMEOUT, ch); } else { ctc_pr_warn("%s: Data-transfer parity error\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_TXPARITY, ch); } } else if (sense & SNS0_CMD_REJECT) { ctc_pr_warn("%s: Command reject\n", ch->id); } else if (sense == 0) { ctc_pr_debug("%s: Unit check ZERO\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_ZERO, ch); } else { ctc_pr_warn("%s: Unit Check with sense code: %02x\n", ch->id, sense); fsm_event(ch->fsm, CH_EVENT_UC_UNKNOWN, ch); }}static voidctc_purge_skb_queue(struct sk_buff_head *q){ struct sk_buff *skb; DBF_TEXT(trace, 5, __FUNCTION__); while ((skb = skb_dequeue(q))) { atomic_dec(&skb->users); dev_kfree_skb_irq(skb); }}static __inline__ intctc_checkalloc_buffer(struct channel *ch, int warn){ DBF_TEXT(trace, 5, __FUNCTION__); if ((ch->trans_skb == NULL) || (ch->flags & CHANNEL_FLAGS_BUFSIZE_CHANGED)) { if (ch->trans_skb != NULL) dev_kfree_skb(ch->trans_skb); clear_normalized_cda(&ch->ccw[1]); ch->trans_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC | GFP_DMA); if (ch->trans_skb == NULL) { if (warn) ctc_pr_warn( "%s: Couldn't alloc %s trans_skb\n", ch->id, (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); return -ENOMEM; } ch->ccw[1].count = ch->max_bufsize; if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) { dev_kfree_skb(ch->trans_skb); ch->trans_skb = NULL; if (warn) ctc_pr_warn( "%s: set_normalized_cda for %s " "trans_skb failed, dropping packets\n", ch->id, (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); return -ENOMEM; } ch->ccw[1].count = 0; ch->trans_skb_data = ch->trans_skb->data; ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED; } return 0;}/** * Dummy NOP action for statemachines */static voidfsm_action_nop(fsm_instance * fi, int event, void *arg){}/** * Actions for channel - statemachines. *****************************************************************************//** * Normal data has been send. Free the corresponding * skb (it's in io_queue), reset dev->tbusy and * revert to idle state. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static voidch_action_txdone(fsm_instance * fi, int event, void *arg){ struct channel *ch = (struct channel *) arg; struct net_device *dev = ch->netdev; struct ctc_priv *privptr = dev->priv; struct sk_buff *skb; int first = 1; int i; unsigned long duration; struct timespec done_stamp = xtime; DBF_TEXT(trace, 4, __FUNCTION__); duration = (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 + (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000; if (duration > ch->prof.tx_time) ch->prof.tx_time = duration; if (ch->irb->scsw.count != 0) ctc_pr_debug("%s: TX not complete, remaining %d bytes\n", dev->name, ch->irb->scsw.count); fsm_deltimer(&ch->timer); while ((skb = skb_dequeue(&ch->io_queue))) { privptr->stats.tx_packets++; privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH; if (first) { privptr->stats.tx_bytes += 2; first = 0; } atomic_dec(&skb->users); dev_kfree_skb_irq(skb); } spin_lock(&ch->collect_lock); clear_normalized_cda(&ch->ccw[4]); if (ch->collect_len > 0) { int rc; if (ctc_checkalloc_buffer(ch, 1)) { spin_unlock(&ch->collect_lock); return; } ch->trans_skb->tail = ch->trans_skb->data = ch->trans_skb_data; ch->trans_skb->len = 0; if (ch->prof.maxmulti < (ch->collect_len + 2)) ch->prof.maxmulti = ch->collect_len + 2; if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue)) ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue); *((__u16 *) skb_put(ch->trans_skb, 2)) = ch->collect_len + 2; i = 0; while ((skb = skb_dequeue(&ch->collect_queue))) { memcpy(skb_put(ch->trans_skb, skb->len), skb->data, skb->len); privptr->stats.tx_packets++; privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH; atomic_dec(&skb->users); dev_kfree_skb_irq(skb); i++; } ch->collect_len = 0; spin_unlock(&ch->collect_lock); ch->ccw[1].count = ch->trans_skb->len; fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); ch->prof.send_stamp = xtime; rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long) ch, 0xff, 0); ch->prof.doios_multi++; if (rc != 0) { privptr->stats.tx_dropped += i; privptr->stats.tx_errors += i; fsm_deltimer(&ch->timer); ccw_check_return_code(ch, rc, "chained TX"); } } else { spin_unlock(&ch->collect_lock); fsm_newstate(fi, CH_STATE_TXIDLE); } ctc_clear_busy(dev);}/** * Initial data is sent. * Notify device statemachine that we are up and * running. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static voidch_action_txidle(fsm_instance * fi, int event, void *arg){ struct channel *ch = (struct channel *) arg; DBF_TEXT(trace, 4, __FUNCTION__); fsm_deltimer(&ch->timer); fsm_newstate(fi, CH_STATE_TXIDLE); fsm_event(((struct ctc_priv *) ch->netdev->priv)->fsm, DEV_EVENT_TXUP, ch->netdev);}/** * Got normal data, check for sanity, queue it up, allocate new buffer * trigger bottom half, and initiate next read. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static voidch_action_rx(fsm_instance * fi, int event, void *arg){ struct channel *ch = (struct channel *) arg; struct net_device *dev = ch->netdev; struct ctc_priv *privptr = dev->priv; int len = ch->max_bufsize - ch->irb->scsw.count; struct sk_buff *skb = ch->trans_skb; __u16 block_len = *((__u16 *) skb->data); int check_len; int rc; DBF_TEXT(trace, 4, __FUNCTION__); fsm_deltimer(&ch->timer); if (len < 8) { ctc_pr_debug("%s: got packet with length %d < 8\n", dev->name, len); privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; goto again; } if (len > ch->max_bufsize) { ctc_pr_debug("%s: got packet with length %d > %d\n", dev->name, len, ch->max_bufsize); privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; goto again; } /** * VM TCP seems to have a bug sending 2 trailing bytes of garbage. */ switch (ch->protocol) { case CTC_PROTO_S390: case CTC_PROTO_OS390: check_len = block_len + 2; break; default: check_len = block_len; break; } if ((len < block_len) || (len > check_len)) { ctc_pr_debug("%s: got block length %d != rx length %d\n", dev->name, block_len, len);#ifdef DEBUG ctc_dump_skb(skb, 0);#endif *((__u16 *) skb->data) = len; privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; goto again; } block_len -= 2; if (block_len > 0) { *((__u16 *) skb->data) = block_len; ctc_unpack_skb(ch, skb); } again: skb->data = skb->tail = ch->trans_skb_data; skb->len = 0; if (ctc_checkalloc_buffer(ch, 1)) return; ch->ccw[1].count = ch->max_bufsize; rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long) ch, 0xff, 0); if (rc != 0) ccw_check_return_code(ch, rc, "normal RX");}static void ch_action_rxidle(fsm_instance * fi, int event, void *arg);/** * Initialize connection by sending a __u16 of value 0. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static voidch_action_firstio(fsm_instance * fi, int event, void *arg){ struct channel *ch = (struct channel *) arg; int rc; DBF_TEXT(trace, 4, __FUNCTION__); if (fsm_getstate(fi) == CH_STATE_TXIDLE) ctc_pr_debug("%s: remote side issued READ?, init ...\n", ch->id); fsm_deltimer(&ch->timer); if (ctc_checkalloc_buffer(ch, 1)) return; if ((fsm_getstate(fi) == CH_STATE_SETUPWAIT) && (ch->protocol == CTC_PROTO_OS390)) { /* OS/390 resp. z/OS */ if (CHANNEL_DIRECTION(ch->flags) == READ) { *((__u16 *) ch->trans_skb->data) = CTC_INITIAL_BLOCKLEN; fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); ch_action_rxidle(fi, event, arg); } else { struct net_device *dev = ch->netdev; fsm_newstate(fi, CH_STATE_TXIDLE); fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXUP, dev); } return; } /** * Don磘 setup a timer for receiving the initial RX frame * if in compatibility mode, since VM TCP delays the initial * frame until it has some data to send. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -