IGbE: Add support for TCP segment offload

This commit is contained in:
Ali Saidi 2008-12-05 13:58:21 -05:00
parent aab595a306
commit 400e516261
3 changed files with 243 additions and 37 deletions

View file

@ -883,45 +883,128 @@ IGbE::RxDescCache::unserialize(Checkpoint *cp, const std::string &section)
IGbE::TxDescCache::TxDescCache(IGbE *i, const std::string n, int s)
: DescCache<TxDesc>(i,n, s), pktDone(false), isTcp(false), pktWaiting(false),
pktEvent(this)
useTso(false), pktEvent(this), headerEvent(this)
{
}
int
IGbE::TxDescCache::getPacketSize()
void
IGbE::TxDescCache::processContextDesc()
{
assert(unusedCache.size());
TxDesc *desc;
DPRINTF(EthernetDesc, "Checking and processing context descriptors\n");
DPRINTF(EthernetDesc, "Starting processing of descriptor\n");
while (!useTso && unusedCache.size() && TxdOp::isContext(unusedCache.front())) {
DPRINTF(EthernetDesc, "Got context descriptor type...\n");
while (unusedCache.size() && TxdOp::isContext(unusedCache.front())) {
DPRINTF(EthernetDesc, "Got context descriptor type... skipping\n");
// I think we can just ignore these for now?
desc = unusedCache.front();
DPRINTF(EthernetDesc, "Descriptor upper: %#x lower: %#X\n", desc->d1,
desc->d2);
DPRINTF(EthernetDesc, "Descriptor upper: %#x lower: %#X\n",
desc->d1, desc->d2);
// is this going to be a tcp or udp packet?
isTcp = TxdOp::tcp(desc) ? true : false;
// make sure it's ipv4
//assert(TxdOp::ip(desc));
if (TxdOp::tse(desc)) {
DPRINTF(EthernetDesc, "TCP offload enabled for packet hdrlen: %d mss: %d paylen %d\n",
TxdOp::hdrlen(desc), TxdOp::mss(desc), TxdOp::getLen(desc));
// setup all the TSO variables
useTso = true;
tsoHeaderLen = TxdOp::hdrlen(desc);
tsoMss = TxdOp::mss(desc);
tsoTotalLen = TxdOp::getLen(desc);
tsoLoadedHeader = false;
tsoDescBytesUsed = 0;
tsoUsedLen = 0;
tsoPrevSeq = 0;
tsoPktHasHeader = false;
tsoPkts = 0;
}
TxdOp::setDd(desc);
unusedCache.pop_front();
usedCache.push_back(desc);
}
if (!unusedCache.size())
return;
if (useTso && !tsoLoadedHeader) {
// we need to fetch a header
DPRINTF(EthernetDesc, "Starting DMA of TSO header\n");
desc = unusedCache.front();
assert(TxdOp::isData(desc) && TxdOp::getLen(desc) >= tsoHeaderLen);
pktWaiting = true;
assert(tsoHeaderLen <= 256);
igbe->dmaRead(igbe->platform->pciToDma(TxdOp::getBuf(desc)),
tsoHeaderLen, &headerEvent, tsoHeader, 0);
}
}
void
IGbE::TxDescCache::headerComplete()
{
DPRINTF(EthernetDesc, "TSO: Fetching TSO header complete\n");
pktWaiting = false;
assert(unusedCache.size());
TxDesc *desc = unusedCache.front();
DPRINTF(EthernetDesc, "TSO: len: %d tsoHeaderLen: %d\n",
TxdOp::getLen(desc), tsoHeaderLen);
if (TxdOp::getLen(desc) == tsoHeaderLen) {
tsoDescBytesUsed = 0;
tsoLoadedHeader = true;
unusedCache.pop_front();
usedCache.push_back(desc);
} else {
// I don't think this case happens, I think the headrer is always
// it's own packet, if it wasn't it might be as simple as just
// incrementing descBytesUsed by the header length, but I'm not
// completely sure
panic("TSO header part of bigger packet, not implemented\n");
}
enableSm();
igbe->checkDrain();
}
int
IGbE::TxDescCache::getPacketSize(EthPacketPtr p)
{
TxDesc *desc;
if (!unusedCache.size())
return -1;
DPRINTF(EthernetDesc, "Starting processing of descriptor\n");
assert(!useTso || tsoLoadedHeader);
desc = unusedCache.front();
if (useTso) {
DPRINTF(EthernetDesc, "getPacket(): TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2);
DPRINTF(EthernetDesc, "TSO: use: %d hdrlen: %d mss: %d total: %d used: %d loaded hdr: %d\n",
useTso, tsoHeaderLen, tsoMss, tsoTotalLen, tsoUsedLen, tsoLoadedHeader);
DPRINTF(EthernetDesc, "TSO: descBytesUsed: %d copyBytes: %d this descLen: %d\n",
tsoDescBytesUsed, tsoCopyBytes, TxdOp::getLen(desc));
DPRINTF(EthernetDesc, "TSO: pktHasHeader: %d\n", tsoPktHasHeader);
if (tsoPktHasHeader)
tsoCopyBytes = std::min((tsoMss + tsoHeaderLen) - p->length, TxdOp::getLen(desc) - tsoDescBytesUsed);
else
tsoCopyBytes = std::min(tsoMss, TxdOp::getLen(desc) - tsoDescBytesUsed);
Addr pkt_size = tsoCopyBytes + (tsoPktHasHeader ? 0 : tsoHeaderLen);
DPRINTF(EthernetDesc, "TSO: Next packet is %d bytes\n", pkt_size);
return pkt_size;
}
DPRINTF(EthernetDesc, "Next TX packet is %d bytes\n",
TxdOp::getLen(unusedCache.front()));
return TxdOp::getLen(unusedCache.front());
TxdOp::getLen(unusedCache.front()));
return TxdOp::getLen(desc);
}
void
@ -939,10 +1022,29 @@ IGbE::TxDescCache::getPacketData(EthPacketPtr p)
pktWaiting = true;
DPRINTF(EthernetDesc, "Starting DMA of packet at offset %d\n", p->length);
igbe->dmaRead(igbe->platform->pciToDma(TxdOp::getBuf(desc)),
if (useTso) {
assert(tsoLoadedHeader);
if (!tsoPktHasHeader) {
DPRINTF(EthernetDesc, "Loading TSO header (%d bytes) into start of packet\n",
tsoHeaderLen);
memcpy(p->data, &tsoHeader,tsoHeaderLen);
p->length +=tsoHeaderLen;
tsoPktHasHeader = true;
}
}
if (useTso) {
tsoDescBytesUsed += tsoCopyBytes;
assert(tsoDescBytesUsed <= TxdOp::getLen(desc));
DPRINTF(EthernetDesc, "Starting DMA of packet at offset %d length: %d\n",
p->length, tsoCopyBytes);
igbe->dmaRead(igbe->platform->pciToDma(TxdOp::getBuf(desc)) + tsoDescBytesUsed,
tsoCopyBytes, &pktEvent, p->data + p->length, igbe->txReadDelay);
} else {
igbe->dmaRead(igbe->platform->pciToDma(TxdOp::getBuf(desc)),
TxdOp::getLen(desc), &pktEvent, p->data + p->length, igbe->txReadDelay);
}
}
void
@ -960,11 +1062,27 @@ IGbE::TxDescCache::pktComplete()
assert((TxdOp::isLegacy(desc) || TxdOp::isData(desc)) && TxdOp::getLen(desc));
DPRINTF(EthernetDesc, "TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2);
DPRINTF(EthernetDesc, "TSO: use: %d hdrlen: %d mss: %d total: %d used: %d loaded hdr: %d\n",
useTso, tsoHeaderLen, tsoMss, tsoTotalLen, tsoUsedLen, tsoLoadedHeader);
if (!TxdOp::eop(desc)) {
// Set the length of the data in the EtherPacket
if (useTso) {
pktPtr->length += tsoCopyBytes;
tsoUsedLen += tsoCopyBytes;
} else
pktPtr->length += TxdOp::getLen(desc);
DPRINTF(EthernetDesc, "TSO: descBytesUsed: %d copyBytes: %d\n",
tsoDescBytesUsed, tsoCopyBytes);
if ((!TxdOp::eop(desc) && !useTso) ||
(pktPtr->length < ( tsoMss + tsoHeaderLen) && tsoTotalLen != tsoUsedLen)) {
assert(!useTso || (tsoDescBytesUsed == TxdOp::getLen(desc)));
unusedCache.pop_front();
usedCache.push_back(desc);
tsoDescBytesUsed = 0;
pktDone = true;
pktWaiting = false;
pktMultiDesc = true;
@ -977,25 +1095,47 @@ IGbE::TxDescCache::pktComplete()
igbe->checkDrain();
return;
}
pktMultiDesc = false;
// Set the length of the data in the EtherPacket
pktPtr->length += TxdOp::getLen(desc);
// no support for vlans
assert(!TxdOp::vle(desc));
// we alway report status
assert(TxdOp::rs(desc));
// we only support single packet descriptors at this point
assert(TxdOp::eop(desc));
if (!useTso)
assert(TxdOp::eop(desc));
// set that this packet is done
TxdOp::setDd(desc);
if (TxdOp::rs(desc))
TxdOp::setDd(desc);
DPRINTF(EthernetDesc, "TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2);
if (useTso) {
IpPtr ip(pktPtr);
if (ip) {
DPRINTF(EthernetDesc, "TSO: Modifying IP header. Id + %d\n",
tsoPkts);
ip->id(ip->id() + tsoPkts++);
ip->len(pktPtr->length - EthPtr(pktPtr)->size());
TcpPtr tcp(ip);
if (tcp) {
DPRINTF(EthernetDesc, "TSO: Modifying TCP header. old seq %d + %d\n",
tcp->seq(), tsoPrevSeq);
tcp->seq(tcp->seq() + tsoPrevSeq);
if (tsoUsedLen != tsoTotalLen)
tcp->flags(tcp->flags() & ~9); // clear fin & psh
}
UdpPtr udp(ip);
if (udp) {
DPRINTF(EthernetDesc, "TSO: Modifying UDP header.\n");
udp->len(pktPtr->length - EthPtr(pktPtr)->size());
}
}
tsoPrevSeq = tsoUsedLen;
}
if (DTRACE(EthernetDesc)) {
IpPtr ip(pktPtr);
if (ip)
@ -1055,14 +1195,23 @@ IGbE::TxDescCache::pktComplete()
}
if (!useTso || TxdOp::getLen(desc) == tsoDescBytesUsed) {
DPRINTF(EthernetDesc, "Descriptor Done\n");
unusedCache.pop_front();
usedCache.push_back(desc);
tsoDescBytesUsed = 0;
}
unusedCache.pop_front();
usedCache.push_back(desc);
if (useTso && tsoUsedLen == tsoTotalLen)
useTso = false;
DPRINTF(EthernetDesc, "------Packet of %d bytes ready for transmission-------\n",
pktPtr->length);
pktDone = true;
pktWaiting = false;
pktPtr = NULL;
DPRINTF(EthernetDesc, "Descriptor Done\n");
tsoPktHasHeader = false;
if (igbe->regs.txdctl.wthresh() == 0) {
DPRINTF(EthernetDesc, "WTHRESH == 0, writing back descriptor\n");
@ -1083,6 +1232,22 @@ IGbE::TxDescCache::serialize(std::ostream &os)
SERIALIZE_SCALAR(isTcp);
SERIALIZE_SCALAR(pktWaiting);
SERIALIZE_SCALAR(pktMultiDesc);
SERIALIZE_SCALAR(useTso);
SERIALIZE_SCALAR(tsoHeaderLen);
SERIALIZE_SCALAR(tsoMss);
SERIALIZE_SCALAR(tsoTotalLen);
SERIALIZE_SCALAR(tsoUsedLen);
SERIALIZE_SCALAR(tsoPrevSeq);;
SERIALIZE_SCALAR(tsoPktPayloadBytes);
SERIALIZE_SCALAR(tsoLoadedHeader);
SERIALIZE_SCALAR(tsoPktHasHeader);
SERIALIZE_ARRAY(tsoHeader, 256);
SERIALIZE_SCALAR(tsoDescBytesUsed);
SERIALIZE_SCALAR(tsoCopyBytes);
SERIALIZE_SCALAR(tsoPkts);
}
void
@ -1093,6 +1258,20 @@ IGbE::TxDescCache::unserialize(Checkpoint *cp, const std::string &section)
UNSERIALIZE_SCALAR(isTcp);
UNSERIALIZE_SCALAR(pktWaiting);
UNSERIALIZE_SCALAR(pktMultiDesc);
UNSERIALIZE_SCALAR(useTso);
UNSERIALIZE_SCALAR(tsoHeaderLen);
UNSERIALIZE_SCALAR(tsoMss);
UNSERIALIZE_SCALAR(tsoTotalLen);
UNSERIALIZE_SCALAR(tsoUsedLen);
UNSERIALIZE_SCALAR(tsoPrevSeq);;
UNSERIALIZE_SCALAR(tsoPktPayloadBytes);
UNSERIALIZE_SCALAR(tsoLoadedHeader);
UNSERIALIZE_SCALAR(tsoPktHasHeader);
UNSERIALIZE_ARRAY(tsoHeader, 256);
UNSERIALIZE_SCALAR(tsoDescBytesUsed);
UNSERIALIZE_SCALAR(tsoCopyBytes);
UNSERIALIZE_SCALAR(tsoPkts);
}
bool
@ -1242,8 +1421,15 @@ IGbE::txStateMachine()
}
txDescCache.processContextDesc();
if (txDescCache.packetWaiting()) {
DPRINTF(EthernetSM, "TXS: Fetching TSO header, stopping ticking\n");
txTick = false;
return;
}
int size;
size = txDescCache.getPacketSize();
size = txDescCache.getPacketSize(txPacket);
if (size > 0 && txFifo.avail() > size) {
DPRINTF(EthernetSM, "TXS: Reserving %d bytes in FIFO and begining "
"DMA of next packet\n", size);

View file

@ -462,8 +462,8 @@ class IGbE : public EtherDevice
int descLeft() const
{
int left = unusedCache.size();
if (cachePnt - descTail() >= 0)
left += (cachePnt - descTail());
if (cachePnt >= descTail())
left += (descLen() - cachePnt + descTail());
else
left += (descTail() - cachePnt);
@ -636,6 +636,21 @@ class IGbE : public EtherDevice
bool pktWaiting;
bool pktMultiDesc;
// tso variables
bool useTso;
Addr tsoHeaderLen;
Addr tsoMss;
Addr tsoTotalLen;
Addr tsoUsedLen;
Addr tsoPrevSeq;;
Addr tsoPktPayloadBytes;
bool tsoLoadedHeader;
bool tsoPktHasHeader;
uint8_t tsoHeader[256];
Addr tsoDescBytesUsed;
Addr tsoCopyBytes;
int tsoPkts;
public:
TxDescCache(IGbE *i, std::string n, int s);
@ -643,8 +658,9 @@ class IGbE : public EtherDevice
* return the size the of the packet to reserve space in tx fifo.
* @return size of the packet
*/
int getPacketSize();
int getPacketSize(EthPacketPtr p);
void getPacketData(EthPacketPtr p);
void processContextDesc();
/** Ask if the packet has been transfered so the state machine can give
* it to the fifo.
@ -670,6 +686,9 @@ class IGbE : public EtherDevice
void pktComplete();
EventWrapper<TxDescCache, &TxDescCache::pktComplete> pktEvent;
void headerComplete();
EventWrapper<TxDescCache, &TxDescCache::headerComplete> headerEvent;
virtual bool hasOutstandingEvents();
virtual void serialize(std::ostream &os);

View file

@ -200,6 +200,7 @@ int ipcso(TxDesc *d) { assert(isContext(d)); return bits(d->d1,15,8); }
int ipcss(TxDesc *d) { assert(isContext(d)); return bits(d->d1,7,0); }
int mss(TxDesc *d) { assert(isContext(d)); return bits(d->d2,63,48); }
int hdrlen(TxDesc *d) { assert(isContext(d)); return bits(d->d2,47,40); }
int utcmd(TxDesc *d) { assert(isContext(d)); return bits(d->d2,24,31); }
} // namespace TxdOp