Print this page
5045 use atomic_{inc,dec}_* instead of atomic_add_*
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/ipp/flowacct/flowacct.c
+++ new/usr/src/uts/common/ipp/flowacct/flowacct.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 */
26 26
27 27 #include <sys/types.h>
28 28 #include <sys/kmem.h>
29 29 #include <sys/conf.h>
30 30 #include <sys/atomic.h>
31 31 #include <netinet/in.h>
32 32 #include <netinet/in_systm.h>
33 33 #include <netinet/ip6.h>
34 34 #include <sys/socket.h>
35 35 #include <sys/acct.h>
36 36 #include <sys/exacct.h>
37 37 #include <inet/common.h>
38 38 #include <inet/ip.h>
39 39 #include <inet/ip6.h>
40 40 #include <sys/ddi.h>
41 41 #include <sys/strsun.h>
42 42 #include <sys/strsubr.h>
43 43 #include <ipp/flowacct/flowacct_impl.h>
44 44
45 45 /*
46 46 * flowacct - IPQoS accounting module. The module maintains an array
47 47 * of 256 hash buckets. When the action routine is invoked for a flow,
48 48 * if the flow (identified by the 5-tuple: saddr, daddr, sport, dport, proto)
49 49 * is already present in the flow table (indexed by the hash function FLOW_HASH)
50 50 * then a check is made to see if an item for this flow with the same
51 51 * dsfield, projid & user id is present. If it is, then the number of packets
52 52 * and the bytes are incremented for that item. If the item does
53 53 * not exist a new item is added for the flow. If the flow is not present
54 54 * an entry is made for the flow.
55 55 *
56 56 * A timer runs thru the table and writes all the flow items that have
57 57 * timed out to the accounting file (via exacct PSARC/1999/119), if present
58 58 * Configuration commands to change the timing interval is provided. The
59 59 * flow timeout value can also be configured. While the timeout is in nsec,
60 60 * the flow timer interval is in usec.
61 61 * Information for an active flow can be obtained by using kstats.
62 62 */
63 63
64 64 /* Used in computing the hash index */
65 65 #define FLOWACCT_ADDR_HASH(addr) \
66 66 ((addr).s6_addr8[8] ^ (addr).s6_addr8[9] ^ \
67 67 (addr).s6_addr8[10] ^ (addr).s6_addr8[13] ^ \
68 68 (addr).s6_addr8[14] ^ (addr).s6_addr8[15])
69 69
70 70 #define FLOWACCT_FLOW_HASH(f) \
71 71 (((FLOWACCT_ADDR_HASH(f->saddr)) + \
72 72 (FLOWACCT_ADDR_HASH(f->daddr)) + \
73 73 (f->proto) + (f->sport) + (f->dport)) \
74 74 % FLOW_TBL_COUNT)
75 75
76 76 /*
77 77 * Compute difference between a and b in nsec and store in delta.
78 78 * delta should be a hrtime_t. Taken from ip_mroute.c.
79 79 */
80 80 #define FLOWACCT_DELTA(a, b, delta) { \
81 81 int xxs; \
82 82 \
83 83 delta = (a).tv_nsec - (b).tv_nsec; \
84 84 if ((xxs = (a).tv_sec - (b).tv_sec) != 0) { \
85 85 switch (xxs) { \
86 86 case 2: \
87 87 delta += NANOSEC; \
88 88 /*FALLTHRU*/ \
89 89 case 1: \
90 90 delta += NANOSEC; \
91 91 break; \
92 92 default: \
93 93 delta += ((hrtime_t)NANOSEC * xxs); \
94 94 } \
95 95 } \
96 96 }
97 97
98 98 /* Debug level */
99 99 int flowacct_debug = 0;
100 100
101 101 /* Collect timed out flows to be written to the accounting file */
102 102 typedef struct flow_records_s {
103 103 flow_usage_t *fl_use;
104 104 struct flow_records_s *next;
105 105 }flow_records_t;
106 106
107 107 /* Get port information from the packet. Ignore fragments. */
108 108 static void
109 109 flowacct_port_info(header_t *header, void *iph, int af, mblk_t *mp)
110 110 {
111 111 uint16_t *up;
112 112
113 113 if (af == AF_INET) {
114 114 ipha_t *ipha = (ipha_t *)iph;
115 115 uint32_t u2, u1;
116 116 uint_t iplen;
117 117
118 118 u2 = ntohs(ipha->ipha_fragment_offset_and_flags);
119 119 u1 = u2 & (IPH_MF | IPH_OFFSET);
120 120 if (u1 != 0) {
121 121 return;
122 122 }
123 123 iplen = (ipha->ipha_version_and_hdr_length & 0xF) << 2;
124 124 up = (uint16_t *)(mp->b_rptr + iplen);
125 125 header->sport = (uint16_t)*up++;
126 126 header->dport = (uint16_t)*up;
127 127 } else {
128 128 ip6_t *ip6h = (ip6_t *)iph;
129 129 uint_t length = IPV6_HDR_LEN;
130 130 uint_t ehdrlen;
131 131 uint8_t *nexthdrp, *whereptr, *endptr;
132 132 ip6_dest_t *desthdr;
133 133 ip6_rthdr_t *rthdr;
134 134 ip6_hbh_t *hbhhdr;
135 135
136 136 whereptr = ((uint8_t *)&ip6h[1]);
137 137 endptr = mp->b_wptr;
138 138 nexthdrp = &ip6h->ip6_nxt;
139 139 while (whereptr < endptr) {
140 140 switch (*nexthdrp) {
141 141 case IPPROTO_HOPOPTS:
142 142 hbhhdr = (ip6_hbh_t *)whereptr;
143 143 ehdrlen = 8 * (hbhhdr->ip6h_len + 1);
144 144 if ((uchar_t *)hbhhdr + ehdrlen > endptr)
145 145 return;
146 146 nexthdrp = &hbhhdr->ip6h_nxt;
147 147 break;
148 148 case IPPROTO_DSTOPTS:
149 149 desthdr = (ip6_dest_t *)whereptr;
150 150 ehdrlen = 8 * (desthdr->ip6d_len + 1);
151 151 if ((uchar_t *)desthdr + ehdrlen > endptr)
152 152 return;
153 153 nexthdrp = &desthdr->ip6d_nxt;
154 154 break;
155 155 case IPPROTO_ROUTING:
156 156 rthdr = (ip6_rthdr_t *)whereptr;
157 157 ehdrlen = 8 * (rthdr->ip6r_len + 1);
158 158 if ((uchar_t *)rthdr + ehdrlen > endptr)
159 159 return;
160 160 nexthdrp = &rthdr->ip6r_nxt;
161 161 break;
162 162 case IPPROTO_FRAGMENT:
163 163 return;
164 164 case IPPROTO_TCP:
165 165 case IPPROTO_UDP:
166 166 case IPPROTO_SCTP:
167 167 /*
168 168 * Verify we have at least ICMP_MIN_TP_HDR_LEN
169 169 * bytes of the ULP's header to get the port
170 170 * info.
171 171 */
172 172 if (((uchar_t *)ip6h + length +
173 173 ICMP_MIN_TP_HDR_LEN) > endptr) {
174 174 return;
175 175 }
176 176 /* Get the protocol & ports */
177 177 header->proto = *nexthdrp;
178 178 up = (uint16_t *)((uchar_t *)ip6h + length);
179 179 header->sport = (uint16_t)*up++;
180 180 header->dport = (uint16_t)*up;
181 181 return;
182 182 case IPPROTO_ICMPV6:
183 183 case IPPROTO_ENCAP:
184 184 case IPPROTO_IPV6:
185 185 case IPPROTO_ESP:
186 186 case IPPROTO_AH:
187 187 header->proto = *nexthdrp;
188 188 return;
189 189 case IPPROTO_NONE:
190 190 default:
191 191 return;
192 192 }
193 193 length += ehdrlen;
194 194 whereptr += ehdrlen;
195 195 }
196 196 }
197 197 }
198 198
199 199 /*
200 200 * flowacct_find_ids(mp, header)
201 201 *
202 202 * attempt to discern the uid and projid of the originator of a packet by
203 203 * looking at the dblks making up the packet - yeuch!
204 204 *
205 205 * We do it by skipping any fragments with a credp of NULL (originated in
206 206 * kernel), taking the first value that isn't NULL to be the cred_t for the
207 207 * whole packet.
208 208 */
209 209 static void
210 210 flowacct_find_ids(mblk_t *mp, header_t *header)
211 211 {
212 212 cred_t *cr;
213 213
214 214 cr = msg_getcred(mp, NULL);
215 215 if (cr != NULL) {
216 216 header->uid = crgetuid(cr);
217 217 header->projid = crgetprojid(cr);
218 218 } else {
219 219 header->uid = (uid_t)-1;
220 220 header->projid = -1;
221 221 }
222 222 }
223 223
224 224 /*
225 225 * Extract header information in a header_t structure so that we don't have
226 226 * have to parse the packet everytime.
227 227 */
228 228 static int
229 229 flowacct_extract_header(mblk_t *mp, header_t *header)
230 230 {
231 231 ipha_t *ipha;
232 232 ip6_t *ip6h;
233 233 #define rptr ((uchar_t *)ipha)
234 234
235 235 /* 0 means no port extracted. */
236 236 header->sport = 0;
237 237 header->dport = 0;
238 238 flowacct_find_ids(mp, header);
239 239
240 240 V6_SET_ZERO(header->saddr);
241 241 V6_SET_ZERO(header->daddr);
242 242
243 243 ipha = (ipha_t *)mp->b_rptr;
244 244 header->isv4 = IPH_HDR_VERSION(ipha) == IPV4_VERSION;
245 245 if (header->isv4) {
246 246 ipha = (ipha_t *)mp->b_rptr;
247 247 V4_PART_OF_V6(header->saddr) = (int32_t)ipha->ipha_src;
248 248 V4_PART_OF_V6(header->daddr) = (int32_t)ipha->ipha_dst;
249 249 header->dsfield = ipha->ipha_type_of_service;
250 250 header->proto = ipha->ipha_protocol;
251 251 header->pktlen = ntohs(ipha->ipha_length);
252 252 if ((header->proto == IPPROTO_TCP) ||
253 253 (header->proto == IPPROTO_UDP) ||
254 254 (header->proto == IPPROTO_SCTP)) {
255 255 flowacct_port_info(header, ipha, AF_INET, mp);
256 256 }
257 257 } else {
258 258 /*
259 259 * Need to pullup everything.
260 260 */
261 261 if (mp->b_cont != NULL) {
262 262 if (!pullupmsg(mp, -1)) {
263 263 flowacct0dbg(("flowacct_extract_header: "\
264 264 "pullup error"));
265 265 return (-1);
266 266 }
267 267 }
268 268 ip6h = (ip6_t *)mp->b_rptr;
269 269 bcopy(ip6h->ip6_src.s6_addr32, header->saddr.s6_addr32,
270 270 sizeof (ip6h->ip6_src.s6_addr32));
271 271 bcopy(ip6h->ip6_dst.s6_addr32, header->daddr.s6_addr32,
272 272 sizeof (ip6h->ip6_dst.s6_addr32));
273 273 header->dsfield = __IPV6_TCLASS_FROM_FLOW(ip6h->ip6_vcf);
274 274 header->proto = ip6h->ip6_nxt;
275 275 header->pktlen = ntohs(ip6h->ip6_plen) +
276 276 ip_hdr_length_v6(mp, ip6h);
277 277 flowacct_port_info(header, ip6h, AF_INET6, mp);
278 278
279 279 }
280 280 #undef rptr
281 281 return (0);
282 282 }
283 283
284 284 /* Check if the flow (identified by the 5-tuple) exists in the hash table */
285 285 static flow_t *
286 286 flowacct_flow_present(header_t *header, int index,
287 287 flowacct_data_t *flowacct_data)
288 288 {
289 289 list_hdr_t *hdr = flowacct_data->flows_tbl[index].head;
290 290 flow_t *flow;
291 291
292 292 while (hdr != NULL) {
293 293 flow = (flow_t *)hdr->objp;
294 294 if ((flow != NULL) &&
295 295 (IN6_ARE_ADDR_EQUAL(&flow->saddr, &header->saddr)) &&
296 296 (IN6_ARE_ADDR_EQUAL(&flow->daddr, &header->daddr)) &&
297 297 (flow->proto == header->proto) &&
298 298 (flow->sport == header->sport) &&
299 299 (flow->dport == header->dport)) {
300 300 return (flow);
301 301 }
302 302 hdr = hdr->next;
303 303 }
304 304 return ((flow_t *)NULL);
305 305 }
306 306
307 307 /*
308 308 * Add an object to the list at insert_point. This could be a flow item or
309 309 * a flow itself.
310 310 */
311 311 static list_hdr_t *
312 312 flowacct_add_obj(list_head_t *tophdr, list_hdr_t *insert_point, void *obj)
313 313 {
314 314 list_hdr_t *new_hdr;
315 315
316 316 if (tophdr == NULL) {
317 317 return ((list_hdr_t *)NULL);
318 318 }
319 319
320 320 new_hdr = (list_hdr_t *)kmem_zalloc(FLOWACCT_HDR_SZ, KM_NOSLEEP);
321 321 if (new_hdr == NULL) {
322 322 flowacct0dbg(("flowacct_add_obj: error allocating mem"));
323 323 return ((list_hdr_t *)NULL);
324 324 }
325 325 gethrestime(&new_hdr->last_seen);
326 326 new_hdr->objp = obj;
327 327 tophdr->nbr_items++;
328 328
329 329 if (insert_point == NULL) {
330 330 if (tophdr->head == NULL) {
331 331 tophdr->head = new_hdr;
332 332 tophdr->tail = new_hdr;
333 333 return (new_hdr);
334 334 }
335 335
336 336 new_hdr->next = tophdr->head;
337 337 tophdr->head->prev = new_hdr;
338 338 tophdr->head = new_hdr;
339 339 return (new_hdr);
340 340 }
341 341
342 342 if (insert_point == tophdr->tail) {
343 343 tophdr->tail->next = new_hdr;
344 344 new_hdr->prev = tophdr->tail;
345 345 tophdr->tail = new_hdr;
346 346 return (new_hdr);
347 347 }
348 348
349 349 new_hdr->next = insert_point->next;
350 350 new_hdr->prev = insert_point;
351 351 insert_point->next->prev = new_hdr;
352 352 insert_point->next = new_hdr;
353 353 return (new_hdr);
354 354 }
355 355
356 356 /* Delete an obj from the list. This could be a flow item or the flow itself */
357 357 static void
358 358 flowacct_del_obj(list_head_t *tophdr, list_hdr_t *hdr, uint_t mode)
359 359 {
360 360 size_t length;
361 361 uint_t type;
362 362
363 363 if ((tophdr == NULL) || (hdr == NULL)) {
364 364 return;
365 365 }
366 366
367 367 type = ((flow_t *)hdr->objp)->type;
368 368
369 369 tophdr->nbr_items--;
370 370
371 371 if (hdr->next != NULL) {
372 372 hdr->next->prev = hdr->prev;
373 373 }
374 374 if (hdr->prev != NULL) {
375 375 hdr->prev->next = hdr->next;
376 376 }
377 377 if (tophdr->head == hdr) {
378 378 tophdr->head = hdr->next;
379 379 }
380 380 if (tophdr->tail == hdr) {
381 381 tophdr->tail = hdr->prev;
382 382 }
383 383
384 384 if (mode == FLOWACCT_DEL_OBJ) {
385 385 switch (type) {
386 386 case FLOWACCT_FLOW:
387 387 length = FLOWACCT_FLOW_SZ;
388 388 break;
389 389 case FLOWACCT_ITEM:
390 390 length = FLOWACCT_ITEM_SZ;
391 391 break;
392 392 }
393 393 kmem_free(hdr->objp, length);
394 394 hdr->objp = NULL;
395 395 }
396 396
397 397 kmem_free((void *)hdr, FLOWACCT_HDR_SZ);
398 398 }
399 399
400 400 /*
401 401 * Checks if the given item (identified by dsfield, project id and uid)
402 402 * is already present for the flow.
403 403 */
404 404 static flow_item_t *
405 405 flowacct_item_present(flow_t *flow, uint8_t dsfield, pid_t proj_id, uint_t uid)
406 406 {
407 407 list_hdr_t *itemhdr;
408 408 flow_item_t *item;
409 409
410 410 itemhdr = flow->items.head;
411 411
412 412 while (itemhdr != NULL) {
413 413 item = (flow_item_t *)itemhdr->objp;
414 414
415 415 if ((item->dsfield != dsfield) || (item->projid != proj_id) ||
416 416 (item->uid != uid)) {
417 417 itemhdr = itemhdr->next;
418 418 continue;
419 419 }
420 420 return (item);
421 421 }
422 422
423 423 return ((flow_item_t *)NULL);
424 424 }
425 425
426 426 /*
427 427 * Add the flow to the table, if not already present. If the flow is
428 428 * present in the table, add the item. Also, update the flow stats.
429 429 * Additionally, re-adjust the timout list as well.
430 430 */
431 431 static int
432 432 flowacct_update_flows_tbl(header_t *header, flowacct_data_t *flowacct_data)
433 433 {
434 434 int index;
435 435 list_head_t *fhead;
436 436 list_head_t *thead;
437 437 list_head_t *ihead;
438 438 boolean_t added_flow = B_FALSE;
439 439 timespec_t now;
440 440 flow_item_t *item;
441 441 flow_t *flow;
442 442
443 443 index = FLOWACCT_FLOW_HASH(header);
444 444 fhead = &flowacct_data->flows_tbl[index];
445 445
446 446 /* The timeout list */
447 447 thead = &flowacct_data->flows_tbl[FLOW_TBL_COUNT];
448 448
449 449 mutex_enter(&fhead->lock);
450 450 flow = flowacct_flow_present(header, index, flowacct_data);
451 451 if (flow == NULL) {
452 452 flow = (flow_t *)kmem_zalloc(FLOWACCT_FLOW_SZ, KM_NOSLEEP);
453 453 if (flow == NULL) {
454 454 mutex_exit(&fhead->lock);
455 455 flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
456 456 "error"));
457 457 return (-1);
458 458 }
459 459 flow->hdr = flowacct_add_obj(fhead, fhead->tail, (void *)flow);
460 460 if (flow->hdr == NULL) {
461 461 mutex_exit(&fhead->lock);
462 462 kmem_free(flow, FLOWACCT_FLOW_SZ);
463 463 flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
464 464 "error"));
465 465 return (-1);
466 466 }
467 467
468 468 flow->type = FLOWACCT_FLOW;
469 469 flow->isv4 = header->isv4;
470 470 bcopy(header->saddr.s6_addr32, flow->saddr.s6_addr32,
471 471 sizeof (header->saddr.s6_addr32));
472 472 bcopy(header->daddr.s6_addr32, flow->daddr.s6_addr32,
473 473 sizeof (header->daddr.s6_addr32));
474 474 flow->proto = header->proto;
475 475 flow->sport = header->sport;
476 476 flow->dport = header->dport;
477 477 flow->back_ptr = fhead;
478 478 added_flow = B_TRUE;
479 479 } else {
480 480 /*
481 481 * We need to make sure that this 'flow' is not deleted
482 482 * either by a scheduled timeout or an explict call
483 483 * to flowacct_timer() below.
484 484 */
485 485 flow->inuse = B_TRUE;
486 486 }
487 487
488 488 ihead = &flow->items;
↓ open down ↓ |
488 lines elided |
↑ open up ↑ |
489 489 item = flowacct_item_present(flow, header->dsfield, header->projid,
490 490 header->uid);
491 491 if (item == NULL) {
492 492 boolean_t just_once = B_TRUE;
493 493 /*
494 494 * For all practical purposes, we limit the no. of entries in
495 495 * the flow table - i.e. the max_limt that a user specifies is
496 496 * the maximum no. of flow items in the table.
497 497 */
498 498 try_again:
499 - atomic_add_32(&flowacct_data->nflows, 1);
499 + atomic_inc_32(&flowacct_data->nflows);
500 500 if (flowacct_data->nflows > flowacct_data->max_limit) {
501 - atomic_add_32(&flowacct_data->nflows, -1);
501 + atomic_dec_32(&flowacct_data->nflows);
502 502
503 503 /* Try timing out once */
504 504 if (just_once) {
505 505 /*
506 506 * Need to release the lock, as this entry
507 507 * could contain a flow that can be timed
508 508 * out.
509 509 */
510 510 mutex_exit(&fhead->lock);
511 511 flowacct_timer(FLOWACCT_JUST_ONE,
512 512 flowacct_data);
513 513 mutex_enter(&fhead->lock);
514 514 /* Lets check again */
515 515 just_once = B_FALSE;
516 516 goto try_again;
517 517 } else {
518 518 flow->inuse = B_FALSE;
519 519 /* Need to remove the flow, if one was added */
520 520 if (added_flow) {
521 521 flowacct_del_obj(fhead, flow->hdr,
522 522 FLOWACCT_DEL_OBJ);
523 523 }
524 524 mutex_exit(&fhead->lock);
525 525 flowacct1dbg(("flowacct_update_flows_tbl: "\
526 526 "maximum active flows exceeded\n"));
527 527 return (-1);
528 528 }
↓ open down ↓ |
17 lines elided |
↑ open up ↑ |
529 529 }
530 530 item = (flow_item_t *)kmem_zalloc(FLOWACCT_ITEM_SZ, KM_NOSLEEP);
531 531 if (item == NULL) {
532 532 flow->inuse = B_FALSE;
533 533 /* Need to remove the flow, if one was added */
534 534 if (added_flow) {
535 535 flowacct_del_obj(fhead, flow->hdr,
536 536 FLOWACCT_DEL_OBJ);
537 537 }
538 538 mutex_exit(&fhead->lock);
539 - atomic_add_32(&flowacct_data->nflows, -1);
539 + atomic_dec_32(&flowacct_data->nflows);
540 540 flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
541 541 "error"));
542 542 return (-1);
543 543 }
544 544 item->hdr = flowacct_add_obj(ihead, ihead->tail, (void *)item);
545 545 if (item->hdr == NULL) {
546 546 flow->inuse = B_FALSE;
547 547 /* Need to remove the flow, if one was added */
548 548 if (added_flow) {
549 549 flowacct_del_obj(fhead, flow->hdr,
550 550 FLOWACCT_DEL_OBJ);
551 551 }
552 552 mutex_exit(&fhead->lock);
553 - atomic_add_32(&flowacct_data->nflows, -1);
553 + atomic_dec_32(&flowacct_data->nflows);
554 554 kmem_free(item, FLOWACCT_ITEM_SZ);
555 555 flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
556 556 "error\n"));
557 557 return (-1);
558 558 }
559 559 /* If a flow was added, add it too */
560 560 if (added_flow) {
561 561 atomic_add_64(&flowacct_data->usedmem,
562 562 FLOWACCT_FLOW_RECORD_SZ);
563 563 }
564 564 atomic_add_64(&flowacct_data->usedmem, FLOWACCT_ITEM_RECORD_SZ);
565 565
566 566 item->type = FLOWACCT_ITEM;
567 567 item->dsfield = header->dsfield;
568 568 item->projid = header->projid;
569 569 item->uid = header->uid;
570 570 item->npackets = 1;
571 571 item->nbytes = header->pktlen;
572 572 item->creation_time = item->hdr->last_seen;
573 573 } else {
574 574 item->npackets++;
575 575 item->nbytes += header->pktlen;
576 576 }
577 577 gethrestime(&now);
578 578 flow->hdr->last_seen = item->hdr->last_seen = now;
579 579 mutex_exit(&fhead->lock);
580 580
581 581 /*
582 582 * Re-adjust the timeout list. The timer takes the thead lock
583 583 * follwed by fhead lock(s), so we release fhead, take thead
584 584 * and re-take fhead.
585 585 */
586 586 mutex_enter(&thead->lock);
587 587 mutex_enter(&fhead->lock);
588 588 /* If the flow was added, append it to the tail of the timeout list */
589 589 if (added_flow) {
590 590 if (thead->head == NULL) {
591 591 thead->head = flow->hdr;
592 592 thead->tail = flow->hdr;
593 593 } else {
594 594 thead->tail->timeout_next = flow->hdr;
595 595 flow->hdr->timeout_prev = thead->tail;
596 596 thead->tail = flow->hdr;
597 597 }
598 598 /*
599 599 * Else, move this flow to the tail of the timeout list, if it is not
600 600 * already.
601 601 * flow->hdr in the timeout list :-
602 602 * timeout_next = NULL, timeout_prev != NULL, at the tail end.
603 603 * timeout_next != NULL, timeout_prev = NULL, at the head.
604 604 * timeout_next != NULL, timeout_prev != NULL, in the middle.
605 605 * timeout_next = NULL, timeout_prev = NULL, not in the timeout list,
606 606 * ignore such flow.
607 607 */
608 608 } else if ((flow->hdr->timeout_next != NULL) ||
609 609 (flow->hdr->timeout_prev != NULL)) {
610 610 if (flow->hdr != thead->tail) {
611 611 if (flow->hdr == thead->head) {
612 612 thead->head->timeout_next->timeout_prev = NULL;
613 613 thead->head = thead->head->timeout_next;
614 614 flow->hdr->timeout_next = NULL;
615 615 thead->tail->timeout_next = flow->hdr;
616 616 flow->hdr->timeout_prev = thead->tail;
617 617 thead->tail = flow->hdr;
618 618 } else {
619 619 flow->hdr->timeout_prev->timeout_next =
620 620 flow->hdr->timeout_next;
621 621 flow->hdr->timeout_next->timeout_prev =
622 622 flow->hdr->timeout_prev;
623 623 flow->hdr->timeout_next = NULL;
624 624 thead->tail->timeout_next = flow->hdr;
625 625 flow->hdr->timeout_prev = thead->tail;
626 626 thead->tail = flow->hdr;
627 627 }
628 628 }
629 629 }
630 630 /*
631 631 * Unset this variable, now it is fine even if this
632 632 * flow gets deleted (i.e. after timing out its
633 633 * flow items) since we are done using it.
634 634 */
635 635 flow->inuse = B_FALSE;
636 636 mutex_exit(&fhead->lock);
637 637 mutex_exit(&thead->lock);
638 638 atomic_add_64(&flowacct_data->tbytes, header->pktlen);
639 639 return (0);
640 640 }
641 641
642 642 /* Timer for timing out flows/items from the flow table */
643 643 void
644 644 flowacct_timeout_flows(void *args)
645 645 {
646 646 flowacct_data_t *flowacct_data = (flowacct_data_t *)args;
647 647 flowacct_timer(FLOWACCT_FLOW_TIMER, flowacct_data);
648 648 flowacct_data->flow_tid = timeout(flowacct_timeout_flows, flowacct_data,
649 649 drv_usectohz(flowacct_data->timer));
650 650 }
651 651
652 652
653 653 /* Delete the item from the flow in the flow table */
654 654 static void
655 655 flowacct_timeout_item(flow_t **flow, list_hdr_t **item_hdr)
656 656 {
657 657 list_hdr_t *next_it_hdr;
658 658
659 659 next_it_hdr = (*item_hdr)->next;
660 660 flowacct_del_obj(&(*flow)->items, *item_hdr, FLOWACCT_DEL_OBJ);
661 661 *item_hdr = next_it_hdr;
662 662 }
663 663
664 664 /* Create a flow record for this timed out item */
665 665 static flow_records_t *
666 666 flowacct_create_record(flow_t *flow, list_hdr_t *ithdr)
667 667 {
668 668 int count;
669 669 flow_item_t *item = (flow_item_t *)ithdr->objp;
670 670 flow_records_t *tmp_frec = NULL;
671 671
672 672 /* Record to be written into the accounting file */
673 673 tmp_frec = kmem_zalloc(sizeof (flow_records_t), KM_NOSLEEP);
674 674 if (tmp_frec == NULL) {
675 675 flowacct0dbg(("flowacct_create_record: mem alloc error.\n"));
676 676 return (NULL);
677 677 }
678 678 tmp_frec->fl_use = kmem_zalloc(sizeof (flow_usage_t), KM_NOSLEEP);
679 679 if (tmp_frec->fl_use == NULL) {
680 680 flowacct0dbg(("flowacct_create_record: mem alloc error\n"));
681 681 kmem_free(tmp_frec, sizeof (flow_records_t));
682 682 return (NULL);
683 683 }
684 684
685 685 /* Copy the IP address */
686 686 for (count = 0; count < 4; count++) {
687 687 tmp_frec->fl_use->fu_saddr[count] =
688 688 htonl(flow->saddr.s6_addr32[count]);
689 689 tmp_frec->fl_use->fu_daddr[count] =
690 690 htonl(flow->daddr.s6_addr32[count]);
691 691 }
692 692
693 693 /*
694 694 * Ports, protocol, version, dsfield, project id, uid, nbytes, npackets
695 695 * creation time and last seen.
696 696 */
697 697 tmp_frec->fl_use->fu_sport = htons(flow->sport);
698 698 tmp_frec->fl_use->fu_dport = htons(flow->dport);
699 699 tmp_frec->fl_use->fu_protocol = flow->proto;
700 700 tmp_frec->fl_use->fu_isv4 = flow->isv4;
701 701 tmp_frec->fl_use->fu_dsfield = item->dsfield;
702 702 tmp_frec->fl_use->fu_projid = item->projid;
703 703 tmp_frec->fl_use->fu_userid = item->uid;
704 704 tmp_frec->fl_use->fu_nbytes = item->nbytes;
705 705 tmp_frec->fl_use->fu_npackets = item->npackets;
706 706 tmp_frec->fl_use->fu_lseen =
707 707 (uint64_t)(ulong_t)ithdr->last_seen.tv_sec;
708 708 tmp_frec->fl_use->fu_ctime =
709 709 (uint64_t)(ulong_t)item->creation_time.tv_sec;
710 710
711 711 return (tmp_frec);
712 712 }
713 713
714 714 /*
715 715 * Scan thru the timeout list and write the records to the accounting file, if
716 716 * possible. Basically step thru the timeout list maintained in the last
717 717 * hash bucket, FLOW_COUNT_TBL + 1, and timeout flows. This could be called
718 718 * from the timer, FLOWACCT_TIMER - delete only timed out flows or when this
719 719 * instance is deleted, FLOWACCT_PURGE_FLOW - delete all the flows from the
720 720 * table or as FLOWACCT_JUST_ONE - delete the first timed out flow. Since the
721 721 * flows are cronologically arranged in the timeout list, when called as
722 722 * FLOWACCT_TIMER and FLOWACCT_JUST_ONE, we can stop when we come across
723 723 * the first flow that has not timed out (which means none of the following
724 724 * flows would have timed out).
725 725 */
726 726 void
727 727 flowacct_timer(int type, flowacct_data_t *flowacct_data)
728 728 {
729 729 hrtime_t diff;
730 730 timespec_t now;
731 731 list_head_t *head, *thead;
732 732 flow_t *flow;
733 733 flow_item_t *item;
734 734 list_hdr_t *fl_hdr, *next_fl_hdr;
735 735 list_hdr_t *ithdr = (list_hdr_t *)NULL;
736 736 flow_records_t *frec = NULL, *tmp_frec, *tail;
737 737 uint64_t flow_size;
738 738 uint64_t item_size;
739 739
740 740 ASSERT(flowacct_data != NULL);
741 741
742 742 /* 2s-complement for subtraction */
743 743 flow_size = ~FLOWACCT_FLOW_RECORD_SZ + 1;
744 744 item_size = ~FLOWACCT_ITEM_RECORD_SZ + 1;
745 745
746 746 /* Get the current time */
747 747 gethrestime(&now);
748 748
749 749 /*
750 750 * For each flow in the table, scan thru all the items and delete
751 751 * those that have exceeded the timeout. If all the items in a
752 752 * flow have timed out, delete the flow entry as well. Finally,
753 753 * write all the delted items to the accounting file.
754 754 */
755 755 thead = &flowacct_data->flows_tbl[FLOW_TBL_COUNT];
756 756
757 757 mutex_enter(&thead->lock);
758 758 fl_hdr = thead->head;
759 759 while (fl_hdr != NULL) {
760 760 uint32_t items_deleted = 0;
761 761
762 762 next_fl_hdr = fl_hdr->timeout_next;
763 763 flow = (flow_t *)fl_hdr->objp;
764 764 head = flow->back_ptr;
765 765 mutex_enter(&head->lock);
766 766
767 767 /*LINTED*/
768 768 FLOWACCT_DELTA(now, fl_hdr->last_seen, diff);
769 769
770 770 /*
771 771 * If type is FLOW_TIMER, then check if the item has timed out.
772 772 * If type is FLOW_PURGE delete the entry anyways.
773 773 */
774 774 if ((type != FLOWACCT_PURGE_FLOW) &&
775 775 (diff < flowacct_data->timeout)) {
776 776 mutex_exit(&head->lock);
777 777 mutex_exit(&thead->lock);
778 778 goto write_records;
779 779 }
780 780
781 781 ithdr = flow->items.head;
782 782 while (ithdr != NULL) {
783 783 item = (flow_item_t *)ithdr->objp;
784 784 /*
785 785 * Fill in the flow record to be
786 786 * written to the accounting file.
787 787 */
788 788 tmp_frec = flowacct_create_record(flow, ithdr);
789 789 /*
790 790 * If we don't have memory for records,
791 791 * we will come back in case this is
792 792 * called as FLOW_TIMER, else we will
793 793 * go ahead and delete the item from
794 794 * the table (when asked to PURGE the
795 795 * table), so there could be some
796 796 * entries not written to the file
797 797 * when this action instance is
798 798 * deleted.
799 799 */
800 800 if (tmp_frec != NULL) {
801 801 tmp_frec->fl_use->fu_aname =
802 802 flowacct_data->act_name;
803 803 if (frec == NULL) {
804 804 frec = tmp_frec;
805 805 tail = frec;
806 806 } else {
807 807 tail->next = tmp_frec;
808 808 tail = tmp_frec;
809 809 }
810 810 } else if (type != FLOWACCT_PURGE_FLOW) {
811 811 mutex_exit(&head->lock);
812 812 mutex_exit(&thead->lock);
813 813 atomic_add_32(&flowacct_data->nflows,
814 814 (~items_deleted + 1));
815 815 goto write_records;
816 816 }
817 817
818 818 /* Update stats */
819 819 atomic_add_64(&flowacct_data->tbytes, (~item->nbytes +
820 820 1));
821 821
822 822 /* Delete the item */
823 823 flowacct_timeout_item(&flow, &ithdr);
824 824 items_deleted++;
825 825 atomic_add_64(&flowacct_data->usedmem, item_size);
826 826 }
827 827 ASSERT(flow->items.nbr_items == 0);
828 828 atomic_add_32(&flowacct_data->nflows, (~items_deleted + 1));
829 829
830 830 /*
831 831 * Don't delete this flow if we are making place for
832 832 * a new item for this flow.
833 833 */
834 834 if (!flow->inuse) {
835 835 if (fl_hdr->timeout_prev != NULL) {
836 836 fl_hdr->timeout_prev->timeout_next =
837 837 fl_hdr->timeout_next;
838 838 } else {
839 839 thead->head = fl_hdr->timeout_next;
840 840 }
841 841 if (fl_hdr->timeout_next != NULL) {
842 842 fl_hdr->timeout_next->timeout_prev =
843 843 fl_hdr->timeout_prev;
844 844 } else {
845 845 thead->tail = fl_hdr->timeout_prev;
846 846 }
847 847 fl_hdr->timeout_prev = NULL;
848 848 fl_hdr->timeout_next = NULL;
849 849 flowacct_del_obj(head, fl_hdr, FLOWACCT_DEL_OBJ);
850 850 atomic_add_64(&flowacct_data->usedmem, flow_size);
851 851 }
852 852 mutex_exit(&head->lock);
853 853 if (type == FLOWACCT_JUST_ONE) {
854 854 mutex_exit(&thead->lock);
855 855 goto write_records;
856 856 }
857 857 fl_hdr = next_fl_hdr;
858 858 }
859 859 mutex_exit(&thead->lock);
860 860 write_records:
861 861 /* Write all the timed out flows to the accounting file */
862 862 while (frec != NULL) {
863 863 tmp_frec = frec->next;
864 864 exacct_commit_flow(frec->fl_use);
865 865 kmem_free(frec->fl_use, sizeof (flow_usage_t));
866 866 kmem_free(frec, sizeof (flow_records_t));
867 867 frec = tmp_frec;
868 868 }
869 869 }
870 870
871 871 /*
872 872 * Get the IP header contents from the packet, update the flow table with
873 873 * this item and return.
874 874 */
875 875 int
876 876 flowacct_process(mblk_t **mpp, flowacct_data_t *flowacct_data)
877 877 {
878 878 header_t *header;
879 879 mblk_t *mp = *mpp;
↓ open down ↓ |
316 lines elided |
↑ open up ↑ |
880 880
881 881 ASSERT(mp != NULL);
882 882
883 883 /* If we don't find an M_DATA, return error */
884 884 if (mp->b_datap->db_type != M_DATA) {
885 885 if ((mp->b_cont != NULL) &&
886 886 (mp->b_cont->b_datap->db_type == M_DATA)) {
887 887 mp = mp->b_cont;
888 888 } else {
889 889 flowacct0dbg(("flowacct_process: no data\n"));
890 - atomic_add_64(&flowacct_data->epackets, 1);
890 + atomic_inc_64(&flowacct_data->epackets);
891 891 return (EINVAL);
892 892 }
893 893 }
894 894
895 895 header = kmem_zalloc(FLOWACCT_HEADER_SZ, KM_NOSLEEP);
896 896 if (header == NULL) {
897 897 flowacct0dbg(("flowacct_process: error allocing mem"));
898 - atomic_add_64(&flowacct_data->epackets, 1);
898 + atomic_inc_64(&flowacct_data->epackets);
899 899 return (ENOMEM);
900 900 }
901 901
902 902 /* Get all the required information into header. */
903 903 if (flowacct_extract_header(mp, header) != 0) {
904 904 kmem_free(header, FLOWACCT_HEADER_SZ);
905 - atomic_add_64(&flowacct_data->epackets, 1);
905 + atomic_inc_64(&flowacct_data->epackets);
906 906 return (EINVAL);
907 907 }
908 908
909 909 /* Updated the flow table with this entry */
910 910 if (flowacct_update_flows_tbl(header, flowacct_data) != 0) {
911 911 kmem_free(header, FLOWACCT_HEADER_SZ);
912 - atomic_add_64(&flowacct_data->epackets, 1);
912 + atomic_inc_64(&flowacct_data->epackets);
913 913 return (ENOMEM);
914 914 }
915 915
916 916 /* Update global stats */
917 - atomic_add_64(&flowacct_data->npackets, 1);
917 + atomic_inc_64(&flowacct_data->npackets);
918 918 atomic_add_64(&flowacct_data->nbytes, header->pktlen);
919 919
920 920 kmem_free(header, FLOWACCT_HEADER_SZ);
921 921 if (flowacct_data->flow_tid == 0) {
922 922 flowacct_data->flow_tid = timeout(flowacct_timeout_flows,
923 923 flowacct_data, drv_usectohz(flowacct_data->timer));
924 924 }
925 925 return (0);
926 926 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX