]>
Commit | Line | Data |
---|---|---|
064af421 BP |
1 | /* |
2 | * Copyright (c) 2008, 2009 Nicira Networks. | |
3 | * | |
a14bc59f BP |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
064af421 | 7 | * |
a14bc59f BP |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
064af421 BP |
15 | */ |
16 | ||
17 | #include <config.h> | |
18 | #include "pktbuf.h" | |
19 | #include <inttypes.h> | |
20 | #include <stdlib.h> | |
21 | #include "coverage.h" | |
22 | #include "ofpbuf.h" | |
23 | #include "timeval.h" | |
24 | #include "util.h" | |
25 | #include "vconn.h" | |
26 | ||
27 | #define THIS_MODULE VLM_pktbuf | |
28 | #include "vlog.h" | |
29 | ||
30 | /* Buffers are identified by a 32-bit opaque ID. We divide the ID | |
31 | * into a buffer number (low bits) and a cookie (high bits). The buffer number | |
32 | * is an index into an array of buffers. The cookie distinguishes between | |
33 | * different packets that have occupied a single buffer. Thus, the more | |
34 | * buffers we have, the lower-quality the cookie... */ | |
35 | #define PKTBUF_BITS 8 | |
36 | #define PKTBUF_MASK (PKTBUF_CNT - 1) | |
37 | #define PKTBUF_CNT (1u << PKTBUF_BITS) | |
38 | ||
39 | #define COOKIE_BITS (32 - PKTBUF_BITS) | |
40 | #define COOKIE_MAX ((1u << COOKIE_BITS) - 1) | |
41 | ||
42 | #define OVERWRITE_MSECS 5000 | |
43 | ||
44 | struct packet { | |
45 | struct ofpbuf *buffer; | |
46 | uint32_t cookie; | |
47 | long long int timeout; | |
48 | uint16_t in_port; | |
49 | }; | |
50 | ||
51 | struct pktbuf { | |
52 | struct packet packets[PKTBUF_CNT]; | |
53 | unsigned int buffer_idx; | |
54 | }; | |
55 | ||
56 | int | |
57 | pktbuf_capacity(void) | |
58 | { | |
59 | return PKTBUF_CNT; | |
60 | } | |
61 | ||
62 | struct pktbuf * | |
63 | pktbuf_create(void) | |
64 | { | |
65 | return xcalloc(1, sizeof *pktbuf_create()); | |
66 | } | |
67 | ||
68 | void | |
69 | pktbuf_destroy(struct pktbuf *pb) | |
70 | { | |
71 | if (pb) { | |
72 | size_t i; | |
73 | ||
74 | for (i = 0; i < PKTBUF_CNT; i++) { | |
75 | ofpbuf_delete(pb->packets[i].buffer); | |
76 | } | |
77 | free(pb); | |
78 | } | |
79 | } | |
80 | ||
81 | uint32_t | |
82 | pktbuf_save(struct pktbuf *pb, struct ofpbuf *buffer, uint16_t in_port) | |
83 | { | |
84 | struct packet *p = &pb->packets[pb->buffer_idx]; | |
85 | pb->buffer_idx = (pb->buffer_idx + 1) & PKTBUF_MASK; | |
86 | if (p->buffer) { | |
87 | if (time_msec() < p->timeout) { | |
88 | return UINT32_MAX; | |
89 | } | |
90 | ofpbuf_delete(p->buffer); | |
91 | } | |
92 | ||
93 | /* Don't use maximum cookie value since all-1-bits ID is special. */ | |
94 | if (++p->cookie >= COOKIE_MAX) { | |
95 | p->cookie = 0; | |
96 | } | |
97 | p->buffer = ofpbuf_clone(buffer); | |
98 | p->timeout = time_msec() + OVERWRITE_MSECS; | |
99 | p->in_port = in_port; | |
100 | return (p - pb->packets) | (p->cookie << PKTBUF_BITS); | |
101 | } | |
102 | ||
103 | int | |
104 | pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct ofpbuf **bufferp, | |
105 | uint16_t *in_port) | |
106 | { | |
107 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 20); | |
108 | struct packet *p; | |
109 | int error; | |
110 | ||
111 | if (!pb) { | |
112 | VLOG_WARN_RL(&rl, "attempt to send buffered packet via connection " | |
113 | "without buffers"); | |
114 | return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_COOKIE); | |
115 | } | |
116 | ||
117 | p = &pb->packets[id & PKTBUF_MASK]; | |
118 | if (p->cookie == id >> PKTBUF_BITS) { | |
119 | struct ofpbuf *buffer = p->buffer; | |
120 | if (buffer) { | |
121 | *bufferp = buffer; | |
122 | *in_port = p->in_port; | |
123 | p->buffer = NULL; | |
124 | COVERAGE_INC(pktbuf_retrieved); | |
125 | return 0; | |
126 | } else { | |
127 | COVERAGE_INC(pktbuf_reuse_error); | |
128 | VLOG_WARN_RL(&rl, "attempt to reuse buffer %08"PRIx32, id); | |
129 | error = ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BUFFER_EMPTY); | |
130 | } | |
131 | } else { | |
132 | COVERAGE_INC(pktbuf_bad_cookie); | |
133 | VLOG_WARN_RL(&rl, "cookie mismatch: %08"PRIx32" != %08"PRIx32, | |
134 | id, (id & PKTBUF_MASK) | (p->cookie << PKTBUF_BITS)); | |
135 | error = ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_COOKIE); | |
136 | } | |
137 | *bufferp = NULL; | |
138 | *in_port = -1; | |
139 | return error; | |
140 | } | |
141 | ||
142 | void | |
143 | pktbuf_discard(struct pktbuf *pb, uint32_t id) | |
144 | { | |
145 | struct packet *p = &pb->packets[id & PKTBUF_MASK]; | |
146 | if (p->cookie == id >> PKTBUF_BITS) { | |
147 | ofpbuf_delete(p->buffer); | |
148 | p->buffer = NULL; | |
149 | } | |
150 | } |