original files of m241
This commit is contained in:
@@ -0,0 +1,539 @@
|
||||
/* qosIngressHooks.c - sample packet classification routines for ingress QoS*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005-2006 Wind River Systems, Inc.
|
||||
*
|
||||
* The right to copy, distribute, modify or otherwise make use
|
||||
* of this software may be licensed only pursuant to the terms
|
||||
* of an applicable Wind River license agreement.
|
||||
*/
|
||||
|
||||
/*
|
||||
modification history
|
||||
--------------------
|
||||
01f,20sep10,h_x Use a new marco (QOS_LOG) instead of KERN_LOG
|
||||
01e,22sep09,zhr Fixed WIND00180271
|
||||
01d,08sep06,kch Cleanup for IPNet integration.
|
||||
01c,03apr06,kch Guarded the entire file with INGRESS_QOS instead of QOS
|
||||
(SPR#119839).
|
||||
01b,26aug05,kch Replaced old vlan tag macros with the new macro from
|
||||
muxL2Lib.h. Also fixed warnings.
|
||||
01a,10aug05,niq Written.
|
||||
*/
|
||||
|
||||
/*
|
||||
DESCRIPTION
|
||||
This library provides some sample packet classifiers to enable QoS on ingress.
|
||||
These routines are provided for demonstration purposes and to enable the
|
||||
customers to develop their own quickly. These packet classifiers can be
|
||||
registered with the QoS facility using the qosIngressHookSet() API.
|
||||
|
||||
To include this library in the vxWorks image, add the
|
||||
INCLUDE_QOS_INGRESS_HOOKS define to the bsp's configuration header file.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef INGRESS_QOS
|
||||
|
||||
#include <vxWorks.h>
|
||||
#include <errnoLib.h>
|
||||
|
||||
#include <endLib.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/if_ether.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <dlink/qosIngressLib.h> /* public qosLib header file */
|
||||
#include <muxL2Lib.h>
|
||||
#include <applUtilLib.h>
|
||||
#include <syslog.h>
|
||||
|
||||
/* IP */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Defines */
|
||||
|
||||
#ifndef ETHERTYPE_TIPC
|
||||
#define ETHERTYPE_TIPC 0x88CA
|
||||
#endif
|
||||
|
||||
|
||||
/* Locals */
|
||||
LOCAL u_short telnetPort;
|
||||
|
||||
/* Forward declarations */
|
||||
|
||||
/* qosIngressHooks.c */
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* qosIngressHooksInit - QoS ingress hooks initialization routine
|
||||
*
|
||||
* This is a dummy initialization routine to pull in this file into the
|
||||
* image when INCLUDE_QOS_INGRESS_HOOKS is define in the bsp configuration
|
||||
* header file.
|
||||
*
|
||||
* RETURNS: OK.
|
||||
*
|
||||
* ERRNO:
|
||||
*
|
||||
* \NOMANUAL
|
||||
*
|
||||
* \INTERNAL
|
||||
*
|
||||
*/
|
||||
|
||||
STATUS qosIngressHooksInit()
|
||||
{
|
||||
telnetPort = ntohs (23);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* etherQosHook - classify packets based on ether-type protocol
|
||||
*
|
||||
* This routine classifies packets based on the ether-type. It accords "urgent"
|
||||
* status to the TIPC (Transparent Inter Process Communication) traffic and
|
||||
* defers the rest at varying priorities.
|
||||
*
|
||||
* This routine is provided for demonstration only.
|
||||
*
|
||||
* RETURNS: QOS_DELIVER_PKT, QOS_DEFER_PKT or QOS_IGNORE
|
||||
*/
|
||||
|
||||
QOS_HOOK_RETVAL etherQosHook
|
||||
(
|
||||
END_OBJ * pEnd, /* END device on which packet was received */
|
||||
M_BLK_ID * ppMblk, /* Received packet */
|
||||
int * pPri /* Location for deferred priority */
|
||||
)
|
||||
{
|
||||
struct ether_header *pEnetHdr;
|
||||
register M_BLK_ID pMblk = *ppMblk;
|
||||
|
||||
/* Ensure that the entire ether_header is in first M_BLK. */
|
||||
|
||||
if (pMblk->mBlkHdr.mLen < SIZEOF_ETHERHEADER)
|
||||
{
|
||||
if ((pMblk = m_pullup (pMblk, SIZEOF_ETHERHEADER)) == NULL)
|
||||
{
|
||||
log_err (QOS_LOG, "failed to get ether_header. Dropping packet\n");
|
||||
return (QOS_IGNORE_PKT);
|
||||
}
|
||||
*ppMblk = pMblk;
|
||||
}
|
||||
pEnetHdr = (struct ether_header *)pMblk->mBlkHdr.mData;
|
||||
|
||||
switch (ntohs(pEnetHdr->ether_type))
|
||||
{
|
||||
case ETHERTYPE_TIPC:
|
||||
return QOS_DELIVER_PKT; /* urgent traffic */
|
||||
|
||||
case ETHERTYPE_IP:
|
||||
*pPri = 18; /* Defer at priority 18 */
|
||||
return QOS_DEFER_PKT;
|
||||
|
||||
case ETHERTYPE_IPV6:
|
||||
*pPri = 16; /* Defer at priority 16 */
|
||||
return QOS_DEFER_PKT;
|
||||
|
||||
case ETHERTYPE_PUP:
|
||||
m_freem (pMblk); /* Drop traffic */
|
||||
return QOS_IGNORE_PKT;
|
||||
|
||||
default:
|
||||
*pPri = -1; /* Defer at default priority */
|
||||
return QOS_DEFER_PKT;
|
||||
}
|
||||
|
||||
/* Control not reached */
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* vlanQosHook - classify packets based on VLAN priority
|
||||
*
|
||||
* This routine classifies packets based on the priority field in the VLAN
|
||||
* header. The VLAN header has a 3-bit priority field ranging from 0-7
|
||||
* that indicates the frame's priority. This routine accords "urgent"
|
||||
* status to priority '7' and queues all other traffic at the priority
|
||||
* indicated in the priority field.
|
||||
*
|
||||
* This routine is provided for demonstration only.
|
||||
*
|
||||
* RETURNS: QOS_DELIVER_PKT, QOS_DEFER_PKT or QOS_IGNORE_PKT.
|
||||
*/
|
||||
|
||||
QOS_HOOK_RETVAL vlanQosHook
|
||||
(
|
||||
END_OBJ * pEnd, /* END device on which packet was received */
|
||||
M_BLK_ID * ppMblk, /* Received packet */
|
||||
int * pPri /* Location for deferred priority */
|
||||
)
|
||||
{
|
||||
|
||||
struct ether_header * pEnetHdr;
|
||||
struct ether_tag_header * pTagHeader;
|
||||
ushort_t qTag;
|
||||
int pri;
|
||||
register M_BLK_ID pMblk = *ppMblk;
|
||||
|
||||
/* Ensure that the entire ether_tag_header is in first M_BLK. */
|
||||
|
||||
if (pMblk->mBlkHdr.mLen < sizeof (struct ether_tag_header))
|
||||
{
|
||||
if ((pMblk = m_pullup (pMblk, sizeof (struct ether_tag_header))) ==
|
||||
NULL)
|
||||
{
|
||||
log_err (QOS_LOG, "failed to get ether_tag header. Dropping "
|
||||
"packet\n");
|
||||
return (QOS_IGNORE_PKT);
|
||||
}
|
||||
*ppMblk = pMblk;
|
||||
}
|
||||
pEnetHdr = (struct ether_header *)pMblk->mBlkHdr.mData;
|
||||
|
||||
/*
|
||||
* For simplicity, we only check for ETHERNET encoding, not 802.3
|
||||
* addressing
|
||||
*/
|
||||
|
||||
if (ntohs(pEnetHdr->ether_type) != ETHERTYPE_VLAN)
|
||||
{
|
||||
/* Not a VLAN tag, just defer at the default priority */
|
||||
|
||||
*pPri = -1;
|
||||
return QOS_DEFER_PKT;
|
||||
}
|
||||
|
||||
/* Extract the vlan tag and priority */
|
||||
|
||||
pTagHeader = (struct ether_tag_header *) pEnetHdr;
|
||||
|
||||
qTag = ((ushort_t)(pTagHeader->etherTag[2]) << 8) |
|
||||
pTagHeader->etherTag[3];
|
||||
|
||||
pri = GET_VLAN_USER_PRIORITY(qTag);
|
||||
|
||||
switch (pri)
|
||||
{
|
||||
case 7:
|
||||
return QOS_DELIVER_PKT; /* urgent traffic */
|
||||
|
||||
case 0:
|
||||
/* Best effort. defer at priority 3 */
|
||||
*pPri = 3;
|
||||
return QOS_DEFER_PKT;
|
||||
|
||||
default:
|
||||
*pPri = pri; /* Defer at given priority */
|
||||
return QOS_DEFER_PKT;
|
||||
}
|
||||
|
||||
/* Control not reached */
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* ipProtoQosHook - classify packets based on the IP protocol and port # field
|
||||
*
|
||||
* This routine classifies packets based on the IP protocol and the protocol's
|
||||
* port number fields. It accords "urgent" status to telnet and voice traffic
|
||||
* and defers the rest at the default priority.
|
||||
*
|
||||
* This routine is provided for demonstration only.
|
||||
*
|
||||
* RETURNS: QOS_DELIVER_PKT, QOS_DEFER_PKT or QOS_IGNORE_PKT.
|
||||
*/
|
||||
|
||||
QOS_HOOK_RETVAL ipProtoQosHook
|
||||
(
|
||||
END_OBJ * pEnd, /* END device on which packet was received */
|
||||
M_BLK_ID * ppMblk, /* Received packet */
|
||||
int * pPri /* Location for deferred priority */
|
||||
)
|
||||
{
|
||||
|
||||
struct ether_header * pEnetHdr;
|
||||
int iphlen;
|
||||
struct ip * ip;
|
||||
struct udphdr * udp;
|
||||
struct tcphdr * tcp;
|
||||
register M_BLK_ID pMblk = *ppMblk;
|
||||
int dataOffset;
|
||||
long etherType;
|
||||
#if 0
|
||||
LL_HDR_INFO llHdrInfo;
|
||||
|
||||
|
||||
/*
|
||||
* For simplicity and speed, we just process the standard Ethernet frames.
|
||||
* For comprehensiveness, use the packetDataGet routine to strip the link
|
||||
* header
|
||||
*/
|
||||
|
||||
if (pEnd->pFuncTable->packetDataGet == NULL ||
|
||||
pEnd->pFuncTable->packetDataGet (pMblk, &llHdrInfo) != OK)
|
||||
{
|
||||
log_err (QOS_LOG, "failed to get packet data. Dropping packet\n");
|
||||
m_freem (pMblk);
|
||||
return (QOS_IGNORE_PKT);
|
||||
}
|
||||
|
||||
etherType = llHdrInfo.pktType;
|
||||
dataOffset = pLinkHdrInfo->dataOffset;
|
||||
|
||||
#else
|
||||
|
||||
/* Ensure that the entire ether_header is in first M_BLK. */
|
||||
|
||||
if (pMblk->mBlkHdr.mLen < SIZEOF_ETHERHEADER)
|
||||
{
|
||||
if ((pMblk = m_pullup (pMblk, SIZEOF_ETHERHEADER)) == NULL)
|
||||
{
|
||||
log_err (QOS_LOG, "failed to get ether_header. Dropping packet\n");
|
||||
return (QOS_IGNORE_PKT);
|
||||
}
|
||||
*ppMblk = pMblk;
|
||||
}
|
||||
pEnetHdr = (struct ether_header *)pMblk->mBlkHdr.mData;
|
||||
if (pEnetHdr == NULL)
|
||||
return QOS_HOOK_FAIL;
|
||||
etherType = ntohs(pEnetHdr->ether_type);
|
||||
dataOffset = SIZEOF_ETHERHEADER;
|
||||
#endif
|
||||
|
||||
if (etherType != ETHERTYPE_IP)
|
||||
{
|
||||
/*
|
||||
* Not an IP packet, just defer at the default priority
|
||||
*/
|
||||
|
||||
*pPri = -1;
|
||||
return QOS_DEFER_PKT;
|
||||
}
|
||||
|
||||
if (pMblk->m_len < (dataOffset + sizeof (*ip)))
|
||||
{
|
||||
/* Make sure that the IP header is in the first mblk */
|
||||
|
||||
if ((pMblk = m_pullup (pMblk, dataOffset + sizeof (*ip))) == NULL)
|
||||
{
|
||||
log_err (QOS_LOG, "failed to get ip. Dropping packet\n");
|
||||
return (QOS_IGNORE_PKT);
|
||||
}
|
||||
*ppMblk = pMblk;
|
||||
}
|
||||
|
||||
ip = (struct ip *)(pMblk->m_data + dataOffset);
|
||||
|
||||
switch (ip->ip_p)
|
||||
{
|
||||
case IPPROTO_TCP:
|
||||
iphlen = ip->ip_hl << 2;
|
||||
if (pMblk->m_len < (dataOffset + iphlen + sizeof (*tcp)))
|
||||
{
|
||||
/* Make sure that the TCP header is in the first mblk */
|
||||
|
||||
if ((pMblk = m_pullup (pMblk, dataOffset + iphlen +
|
||||
sizeof (*tcp))) == NULL)
|
||||
{
|
||||
log_err (QOS_LOG, "failed to get tcp. Dropping packet\n");
|
||||
return (QOS_IGNORE_PKT);
|
||||
}
|
||||
*ppMblk = pMblk;
|
||||
}
|
||||
tcp = (struct tcphdr *)(pMblk->m_data + dataOffset + iphlen);
|
||||
|
||||
/*
|
||||
* If it's telnet traffic, process it urgently, else defer at
|
||||
* default priority. Note that telnetPort is already in
|
||||
* network byte order.
|
||||
*/
|
||||
|
||||
if (tcp->th_dport == telnetPort || tcp->th_sport == telnetPort)
|
||||
return QOS_DELIVER_PKT; /* urgent traffic */
|
||||
|
||||
*pPri = -1;
|
||||
return QOS_DEFER_PKT; /* defer at default priority */
|
||||
|
||||
case IPPROTO_UDP:
|
||||
iphlen = ip->ip_hl << 2;
|
||||
if (pMblk->m_len < (dataOffset + iphlen + sizeof (*udp)))
|
||||
{
|
||||
/* Make sure that the udp header is in the first mblk */
|
||||
|
||||
if ((pMblk = m_pullup (pMblk, dataOffset + iphlen +
|
||||
sizeof (*udp))) == NULL)
|
||||
{
|
||||
log_err (QOS_LOG, "failed to get udp. Dropping packet\n");
|
||||
return (QOS_IGNORE_PKT);
|
||||
}
|
||||
*ppMblk = pMblk;
|
||||
}
|
||||
|
||||
udp = (struct udphdr *)(pMblk->m_data + dataOffset + iphlen);
|
||||
|
||||
/*
|
||||
* The UDP port space is divided up into four priority ranges:
|
||||
* [0, 16384) : unclassified - default priority
|
||||
* [16384, 32768) : audio - highest priority
|
||||
* [32768, 49152) : white-board - medium priority
|
||||
* [49152, 65536) : video - low priority
|
||||
*
|
||||
* Everything else gets default priority.
|
||||
*/
|
||||
|
||||
switch (ntohs (udp->uh_dport) & 0xc000)
|
||||
{
|
||||
case 0x4000: /* audio traffic */
|
||||
return QOS_DELIVER_PKT;
|
||||
|
||||
case 0x8000:
|
||||
*pPri = 20; /* defer at medium priority */
|
||||
return QOS_DEFER_PKT;
|
||||
|
||||
case 0xc000:
|
||||
*pPri = 10; /* defer at low priority */
|
||||
return QOS_DEFER_PKT;
|
||||
|
||||
default:
|
||||
*pPri = -1; /* defer at default priority */
|
||||
return QOS_DEFER_PKT;
|
||||
}
|
||||
|
||||
/* Control not reached */
|
||||
|
||||
|
||||
default:
|
||||
*pPri = -1; /* Defer at default priority */
|
||||
return QOS_DEFER_PKT;
|
||||
}
|
||||
|
||||
/* Control not reached */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* dscpQosHook - classify packets based on the DSCP values
|
||||
*
|
||||
* This routine classifies packets based on the differentiated services
|
||||
* code point field. All packets with a DSCP value >= 48 are accorded "urgent"
|
||||
* status, whereas the rest are deferred at a priority relative to the DSCP
|
||||
* value. Traffic with DSCP value 1 is dropped.
|
||||
*
|
||||
* This routine is provided for demonstration only.
|
||||
*
|
||||
* RETURNS: QOS_DELIVER_PKT, QOS_DEFER_PKT or QOS_IGNORE_PKT.
|
||||
*/
|
||||
|
||||
QOS_HOOK_RETVAL dscpQosHook
|
||||
(
|
||||
END_OBJ * pEnd, /* END device on which packet was received */
|
||||
M_BLK_ID * ppMblk, /* Received packet */
|
||||
int * pPri /* Location for deferred priority */
|
||||
)
|
||||
{
|
||||
|
||||
struct ether_header * pEnetHdr;
|
||||
struct ip * ip;
|
||||
register M_BLK_ID pMblk = *ppMblk;
|
||||
int dataOffset;
|
||||
long etherType;
|
||||
char dscp;
|
||||
#if 0
|
||||
LL_HDR_INFO llHdrInfo;
|
||||
|
||||
|
||||
/*
|
||||
* For simplicity and speed, we just process the standard Ethernet frames.
|
||||
* For comprehensiveness, use the packetDataGet routine to strip the link
|
||||
* header
|
||||
*/
|
||||
|
||||
if (pEnd->pFuncTable->packetDataGet == NULL ||
|
||||
pEnd->pFuncTable->packetDataGet (pMblk, &llHdrInfo) != OK)
|
||||
{
|
||||
log_err (QOS_LOG, "failed to get packet data. Dropping packet\n");
|
||||
m_freem (pMblk);
|
||||
return (QOS_IGNORE_PKT);
|
||||
}
|
||||
|
||||
etherType = llHdrInfo.pktType;
|
||||
dataOffset = pLinkHdrInfo->dataOffset;
|
||||
|
||||
#else
|
||||
|
||||
/* Ensure that the entire ether_header is in first M_BLK. */
|
||||
|
||||
if (pMblk->mBlkHdr.mLen < SIZEOF_ETHERHEADER)
|
||||
{
|
||||
if ((pMblk = m_pullup (pMblk, SIZEOF_ETHERHEADER)) == NULL)
|
||||
{
|
||||
log_err (QOS_LOG, "failed to get ether_header. Dropping packet\n");
|
||||
return (QOS_IGNORE_PKT);
|
||||
}
|
||||
*ppMblk = pMblk;
|
||||
}
|
||||
pEnetHdr = (struct ether_header *)pMblk->mBlkHdr.mData;
|
||||
|
||||
if (pEnetHdr == NULL)
|
||||
return QOS_HOOK_FAIL;
|
||||
etherType = ntohs(pEnetHdr->ether_type);
|
||||
dataOffset = SIZEOF_ETHERHEADER;
|
||||
#endif
|
||||
|
||||
if (etherType != ETHERTYPE_IP)
|
||||
{
|
||||
/*
|
||||
* Not an IP packet, just defer at the default priority
|
||||
*/
|
||||
|
||||
*pPri = -1;
|
||||
return QOS_DEFER_PKT;
|
||||
}
|
||||
|
||||
if (pMblk->m_len < (dataOffset + sizeof (*ip)))
|
||||
{
|
||||
/* Make sure that the IP header is in the first mblk */
|
||||
|
||||
if ((pMblk = m_pullup (pMblk, dataOffset + sizeof (*ip))) == NULL)
|
||||
{
|
||||
log_err (QOS_LOG, "failed to get ip. Dropping packet\n");
|
||||
return (QOS_IGNORE_PKT);
|
||||
}
|
||||
*ppMblk = pMblk;
|
||||
}
|
||||
|
||||
ip = (struct ip *)(pMblk->m_data + dataOffset);
|
||||
|
||||
dscp = ip->ip_tos >> 2;
|
||||
|
||||
if (dscp > 47)
|
||||
return QOS_DELIVER_PKT; /* urgent traffic */
|
||||
else if (dscp == 0)
|
||||
{
|
||||
*pPri = -1; /* Defer at default priority */
|
||||
return QOS_DEFER_PKT;
|
||||
}
|
||||
else if (dscp == 1)
|
||||
{
|
||||
m_freem (pMblk); /* Drop traffic */
|
||||
return QOS_IGNORE_PKT;
|
||||
}
|
||||
else /* dscp = 2 to 47 */
|
||||
{
|
||||
*pPri = dscp / 2; /* Defer at dscp/2 priority (1-23)*/
|
||||
return QOS_DEFER_PKT;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* INGRESS_QOS */
|
||||
@@ -0,0 +1,991 @@
|
||||
/* qosIngressLib.c - routines to support QoS on ingress. */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005-2006, 2009 Wind River Systems, Inc.
|
||||
*
|
||||
* The right to copy, distribute, modify or otherwise make use
|
||||
* of this software may be licensed only pursuant to the terms
|
||||
* of an applicable Wind River license agreement.
|
||||
*/
|
||||
|
||||
/*
|
||||
modification history
|
||||
--------------------
|
||||
01g,20sep10,h_x Use a new marco (QOS_LOG) instead of KERN_LOG
|
||||
01f,26aug09,dlk WIND00178933 - use muxDevAcquire()/muxDevRelease() instead
|
||||
of muxEndTake()/muxEndGive().
|
||||
01e,26oct06,kch Fixed warning.
|
||||
01d,08sep06,kch Cleanup for IPNet integration.
|
||||
01c,03apr06,kch Guarded the entire file with INGRESS_QOS instead of QOS
|
||||
(SPR#119839).
|
||||
01b,08sep05,niq Code review changes + enhanced statistics gathering
|
||||
01a,04aug05,niq Written.
|
||||
*/
|
||||
|
||||
/*
|
||||
DESCRIPTION
|
||||
This library provides the framework to implement quality of service (QoS)
|
||||
on ingress.
|
||||
|
||||
In the absence of QoS support, systems process incoming traffic on a
|
||||
first come first served basis. With the scaling of Internet and
|
||||
proliferation of real-time applications using the Internet, this simple
|
||||
model is no longer sufficient and some mechanism to prioritize certain
|
||||
traffic needs to be provided.
|
||||
|
||||
In the current network stack, packets are processed on a first come first
|
||||
served basis. When a packet arrives on an interface, the driver's interrupt
|
||||
service routine schedules a QJOB that is run by tNetTask. This job dequeues
|
||||
the packets from the packet ring and calls the registered receive routine
|
||||
(usually muxReceive() (or muxTkReceive()) to process the packet through the
|
||||
stack. Thus a lower priority packet gets processed ahead of any other higher
|
||||
priority packets if it happens to precede the higher priority packets. To
|
||||
allow higher priority packets to be processed ahead of lower priority ones,
|
||||
irrespective of the order of reception, it is necessary to provide a mechanism
|
||||
whereby lower priority packets could be queued for deferred processing. This
|
||||
library implements such a mechanism.
|
||||
|
||||
The two key components of the QoS infrastructure are
|
||||
1) packet classification
|
||||
2) packet queuing and scheduling
|
||||
|
||||
\sh Packet classification
|
||||
Packet classification involves looking at the packet and deciding what level
|
||||
of service it needs to be provided. Currently, there are 3 dispositions defined
|
||||
for a packet
|
||||
|
||||
1) process it now (for traffic that cannot absolutely be delayed)
|
||||
2) defer it
|
||||
3) ignore it. This typically happens when the hook routine
|
||||
detects an error and frees the packet.
|
||||
|
||||
Additionally, packets can be deferred at up to 32 different priorities
|
||||
providing further control over their processing.
|
||||
|
||||
Packet classification, per interface, is performed by the user via a hook
|
||||
registered with the qosIngressHookSet() API. This will cause each incoming
|
||||
packet on the enabled interfaces to be passed to the user hook for
|
||||
classification. The hook routine (packet classifier) is expected to return
|
||||
one of the 3 dispositions described above. If the hook decides to defer the
|
||||
packet for later processing, it must also return the priority at which that
|
||||
packet will be processed.
|
||||
|
||||
Some network interfaces may provide QoS features in hardware. For such
|
||||
interfaces, the use of the software approach described above may be
|
||||
redundant. This library does not check to see the capabilities of the
|
||||
underlying hardware.
|
||||
|
||||
\sh Packet queuing and scheduling
|
||||
This library provide 32 priority queues (0 - 31) on which the packet
|
||||
classifier can defer the packet for processing. These priority queues are
|
||||
processed in a strict priority order with the highest priority non-empty queue
|
||||
always processed ahead of any lower priority queues.
|
||||
|
||||
This library uses the JobQueueLib mechanism to schedule the processing of
|
||||
packets on the deferred priority queues. The Job queue used and the priority
|
||||
at which the packet processing job is queued, is configurable using the
|
||||
parameters described in the configuration section.
|
||||
|
||||
NOTE
|
||||
While the priority scheduling model guarantees that the higher priority traffic
|
||||
gets absolute priority over lower priority traffic, it can lead to indefinite
|
||||
starvation of the lower priority queues. Additionally, with no administrative
|
||||
control on the length of the queues, as the lower priority queues build up,
|
||||
they consume resources (buffers) which may affect the ability of the system
|
||||
to receive higher priority traffic.
|
||||
|
||||
\sh Configuration
|
||||
To include this library in the vxWorks image, add the INCLUDE_QOS_INGRESS
|
||||
define to the bsp's configuration header file. Additionally
|
||||
INCLUDE_QOS_INGRESS_HOOKS can be defined to include the sample packet
|
||||
classification hooks provided in qosIngressHooks.c
|
||||
|
||||
\sh External Interfaces:
|
||||
|
||||
Initialization API:
|
||||
\is
|
||||
\i qosIngressLibInit()
|
||||
Initialize the ingress QoS support facility
|
||||
\ie
|
||||
|
||||
Configuration API:
|
||||
\is
|
||||
\i qosIngressHookSet()
|
||||
Enable/Disable software QoS support on the specified interface and register the
|
||||
user packet classification routine.
|
||||
\ie
|
||||
|
||||
|
||||
\sh Usage Examples:
|
||||
The following example enables software based QoS support on the motscc0
|
||||
interface.
|
||||
|
||||
\cs
|
||||
-> qosIngressHookSet (0, "motscc", myClassificationRoutine);
|
||||
\ce
|
||||
|
||||
The following example disables software based QoS support on the motscc0
|
||||
interface.
|
||||
|
||||
\cs
|
||||
-> qosIngressHookSet (0, "motscc", NULL);
|
||||
\ce
|
||||
|
||||
Configuration parameters:
|
||||
\is
|
||||
\i QOS_JOBQ
|
||||
This parameter defines the Job queue that will be used to schedule the
|
||||
deferred queue processing job
|
||||
\i QOS_JOBQ_PRI
|
||||
This parameter defines the priority at which the deferred queue processing job
|
||||
will be scheduled on the Job queue defined by QOS_JOBQ. Valid values range from
|
||||
0 to 31. For correct operation, this value should be less than the priority at which the driver's receive routine executes.
|
||||
\i QOS_DEFAULT_PRI
|
||||
This parameter specifies the priority queue on which the packet is deferred
|
||||
for processing, if the packet classification routine returns QOS_DEFER_PKT
|
||||
with -1 in the priority field. Valid values range from 0 to 31.
|
||||
\ie
|
||||
|
||||
|
||||
INCLUDE FILES: qosIngressLib.h
|
||||
|
||||
\INTERNAL
|
||||
All data structure manipulation currently happens in a single thread (in
|
||||
tNetTask's) context, so no mutex protection is needed.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef INGRESS_QOS
|
||||
|
||||
#include <vxWorks.h>
|
||||
#include <ioctl.h>
|
||||
#include <ioLib.h>
|
||||
#include <errnoLib.h>
|
||||
#include <intLib.h>
|
||||
#include <taskLib.h>
|
||||
|
||||
#include <endLib.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#include <dlink/qosIngressLib.h> /* public qosIngressLib header file */
|
||||
#include <private/qosIngressLibP.h> /* private qosIngressLib header file */
|
||||
#include <netffs.h> /* FFS definitions */
|
||||
#include <applUtilLib.h>
|
||||
#include <syslog.h>
|
||||
#include <private/jobQueueLibP.h>
|
||||
|
||||
/* IP */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Defines */
|
||||
#ifdef DEBUG
|
||||
#undef LOCAL
|
||||
#define LOCAL
|
||||
#endif /* DEBUG */
|
||||
|
||||
/* Locals */
|
||||
QOS_CONFIG_PARAMS qosDefaultConfigParams;
|
||||
LOCAL QOSQ_VARS qosQVars;
|
||||
LOCAL BOOL qosLibInitialized = FALSE;
|
||||
|
||||
/* forward declarations */
|
||||
|
||||
/* qosIngressLib.c */
|
||||
|
||||
STATUS qosIngressHookSet (int unit, char * ifname, QOS_ING_HOOK hookRtn);
|
||||
LOCAL STATUS qosQFlush (void * pEnd);
|
||||
LOCAL void qosQFlushHandler (struct qosqFlushJob * pJob);
|
||||
STATUS qosPolicyReceive (END_OBJ * pEnd, M_BLK_ID pMblk);
|
||||
LOCAL void qosqJobHandler (QJOB * pJob);
|
||||
void qosLibShow (void);
|
||||
void qosStatsClear (void);
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* qosIngressLibInit - initialize the ingress QoS support library
|
||||
*
|
||||
* This function initializes the ingress QoS support library. It is
|
||||
* automatically included when INCLUDE_QOS_INGRESS is defined.
|
||||
*
|
||||
* RETURNS: OK.
|
||||
*
|
||||
*/
|
||||
|
||||
STATUS qosIngressLibInit
|
||||
(
|
||||
void *cfgParams /* QOS configuration parameters */
|
||||
)
|
||||
{
|
||||
#define QOS_CFG ((QOS_CONFIG_PARAMS *)cfgParams)
|
||||
|
||||
int i;
|
||||
|
||||
if (qosLibInitialized)
|
||||
return(OK);
|
||||
|
||||
/* Read in the configuration parameters */
|
||||
|
||||
qosQVars.qosDefaultPri = QOS_CFG->cfg_qosDefaultPri;
|
||||
qosQVars.pJobQueue = QOS_CFG->cfg_pJobQueue;
|
||||
|
||||
/* Initialize the process QJOB */
|
||||
|
||||
qosQVars.processJob.func = (QJOB_FUNC) qosqJobHandler;
|
||||
QJOB_SET_PRI (&qosQVars.processJob, QOS_CFG->cfg_jobQPri);
|
||||
|
||||
/* Initialize the array of qosqs and other statistics */
|
||||
|
||||
for (i = 0; i < QOS_MAX_PRI + 1; i++)
|
||||
{
|
||||
qosQVars.qosqArr[i].qosq_head = NULL;
|
||||
qosQVars.qosqArr[i].qosq_tail = NULL;
|
||||
#ifdef QOS_STATS
|
||||
qosQVars.qosqArr[i].qosq_len = 0;
|
||||
qosQVars.qosqArr[i].qosq_maxlen = 0;
|
||||
qosQVars.qosqArr[i].qosq_total = 0;
|
||||
qosQVars.qosqArr[i].qosq_drops = 0;
|
||||
qosQVars.qosqArr[i].qosq_maxlim = 0;
|
||||
#endif /* QOS_STATS */
|
||||
}
|
||||
|
||||
qosQVars.qosqActivePri = 0;
|
||||
#ifdef QOS_STATS
|
||||
qosQVars.numPackets = 0;
|
||||
qosQVars.discards = 0;
|
||||
#endif /* QOS_STATS */
|
||||
|
||||
qosLibInitialized = TRUE;
|
||||
|
||||
return (OK);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* qosIngressHookSet - set the packet classification hook
|
||||
*
|
||||
* This routine is used to turn on or off QoS support on ingress for the
|
||||
* interface specified by <ifname> and <unit>. To turn on the QoS support,
|
||||
* set the <hookRtn> argument to your packet classification routine.
|
||||
* To turn off QoS support, set <hookRtn> to NULL.
|
||||
* This routine sets the packet classification hook for the interface specified
|
||||
* by <ifname> and <unit> to <hookRtn>. This will cause all incoming packets
|
||||
* to be passed to the <hookRtn> for classification. The <hookRtn> should
|
||||
* conform to the following prototype
|
||||
* \cs
|
||||
* int myPacketClassifier
|
||||
* (
|
||||
* END_OBJ * pEnd,
|
||||
* MBLK_ID * ppMblk,
|
||||
* int * pPri
|
||||
* )
|
||||
* \ce
|
||||
*
|
||||
* where
|
||||
* \is
|
||||
* \i <pEnd>
|
||||
* describes the END device
|
||||
* \i <ppMblk>
|
||||
* describes the incoming packet
|
||||
* \i <pPri>
|
||||
* where the <hookRtn> should return the priority at which the packet should
|
||||
* be enqueued if the <hookRtn> returns QOS_DEFER_PKT. The priority could
|
||||
* be any value from 0 to 31, with 31 being the highest priority. Additionally,
|
||||
* you can set priority to -1 to indicate that the default priority,
|
||||
* configured through the QOS_DEFAULT_PRI configuration parameter, should
|
||||
* be used.
|
||||
* \ie
|
||||
*
|
||||
* The <hookRtn> should return one of the following values
|
||||
* 1) QOS_DELIVER_PKT - deliver the packet immediately
|
||||
* 2) QOS_DEFER_PKT - defer the packet at priority indicated in *<pPri>
|
||||
* The packet will be enqueued for later processing
|
||||
* 3) QOS_IGNORE_PKT - ignore the packet. This is typically returned when
|
||||
* the hook routine detects an error and frees
|
||||
* the packet. If this values is returned, then the
|
||||
* hook routine assumes responsibility of freeing
|
||||
* the packet.
|
||||
*
|
||||
* NOTE
|
||||
* This API allows you to defer processing of non-urgent traffic received
|
||||
* on an interface. If you have another interface on the system on which QoS
|
||||
* support has not been enabled, then all traffic received on that interface,
|
||||
* including non-urgent traffic, will be processed at the same priority - that
|
||||
* of the urgent traffic. This may "unfairly" de-prioritize the non-urgent
|
||||
* traffic on QoS-enabled interfaces. If this is a concern, you might want
|
||||
* to enables QoS support on all interfaces.
|
||||
*
|
||||
* \INTERNAL
|
||||
* This routine replaces the original receiveRtn registered with pEnd
|
||||
* (normally muxReceive() or muxTkReceive()) with an internal qosPolicyReceive()
|
||||
* routine that in turn calls the hookRtn and processes the packet accordingly.
|
||||
*
|
||||
* RETURNS: OK on success, ERROR on failure
|
||||
*
|
||||
* ERRNO:
|
||||
* EINVAL - QOS library not initialized.
|
||||
*/
|
||||
|
||||
STATUS qosIngressHookSet
|
||||
(
|
||||
int unit, /* Unit number, e.g., 0 */
|
||||
char * ifname, /* device name, e.g., "fei" */
|
||||
QOS_ING_HOOK hookRtn /* classifier routine */
|
||||
)
|
||||
{
|
||||
END_OBJ * pEnd;
|
||||
STATUS status;
|
||||
|
||||
if (!qosLibInitialized)
|
||||
return ERROR;
|
||||
|
||||
pEnd = (END_OBJ *)muxDevAcquire (ifname, unit);
|
||||
if (pEnd == NULL)
|
||||
return ERROR;
|
||||
|
||||
if (hookRtn != NULL)
|
||||
{
|
||||
/*
|
||||
* Replace the receiveRtn with the qosPolicyReceive routine and
|
||||
* record the hook. Record the QoS hook before setting the receive
|
||||
* routine to prevent a race condition wherein the receive routine is
|
||||
* changed but the hook is still not set.
|
||||
*/
|
||||
|
||||
pEnd->qosHookRtn = (FUNCPTR)hookRtn;
|
||||
|
||||
/*
|
||||
* This API may be called multiple times to install different
|
||||
* hooks. We record the original receive routine only the first
|
||||
* time so as not to set it to the previously installed hook routine
|
||||
* the next time this API is called
|
||||
*/
|
||||
|
||||
if (pEnd->origReceiveRtn == NULL)
|
||||
{
|
||||
pEnd->origReceiveRtn = pEnd->receiveRtn;
|
||||
pEnd->receiveRtn = qosPolicyReceive;
|
||||
}
|
||||
|
||||
status = OK;
|
||||
}
|
||||
else if (pEnd->origReceiveRtn != NULL)
|
||||
{
|
||||
/*
|
||||
* Queue a job to flush the packet queue of all packets
|
||||
* received on this END device, and reset the original receiveRtn.
|
||||
* Doing it here might cause a race condition with the jobQueue's
|
||||
* task trying to access the receive routine and qos Hook.
|
||||
*/
|
||||
|
||||
status = qosQFlush (pEnd);
|
||||
}
|
||||
|
||||
muxDevRelease (pEnd);
|
||||
return status;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* qosQFlush - flush packets on the QoS queues
|
||||
*
|
||||
* This routine is called by qosIngressHookSet() to flush packets on the
|
||||
* QoS queues when the QoS support is turned off. All packets received
|
||||
* on the END device indicated by <pEnd> are flushed. This routine
|
||||
* schedules a QJOB on the tNetTask queue to flush the queues. This strategy
|
||||
* obviates the need to lock the QOSQ data structures as there is only one
|
||||
* thread of execution (tNetTask). It also prevents any race condition
|
||||
* between the flushing and the tNetTask processing the deferred packets, which
|
||||
* would occur were we to flush the queue in a task other than tNetTask.
|
||||
*
|
||||
* After scheduling the flushing job, this routine waits for it to complete
|
||||
* before returning to the caller.
|
||||
*
|
||||
* RETURNS: OK on success, ERROR on failure
|
||||
*
|
||||
* ERRNO:
|
||||
*
|
||||
* \NOMANUAL
|
||||
*
|
||||
* \INTERNAL
|
||||
*
|
||||
*/
|
||||
|
||||
LOCAL STATUS qosQFlush
|
||||
(
|
||||
void * pEnd
|
||||
)
|
||||
{
|
||||
struct qosqFlushJob job;
|
||||
|
||||
job.pEnd = pEnd;
|
||||
|
||||
/*
|
||||
* Check if we are running in the context of the jobQueue task.
|
||||
* This normally shouldn't happen, but if it does, then no need to
|
||||
* queue the job, just flush the messages.
|
||||
*/
|
||||
|
||||
if (jobQueueTask (qosQVars.pJobQueue) == taskIdSelf())
|
||||
{
|
||||
qosQFlushHandler (&job);
|
||||
return (OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* We could use a pre-allocated one, but allowing
|
||||
* multiple flush requests would take some more work. With stack
|
||||
* allocated jobs, it's easier. And we don't really have to worry about
|
||||
* performance as this is a one-off operation in the control plane.
|
||||
*/
|
||||
|
||||
/* Initialize the flush QJOB */
|
||||
|
||||
job.flushJob.func = (QJOB_FUNC) qosQFlushHandler;
|
||||
|
||||
/*
|
||||
* Ideally, the priority of the flush job should be greater than
|
||||
* the process job for faster turnaround time. We could also
|
||||
* set the priority for the flush job lower than the process job,
|
||||
* if we want to process the packets rather than flushing them.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
QJOB_SET_PRI (&job.flushJob, QJOB_GET_PRI (&qosQVars.processJob) + 1);
|
||||
if (QJOB_GET_PRI (&job.flushJob) > QJOB_PRI_HIGHEST)
|
||||
QJOB_SET_PRI (&job.flushJob, QJOB_PRI_HIGHEST);
|
||||
#else
|
||||
/*
|
||||
* Just use the highest priority. The next higher priority might be
|
||||
* masked. So could be the maximum priority. Gotta pick one. MAX wins!!
|
||||
* Also we have the END device locked. So we should complete the Job
|
||||
* ASAP.
|
||||
*/
|
||||
QJOB_SET_PRI (&job.flushJob, QJOB_PRI_HIGHEST);
|
||||
#endif
|
||||
|
||||
/* Set done to FALSE, The handler will set it to true when done */
|
||||
|
||||
job.done = FALSE;
|
||||
job.pEnd = pEnd;
|
||||
|
||||
/* Now post the job */
|
||||
|
||||
jobQueuePost (qosQVars.pJobQueue, &job.flushJob);
|
||||
|
||||
/*
|
||||
* Now wait for the job to finish. We could leave without waiting for
|
||||
* the job to finish, but to make sure that the operation is truly
|
||||
* finished, we do. Also, if we leave before the job finishes and the
|
||||
* user sets another hook routine immediately afterwards, then when the
|
||||
* job executes, it will clear out the latest hook. That is just a
|
||||
* theoretical possiblity though as we queue the flush job at the
|
||||
* highest priority and it should execute almost instantaneously, which
|
||||
* is another reason why waiting for it to complete should not be
|
||||
* an issue.
|
||||
*/
|
||||
|
||||
while (! job.done)
|
||||
taskDelay (1);
|
||||
|
||||
return (OK);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* qosQFlushHandler - flush packets for the matching end device
|
||||
*
|
||||
* This is the QJOB handler routine which flushes all packets received
|
||||
* on the END device indicated in pJob->pEnd. This is called when QOS
|
||||
* support is disabled on an END device. The QOS flushing function,
|
||||
* qosQFlush() schedules this QJOB to flush the packets on the
|
||||
* QoS queues. Then it (qosQFlush) waits for this job to signal
|
||||
* completion before returning to the caller.
|
||||
*
|
||||
* NOTE
|
||||
* Since all queue manipulation happens in tNetTask context,
|
||||
* in a single thread, we don't need any special mutex protection.
|
||||
*
|
||||
* RETURNS: N/A
|
||||
*
|
||||
* \NOMANUAL
|
||||
*/
|
||||
|
||||
LOCAL void qosQFlushHandler
|
||||
(
|
||||
struct qosqFlushJob * pJob
|
||||
)
|
||||
{
|
||||
UINT32 activePri = qosQVars.qosqActivePri;
|
||||
int pri;
|
||||
struct mbuf **next;
|
||||
struct mbuf *prev, *tmp;
|
||||
register END_OBJ * pEnd = (END_OBJ *)pJob->pEnd;
|
||||
|
||||
/*
|
||||
* Walk through all the non-empty lists purging messages
|
||||
* that were received on pJob->pEnd.
|
||||
*/
|
||||
|
||||
while (activePri != 0)
|
||||
{
|
||||
/*
|
||||
* Higher priority values are actually higher priority!
|
||||
* We need ffsMsb() to be optimized as much as possible for
|
||||
* each architecture.
|
||||
*/
|
||||
|
||||
pri = FFS (activePri);
|
||||
|
||||
next = &qosQVars.qosqArr[pri].qosq_head;
|
||||
prev = NULL;
|
||||
|
||||
while (*next != NULL)
|
||||
{
|
||||
if ((void *)((*next)->m_pkthdr.rcvif) == pEnd)
|
||||
{
|
||||
/* Unlink the message and free it */
|
||||
|
||||
tmp = *next;
|
||||
if (((*next) = tmp->m_nextpkt) == NULL) /* Last on the list */
|
||||
qosQVars.qosqArr[pri].qosq_tail = prev;
|
||||
else
|
||||
tmp->m_nextpkt = NULL;
|
||||
m_freem (tmp);
|
||||
#ifdef QOS_STATS
|
||||
qosQVars.qosqArr[pri].qosq_len--;
|
||||
qosQVars.numPackets--;
|
||||
qosQVars.discards++;
|
||||
#endif /* QOS_STATS */
|
||||
log_info (QOS_LOG, "flushed message\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
prev = *next;
|
||||
next = & (prev->m_nextpkt);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the list is empty now, update the active priorities */
|
||||
|
||||
if (qosQVars.qosqArr[pri].qosq_head == NULL)
|
||||
qosQVars.qosqActivePri &= ~ (1 << pri);
|
||||
|
||||
/* Turn off this priority so that we can go to the next one */
|
||||
|
||||
activePri &= ~ (1 << pri);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the QoS hook routine and restore the original receive
|
||||
* routine
|
||||
*/
|
||||
|
||||
pEnd->receiveRtn = pEnd->origReceiveRtn;
|
||||
pEnd->qosHookRtn = NULL;
|
||||
pEnd->origReceiveRtn = NULL;
|
||||
|
||||
/*
|
||||
* Now that we are done, tell qosIngressHookSet() that it's OK to continue.
|
||||
* It will free the QJOB.
|
||||
*/
|
||||
|
||||
pJob->done = TRUE;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* qosPolicyReceive - apply the QoS policy on ingress
|
||||
*
|
||||
* This routine applies the QoS policy to packets on ingress. It is installed
|
||||
* as the pEnd->receiveRtn when QOS support is enabled on an interface.
|
||||
* When a packet is received, this routine calls the user installed
|
||||
* packet classifier to classify the packet. Based upon the
|
||||
* classifier's return value, the packet is either processed inline,
|
||||
* deferred on a priority queue or ignored.
|
||||
*
|
||||
* NOTE
|
||||
* Since all queue manipulation happens in tNetTask context,
|
||||
* in a single thread, we don't need any special mutex protection.
|
||||
*
|
||||
* CAVEATS
|
||||
* There is also currently no administrative limit on the number of packets
|
||||
* queued. So as lower priority queues build up, they consume buffers.
|
||||
* This may affect the ability of the system to receive higher priority traffic.
|
||||
*
|
||||
* RETURNS: N/A
|
||||
*
|
||||
* \NOMANUAL
|
||||
*/
|
||||
|
||||
STATUS qosPolicyReceive
|
||||
(
|
||||
END_OBJ * pEnd, /* device identifier from driver's load routine */
|
||||
M_BLK_ID pMblk /* buffer containing received frame */
|
||||
)
|
||||
{
|
||||
int action;
|
||||
int pri;
|
||||
register struct qosqueue * pQosQ;
|
||||
|
||||
action = pEnd->qosHookRtn (pEnd, &pMblk, &pri);
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case QOS_DELIVER_PKT:
|
||||
log_debug (QOS_LOG, "qosPolicyRecv:delivering packet\n");
|
||||
#ifdef QOS_STATS
|
||||
qosQVars.delivered++;
|
||||
#endif /* QOS_STATS */
|
||||
|
||||
return (pEnd->origReceiveRtn (pEnd, pMblk));
|
||||
|
||||
case QOS_DEFER_PKT:
|
||||
/* Use default priority if asked to. ((UNIT32) -1) > 31 */
|
||||
|
||||
if ((UINT32)pri > QOS_MAX_PRI)
|
||||
pri = qosQVars.qosDefaultPri;
|
||||
|
||||
pQosQ = & (qosQVars.qosqArr[pri]);
|
||||
|
||||
/* Stuff the pEnd pointer in the as yet unused field */
|
||||
|
||||
pMblk->m_pkthdr.rcvif = (void *)pEnd;
|
||||
|
||||
/* Queue the packet and update the activePri variable */
|
||||
|
||||
pMblk->m_nextpkt = 0;
|
||||
if (pQosQ->qosq_tail == NULL)
|
||||
{
|
||||
pQosQ->qosq_head = pMblk;
|
||||
qosQVars.qosqActivePri |= (1 << pri);
|
||||
|
||||
#if (defined (DEBUG_CHECKS) && defined (QOS_STATS))
|
||||
if (qosQVars.qosqArr[pri].qosq_len != 0)
|
||||
printf ("\n!!! qosPolicyReceive: Incorrect packet count "
|
||||
"on queue. expected none, found %d !!!\n",
|
||||
qosQVars.qosqArr[pri].qosq_len);
|
||||
#endif /* DEBUG_CHECKS && QOS_STATS*/
|
||||
|
||||
/* Now schedule the process job if not already scheduled */
|
||||
|
||||
if (! QJOB_IS_BUSY (&qosQVars.processJob))
|
||||
{
|
||||
QJOB_SET_BUSY (&qosQVars.processJob);
|
||||
jobQueuePost (qosQVars.pJobQueue, &qosQVars.processJob);
|
||||
log_debug (QOS_LOG, "scheduled processing job\n");
|
||||
|
||||
#if (defined (DEBUG_CHECKS) && defined (QOS_STATS))
|
||||
if (qosQVars.numPackets != 0)
|
||||
printf ("\n!!! qosPolicyReceive: Job not scheduled: "
|
||||
"%d packets already in queue !!!\n",
|
||||
qosQVars.numPackets);
|
||||
#endif /* DEBUG_CHECKS && QOS_STATS*/
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pQosQ->qosq_tail->m_nextpkt = pMblk;
|
||||
|
||||
#if (defined (DEBUG_CHECKS) && defined (QOS_STATS))
|
||||
if (! (qosQVars.processJob.priInfo & QJOB_ENQUEUED))
|
||||
{
|
||||
printf ("\n!!! qosPolicyReceive: Job not scheduled2: "
|
||||
"%d packets already in queue !!!\n",
|
||||
qosQVars.numPackets);
|
||||
}
|
||||
#endif /* DEBUG_CHECKS && QOS_STATS*/
|
||||
}
|
||||
|
||||
pQosQ->qosq_tail = pMblk;
|
||||
|
||||
#ifdef QOS_STATS
|
||||
pQosQ->qosq_len++;
|
||||
pQosQ->qosq_total++;
|
||||
if (pQosQ->qosq_maxlen < pQosQ->qosq_len)
|
||||
pQosQ->qosq_maxlen = pQosQ->qosq_len;
|
||||
|
||||
/* Bump up the count of total deferred packets */
|
||||
|
||||
qosQVars.numPackets++;
|
||||
|
||||
log_debug (QOS_LOG, "deferred packet at pri:%d, at this pri: %d "
|
||||
"total deferred:%d\n", pri,
|
||||
qosQVars.qosqArr[pri].qosq_len, qosQVars.numPackets);
|
||||
#endif /* QOS_STATS */
|
||||
|
||||
break;
|
||||
|
||||
default: /* case QOS_IGNORE_PKT: */
|
||||
/* The Hook routine should have freed the packet */
|
||||
log_debug (QOS_LOG, "Dropped packet\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return (OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* qosqJobHandler - process deferred packets
|
||||
*
|
||||
* This is the QJOB handler routine which processes packets on the deferred
|
||||
* priority queues. It always processes the packet with the highest priority.
|
||||
* This routine keeps processing packets off the priority queue (in strict
|
||||
* priority order) until it detects that another higher priority job is
|
||||
* enqueued in the job queue. This is done so that the latency for the higher
|
||||
* priority jobs could be kept as low as possible. If more packets remain on
|
||||
* the deferred priority queues, this routine reschedules itself to run at a
|
||||
* later time.
|
||||
*
|
||||
* This routine reuses the same (preallocated) job to reschedule itself.
|
||||
*
|
||||
* NOTE
|
||||
* Since all queue manipulation happens in tNetTask context, in a single thread,
|
||||
* we don't need any special mutex protection.
|
||||
*
|
||||
* CAVEATS
|
||||
* The packet scheduling algorithm for processing deferred packets is strictly
|
||||
* priority based, i.e., packets of higher priority always get processed
|
||||
* ahead of lower priority packets. This could cause the lower priority packets
|
||||
* to starve indefinitely if the higher priority queues don't drain completely.
|
||||
*
|
||||
* There is also currently no administrative limit on the number of packets
|
||||
* queued. So as lower priority queues build up, they consume buffers.
|
||||
* This may affect the ability of the system to receive higher priority traffic.
|
||||
*
|
||||
* RETURNS: N/A
|
||||
*
|
||||
* \NOMANUAL
|
||||
*/
|
||||
|
||||
LOCAL void qosqJobHandler
|
||||
(
|
||||
QJOB * pJob
|
||||
)
|
||||
{
|
||||
register UINT32 activePri = qosQVars.qosqActivePri;
|
||||
register int pri;
|
||||
struct mbuf * pMblk;
|
||||
END_OBJ * pEnd;
|
||||
register struct qosqueue * pQosQ;
|
||||
UINT32 higherPriMask = ((UINT32)(-2) <<
|
||||
QJOB_GET_PRI (pJob));
|
||||
|
||||
/*
|
||||
* Walk through all the non-empty lists according to their priority
|
||||
* calling the origReceiveRtn.
|
||||
*/
|
||||
|
||||
/*
|
||||
* activePri might be 0 at entry if a previous flush emptied the
|
||||
* queues. Ideally we would have un-posted the job, but there is no
|
||||
* such provision in jobQueueLib. So we need to test activePri
|
||||
* here rather than at the end of the loop
|
||||
*/
|
||||
|
||||
#ifdef QOS_STATS
|
||||
log_debug (QOS_LOG, "qosqJobHandler: entry: activePri=0x%x, numPkts=%d",
|
||||
activePri, qosQVars.numPackets);
|
||||
#endif /* QOS_STATS */
|
||||
|
||||
QJOB_CLEAR_BUSY (pJob);
|
||||
|
||||
while (activePri != 0)
|
||||
{
|
||||
/*
|
||||
* Higher priority values are actually higher priority!
|
||||
* We need ffsMsb() to be optimized as much as possible for
|
||||
* each architecture.
|
||||
*/
|
||||
|
||||
pri = FFS (activePri);
|
||||
pQosQ = &(qosQVars.qosqArr[pri]);
|
||||
|
||||
|
||||
#ifndef _FREE_VERSION
|
||||
if (pQosQ->qosq_head == NULL)
|
||||
{
|
||||
#ifdef QOS_STATS
|
||||
printf ("\n!!! qosqJobHandler: Incorrect priority mask for"
|
||||
" pri:%d pkt count:%d No packets found !!!\n",
|
||||
pri, pQosQ->qosq_len);
|
||||
#endif /* QOS_STATS */
|
||||
|
||||
activePri &= ~ (1 << pri);
|
||||
|
||||
continue;
|
||||
}
|
||||
#endif /* _FREE_VERSION */
|
||||
|
||||
do
|
||||
{
|
||||
/* De-queue the packet from the list */
|
||||
|
||||
pMblk = pQosQ->qosq_head;
|
||||
pQosQ->qosq_head = pMblk->m_nextpkt;
|
||||
pMblk->m_nextpkt = NULL;
|
||||
#ifdef QOS_STATS
|
||||
pQosQ->qosq_len--;
|
||||
qosQVars.numPackets--;
|
||||
#endif /* QOS_STATS */
|
||||
|
||||
log_debug (QOS_LOG, "qosqJobHandler: delivering deferred packet "
|
||||
":for pri:%d\n", pri);
|
||||
|
||||
/* Retrieve the pEnd pointer from the packet */
|
||||
|
||||
pEnd = (END_OBJ *)pMblk->m_pkthdr.rcvif;
|
||||
pMblk->m_pkthdr.rcvif = NULL;
|
||||
|
||||
pEnd->origReceiveRtn (pEnd, pMblk);
|
||||
|
||||
/*
|
||||
* If there are higher priority QJOBs in tNetTask jobQueue,
|
||||
* return after rescheduling us; else move on to the next
|
||||
* queue
|
||||
*/
|
||||
|
||||
if (higherPriMask & qosQVars.pJobQueue->activePri)
|
||||
{
|
||||
/*
|
||||
* Turn off this priority in the active bit-mask if this list
|
||||
* is empty now
|
||||
*/
|
||||
|
||||
if (pQosQ->qosq_head == NULL)
|
||||
{
|
||||
pQosQ->qosq_tail = NULL;
|
||||
|
||||
if ((activePri &= ~(1 << pri)) == 0)
|
||||
{
|
||||
#if (defined (DEBUG_CHECKS) && defined (QOS_STATS))
|
||||
log_debug (KERN_LOG,
|
||||
"qosqJobHandler: preempted, not-"
|
||||
"rescheduling with %d pkts on queue and "
|
||||
"activePri=0x%x\n", qosQVars.numPackets,
|
||||
qosQVars.qosqActivePri);
|
||||
#endif /* DEBUG_CHECKS && QOS_STATS*/
|
||||
|
||||
goto skip_post;
|
||||
}
|
||||
#if (defined (DEBUG_CHECKS) && defined (QOS_STATS))
|
||||
else
|
||||
{
|
||||
if (qosQVars.numPackets == 0)
|
||||
log_err (KERN_LOG,
|
||||
"\nqosqJobHandler: !!! Zero packet count,"
|
||||
" when activePri is non zero:0x%x !!!\n",
|
||||
qosQVars.qosqActivePri);
|
||||
else
|
||||
log_debug (KERN_LOG,
|
||||
"qosqJobHandler: rescheduling with %d "
|
||||
"pkts on queue and activePri=0x%x\n",
|
||||
qosQVars.numPackets,
|
||||
qosQVars.qosqActivePri);
|
||||
}
|
||||
#endif /* DEBUG_CHECKS && QOS_STATS*/
|
||||
}
|
||||
|
||||
QJOB_SET_BUSY (pJob);
|
||||
jobQueuePost (qosQVars.pJobQueue, pJob);
|
||||
skip_post:
|
||||
qosQVars.qosqActivePri = activePri;
|
||||
return;
|
||||
}
|
||||
} while (pQosQ->qosq_head != NULL);
|
||||
|
||||
pQosQ->qosq_tail = NULL;
|
||||
activePri &= ~ (1 << pri);
|
||||
}
|
||||
|
||||
qosQVars.qosqActivePri = 0;
|
||||
|
||||
#if (defined (DEBUG_CHECKS) && defined (QOS_STATS))
|
||||
if (qosQVars.numPackets != 0)
|
||||
log_err (KERN_LOG, "\n !!! Wrong packet count (%d), when activePri "
|
||||
"is 0 !!!\n", qosQVars.numPackets);
|
||||
else
|
||||
log_debug (KERN_LOG, "qjobProcess: processed all packets\n");
|
||||
#endif /* DEBUG_CHECKS && QOS_STATS*/
|
||||
|
||||
}
|
||||
|
||||
#ifdef QOS_STATS
|
||||
/******************************************************************************
|
||||
*
|
||||
* qosLibShow - display queuing statistics
|
||||
*
|
||||
* RETURNS: NONE
|
||||
*
|
||||
* \NOMANUAL
|
||||
*/
|
||||
|
||||
void qosLibShow (void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (! qosLibInitialized)
|
||||
{
|
||||
printf ("qosLib not initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf ("Active priority mask: 0x%x\n", qosQVars.qosqActivePri);
|
||||
|
||||
|
||||
printf ("%-20s%-20s%-30s%-9s\n", "Pri", "# packets queued",
|
||||
"Max packets at any time", "Total");
|
||||
for (i = j = 0; i < QOS_MAX_PRI + 1; i++)
|
||||
{
|
||||
printf ("%-20d%-20d%-30d%-9d\n", i, qosQVars.qosqArr[i].qosq_len,
|
||||
qosQVars.qosqArr[i].qosq_maxlen,
|
||||
qosQVars.qosqArr[i].qosq_total);
|
||||
if (qosQVars.qosqArr[i].qosq_len != 0)
|
||||
j++;
|
||||
}
|
||||
|
||||
printf ("\nTotal %d packets queued in %d packet queue(s)\n",
|
||||
qosQVars.numPackets, j);
|
||||
printf ("Total packets discarded: %d\n",
|
||||
qosQVars.discards);
|
||||
printf ("Total packets delivered inline: %d\n",
|
||||
qosQVars.delivered);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* qosStatsClear - clear queuing statistics
|
||||
*
|
||||
* RETURNS: NONE
|
||||
*
|
||||
* \NOMANUAL
|
||||
*/
|
||||
|
||||
void qosStatsClear (void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (! qosLibInitialized)
|
||||
{
|
||||
printf ("qosLib not initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = j = 0; i < QOS_MAX_PRI + 1; i++)
|
||||
{
|
||||
qosQVars.qosqArr[i].qosq_maxlen = 0;
|
||||
qosQVars.qosqArr[i].qosq_total = 0;
|
||||
}
|
||||
|
||||
qosQVars.discards = 0;
|
||||
qosQVars.delivered = 0;
|
||||
}
|
||||
|
||||
#endif /* QOS_STATS */
|
||||
|
||||
#endif /* INGRESS_QOS */
|
||||
Reference in New Issue
Block a user