original files of m241

This commit is contained in:
neelimap
2026-07-30 11:19:44 +05:30
parent 170c2b3f3a
commit afc7070c1f
12254 changed files with 7655111 additions and 0 deletions
@@ -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 */