blob: a328b3ecb2d8784a90fe85c239bb9fecf33fac20 [file] [log] [blame]
Rich Lanea06d0c32013-03-25 08:52:03 -07001:: # Copyright 2013, Big Switch Networks, Inc.
2:: #
3:: # LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4:: # the following special exception:
5:: #
6:: # LOXI Exception
7:: #
8:: # As a special exception to the terms of the EPL, you may distribute libraries
9:: # generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10:: # that copyright and licensing notices generated by LoxiGen are not altered or removed
11:: # from the LoxiGen Libraries and the notice provided below is (i) included in
12:: # the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13:: # documentation for the LoxiGen Libraries, if distributed in binary form.
14:: #
15:: # Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16:: #
17:: # You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18:: # a copy of the EPL at:
19:: #
20:: # http://www.eclipse.org/legal/epl-v10.html
21:: #
22:: # Unless required by applicable law or agreed to in writing, software
23:: # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24:: # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25:: # EPL for the specific language governing permissions and limitations
26:: # under the EPL.
27::
Rich Laned983aa52013-06-13 11:48:37 -070028:: include('_copyright.c')
Rich Lanea06d0c32013-03-25 08:52:03 -070029
30/****************************************************************
31 *
32 * of_object.c
33 *
34 * These are the low level object constructor/destructor operators.
35 *
36 ****************************************************************/
37
38#include "loci_log.h"
39#include <loci/loci.h>
40#include <loci/loci_validator.h>
41
42#if defined(OF_OBJECT_TRACKING)
43#include <BigList/biglist.h>
44
45loci_object_track_t loci_global_tracking;
46
47#define TRACK (&loci_global_tracking)
48#define TRACK_OBJS (TRACK->objects)
49#define CHECK_MAX(val, max) if ((val) > (max)) (max) = (val)
50
51#endif
52
53/**
54 * Create a generic new object and possibly underlying wire buffer
55 * @param bytes The number of bytes to allocate in the underlying buffer
56 *
57 * If bytes <= 0, do not allocate a wire buffer.
58 *
59 * Note that this is an internal function. The class specific
60 * new functions should be called to properly initialize and track an
61 * OF object.
62 */
63
64of_object_t *
65of_object_new(int bytes)
66{
67 of_object_t *obj;
68
69 if ((obj = (of_object_t *)MALLOC(sizeof(of_generic_t))) == NULL) {
70 return NULL;
71 }
72 MEMSET(obj, 0, sizeof(of_generic_t));
73
74 if (bytes > 0) {
75 if ((obj->wire_object.wbuf = of_wire_buffer_new(bytes)) == NULL) {
76 FREE(obj);
77 return NULL;
78 }
79 obj->wire_object.owned = 1;
80 }
81
82 return obj;
83}
84
85/**
86 * The delete function for LOCI objects
87 *
88 * @param obj Pointer to the object to be deleted
89 *
90 * This can be called on any LOCI object; it should not need to be
91 * overridden.
92 */
93
94void
95of_object_delete(of_object_t *obj)
96{
97 if (obj == NULL) {
98 return;
99 }
100
101#if defined(OF_OBJECT_TRACKING)
102 ASSERT(obj->track_info.magic == OF_OBJECT_TRACKING_MAGIC &&
103 "of_object double free?");
104 LOCI_LOG_TRACE("OF obj delete %p. Wire buf %p.\n", obj,
105 obj->wire_object.wbuf);
106 ASSERT(TRACK->count_current > 0);
107 TRACK->count_current -= 1;
108 TRACK->deletes += 1;
109
110 TRACK_OBJS = biglist_remove_link_free(TRACK_OBJS,
111 obj->track_info.bl_entry);
112 obj->track_info.magic = 0;
113#endif
114
115 /*
116 * Make callback if present
117 */
118 if (obj->track_info.delete_cb != NULL) {
119 obj->track_info.delete_cb(obj);
120 }
121
122 if (obj->wire_object.owned) {
123 of_wire_buffer_free(obj->wire_object.wbuf);
124 }
125
126 FREE(obj);
127}
128
129/**
130 * Duplicate an object
131 * @param src The object to be duplicated
132 * @returns Pointer to the duplicate or NULL on error. Caller is responsible
133 * for freeing the returned object.
134 */
135
136of_object_t *
137of_object_dup_(of_object_t *src)
138{
139 of_object_t *dst;
140 of_object_init_f init_fn;
141
142 if ((dst = (of_object_t *)MALLOC(sizeof(of_generic_t))) == NULL) {
143 return NULL;
144 }
145
146 MEMSET(dst, 0, sizeof(*dst));
147
148 /* Allocate a minimal wire buffer assuming we will not write to it. */
149 if ((dst->wire_object.wbuf = of_wire_buffer_new(src->length)) == NULL) {
150 FREE(dst);
151 return NULL;
152 }
153
154 dst->wire_object.owned = 1;
155
156 init_fn = of_object_init_map[src->object_id];
157 init_fn(dst, src->version, src->length, 0);
158
159 MEMCPY(OF_OBJECT_BUFFER_INDEX(dst, 0),
160 OF_OBJECT_BUFFER_INDEX(src, 0),
161 src->length);
162
163 return dst;
164}
165
166#if defined(OF_OBJECT_TRACKING)
167
168/**
169 * Record an object for tracking
170 *
171 * @param obj The object being tracked
172 * @param file The file name where the allocation is happening
173 * @param line The line number in the file where the alloc is happening
174 */
175
176void
177of_object_track(of_object_t *obj, const char *file, int line)
178{
179 if (obj != NULL) {
180 LOCI_LOG_TRACE("OF obj track %p, wire buf %p\n%s:%d\\n",
181 obj, obj->wire_object.wbuf, file, line);
182 obj->track_info.file = file;
183 obj->track_info.line = line;
184 TRACK_OBJS = biglist_prepend(TRACK_OBJS, (void *)obj);
185 obj->track_info.bl_entry = TRACK_OBJS;
186 obj->track_info.magic = OF_OBJECT_TRACKING_MAGIC;
187
188 TRACK->allocs += 1;
189 TRACK->count_current += 1;
190 CHECK_MAX(TRACK->count_current, TRACK->count_max);
191 }
192}
193
194/**
195 * The dup function when tracking is enabled
196 */
197
198of_object_t *
199of_object_dup_tracking(of_object_t *src, const char *file, int line)
200{
201 of_object_t *obj;
202
203 obj = of_object_dup_(src);
204 of_object_track(obj, file, line);
205
206 return obj;
207}
208
209/**
210 * Display track info for one object
211 */
212
213void
214of_object_track_output(of_object_t *obj, loci_writer_f writer, void* cookie)
215{
216 const char *offset;
217 static const char *unknown = "Unknown file";
218
219 if (obj->track_info.file) {
220 offset = strstr(obj->track_info.file, "Modules/");
221 if (offset == NULL) {
222 offset = obj->track_info.file;
223 } else {
224 offset += 8; /* Jump over Modules/ too */
225 }
226 } else {
227 offset = unknown;
228 }
229 writer(cookie, "obj %p. type %s.\n%s:%d\n",
230 obj, of_object_id_str[obj->object_id],
231 offset, obj->track_info.line);
232}
233
234/**
235 * Dump out the current object list from LOCI
236 *
237 * @param log_fn The output printf vector
238 *
239 */
240
241void
242of_object_track_report(loci_writer_f writer, void* cookie)
243{
244 biglist_t *elt;
245 of_object_t *obj;
246 int count = 0;
247
248 writer(cookie, "\nLOCI Outstanding object list.\n");
249 writer(cookie, "Objs: Current %d. Max %d. Created %d. Deleted %d\n",
250 TRACK->count_current, TRACK->count_max, TRACK->allocs,
251 TRACK->deletes);
252 if (TRACK_OBJS) {
253 BIGLIST_FOREACH_DATA(elt, TRACK_OBJS, of_object_t *, obj) {
254 of_object_track_output(obj, writer, cookie);
255 ++count;
256 }
257 }
258 if (count != TRACK->count_current) {
259 writer(cookie, "\nERROR: List has %d, but track count is %d\n",
260 count, TRACK->count_current);
261 }
262 writer(cookie, "\nEnd of outstanding object list\n");
263}
264
265#endif
266
267/**
268 * Generic new from message call
269 */
270
271of_object_t *
272of_object_new_from_message(of_message_t msg, int len)
273{
274 of_object_id_t object_id;
275 of_object_t *obj;
276 of_version_t version;
277
278 version = of_message_version_get(msg);
279 if (!OF_VERSION_OKAY(version)) {
280 return NULL;
281 }
282
283 if (of_validate_message(msg, len) != 0) {
284 LOCI_LOG_ERROR("message validation failed\n");
285 return NULL;
286 }
287
288 object_id = of_message_to_object_id(msg, len);
289 ASSERT(object_id != OF_OBJECT_INVALID);
290
291 if ((obj = of_object_new(-1)) == NULL) {
292 return NULL;
293 }
294
295 of_object_init_map[object_id](obj, version, 0, 0);
296
297 if (of_object_buffer_bind(obj, OF_MESSAGE_TO_BUFFER(msg), len,
298 OF_MESSAGE_FREE_FUNCTION) < 0) {
299 FREE(obj);
300 return NULL;
301 }
302 obj->length = len;
303 obj->version = version;
304
305#if defined(OF_OBJECT_TRACKING)
306 /* @FIXME Would be nice to get caller; for now only in cxn_instance */
307 of_object_track(obj, __FILE__, __LINE__);
308#endif
309
310 return obj;
311}
312
313/**
314 * Bind an existing buffer to an LOCI object
315 *
316 * @param obj Pointer to the object to be updated
317 * @param buf Pointer to the buffer to bind to obj
318 * @param bytes Length of buf
319 * @param buf_free An optional free function to be applied to
320 * buf on deallocation
321 *
322 * This can be called on any LOCI object; it should not need to be
323 * overridden.
324 */
325
326int
327of_object_buffer_bind(of_object_t *obj, uint8_t *buf, int bytes,
328 of_buffer_free_f buf_free)
329{
330 of_wire_object_t *wobj;
331 of_wire_buffer_t *wbuf;
332
333 ASSERT(buf != NULL);
334 ASSERT(bytes > 0);
335 // ASSERT(wobj is not bound);
336
337 wobj = &obj->wire_object;
338 MEMSET(wobj, 0, sizeof(*wobj));
339
340 wbuf = of_wire_buffer_new_bind(buf, bytes, buf_free);
341 if (wbuf == NULL) {
342 return OF_ERROR_RESOURCE;
343 }
344
345 wobj->wbuf = wbuf;
346 wobj->owned = 1;
347 obj->length = bytes;
348
349 return OF_ERROR_NONE;
350}
351
352/**
353 * Connect a child to a parent at the wire buffer level
354 *
355 * @param parent The top level object to bind to
356 * @param child The sub-object connecting to the parent
357 * @param offset The offset at which to attach the child RELATIVE
358 * TO THE PARENT in the buffer
359 * @param bytes The amount of the buffer dedicated to the child; see below
360 * @param inc_ref_count Should the ref count of the parent be incremented
361 *
362 * This is used for 'get' accessors for composite types as well as
363 * iterator functions for lists, both read (first/next) and write
364 * (append_init, append_advance).
365 *
366 * Connect a child object to a parent by setting up the child's
367 * wire_object to point to the parent's underlying buffer. The value
368 * of the parameter bytes is important in determining how the child
369 * is initialized:
370 * @li If bytes <= 0, the length and type of the child are not modified;
371 * no additional space is added to the buffer.
372 * @li If bytes > 0, the current wire buffer is grown to
373 * accomodate this many bytes. This is to support append operations.
374 *
375 * If an error is returned, future references to the child object
376 * (until it is reinitialized) are undefined.
377 */
378static void
379object_child_attach(of_object_t *parent, of_object_t *child,
380 int offset, int bytes)
381{
382 of_wire_object_t *c_wobj; /* Pointer to child's wire object */
383 of_wire_buffer_t *wbuf; /* Pointer to common wire buffer manager */
384
385 child->parent = parent;
386 wbuf = parent->wire_object.wbuf;
387
388 /* Set up the child's wire buf to point to same as parent */
389 c_wobj = &child->wire_object;
390 c_wobj->wbuf = wbuf;
391 c_wobj->obj_offset = parent->wire_object.obj_offset + offset;
392 c_wobj->owned = 0;
393
394 /*
395 * bytes determines if this is a read or write setup.
396 * If > 0, grow the buffer to accomodate the space
397 * Otherwise do nothing
398 */
399 if (bytes > 0) { /* Set internal length, request buffer space */
400 int tot_bytes; /* Total bytes to request for buffer if updated */
401
402 /* Set up space for the child in the parent's buffer */
403 tot_bytes = parent->wire_object.obj_offset + offset + bytes;
404
405 of_wire_buffer_grow(wbuf, tot_bytes);
406 child->length = bytes;
407 }
408 /* if bytes == 0 don't do anything */
409}
410
411/**
412 * Check for room in an object's wire buffer.
413 * @param obj The object being checked
414 * @param new_len The desired length
415 * @return Boolean
416 */
417
418int
419of_object_can_grow(of_object_t *obj, int new_len)
420{
421 return OF_OBJECT_ABSOLUTE_OFFSET(obj, new_len) <=
422 WBUF_ALLOC_BYTES(obj->wire_object.wbuf);
423}
424
425/**
426 * Set the xid of a message object
427 * @param obj The object being accessed
428 * @param xid The xid value to store in the wire buffer
429 * @return OF_ERROR_
430 * Since the XID is common across all versions, this is used
431 * for all XID accessors.
432 */
433
434int
435of_object_xid_set(of_object_t *obj, uint32_t xid)
436{
437 of_wire_buffer_t *wbuf;
438
439 if ((wbuf = OF_OBJECT_TO_WBUF(obj)) == NULL) {
440 return OF_ERROR_PARAM;
441 }
442 of_wire_buffer_u32_set(wbuf,
443 OF_OBJECT_ABSOLUTE_OFFSET(obj, OF_MESSAGE_XID_OFFSET), xid);
444 return OF_ERROR_NONE;
445}
446
447/**
448 * Get the xid of a message object
449 * @param obj The object being accessed
450 * @param xid Pointer to where to store the xid value
451 * @return OF_ERROR_
452 * Since the XID is common across all versions, this is used
453 * for all XID accessors.
454 */
455
456int
457of_object_xid_get(of_object_t *obj, uint32_t *xid)
458{
459 of_wire_buffer_t *wbuf;
460
461 if ((wbuf = OF_OBJECT_TO_WBUF(obj)) == NULL) {
462 return OF_ERROR_PARAM;
463 }
464 of_wire_buffer_u32_get(wbuf,
465 OF_OBJECT_ABSOLUTE_OFFSET(obj, OF_MESSAGE_XID_OFFSET), xid);
466 return OF_ERROR_NONE;
467}
468
469/****************************************************************
470 *
471 * Generic list operation implementations
472 *
473 ****************************************************************/
474
475/**
476 * Set up a child for appending to a parent list
477 * @param parent The parent; must be a list object
478 * @param child The child object; must be of type list element
479 * @return OF_ERROR_
480 *
481 * Attaches the wire buffer of the parent to the child by pointing
482 * the child to the end of the parent.
483 *
484 * Set the wire length and type from the child.
485 * Update the parent length adding the current child length
486 *
487 * After calling this function, the child object may be updated
488 * resulting in changes to the parent's wire buffer
489 *
490 */
491
492int
493of_list_append_bind(of_object_t *parent, of_object_t *child)
494{
495 if (parent == NULL || child == NULL ||
496 parent->wire_object.wbuf == NULL) {
497 return OF_ERROR_PARAM;
498 }
499
500 if (!of_object_can_grow(parent, parent->length + child->length)) {
501 return OF_ERROR_RESOURCE;
502 }
503
504 object_child_attach(parent, child, parent->length,
505 child->length);
506
507 /* Update the wire length and type if needed */
508 if (child->wire_length_set) {
509 child->wire_length_set(child, child->length);
510 }
511
512 if (child->wire_type_set) {
Rich Lane92feca82013-12-10 15:57:13 -0800513 child->wire_type_set(child);
Rich Lanea06d0c32013-03-25 08:52:03 -0700514 }
515
516 /* Update the parent's length */
517 of_object_parent_length_update(parent, child->length);
518
519 OF_LENGTH_CHECK_ASSERT(parent);
520
521 return OF_ERROR_NONE;
522}
523
524/**
525 * Generic atomic list append operation
526 * @param list The list to which an item is being appended
527 * @param item THe item to append to the list
528 *
529 * The contents of the item are copied to the end of the list.
530 * Currently assumes the list is at the end of its parent.
531 */
532int
533of_list_append(of_object_t *list, of_object_t *item)
534{
535 int new_len;
536
537 new_len = list->length + item->length;
538
539 if (!of_object_can_grow(list, new_len)) {
540 return OF_ERROR_RESOURCE;
541 }
542
543 of_wire_buffer_grow(list->wire_object.wbuf,
544 OF_OBJECT_ABSOLUTE_OFFSET(list, new_len));
545
546 MEMCPY(OF_OBJECT_BUFFER_INDEX(list, list->length),
547 OF_OBJECT_BUFFER_INDEX(item, 0), item->length);
548
549 /* Update the list's length */
550 of_object_parent_length_update(list, item->length);
551
552 OF_LENGTH_CHECK_ASSERT(list);
553
554 return OF_ERROR_NONE;
555}
556
557/**
558 * Generic list first function
559 * @param parent The parent; must be a list object
560 * @param child The child object; must be of type list element
561 * @return OF_ERROR_RANGE if list is empty
562 * @return OF_ERROR_
563 *
564 * Sets up the child to point to the first element in the list
565 *
566 * Child init must be called before this is called.
567 *
568 * @note TREAT AS PRIVATE
569 * Does not fully initialized object
570 */
571int
572of_list_first(of_object_t *parent, of_object_t *child)
573{
574 if (parent->length == 0) { /* Empty list */
575 return OF_ERROR_RANGE;
576 }
577
578 object_child_attach(parent, child, 0, 0);
579
580 return OF_ERROR_NONE;
581}
582
583/**
584 * Return boolean indicating if child is pointing to last entry in parent
585 * @param parent The parent; must be a list object
586 * @param child The child object; must be of type list element
587 * @return OF_ERROR_RANGE if list is empty
588 * @return OF_ERROR_
589 *
590 */
591static int
592of_list_is_last(of_object_t *parent, of_object_t *child)
593{
594 if (child->wire_object.obj_offset + child->length >=
595 parent->wire_object.obj_offset + parent->length) {
596 return 1;
597 }
598
599 return 0;
600}
601
602/**
603 * Generic list next function
604 * @param parent The parent; must be a list object
605 * @param child The child object; must be of type list element
606 * @return OF_ERROR_RANGE if at end of list
607 * @return OF_ERROR_
608 *
609 * Advances the child to point to the subsequent element in the list.
610 * The wire buffer object must not have been modified since the
611 * previous call to _first or _next.
612 *
613 * @note TREAT AS PRIVATE
614 * Does not fully initialized object
615 */
616int
617of_list_next(of_object_t *parent, of_object_t *child)
618{
619 int offset;
620
621 ASSERT(child->length > 0);
622
623 /* Get offset of parent */
624 if (of_list_is_last(parent, child)) {
625 return OF_ERROR_RANGE; /* We were on the last object */
626 }
627
628 /* Offset is relative to parent start */
629 offset = (child->wire_object.obj_offset - parent->wire_object.obj_offset) +
630 child->length;
631 object_child_attach(parent, child, offset, 0);
632
633 return OF_ERROR_NONE;
634}
635
636void
637of_object_wire_buffer_steal(of_object_t *obj, uint8_t **buffer)
638{
639 ASSERT(obj != NULL);
640 of_wire_buffer_steal(obj->wire_object.wbuf, buffer);
641 obj->wire_object.wbuf = NULL;
642}
643
Rich Lane50aa5942013-12-15 16:20:38 -0800644#define _MAX_PARENT_ITERATIONS 4
645/**
646 * Iteratively update parent lengths thru hierarchy
647 * @param obj The object whose length is being updated
648 * @param delta The difference between the current and new lengths
649 *
650 * Note that this includes updating the object itself. It will
651 * iterate thru parents.
652 *
653 * Assumes delta > 0.
654 */
655void
656of_object_parent_length_update(of_object_t *obj, int delta)
657{
658#ifndef NDEBUG
659 int count = 0;
660 of_wire_buffer_t *wbuf; /* For debug asserts only */
661#endif
662
663 while (obj != NULL) {
664 ASSERT(count++ < _MAX_PARENT_ITERATIONS);
665 obj->length += delta;
666 if (obj->wire_length_set != NULL) {
667 obj->wire_length_set(obj, obj->length);
668 }
669#ifndef NDEBUG
670 wbuf = obj->wire_object.wbuf;
671#endif
672
673 /* Asserts for wire length checking */
674 ASSERT(obj->length + obj->wire_object.obj_offset <=
675 WBUF_CURRENT_BYTES(wbuf));
676 if (obj->parent == NULL) {
677 ASSERT(obj->length + obj->wire_object.obj_offset ==
678 WBUF_CURRENT_BYTES(wbuf));
679 }
680
681 obj = obj->parent;
682 }
683}
684
Rich Lanea06d0c32013-03-25 08:52:03 -0700685/*
686 * Set member:
687 * get_wbuf_extent
688 * find offset of start of member
689 * if offset is at wbuf_extent (append new data)
690 * copy data at extent
691 * update parent length
692 * else
693 * find length of current entry
694 * move from end of current to extent to create (or remove) space
695 * copy data to offset
696 * update my length -- NEED LOCAL INFO TO DO THIS for some cases
697 */
698
699/* Also need: get offset of member for all combinations */
700/* Also need: get length of member for all combinations */
701#if 0
702/**
703 * Append the wire buffer data from src to the end of dst's wire buffer
704 */
705int
706of_object_append_buffer(of_object_t *dst, of_object_t *src)
707{
708 of_wire_buffer_t *s_wbuf, *d_wbuf;
709 int orig_len, dst_offset, src_offset;
710
711 d_wbuf = OF_OBJECT_TO_WBUF(dst);
712 s_wbuf = OF_OBJECT_TO_WBUF(src);
713 dst_offset = dst->wire_object.obj_offset + dst_length;
714 src_offset = src->wire_object.obj_offset;
715 OF_WIRE_BUFFER_INIT_CHECK(d_wbuf, dst_offset + src->length);
716 MEMCPY(OF_WBUF_BUFFER_POINTER(d_wbuf, dst_offset),
717 OF_WBUF_BUFFER_POINTER(s_wbuf, 0), src->length);
718
719 orig_len = dst->length;
720 dst->length += src->length;
721
722 return OF_ERROR_NONE;
723}
724
725/**
726 * Set the length of the actions object in a packet_in object
727 */
728
729int
730of_packet_out_actions_length_set(of_packet_t *obj, int len)
731{
732 if (obj == NULL || obj->object_id != OF_PACKET_IN ||
733 obj->wire_object.wbuf == NULL) {
734 return OF_ERROR_PARAM;
735 }
736
737 obj->actions_len_set(obj, len);
738}
739
740int
741_packet_out_data_offset_get(of_packet_t *obj)
742{
743 if (obj == NULL || obj->object_id != OF_PACKET_IN ||
744 obj->wire_object.wbuf == NULL) {
745 return -1;
746 }
747
748 return OF_PACKET_OUT_FIXED_LENGTH + _packet_out_actions_length_get(obj);
749}
750
751
752/**
753 * Simple length derivation function
754 *
755 * Most variable length fields are alone at the end of a structure.
756 * Their length is a simple calculation, just the total length of
757 * the parent minus the length of the non-variable part of the
758 * parent's class type.
759 *
760 * @param parent The parent object
761 * @param length (out) Where to store the length of the final
762 * variable length member
763 */
764int
765of_object_simple_length_derive(of_object_t *obj, int *length)
766{
767
768}
769#endif