ipath_verbs.c
来自「linux 内核源代码」· C语言 代码 · 共 1,887 行 · 第 1/4 页
C
1,887 行
case IB_QPT_RC: ipath_rc_rcv(dev, hdr, has_grh, data, tlen, qp); break; case IB_QPT_UC: ipath_uc_rcv(dev, hdr, has_grh, data, tlen, qp); break; default: break; }}/** * ipath_ib_rcv - process an incoming packet * @arg: the device pointer * @rhdr: the header of the packet * @data: the packet data * @tlen: the packet length * * This is called from ipath_kreceive() to process an incoming packet at * interrupt level. Tlen is the length of the header + data + CRC in bytes. */void ipath_ib_rcv(struct ipath_ibdev *dev, void *rhdr, void *data, u32 tlen){ struct ipath_ib_header *hdr = rhdr; struct ipath_other_headers *ohdr; struct ipath_qp *qp; u32 qp_num; int lnh; u8 opcode; u16 lid; if (unlikely(dev == NULL)) goto bail; if (unlikely(tlen < 24)) { /* LRH+BTH+CRC */ dev->rcv_errors++; goto bail; } /* Check for a valid destination LID (see ch. 7.11.1). */ lid = be16_to_cpu(hdr->lrh[1]); if (lid < IPATH_MULTICAST_LID_BASE) { lid &= ~((1 << dev->dd->ipath_lmc) - 1); if (unlikely(lid != dev->dd->ipath_lid)) { dev->rcv_errors++; goto bail; } } /* Check for GRH */ lnh = be16_to_cpu(hdr->lrh[0]) & 3; if (lnh == IPATH_LRH_BTH) ohdr = &hdr->u.oth; else if (lnh == IPATH_LRH_GRH) ohdr = &hdr->u.l.oth; else { dev->rcv_errors++; goto bail; } opcode = be32_to_cpu(ohdr->bth[0]) >> 24; dev->opstats[opcode].n_bytes += tlen; dev->opstats[opcode].n_packets++; /* Get the destination QP number. */ qp_num = be32_to_cpu(ohdr->bth[1]) & IPATH_QPN_MASK; if (qp_num == IPATH_MULTICAST_QPN) { struct ipath_mcast *mcast; struct ipath_mcast_qp *p; if (lnh != IPATH_LRH_GRH) { dev->n_pkt_drops++; goto bail; } mcast = ipath_mcast_find(&hdr->u.l.grh.dgid); if (mcast == NULL) { dev->n_pkt_drops++; goto bail; } dev->n_multicast_rcv++; list_for_each_entry_rcu(p, &mcast->qp_list, list) ipath_qp_rcv(dev, hdr, 1, data, tlen, p->qp); /* * Notify ipath_multicast_detach() if it is waiting for us * to finish. */ if (atomic_dec_return(&mcast->refcount) <= 1) wake_up(&mcast->wait); } else { qp = ipath_lookup_qpn(&dev->qp_table, qp_num); if (qp) { dev->n_unicast_rcv++; ipath_qp_rcv(dev, hdr, lnh == IPATH_LRH_GRH, data, tlen, qp); /* * Notify ipath_destroy_qp() if it is waiting * for us to finish. */ if (atomic_dec_and_test(&qp->refcount)) wake_up(&qp->wait); } else dev->n_pkt_drops++; }bail:;}/** * ipath_ib_timer - verbs timer * @arg: the device pointer * * This is called from ipath_do_rcv_timer() at interrupt level to check for * QPs which need retransmits and to collect performance numbers. */static void ipath_ib_timer(struct ipath_ibdev *dev){ struct ipath_qp *resend = NULL; struct list_head *last; struct ipath_qp *qp; unsigned long flags; if (dev == NULL) return; spin_lock_irqsave(&dev->pending_lock, flags); /* Start filling the next pending queue. */ if (++dev->pending_index >= ARRAY_SIZE(dev->pending)) dev->pending_index = 0; /* Save any requests still in the new queue, they have timed out. */ last = &dev->pending[dev->pending_index]; while (!list_empty(last)) { qp = list_entry(last->next, struct ipath_qp, timerwait); list_del_init(&qp->timerwait); qp->timer_next = resend; resend = qp; atomic_inc(&qp->refcount); } last = &dev->rnrwait; if (!list_empty(last)) { qp = list_entry(last->next, struct ipath_qp, timerwait); if (--qp->s_rnr_timeout == 0) { do { list_del_init(&qp->timerwait); tasklet_hi_schedule(&qp->s_task); if (list_empty(last)) break; qp = list_entry(last->next, struct ipath_qp, timerwait); } while (qp->s_rnr_timeout == 0); } } /* * We should only be in the started state if pma_sample_start != 0 */ if (dev->pma_sample_status == IB_PMA_SAMPLE_STATUS_STARTED && --dev->pma_sample_start == 0) { dev->pma_sample_status = IB_PMA_SAMPLE_STATUS_RUNNING; ipath_snapshot_counters(dev->dd, &dev->ipath_sword, &dev->ipath_rword, &dev->ipath_spkts, &dev->ipath_rpkts, &dev->ipath_xmit_wait); } if (dev->pma_sample_status == IB_PMA_SAMPLE_STATUS_RUNNING) { if (dev->pma_sample_interval == 0) { u64 ta, tb, tc, td, te; dev->pma_sample_status = IB_PMA_SAMPLE_STATUS_DONE; ipath_snapshot_counters(dev->dd, &ta, &tb, &tc, &td, &te); dev->ipath_sword = ta - dev->ipath_sword; dev->ipath_rword = tb - dev->ipath_rword; dev->ipath_spkts = tc - dev->ipath_spkts; dev->ipath_rpkts = td - dev->ipath_rpkts; dev->ipath_xmit_wait = te - dev->ipath_xmit_wait; } else dev->pma_sample_interval--; } spin_unlock_irqrestore(&dev->pending_lock, flags); /* XXX What if timer fires again while this is running? */ for (qp = resend; qp != NULL; qp = qp->timer_next) { struct ib_wc wc; spin_lock_irqsave(&qp->s_lock, flags); if (qp->s_last != qp->s_tail && qp->state == IB_QPS_RTS) { dev->n_timeouts++; ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); } spin_unlock_irqrestore(&qp->s_lock, flags); /* Notify ipath_destroy_qp() if it is waiting. */ if (atomic_dec_and_test(&qp->refcount)) wake_up(&qp->wait); }}static void update_sge(struct ipath_sge_state *ss, u32 length){ struct ipath_sge *sge = &ss->sge; sge->vaddr += length; sge->length -= length; sge->sge_length -= length; if (sge->sge_length == 0) { if (--ss->num_sge) *sge = *ss->sg_list++; } else if (sge->length == 0 && sge->mr != NULL) { if (++sge->n >= IPATH_SEGSZ) { if (++sge->m >= sge->mr->mapsz) return; sge->n = 0; } sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr; sge->length = sge->mr->map[sge->m]->segs[sge->n].length; }}#ifdef __LITTLE_ENDIANstatic inline u32 get_upper_bits(u32 data, u32 shift){ return data >> shift;}static inline u32 set_upper_bits(u32 data, u32 shift){ return data << shift;}static inline u32 clear_upper_bytes(u32 data, u32 n, u32 off){ data <<= ((sizeof(u32) - n) * BITS_PER_BYTE); data >>= ((sizeof(u32) - n - off) * BITS_PER_BYTE); return data;}#elsestatic inline u32 get_upper_bits(u32 data, u32 shift){ return data << shift;}static inline u32 set_upper_bits(u32 data, u32 shift){ return data >> shift;}static inline u32 clear_upper_bytes(u32 data, u32 n, u32 off){ data >>= ((sizeof(u32) - n) * BITS_PER_BYTE); data <<= ((sizeof(u32) - n - off) * BITS_PER_BYTE); return data;}#endifstatic void copy_io(u32 __iomem *piobuf, struct ipath_sge_state *ss, u32 length, unsigned flush_wc){ u32 extra = 0; u32 data = 0; u32 last; while (1) { u32 len = ss->sge.length; u32 off; if (len > length) len = length; if (len > ss->sge.sge_length) len = ss->sge.sge_length; BUG_ON(len == 0); /* If the source address is not aligned, try to align it. */ off = (unsigned long)ss->sge.vaddr & (sizeof(u32) - 1); if (off) { u32 *addr = (u32 *)((unsigned long)ss->sge.vaddr & ~(sizeof(u32) - 1)); u32 v = get_upper_bits(*addr, off * BITS_PER_BYTE); u32 y; y = sizeof(u32) - off; if (len > y) len = y; if (len + extra >= sizeof(u32)) { data |= set_upper_bits(v, extra * BITS_PER_BYTE); len = sizeof(u32) - extra; if (len == length) { last = data; break; } __raw_writel(data, piobuf); piobuf++; extra = 0; data = 0; } else { /* Clear unused upper bytes */ data |= clear_upper_bytes(v, len, extra); if (len == length) { last = data; break; } extra += len; } } else if (extra) { /* Source address is aligned. */ u32 *addr = (u32 *) ss->sge.vaddr; int shift = extra * BITS_PER_BYTE; int ushift = 32 - shift; u32 l = len; while (l >= sizeof(u32)) { u32 v = *addr; data |= set_upper_bits(v, shift); __raw_writel(data, piobuf); data = get_upper_bits(v, ushift); piobuf++; addr++; l -= sizeof(u32); } /* * We still have 'extra' number of bytes leftover. */ if (l) { u32 v = *addr; if (l + extra >= sizeof(u32)) { data |= set_upper_bits(v, shift); len -= l + extra - sizeof(u32); if (len == length) { last = data; break; } __raw_writel(data, piobuf); piobuf++; extra = 0; data = 0; } else { /* Clear unused upper bytes */ data |= clear_upper_bytes(v, l, extra); if (len == length) { last = data; break; } extra += l; } } else if (len == length) { last = data; break; } } else if (len == length) { u32 w; /* * Need to round up for the last dword in the * packet. */ w = (len + 3) >> 2; __iowrite32_copy(piobuf, ss->sge.vaddr, w - 1); piobuf += w - 1; last = ((u32 *) ss->sge.vaddr)[w - 1]; break; } else { u32 w = len >> 2; __iowrite32_copy(piobuf, ss->sge.vaddr, w); piobuf += w; extra = len & (sizeof(u32) - 1); if (extra) { u32 v = ((u32 *) ss->sge.vaddr)[w]; /* Clear unused upper bytes */ data = clear_upper_bytes(v, extra, 0); } } update_sge(ss, len); length -= len; } /* Update address before sending packet. */ update_sge(ss, length); if (flush_wc) { /* must flush early everything before trigger word */ ipath_flush_wc(); __raw_writel(last, piobuf); /* be sure trigger word is written */ ipath_flush_wc(); } else __raw_writel(last, piobuf);}static int ipath_verbs_send_pio(struct ipath_qp *qp, u32 *hdr, u32 hdrwords, struct ipath_sge_state *ss, u32 len, u32 plen, u32 dwords){ struct ipath_devdata *dd = to_idev(qp->ibqp.device)->dd; u32 __iomem *piobuf; unsigned flush_wc; int ret; piobuf = ipath_getpiobuf(dd, NULL); if (unlikely(piobuf == NULL)) { ret = -EBUSY; goto bail; } /* * Write len to control qword, no flags. * We have to flush after the PBC for correctness on some cpus * or WC buffer can be written out of order. */ writeq(plen, piobuf); piobuf += 2; flush_wc = dd->ipath_flags & IPATH_PIO_FLUSH_WC; if (len == 0) { /* * If there is just the header portion, must flush before * writing last word of header for correctness, and after * the last header word (trigger word). */ if (flush_wc) { ipath_flush_wc(); __iowrite32_copy(piobuf, hdr, hdrwords - 1); ipath_flush_wc(); __raw_writel(hdr[hdrwords - 1], piobuf + hdrwords - 1); ipath_flush_wc(); } else __iowrite32_copy(piobuf, hdr, hdrwords); goto done; } if (flush_wc) ipath_flush_wc(); __iowrite32_copy(piobuf, hdr, hdrwords); piobuf += hdrwords; /* The common case is aligned and contained in one segment. */ if (likely(ss->num_sge == 1 && len <= ss->sge.length && !((unsigned long)ss->sge.vaddr & (sizeof(u32) - 1)))) { u32 *addr = (u32 *) ss->sge.vaddr; /* Update address before sending packet. */ update_sge(ss, len); if (flush_wc) { __iowrite32_copy(piobuf, addr, dwords - 1); /* must flush early everything before trigger word */ ipath_flush_wc(); __raw_writel(addr[dwords - 1], piobuf + dwords - 1); /* be sure trigger word is written */ ipath_flush_wc(); } else __iowrite32_copy(piobuf, addr, dwords); goto done; } copy_io(piobuf, ss, len, flush_wc);done: if (qp->s_wqe) ipath_send_complete(qp, qp->s_wqe, IB_WC_SUCCESS); ret = 0;bail: return ret;}/** * ipath_verbs_send - send a packet * @qp: the QP to send on
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?