1/* 2 * LICENSE NOTICE. 3 * 4 * Use of the Microsoft Windows Rally Development Kit is covered under 5 * the Microsoft Windows Rally Development Kit License Agreement, 6 * which is provided within the Microsoft Windows Rally Development 7 * Kit or at http://www.microsoft.com/whdc/rally/rallykit.mspx. If you 8 * want a license from Microsoft to use the software in the Microsoft 9 * Windows Rally Development Kit, you must (1) complete the designated 10 * "licensee" information in the Windows Rally Development Kit License 11 * Agreement, and (2) sign and return the Agreement AS IS to Microsoft 12 * at the address provided in the Agreement. 13 */ 14 15/* 16 * Copyright (c) Microsoft Corporation 2005. All rights reserved. 17 * This software is provided with NO WARRANTY. 18 */ 19 20#include <stdio.h> 21#include <stdlib.h> 22#include <ctype.h> 23#include <assert.h> 24 25#include <string.h> 26#include <errno.h> 27#include <sys/ioctl.h> 28 29#include "globals.h" 30#include "statemachines.h" 31#include "packetio.h" 32 33extern void * 34fmt_base(uint8_t *buf, const etheraddr_t *srchw, const etheraddr_t *dsthw, lld2_tos_t tos, 35 topo_opcode_t g_opcode, uint16_t seqnum, bool_t use_broadcast); 36 37extern void 38tx_write(uint8_t *buf, size_t nbytes); 39 40/* with only two linkages, this isn't really needed... #include "qospktio.h" */ 41 42/********************************* U T I L I T Y F U N C T I O N S **************************************/ 43static 44void 45stamp_time(uint64_t* pTime) 46{ 47 struct timeval now; 48 uint64_t temp; 49 50 gettimeofday(&now, NULL); 51 temp = now.tv_sec * (uint64_t)1000000UL; 52 *pTime = temp + now.tv_usec; 53} 54 55 56static 57void 58get_raw_samples(void) 59{ 60 FILE *procdev; 61 62 if ( (procdev = fopen("/proc/net/dev", "r")) != (FILE*) 0) 63 { 64 uint32_t rbytes, rpkts, tbytes, tpkts; 65 bool_t cntrs_parsedOK = FALSE; 66 char ifname[16]; 67 68 rbytes = rpkts = tbytes = tpkts = -1; 69 strncpy(ifname, g_interface, 14); 70 strcat(ifname,":"); 71 72 while (fgets(g_buf, sizeof(g_buf)/sizeof(g_buf[0]), procdev) == g_buf) 73 { 74 char *ifn; 75 76 if ((ifn=strstr(g_buf, ifname)) != 0) 77 { 78 int skipcol; 79 char *val = ifn; 80 char dummy[] = {"0 0 0 0 0 0"}; 81 82 val += strlen(ifname); 83 rbytes = strtoul(val,&val,10); 84 rpkts = strtoul(val,&val,10); 85 /* Skip over 6 more un-needed columns */ 86 for (skipcol=0;skipcol<6;skipcol++) 87 { 88 long discard; 89 90 discard = strtoul(val,&val,10); 91 if (*val == '\0') 92 { 93 warn("get_raw_samples: using dummy values due to parse error!\n"); 94 val = dummy; 95 break; 96 } 97 } 98 /* "val" now points to the tx-byte-counter */ 99 tbytes = strtoul(val,&val,10); 100 tpkts = strtoul(val,NULL,10); 101 cntrs_parsedOK = TRUE; 102 break; // out of "while (reading lines)..." 103 } 104 } 105 if (cntrs_parsedOK) 106 { 107 warn("get_raw_samples: failed reading /proc/dev for device statistics!\n"); 108 g_rbytes = g_rpkts = g_tbytes = g_tpkts = 0; 109 } else { 110 /* got a valid parse of the /proc/net/dev line for our device */ 111 g_rbytes = rbytes; 112 g_rpkts = rpkts; 113 g_tbytes = tbytes; 114 g_tpkts = tpkts; 115 } 116 fclose(procdev); 117 } else { 118 warn("get_raw_samples: failed opening /proc/dev for device statistics!\n"); 119 g_rbytes = g_rpkts = g_tbytes = g_tpkts = 0; 120 } 121 IF_TRACED(TRC_QOS) 122 dbgprintf("qos perf-cntr: g_rbytes=" FMT_UINT32 "; g_rpkts=" FMT_UINT32 \ 123 "; g_tbytes=" FMT_UINT32 "; g_tpkts=" FMT_UINT32 "\n", 124 g_rbytes, g_rpkts, g_tbytes, g_tpkts); 125 END_TRACE 126} 127 128 129static 130void 131get_timestamp(uint64_t* pTime) 132{ 133 struct timeval now; 134 uint64_t temp; 135 136 ioctl(g_osl->sock, SIOCGSTAMP, &now); 137 temp = now.tv_sec * (uint64_t)1000000UL; 138 *pTime = temp + now.tv_usec; 139} 140 141 142static qos_session_t* 143qos_find_session(void) 144{ 145 unsigned int i; 146 147 for (i=0;i<MAX_QOS_SESSIONS;i++) 148 { 149 if (g_QosSessions[i].qssn_is_valid && 150 ETHERADDR_EQUALS(&g_base_hdr->tbh_realsrc, &g_QosSessions[i].qssn_ctrlr_real)) 151 { 152 return &g_QosSessions[i]; 153 } 154 } 155 return NULL; 156} 157 158static qosEventBucket_t* 159qos_find_bucket(qos_session_t *pSsn) 160{ 161 unsigned int i = pSsn->qssn_first_bucket; 162 unsigned int j = i + pSsn->qssn_num_active_buckets; 163 164 while (i < j) 165 { 166 qosEventBucket_t* bucket = &pSsn->qssn_evt_buckets[i & (MAX_QOS_BUCKETS - 1)]; 167 168 if (bucket->evt_seqNum == g_sequencenum) 169 { 170 return bucket; 171 } 172 173 i++; 174 } 175 176 return NULL; 177} 178 179/***************************** T I M E R S E R V I C E R O U T I N E S *****************************/ 180static 181void 182qos_inactivity_timeout(void *state) 183{ 184 int i; 185 qos_session_t* pSsn = &g_QosSessions[0]; 186 struct timeval now; 187 188 for (i=0; i<MAX_QOS_SESSIONS; i++, pSsn++) 189 { 190 if (pSsn->qssn_is_valid == TRUE) 191 { 192 if (pSsn->qssn_ticks_til_discard) pSsn->qssn_ticks_til_discard--; 193 if (pSsn->qssn_ticks_til_discard == 0) 194 { 195 pSsn->qssn_is_valid = FALSE; 196 pSsn->qssn_num_active_buckets = 0; 197 } 198 } 199 } 200 201 /* repeats every 30 seconds - a session is killed after 4 ticks' inactivity */ 202 gettimeofday(&now, NULL); 203 now.tv_sec += 30; 204 g_qos_inactivity_timer = event_add(&now, qos_inactivity_timeout, /*state:*/NULL); 205} 206 207 208#define BYTE_SCALE_FACTOR 1024 // value for reducing counters 209#define BYTE_SCALE 0 // equivalent value for response 210#define PKT_SCALE_FACTOR 1 // value for reducing counters 211#define PKT_SCALE 0 // equivalent value for response 212static 213void 214interface_counter_recorder(void *state) 215{ 216 struct timeval now; 217 218 if (--g_samples_remaining != 0) 219 { 220 qos_perf_sample *thisSample = &g_perf_samples[g_next_sample]; 221 222 uint32_t rbytes = g_rbytes; 223 uint32_t rpkts = g_rpkts; 224 uint32_t tbytes = g_tbytes; 225 uint32_t tpkts = g_tpkts; 226 uint32_t delta; 227 228 get_raw_samples(); // get current values for g_rbytes, g_rpkts, g_tbytes, g_tpkts 229 230 delta = (g_rbytes-rbytes > 0)? (g_rbytes-rbytes) : 0; 231 IF_TRACED(TRC_QOS) 232 dbgprintf("qos perf-cntr: delta-rbytes=" FMT_UINT32, delta); 233 END_TRACE 234 thisSample->bytes_rcvd = delta / BYTE_SCALE_FACTOR; 235 236 delta = (g_rpkts-rpkts > 0)? (g_rpkts-rpkts) : 0; 237 IF_TRACED(TRC_QOS) 238 dbgprintf(" delta-rpkts=" FMT_UINT32, delta); 239 END_TRACE 240 thisSample->pkts_rcvd = delta / PKT_SCALE_FACTOR; 241 242 delta = (g_tbytes-tbytes > 0)? (g_tbytes-tbytes) : 0; 243 IF_TRACED(TRC_QOS) 244 dbgprintf(" delta-tbytes=" FMT_UINT32, delta); 245 END_TRACE 246 thisSample->bytes_sent = delta / BYTE_SCALE_FACTOR; 247 248 delta = (g_tpkts-tpkts > 0)? (g_tpkts-tpkts) : 0; 249 IF_TRACED(TRC_QOS) 250 dbgprintf(" delta-tpkts=" FMT_UINT32 "\n", delta); 251 END_TRACE 252 thisSample->pkts_sent = delta / PKT_SCALE_FACTOR; 253 254 stamp_time(&g_perf_timestamp); 255 256 IF_TRACED(TRC_QOS) 257 dbgprintf("qos perf-cntr: sample-rbytes=%d; sample-rpkts=%d; sample-tbytes=%d; sample-tpkts=%d\n", 258 thisSample->bytes_rcvd, thisSample->pkts_rcvd, thisSample->bytes_sent, thisSample->pkts_sent); 259 END_TRACE 260 261 g_next_sample++; 262 g_next_sample = g_next_sample % 60; 263 264 if (g_sample_count < 60) 265 { 266 g_sample_count++; 267 } 268 269 /* repeats every second - until the lease runs out */ 270 gettimeofday(&now, NULL); 271 now.tv_sec += 1; 272 g_qos_CTA_timer = event_add(&now, interface_counter_recorder, /*state:*/NULL); 273 } else { 274 IF_TRACED(TRC_QOS) 275 dbgprintf("qos perf-cntr: lease has run out - zero'ing counters, and stopping the timer...\n"); 276 END_TRACE 277 278 g_next_sample = 0; 279 g_sample_count = 0; 280 } 281} 282 283 284extern void qos_init(void); 285 286void 287qos_init(void) 288{ 289 int i; 290 qos_session_t* pSsn = &g_QosSessions[0]; 291 struct timeval now; 292 293 /* Initialize all the session structures */ 294 for (i=0; i<MAX_QOS_SESSIONS; i++, pSsn++) 295 { 296 pSsn->qssn_ticks_til_discard = 0; 297 pSsn->qssn_first_bucket = 0; 298 pSsn->qssn_num_active_buckets = 0; 299 pSsn->qssn_is_valid = FALSE; 300 memset(&pSsn->qssn_ctrlr_real, 0, sizeof(etheraddr_t)); 301 } 302 g_qprb_hdr = NULL; /* pointer to qos probe-header in rxbuf */ 303 g_qinit_hdr = NULL; /* pointer to qos init-sink--header in rxbuf */ 304 g_LinkSpeed = 1000000; 305 g_TimeStampFreq = 1000000; /* usec granularity */ 306 g_pktio_timestamp = 0; 307 308 /* repeats every 30 seconds - a session is killed after 4 ticks' inactivity */ 309 gettimeofday(&now, NULL); 310 now.tv_sec += 30; 311 g_qos_inactivity_timer = event_add(&now, qos_inactivity_timeout, /*state:*/NULL); 312 313#ifdef START_WITH_COUNTER_LEASE 314 /* start collection: repeats every second - until the lease runs out */ 315 gettimeofday(&now, NULL); 316 now.tv_sec += 1; 317 g_qos_CTA_timer = event_add(&now, interface_counter_recorder, /*state:*/NULL); 318 g_samples_remaining = 300; 319#endif 320} 321 322 323/************************** Q O S M S G H A N D L E R S **************************/ 324 325static 326void 327qos_initsink(void) 328{ 329 qos_session_t* pThisSsn; 330 size_t nbytes; 331 int i; 332 uint16_t errcode; 333 334 if (g_rcvd_pkt_len < sizeof(topo_ether_header_t) + sizeof(topo_base_header_t) + sizeof(qos_initsink_header_t)) 335 { 336 warn("qos_initsink: frame with truncated InitializeSink header (len=" FMT_SIZET " src=" 337 ETHERADDR_FMT " dst=" ETHERADDR_FMT "); ignoring\n", 338 g_rcvd_pkt_len, ETHERADDR_PRINT(&g_ethernet_hdr->eh_src), ETHERADDR_PRINT(&g_ethernet_hdr->eh_dst)); 339 return; 340 } 341 342 /* Check interrupt moderation request */ 343 if (g_qinit_hdr->init_intmod_ctrl != 0xFF) 344 { 345 /* Compose msg to return */ 346 fmt_base( 347 g_txbuf, 348 &g_hwaddr, 349 &g_base_hdr->tbh_realsrc, 350 ToS_QoSDiagnostics, 351 (topo_opcode_t)Qopcode_Error, 352 g_sequencenum, 353 FALSE /*no broadcast*/ 354 ); 355 nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t); 356 357 /* add Error-Code */ 358 errcode = htons(Qoserror_ModerationNotAvailable); 359 memcpy(&g_txbuf[nbytes], &errcode, sizeof(uint16_t)); 360 nbytes += sizeof(uint16_t); 361 362 tx_write(g_txbuf, nbytes); 363 364 IF_TRACED(TRC_PACKET) 365 dbgprintf("qos_initsink: unsupported interrupt moderation request (intmod=0x%02x)\n", g_qinit_hdr->init_intmod_ctrl); 366 END_TRACE 367 368 return; 369 } 370 371 /* Check for existing session with this controller and use it, if found; */ 372 /* If not found (the usual case), get an unused one... */ 373 if ((pThisSsn = qos_find_session()) == NULL) 374 { 375 /* Check for available session slot, reject with ErrBusy if none available */ 376 pThisSsn = g_QosSessions; 377 for (i=0;i<MAX_QOS_SESSIONS;i++) 378 { 379 if (!pThisSsn->qssn_is_valid) break; 380 pThisSsn++; 381 } 382 if (i>=MAX_QOS_SESSIONS) 383 { 384 /* Compose Busy msg to return. */ 385 fmt_base(g_txbuf, &g_hwaddr, &g_base_hdr->tbh_realsrc, ToS_QoSDiagnostics, 386 Qopcode_Error, g_sequencenum, FALSE /*no broadcast*/); 387 nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t); 388 389 /* add Error-Code */ 390 391 errcode = htons(Qoserror_Busy); 392 memcpy(&g_txbuf[nbytes], &errcode, sizeof(uint16_t)); 393 nbytes += sizeof(uint16_t); 394 395 tx_write(g_txbuf, nbytes); 396 397 IF_TRACED(TRC_PACKET) 398 dbgprintf("qos_initsink: tx_error_Busy, seq=%d -> " ETHERADDR_FMT "\n", 399 g_sequencenum, ETHERADDR_PRINT(&g_base_hdr->tbh_realsrc)); 400 END_TRACE 401 return; 402 } 403 } 404 405 /* Record session data, */ 406 pThisSsn->qssn_is_valid = TRUE; 407 memcpy( &pThisSsn->qssn_ctrlr_real, &g_base_hdr->tbh_realsrc, sizeof(etheraddr_t) ); 408 pThisSsn->qssn_ticks_til_discard = 4; 409 410 /* and compose & send a Ready msg. */ 411 fmt_base(g_txbuf, &g_hwaddr, &pThisSsn->qssn_ctrlr_real, ToS_QoSDiagnostics, 412 Qopcode_Ready, g_sequencenum, FALSE /*no broadcast*/); 413 nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t); 414 415 /* add Sink Link Speed */ 416 g_reorder_buffer = htonl(g_LinkSpeed); 417 memcpy(&g_txbuf[nbytes], &g_reorder_buffer, sizeof(uint32_t)); 418 nbytes += sizeof(uint32_t); 419 420 /* add Performance Counter Frequency */ 421 422 cpy_hton64(&g_txbuf[nbytes], &g_TimeStampFreq); 423 nbytes += sizeof(uint64_t); 424 425 tx_write(g_txbuf, nbytes); 426 427 IF_TRACED(TRC_PACKET) 428 dbgprintf("qos_initsink: tx_ready, seq=%d -> " ETHERADDR_FMT "\n", 429 g_sequencenum, ETHERADDR_PRINT(&pThisSsn->qssn_ctrlr_real)); 430 END_TRACE 431} 432 433 434static 435void 436qos_reset(void) 437{ 438 qos_session_t* pThisSsn = qos_find_session(); 439 size_t nbytes; 440 441 /* Find associated session, and reject with silence if none found. */ 442 if (pThisSsn == NULL) 443 { 444 warn("packetio_recv_handler: no session active for " ETHERADDR_FMT "; ignoring...\n", 445 ETHERADDR_PRINT(&g_base_hdr->tbh_realsrc)); 446 return; 447 } 448 449 /* Otherwise, clear the associated session */ 450 pThisSsn->qssn_is_valid = FALSE; 451 pThisSsn->qssn_ticks_til_discard = 0; 452 pThisSsn->qssn_num_active_buckets = 0; 453 454 /* and compose & send an ACK msg */ 455 456 fmt_base(g_txbuf, &g_hwaddr, &pThisSsn->qssn_ctrlr_real, ToS_QoSDiagnostics, 457 Qopcode_ACK, g_sequencenum, FALSE /*no broadcast*/); 458 nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t); 459 460 tx_write(g_txbuf, nbytes); 461 462 IF_TRACED(TRC_PACKET) 463 dbgprintf("qos_reset: tx_ack, seq=%d -> " ETHERADDR_FMT "\n", 464 g_sequencenum, ETHERADDR_PRINT(&pThisSsn->qssn_ctrlr_real)); 465 END_TRACE 466} 467 468 469 470static 471void 472qos_probe(void) 473{ 474 qos_session_t* pThisSsn; 475 476 /* Pick up the rx-timestamp from the driver, put in global timestamp */ 477 get_timestamp(&g_pktio_timestamp); 478 479 /* Find associated session, and reject with silence if none found. */ 480 pThisSsn = qos_find_session(); 481 482 if (pThisSsn == NULL) 483 { 484 IF_TRACED(TRC_QOS) 485 dbgprintf("qos_probe: no matching session found; ignoring.\n"); 486 END_TRACE 487 return; 488 } 489 490 /* Valid session - mark it as "still active" */ 491 pThisSsn->qssn_ticks_til_discard = 4; 492 493 /* If ProbeGap, just stamp it and reflect it, unless it has an 802.1p field */ 494 if (g_qprb_hdr->probe_testtype == 1) 495 { 496 if ((g_qprb_hdr->probe_pqval & 0x80) == 0) 497 { 498 /* change the test-type to 2 when reflecting */ 499 g_qprb_hdr->probe_testtype = 2; 500 501 /* setup ethernet and Base-hdr source and dest addresses to return to sender */ 502 memcpy(&g_ethernet_hdr->eh_dst, &g_base_hdr->tbh_realsrc, sizeof(etheraddr_t)); 503 memcpy(&g_ethernet_hdr->eh_src, &g_base_hdr->tbh_realdst, sizeof(etheraddr_t)); 504 memcpy(&g_base_hdr->tbh_realdst, &g_base_hdr->tbh_realsrc, sizeof(etheraddr_t)); 505 memcpy(&g_base_hdr->tbh_realsrc, &g_ethernet_hdr->eh_src, sizeof(etheraddr_t)); 506 507 /* Add the rcv timestamp from the global save area */ 508 cpy_hton64(&g_qprb_hdr->probe_rxstamp, &g_pktio_timestamp); 509 510 /* Add the rtx timestamp just before sending */ 511 get_timestamp(&g_pktio_timestamp); 512 cpy_hton64(&g_qprb_hdr->probe_rtxstamp, &g_pktio_timestamp); 513 514 /* and return the packet - do not save in re_txbuf! */ 515 tx_write(g_rxbuf, g_rcvd_pkt_len); 516 517 IF_TRACED(TRC_PACKET) 518 dbgprintf("qos_probegap: reflecting, no 802.1p, seq=%d -> " ETHERADDR_FMT "\n", 519 g_sequencenum, ETHERADDR_PRINT(&pThisSsn->qssn_ctrlr_real)); 520 END_TRACE 521 } else { 522 /* there is a valid 802.1p field, so the reflected packet must be tagged. */ 523 qos_ether_header_t *ethr_hdr; /* pointer to qos ethernet-header in txbuf */ 524 qos_base_header_t *base_hdr; /* pointer to qos base-header in txbuf */ 525 qos_probe_header_t *qprb_hdr; /* pointer to qos probe-header in txbuf */ 526 527 ethr_hdr = (qos_ether_header_t*) g_txbuf; 528 base_hdr = (qos_base_header_t*) (ethr_hdr+1); 529 qprb_hdr = (qos_probe_header_t*)(base_hdr+1); 530 531 /* setup ethernet and base-hdr source and dest addresses to return to sender */ 532 memcpy(ðr_hdr->qeh_dst, &g_base_hdr->tbh_realsrc, sizeof(etheraddr_t)); 533 memcpy(ðr_hdr->qeh_src, &g_base_hdr->tbh_realdst, sizeof(etheraddr_t)); 534 memcpy(&base_hdr->qbh_realdst, &g_base_hdr->tbh_realsrc, sizeof(etheraddr_t)); 535 memcpy(&base_hdr->qbh_realsrc, &g_base_hdr->tbh_realdst, sizeof(etheraddr_t)); 536 537 /* Set up the 802.1q tag, then insert the .1p value in the highest 7 bits (vlan==0) */ 538 ethr_hdr->qeh_qtag = 0x0081; 539 ethr_hdr->qeh_ptag = htons((g_qprb_hdr->probe_pqval << 9)); 540 ethr_hdr->qeh_ethertype = g_ethernet_hdr->eh_ethertype; 541 542 /* fill out rest of base header */ 543 base_hdr->qbh_version = g_base_hdr->tbh_version; 544 base_hdr->qbh_tos = g_base_hdr->tbh_tos; 545 base_hdr->qbh_resrvd = g_base_hdr->tbh_resrvd; 546 base_hdr->qbh_opcode = g_base_hdr->tbh_opcode; 547 base_hdr->qbh_seqnum = g_base_hdr->tbh_seqnum; 548 549 /* Fill out the probe-hdr */ 550 qprb_hdr->probe_txstamp = g_qprb_hdr->probe_txstamp; 551 qprb_hdr->probe_testtype = 2; 552 qprb_hdr->probe_pktID = g_qprb_hdr->probe_pktID; 553 qprb_hdr->probe_pqval = g_qprb_hdr->probe_pqval; 554 555 /* Add the rcv timestamp from the global save area */ 556 cpy_hton64(&qprb_hdr->probe_rxstamp, &g_pktio_timestamp); 557 558 /* Copy the payload */ 559 memcpy(&qprb_hdr->probe_payload, &g_qprb_hdr->probe_payload, 560 g_rcvd_pkt_len - (((uint8_t*)&g_qprb_hdr->probe_payload) - g_txbuf)); 561 562 /* Add the rtx timestamp just before sending */ 563 get_timestamp(&g_pktio_timestamp); 564 cpy_hton64(&qprb_hdr->probe_rtxstamp, &g_pktio_timestamp); 565 566 /* and return the packet (4 bytes longer due to tags) - do not save in re_txbuf! */ 567 tx_write(g_txbuf, g_rcvd_pkt_len+4); 568 569 IF_TRACED(TRC_PACKET) 570 dbgprintf("qos_probegap: reflecting, with 802.1p priority of: %d, seq=%d -> " ETHERADDR_FMT "\n", 571 (g_qprb_hdr->probe_pqval & 0x7f), g_sequencenum, 572 ETHERADDR_PRINT(&pThisSsn->qssn_ctrlr_real)); 573 END_TRACE 574 } 575 } else if (g_qprb_hdr->probe_testtype == 0) { /* timed probe */ 576 qosEventDescr_t* evt; 577 qosEventBucket_t* bucket = qos_find_bucket(pThisSsn); 578 579 do 580 { 581 if (bucket) 582 { 583 // Make sure we don't try to store more than fits in the bucket 584 if (bucket->evt_numEvts >= MAX_QOS_EVENTS_PER_BUCKET) 585 { 586 break; 587 } 588 } 589 else 590 { 591 /* There is no existing bucket to dump the event into, so find a new one */ 592 if (pThisSsn->qssn_num_active_buckets >= MAX_QOS_BUCKETS) 593 { 594 /* Reuse the oldest bucket */ 595 bucket = &pThisSsn->qssn_evt_buckets[pThisSsn->qssn_first_bucket]; 596 pThisSsn->qssn_first_bucket = (pThisSsn->qssn_first_bucket + 1) & (MAX_QOS_BUCKETS - 1); 597 } 598 else 599 { 600 /* Use the next available bucket */ 601 bucket = &pThisSsn->qssn_evt_buckets[(pThisSsn->qssn_first_bucket + pThisSsn->qssn_num_active_buckets) & (MAX_QOS_BUCKETS - 1)]; 602 pThisSsn->qssn_num_active_buckets++; 603 } 604 605 bucket->evt_seqNum = g_sequencenum; 606 bucket->evt_numEvts = 0; 607 } 608 609 evt = &bucket->evt_descs[bucket->evt_numEvts++]; 610 611 /* Copy timestamps, packet-ID, and set reserved-byte... */ 612 memcpy(&evt->ctrlr_txstamp, &g_qprb_hdr->probe_txstamp, sizeof(uint64_t)); 613 614 /* Pick up the rx-timestamp from the driver, put in global timestamp */ 615 cpy_hton64(&evt->sink_rxstamp, &g_pktio_timestamp); 616 617 evt->evt_pktID = g_qprb_hdr->probe_pktID; 618 evt->evt_reserved = 0; 619 } while (FALSE); 620 621 IF_TRACED(TRC_PACKET) 622 dbgprintf("qos_timedprobe processed: seq=" FMT_UINT16 ", evtcount=" FMT_UINT32 "\n", 623 g_sequencenum, bucket->evt_numEvts); 624 END_TRACE 625 } 626} 627 628 629static 630void 631qos_query(void) 632{ 633 qosEventBucket_t* bucket; 634 qos_session_t* pThisSsn = qos_find_session(); 635 size_t nbytes; 636 uint16_t numEvts; 637 638 /* Find associated session, and reject with silence if none found. */ 639 if (pThisSsn == NULL) return; 640 641 /* Valid session - mark it as "still active" */ 642 pThisSsn->qssn_ticks_til_discard = 4; 643 644 /* Compose the response headers in the space left before the events descrs */ 645 /* Build the ethernet header and base header */ 646 fmt_base(g_txbuf, &g_hwaddr, &pThisSsn->qssn_ctrlr_real, ToS_QoSDiagnostics, 647 Qopcode_QueryResp, g_sequencenum, FALSE /*no broadcast*/); 648 nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t); 649 650 /* Locate the event bucket */ 651 bucket = qos_find_bucket(pThisSsn); 652 653 /* Add the number of events */ 654 numEvts = bucket ? bucket->evt_numEvts : 0; 655 g_short_reorder_buffer = htons(numEvts); 656 memcpy(&g_txbuf[nbytes], &g_short_reorder_buffer, sizeof(uint16_t)); 657 nbytes += sizeof(uint16_t); 658 659 /* Copy the events */ 660 if (numEvts) 661 { 662 memcpy(&g_txbuf[nbytes], bucket->evt_descs, sizeof(qosEventDescr_t) * numEvts); 663 nbytes += sizeof(qosEventDescr_t) * numEvts; 664 } 665 666 /* And send it... */ 667 tx_write(g_txbuf, nbytes); 668 669 IF_TRACED(TRC_PACKET) 670 dbgprintf("qos_query_response: sending " FMT_UINT32 " events, seq=" FMT_UINT16 " -> " ETHERADDR_FMT "\n", 671 numEvts, g_sequencenum, ETHERADDR_PRINT(&pThisSsn->qssn_ctrlr_real)); 672 END_TRACE 673} 674 675 676static 677void 678qos_counterlease(void) 679{ 680 struct timeval now; 681 682 IF_TRACED(TRC_QOS) 683 dbgprintf("qos_counter_lease: timeout was: " FMT_UINT32 ", now: 300",g_samples_remaining); 684 END_TRACE 685 686 if (g_samples_remaining == 0) 687 { 688 /* start collection: repeats every second - until the lease runs out */ 689 gettimeofday(&now, NULL); 690 now.tv_sec += 1; 691 g_qos_CTA_timer = event_add(&now, interface_counter_recorder, /*state:*/NULL); 692 IF_TRACED(TRC_QOS) 693 dbgprintf("qos_counter_lease: 1-sec timer started\n"); 694 END_TRACE 695 } 696 g_samples_remaining = 300; 697} 698 699 700static 701void 702qos_snapshot(void) 703{ 704 size_t nbytes; 705 uint8_t *pMsg, sample_cnt; 706 uint64_t perf_timestamp; 707 uint count, next; 708 qos_perf_sample *pSample, *thisSample; 709 710 /* Build the ethernet header and base header */ 711 pMsg = g_txbuf; 712 fmt_base(pMsg, &g_hwaddr, &g_base_hdr->tbh_realsrc, ToS_QoSDiagnostics, 713 Qopcode_CounterResult, g_sequencenum, FALSE /*no broadcast*/); 714 nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t); 715 716 /* Calculate the sub-second interval and write the CounterResult header */ 717 stamp_time(&perf_timestamp); 718 pMsg[nbytes++] = (uint8_t)(((perf_timestamp - g_perf_timestamp)*256)/1000000); // sub-second span 719 pMsg[nbytes++] = (uint8_t)BYTE_SCALE; 720 pMsg[nbytes++] = (uint8_t)PKT_SCALE; 721 sample_cnt = (g_snap_hdr->cnt_rqstd <= (uint8_t)g_sample_count) ? g_snap_hdr->cnt_rqstd : (uint8_t)g_sample_count; 722 pMsg[nbytes++] = sample_cnt; 723 724 IF_TRACED(TRC_QOS) 725 dbgprintf("qos_snapshot: subsec: %d; sample-cnt: " FMT_UINT32 "\n",(int)pMsg[32], g_sample_count); 726 END_TRACE 727 728 /* Now copy the samples to the QosCounterResult msg */ 729 next = (g_next_sample + (60 - sample_cnt)) % 60; 730 pSample = (qos_perf_sample*)&pMsg[nbytes]; 731 for (count=sample_cnt; count>0; count--) 732 { 733 thisSample = &g_perf_samples[next++]; 734 next %= 60; 735 pSample->bytes_rcvd = htons(thisSample->bytes_rcvd); 736 pSample->pkts_rcvd = htons(thisSample->pkts_rcvd); 737 pSample->bytes_sent = htons(thisSample->bytes_sent); 738 pSample->pkts_sent = htons(thisSample->pkts_sent); 739 IF_TRACED(TRC_QOS) 740 dbgprintf(" sample: rcvd: %d; r-pkts: %d; sent: %d; s-pkts: %d\n", 741 thisSample->bytes_rcvd,thisSample->pkts_rcvd,thisSample->bytes_sent,thisSample->pkts_sent); 742 END_TRACE 743 pSample++; 744 nbytes += sizeof(qos_perf_sample); 745 } 746 /* Now take a sub-second sample, and put it in the QosCounterResult msg */ 747 { 748 uint32_t rbytes, rpkts, tbytes, tpkts; 749 uint32_t delta1, delta2, delta3, delta4 ; 750 751 /* save the counters, so normal sampling won't be disrupted */ 752 rbytes = g_rbytes; rpkts = g_rpkts; tbytes = g_tbytes; tpkts = g_tpkts; 753 754 get_raw_samples(); // get current values for g_rbytes, g_rpkts, g_tbytes, g_tpkts 755 756 delta1 = (g_rbytes-rbytes > 0)? (g_rbytes-rbytes) : 0; 757 pSample->bytes_rcvd = htons(delta1 / BYTE_SCALE_FACTOR); 758 759 delta2 = (g_rpkts-rpkts > 0)? (g_rpkts-rpkts) : 0; 760 pSample->pkts_rcvd = htons(delta2 / PKT_SCALE_FACTOR); 761 762 delta3 = (g_tbytes-tbytes > 0)? (g_tbytes-tbytes) : 0; 763 pSample->bytes_sent = htons(delta3 / BYTE_SCALE_FACTOR); 764 765 delta4 = (g_tpkts-tpkts > 0)? (g_tpkts-tpkts) : 0; 766 pSample->pkts_sent = htons(delta4 / PKT_SCALE_FACTOR); 767 768 IF_TRACED(TRC_QOS) 769 dbgprintf(" sub-sec sample: rcvd: " FMT_UINT32 "; r-pkts: " FMT_UINT32 \ 770 "; sent: " FMT_UINT32 "; s-pkts: " FMT_UINT32 "\n", 771 delta1, delta2, delta3, delta4); 772 END_TRACE 773 774 /* restore the saved counters */ 775 g_rbytes = rbytes; g_rpkts = rpkts; g_tbytes = tbytes; g_tpkts = tpkts; 776 777 /* count the subsecond sample in the packet length */ 778 nbytes += sizeof(qos_perf_sample); 779 } 780 781 /* And send it... */ 782 tx_write( g_txbuf, nbytes ); 783 784 IF_TRACED(TRC_PACKET) 785 dbgprintf("qos_counter_result: sending " FMT_UINT32 " perf-samples + sub-sec-sample, seq=" FMT_UINT16 \ 786 " -> " ETHERADDR_FMT "\n", 787 g_sample_count, g_sequencenum, ETHERADDR_PRINT(&g_base_hdr->tbh_realsrc)); 788 END_TRACE 789} 790 791 792/************************** Q O S M E S S A G E R E C E I V E R **************************/ 793 794/* Called by packetio_recv_handler() when msg ToS indicates QOS. 795 * The ether and base headers are validated, and ether- and base-header ptrs are set up. */ 796extern void qosrcvpkt(void); 797 798void 799qosrcvpkt(void) 800{ 801 uint16_t thisSeqnum; 802 803 /* check global g_opcode for QOS case */ 804 if (g_opcode < 0 || g_opcode >= Qopcode_INVALID) 805 { 806 warn("qospktrcv: g_opcode=%d is out of range for QoS msg; ignoring\n", g_opcode); 807 return; 808 } 809 IF_TRACED(TRC_PACKET) 810 dbgprintf("QOS: g_opcode=%d\n",g_opcode); 811 END_TRACE 812 813 thisSeqnum = ntohs(g_base_hdr->tbh_seqnum); 814 815 /* QosCounterLease frame must not be sequenced, everything else must be sequenced */ 816 /* QosCounterLease is the only one that's broadcasted */ 817 if (g_opcode != Qopcode_CounterLease) 818 { 819 if (thisSeqnum == 0) 820 { 821 warn("qospktrcv: g_opcode=%d must be sequenced; ignoring\n", g_opcode); 822 return; 823 } 824 else if (!ETHERADDR_EQUALS(&g_base_hdr->tbh_realdst, &g_hwaddr)) 825 { 826 warn("qospktrcv: g_opcode=%d must be directed; ignoring\n", g_opcode); 827 return; 828 } 829 } 830 else if (thisSeqnum != 0) 831 { 832 warn("qospktrcv: QosCounterLease must not be sequenced; ignoring\n"); 833 return; 834 } 835 else if (!ETHERADDR_IS_BCAST(&g_base_hdr->tbh_realdst)) 836 { 837 warn("qospktrcv: QosCounterLease must be broadcasted; ignoring\n"); 838 return; 839 } 840 841 /* Validate source/dest real/ether addresses */ 842 if (!ETHERADDR_EQUALS(&g_ethernet_hdr->eh_src, &g_base_hdr->tbh_realsrc) || 843 !ETHERADDR_EQUALS(&g_ethernet_hdr->eh_dst, &g_base_hdr->tbh_realdst)) 844 { 845 return; 846 } 847 848 /* print the frame */ 849 IF_TRACED(TRC_PACKET) 850 dbgprintf(" [" ETHERADDR_FMT "] -> [" ETHERADDR_FMT "] %s (seq=%d)\n", 851 ETHERADDR_PRINT(&g_ethernet_hdr->eh_src), ETHERADDR_PRINT(&g_ethernet_hdr->eh_dst), 852 Qos_opcode_names[g_opcode], thisSeqnum); 853 END_TRACE 854 855 /* By this time, we are pretty sure the sequence number is valid, so save a global copy... */ 856 g_sequencenum = thisSeqnum; 857 858 /* Set up global pointers to the 2 possible types of received headers that are bigger than the base hdr */ 859 g_qprb_hdr = (qos_probe_header_t*) (g_base_hdr + 1); 860 g_qinit_hdr = (qos_initsink_header_t*)(g_base_hdr + 1); 861 g_snap_hdr = (qos_snapshot_header_t*)(g_base_hdr + 1); 862 863 /* Finally, perform per-g_opcode validation & processing */ 864 switch (g_opcode) 865 { 866 case Qopcode_Probe: 867 qos_probe(); 868 break; 869 870 case Qopcode_Query: 871 qos_query(); 872 break; 873 874 case Qopcode_CounterSnapshot: 875 qos_snapshot(); 876 break; 877 878 case Qopcode_CounterLease: 879 qos_counterlease(); 880 break; 881 882 case Qopcode_InitializeSink: 883 qos_initsink(); 884 break; 885 886 case Qopcode_Reset: 887 qos_reset(); 888 break; 889 890 /* (invalid- or Sink-sent-packets are ignored completely) */ 891 case Qopcode_ACK: // Sent by Sink 892 case Qopcode_QueryResp: // Sent by Sink 893 case Qopcode_Ready: // Sent by Sink 894 case Qopcode_Error: // Sent by Sink 895 case Qopcode_CounterResult:// Sent by Sink 896 case Opcode_INVALID: 897 default: 898 break; 899 } 900} 901