2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 09:58:01 +00:00

lib: Retire packet buffering feature.

OVS implementation of buffering packets that are sent to the
controller is not compliant with the OpenFlow specifications after
OpenFlow 1.0, which is possibly true since OpenFlow 1.0 is not really
specifying the packet buffering behavior.

OVS implementation executes the buffered packet against the actions of
the modified or added rule, whereas OpenFlow (since 1.1) specifies
that the packet should be matched against the flow table 0 and
processed accordingly.

Rather than fix this behavior, and potentially break OVS users, the
packet buffering feature is removed altogether.  After all, such
packet buffering is an optional OpenFlow feature, and as such any
possible users should continue to work without this feature.

This patch also makes OVS check the received 'buffer_id' values more
rigorously, and fixes some internal users accordingly.

Found by inspection.

Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
Jarno Rajahalme 2016-08-30 10:20:51 -07:00
parent 88e62998a2
commit c184807ced
16 changed files with 65 additions and 579 deletions

62
FAQ.md
View File

@ -1970,55 +1970,21 @@ A: Reconfiguring your bridge can change your bridge's datapath-id because
ovs-vsctl set bridge br0 other-config:datapath-id=0123456789abcdef
### Q: My controller is getting errors about "buffers". What's going on?
### Q: My controller complains that OVS is not buffering packets.
What's going on?
A: When a switch sends a packet to an OpenFlow controller using a
"packet-in" message, it can also keep a copy of that packet in a
"buffer", identified by a 32-bit integer "buffer_id". There are
two advantages to buffering. First, when the controller wants to
tell the switch to do something with the buffered packet (with a
"packet-out" OpenFlow request), it does not need to send another
copy of the packet back across the OpenFlow connection, which
reduces the bandwidth cost of the connection and improves latency.
This enables the second advantage: the switch can optionally send
only the first part of the packet to the controller (assuming that
the switch only needs to look at the first few bytes of the
packet), further reducing bandwidth and improving latency.
However, buffering introduces some issues of its own. First, any
switch has limited resources, so if the controller does not use a
buffered packet, the switch has to decide how long to keep it
buffered. When many packets are sent to a controller and buffered,
Open vSwitch can discard buffered packets that the controller has
not used after as little as 5 seconds. This means that
controllers, if they make use of packet buffering, should use the
buffered packets promptly. (This includes sending a "packet-out"
with no actions if the controller does not want to do anything with
a buffered packet, to clear the packet buffer and effectively
"drop" its packet.)
Second, packet buffers are one-time-use, meaning that a controller
cannot use a single packet buffer in two or more "packet-out"
commands. Open vSwitch will respond with an error to the second
and subsequent "packet-out"s in such a case.
Finally, a common error early in controller development is to try
to use buffer_id 0 in a "packet-out" message as if 0 represented
"no buffered packet". This is incorrect usage: the buffer_id with
this meaning is actually 0xffffffff.
ovs-vswitchd(8) describes some details of Open vSwitch packet
buffering that the OpenFlow specification requires implementations
to document.
Note that the packet buffering support is deprecated in OVS 2.6
release, and will be removed in OVS 2.7. After the change OVS
always sends the 'buffer_id' as 0xffffffff in "packet-in" messages
and will send an error response if any other value of this field is
included in "packet-out" and "flow mod" sent by a controller.
Controllers are already expected to work properly in cases where
the switch can not buffer packets, so this change should not affect
existing users.
A: "Packet buffering" is an optional OpenFlow feature, and controllers
should detect how many "buffers" an OpenFlow switch implements. It
was recently noticed that OVS implementation of the buffering
feature was not compliant to OpenFlow specifications. Rather than
fix it and risk controller incompatibility, the buffering feature
is removed as of OVS 2.7. Controllers are already expected to work
properly in cases where the switch can not buffer packets, but
sends full packets in "packet-in" messages instead, so this change
should not affect existing users. After the change OVS always
sends the 'buffer_id' as 0xffffffff in "packet-in" messages and
will send an error response if any other value of this field is
included in a "packet-out" or a "flow mod" sent by a controller.
### Q: How does OVS divide flows among buckets in an OpenFlow "select" group?

View File

@ -36,7 +36,6 @@
struct ofpbuf;
union ofp_action;
struct ofpact_set_field;
struct pktbuf;
/* Port numbers. */
enum ofperr ofputil_port_from_ofp11(ovs_be32 ofp11_port,
@ -498,8 +497,7 @@ struct ofputil_packet_in_private {
struct ofpbuf *ofputil_encode_packet_in_private(
const struct ofputil_packet_in_private *,
enum ofputil_protocol protocol,
enum nx_packet_in_format,
uint16_t max_len, struct pktbuf *);
enum nx_packet_in_format);
enum ofperr ofputil_decode_packet_in_private(
const struct ofp_header *, bool loose,

View File

@ -201,8 +201,6 @@ lib_libopenvswitch_la_SOURCES = \
lib/pcap-file.h \
lib/perf-counter.h \
lib/perf-counter.c \
lib/pktbuf.c \
lib/pktbuf.h \
lib/poll-loop.c \
lib/poll-loop.h \
lib/process.c \

View File

@ -45,7 +45,6 @@
#include "openvswitch/vlog.h"
#include "openflow/intel-ext.h"
#include "packets.h"
#include "pktbuf.h"
#include "random.h"
#include "tun-metadata.h"
#include "unaligned.h"
@ -3588,17 +3587,14 @@ encode_packet_in_reason(enum ofp_packet_in_reason reason,
* function omits it. The caller can add it itself if desired. */
static void
ofputil_put_packet_in(const struct ofputil_packet_in *pin,
enum ofp_version version, uint32_t buffer_id,
size_t include_bytes, struct ofpbuf *msg)
enum ofp_version version, size_t include_bytes,
struct ofpbuf *msg)
{
/* Add packet properties. */
ofpprop_put(msg, NXPINT_PACKET, pin->packet, include_bytes);
if (include_bytes != pin->packet_len) {
ofpprop_put_u32(msg, NXPINT_FULL_LEN, pin->packet_len);
}
if (buffer_id != UINT32_MAX) {
ofpprop_put_u32(msg, NXPINT_BUFFER_ID, buffer_id);
}
/* Add flow properties. */
ofpprop_put_u8(msg, NXPINT_TABLE_ID, pin->table_id);
@ -3642,11 +3638,10 @@ enum nx_continuation_prop_type {
* function omits it. The caller can add it itself if desired. */
static void
ofputil_put_packet_in_private(const struct ofputil_packet_in_private *pin,
enum ofp_version version, uint32_t buffer_id,
size_t include_bytes, struct ofpbuf *msg)
enum ofp_version version, size_t include_bytes,
struct ofpbuf *msg)
{
ofputil_put_packet_in(&pin->public, version, buffer_id,
include_bytes, msg);
ofputil_put_packet_in(&pin->public, version, include_bytes, msg);
size_t continuation_ofs = ofpprop_start_nested(msg, NXPINT_CONTINUATION);
size_t inner_ofs = msg->size;
@ -3734,8 +3729,7 @@ ofputil_put_packet_in_private(const struct ofputil_packet_in_private *pin,
}
static struct ofpbuf *
ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin,
uint32_t buffer_id)
ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin)
{
struct ofp10_packet_in *opi;
struct ofpbuf *msg;
@ -3746,14 +3740,14 @@ ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin,
opi->total_len = htons(pin->packet_len);
opi->in_port = htons(ofp_to_u16(pin->flow_metadata.flow.in_port.ofp_port));
opi->reason = encode_packet_in_reason(pin->reason, OFP10_VERSION);
opi->buffer_id = htonl(buffer_id);
opi->buffer_id = htonl(UINT32_MAX);
return msg;
}
static struct ofpbuf *
ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin,
enum ofp_version version, uint32_t buffer_id)
enum ofp_version version)
{
struct nx_packet_in *npi;
struct ofpbuf *msg;
@ -3767,7 +3761,7 @@ ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin,
ofpbuf_put_zeros(msg, 2);
npi = msg->msg;
npi->buffer_id = htonl(buffer_id);
npi->buffer_id = htonl(UINT32_MAX);
npi->total_len = htons(pin->packet_len);
npi->reason = encode_packet_in_reason(pin->reason, version);
npi->table_id = pin->table_id;
@ -3779,8 +3773,7 @@ ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin,
static struct ofpbuf *
ofputil_encode_nx_packet_in2(const struct ofputil_packet_in_private *pin,
enum ofp_version version, uint32_t buffer_id,
size_t include_bytes)
enum ofp_version version, size_t include_bytes)
{
/* 'extra' is just an estimate of the space required. */
size_t extra = (pin->public.packet_len
@ -3792,7 +3785,7 @@ ofputil_encode_nx_packet_in2(const struct ofputil_packet_in_private *pin,
struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN2, version,
htonl(0), extra);
ofputil_put_packet_in_private(pin, version, buffer_id, include_bytes, msg);
ofputil_put_packet_in_private(pin, version, include_bytes, msg);
if (pin->public.userdata_len) {
ofpprop_put(msg, NXPINT_USERDATA, pin->public.userdata,
pin->public.userdata_len);
@ -3803,8 +3796,7 @@ ofputil_encode_nx_packet_in2(const struct ofputil_packet_in_private *pin,
}
static struct ofpbuf *
ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin,
uint32_t buffer_id)
ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin)
{
struct ofp11_packet_in *opi;
struct ofpbuf *msg;
@ -3812,7 +3804,7 @@ ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin,
msg = ofpraw_alloc_xid(OFPRAW_OFPT11_PACKET_IN, OFP11_VERSION,
htonl(0), pin->packet_len);
opi = ofpbuf_put_zeros(msg, sizeof *opi);
opi->buffer_id = htonl(buffer_id);
opi->buffer_id = htonl(UINT32_MAX);
opi->in_port = ofputil_port_to_ofp11(
pin->flow_metadata.flow.in_port.ofp_port);
opi->in_phy_port = opi->in_port;
@ -3825,8 +3817,7 @@ ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin,
static struct ofpbuf *
ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin,
enum ofp_version version,
uint32_t buffer_id)
enum ofp_version version)
{
enum ofpraw raw = (version >= OFP13_VERSION
? OFPRAW_OFPT13_PACKET_IN
@ -3838,7 +3829,7 @@ ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin,
htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len);
struct ofp12_packet_in *opi = ofpbuf_put_zeros(msg, sizeof *opi);
opi->buffer_id = htonl(buffer_id);
opi->buffer_id = htonl(UINT32_MAX);
opi->total_len = htons(pin->packet_len);
opi->reason = encode_packet_in_reason(pin->reason, version);
opi->table_id = pin->table_id;
@ -3857,11 +3848,6 @@ ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin,
/* Converts abstract ofputil_packet_in_private 'pin' into a PACKET_IN message
* for 'protocol', using the packet-in format specified by 'packet_in_format'.
*
* If 'pkt_buf' is nonnull and 'max_len' allows the packet to be buffered, this
* function will attempt to obtain a buffer ID from 'pktbuf' and truncate the
* packet to 'max_len' bytes. Otherwise, or if 'pktbuf' doesn't have a free
* buffer, it will send the whole packet without buffering.
*
* This function is really meant only for use by ovs-vswitchd. To any other
* code, the "continuation" data, i.e. the data that is in struct
* ofputil_packet_in_private but not in struct ofputil_packet_in, is supposed
@ -3873,28 +3859,10 @@ ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin,
struct ofpbuf *
ofputil_encode_packet_in_private(const struct ofputil_packet_in_private *pin,
enum ofputil_protocol protocol,
enum nx_packet_in_format packet_in_format,
uint16_t max_len, struct pktbuf *pktbuf)
enum nx_packet_in_format packet_in_format)
{
enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
/* Get buffer ID. */
ofp_port_t in_port = pin->public.flow_metadata.flow.in_port.ofp_port;
uint32_t buffer_id = (max_len != OFPCML12_NO_BUFFER && pktbuf
? pktbuf_save(pktbuf, pin->public.packet,
pin->public.packet_len, in_port)
: UINT32_MAX);
/* Calculate the number of bytes of the packet to include in the
* packet-in:
*
* - If not buffered, the whole thing.
*
* - Otherwise, no more than 'max_len' bytes. */
size_t include_bytes = (buffer_id == UINT32_MAX
? pin->public.packet_len
: MIN(max_len, pin->public.packet_len));
struct ofpbuf *msg;
switch (packet_in_format) {
case NXPIF_STANDARD:
@ -3903,11 +3871,11 @@ ofputil_encode_packet_in_private(const struct ofputil_packet_in_private *pin,
case OFPUTIL_P_OF10_STD_TID:
case OFPUTIL_P_OF10_NXM:
case OFPUTIL_P_OF10_NXM_TID:
msg = ofputil_encode_ofp10_packet_in(&pin->public, buffer_id);
msg = ofputil_encode_ofp10_packet_in(&pin->public);
break;
case OFPUTIL_P_OF11_STD:
msg = ofputil_encode_ofp11_packet_in(&pin->public, buffer_id);
msg = ofputil_encode_ofp11_packet_in(&pin->public);
break;
case OFPUTIL_P_OF12_OXM:
@ -3915,7 +3883,7 @@ ofputil_encode_packet_in_private(const struct ofputil_packet_in_private *pin,
case OFPUTIL_P_OF14_OXM:
case OFPUTIL_P_OF15_OXM:
case OFPUTIL_P_OF16_OXM:
msg = ofputil_encode_ofp12_packet_in(&pin->public, version, buffer_id);
msg = ofputil_encode_ofp12_packet_in(&pin->public, version);
break;
default:
@ -3924,18 +3892,18 @@ ofputil_encode_packet_in_private(const struct ofputil_packet_in_private *pin,
break;
case NXPIF_NXT_PACKET_IN:
msg = ofputil_encode_nx_packet_in(&pin->public, version, buffer_id);
msg = ofputil_encode_nx_packet_in(&pin->public, version);
break;
case NXPIF_NXT_PACKET_IN2:
return ofputil_encode_nx_packet_in2(pin, version, buffer_id,
include_bytes);
return ofputil_encode_nx_packet_in2(pin, version,
pin->public.packet_len);
default:
OVS_NOT_REACHED();
}
ofpbuf_put(msg, pin->public.packet, include_bytes);
ofpbuf_put(msg, pin->public.packet, pin->public.packet_len);
ofpmsg_update_length(msg);
return msg;
}
@ -4004,7 +3972,7 @@ ofputil_encode_resume(const struct ofputil_packet_in *pin,
size_t extra = pin->packet_len + NXM_TYPICAL_LEN + continuation->size;
struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_RESUME, version,
0, extra);
ofputil_put_packet_in(pin, version, UINT32_MAX, pin->packet_len, msg);
ofputil_put_packet_in(pin, version, pin->packet_len, msg);
ofpprop_put_nested(msg, NXPINT_CONTINUATION, continuation);
ofpmsg_update_length(msg);
return msg;

View File

@ -1,220 +0,0 @@
/*
* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <config.h>
#include "pktbuf.h"
#include <inttypes.h>
#include <stdlib.h>
#include "coverage.h"
#include "openvswitch/ofp-util.h"
#include "dp-packet.h"
#include "timeval.h"
#include "util.h"
#include "openvswitch/vconn.h"
#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(pktbuf);
COVERAGE_DEFINE(pktbuf_buffer_unknown);
COVERAGE_DEFINE(pktbuf_retrieved);
COVERAGE_DEFINE(pktbuf_reuse_error);
/* Buffers are identified by a 32-bit opaque ID. We divide the ID
* into a buffer number (low bits) and a cookie (high bits). The buffer number
* is an index into an array of buffers. The cookie distinguishes between
* different packets that have occupied a single buffer. Thus, the more
* buffers we have, the lower-quality the cookie... */
#define PKTBUF_BITS 8
#define PKTBUF_MASK (PKTBUF_CNT - 1)
#define PKTBUF_CNT (1u << PKTBUF_BITS)
#define COOKIE_BITS (32 - PKTBUF_BITS)
#define COOKIE_MAX ((1u << COOKIE_BITS) - 1)
#define OVERWRITE_MSECS 5000
struct packet {
struct dp_packet *buffer;
uint32_t cookie;
long long int timeout;
ofp_port_t in_port;
};
struct pktbuf {
struct packet packets[PKTBUF_CNT];
unsigned int buffer_idx;
unsigned int null_idx;
};
int
pktbuf_capacity(void)
{
return PKTBUF_CNT;
}
struct pktbuf *
pktbuf_create(void)
{
return xzalloc(sizeof *pktbuf_create());
}
void
pktbuf_destroy(struct pktbuf *pb)
{
if (pb) {
size_t i;
for (i = 0; i < PKTBUF_CNT; i++) {
dp_packet_delete(pb->packets[i].buffer);
}
free(pb);
}
}
static unsigned int
make_id(unsigned int buffer_idx, unsigned int cookie)
{
return buffer_idx | (cookie << PKTBUF_BITS);
}
/* Attempts to allocate an OpenFlow packet buffer id within 'pb'. The packet
* buffer will store a copy of 'buffer_size' bytes in 'buffer' and the port
* number 'in_port', which should be the OpenFlow port number on which 'buffer'
* was received.
*
* If successful, returns the packet buffer id (a number other than
* UINT32_MAX). pktbuf_retrieve() can later be used to retrieve the buffer and
* its input port number (buffers do expire after a time, so this is not
* guaranteed to be true forever). On failure, returns UINT32_MAX.
*
* The caller retains ownership of 'buffer'. */
uint32_t
pktbuf_save(struct pktbuf *pb, const void *buffer, size_t buffer_size,
ofp_port_t in_port)
{
struct packet *p = &pb->packets[pb->buffer_idx];
pb->buffer_idx = (pb->buffer_idx + 1) & PKTBUF_MASK;
if (p->buffer) {
if (time_msec() < p->timeout) {
return UINT32_MAX;
}
dp_packet_delete(p->buffer);
}
/* Don't use maximum cookie value since all-1-bits ID is special. */
if (++p->cookie >= COOKIE_MAX) {
p->cookie = 0;
}
/* Use 2 bytes of headroom to 32-bit align the L3 header. */
p->buffer = dp_packet_clone_data_with_headroom(buffer, buffer_size, 2);
p->timeout = time_msec() + OVERWRITE_MSECS;
p->in_port = in_port;
return make_id(p - pb->packets, p->cookie);
}
/* Attempts to retrieve a saved packet with the given 'id' from 'pb'. Returns
* 0 if successful, otherwise an OpenFlow error code.
*
* On success, stores the buffered packet in '*bufferp' and the OpenFlow port
* number on which the packet was received in '*in_port'. The caller becomes
* responsible for freeing the buffer.
*
* 'in_port' may be NULL if the input port is not of interest.
*
* The L3 header of a returned packet will be 32-bit aligned.
*
* On failure, stores NULL in in '*bufferp' and UINT16_MAX in '*in_port'. */
enum ofperr
pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct dp_packet **bufferp,
ofp_port_t *in_port)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 20);
struct packet *p;
enum ofperr error;
if (id == UINT32_MAX) {
error = 0;
goto error;
}
if (!pb) {
VLOG_WARN_RL(&rl, "attempt to send buffered packet via connection "
"without buffers");
error = OFPERR_OFPBRC_BUFFER_UNKNOWN;
goto error;
}
p = &pb->packets[id & PKTBUF_MASK];
if (p->cookie == id >> PKTBUF_BITS) {
struct dp_packet *buffer = p->buffer;
if (buffer) {
*bufferp = buffer;
if (in_port) {
*in_port = p->in_port;
}
p->buffer = NULL;
COVERAGE_INC(pktbuf_retrieved);
return 0;
} else {
COVERAGE_INC(pktbuf_reuse_error);
VLOG_WARN_RL(&rl, "attempt to reuse buffer %08"PRIx32, id);
error = OFPERR_OFPBRC_BUFFER_EMPTY;
}
} else {
COVERAGE_INC(pktbuf_buffer_unknown);
VLOG_WARN_RL(&rl, "cookie mismatch: %08"PRIx32" != %08"PRIx32,
id, (id & PKTBUF_MASK) | (p->cookie << PKTBUF_BITS));
error = OFPERR_OFPBRC_BUFFER_UNKNOWN;
}
error:
*bufferp = NULL;
if (in_port) {
*in_port = OFPP_NONE;
}
return error;
}
void
pktbuf_discard(struct pktbuf *pb, uint32_t id)
{
struct packet *p = &pb->packets[id & PKTBUF_MASK];
if (p->cookie == id >> PKTBUF_BITS) {
dp_packet_delete(p->buffer);
p->buffer = NULL;
}
}
/* Returns the number of packets buffered in 'pb'. Returns 0 if 'pb' is
* null. */
unsigned int
pktbuf_count_packets(const struct pktbuf *pb)
{
int n = 0;
if (pb) {
int i;
for (i = 0; i < PKTBUF_CNT; i++) {
if (pb->packets[i].buffer) {
n++;
}
}
}
return n;
}

View File

@ -1,40 +0,0 @@
/*
* Copyright (c) 2008, 2009, 2011, 2012, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef PKTBUF_H
#define PKTBUF_H 1
#include <stddef.h>
#include <stdint.h>
#include "openvswitch/ofp-errors.h"
struct pktbuf;
struct dp_packet;
int pktbuf_capacity(void);
struct pktbuf *pktbuf_create(void);
void pktbuf_destroy(struct pktbuf *);
uint32_t pktbuf_save(struct pktbuf *, const void *buffer, size_t buffer_size,
ofp_port_t in_port);
enum ofperr pktbuf_retrieve(struct pktbuf *, uint32_t id,
struct dp_packet **bufferp, ofp_port_t *in_port);
void pktbuf_discard(struct pktbuf *, uint32_t id);
unsigned int pktbuf_count_packets(const struct pktbuf *);
#endif /* pktbuf.h */

View File

@ -32,7 +32,6 @@
#include "openvswitch/vlog.h"
#include "pinsched.h"
#include "poll-loop.h"
#include "pktbuf.h"
#include "rconn.h"
#include "openvswitch/shash.h"
#include "simap.h"

View File

@ -34,7 +34,6 @@
#include "openvswitch/vlog.h"
#include "pinsched.h"
#include "poll-loop.h"
#include "pktbuf.h"
#include "rconn.h"
#include "openvswitch/shash.h"
#include "simap.h"
@ -78,7 +77,6 @@ struct ofconn {
struct rconn_packet_counter *packet_in_counter; /* # queued on 'rconn'. */
#define N_SCHEDULERS 2
struct pinsched *schedulers[N_SCHEDULERS];
struct pktbuf *pktbuf; /* OpenFlow packet buffers. */
int miss_send_len; /* Bytes to send of buffered packets. */
uint16_t controller_id; /* Connection controller ID. */
@ -416,7 +414,6 @@ connmgr_get_memory_usage(const struct connmgr *mgr, struct simap *usage)
pinsched_get_stats(ofconn->schedulers[i], &stats);
packets += stats.n_queued;
}
packets += pktbuf_count_packets(ofconn->pktbuf);
}
simap_increase(usage, "ofconns", ofconns);
simap_increase(usage, "packets", packets);
@ -694,7 +691,6 @@ add_controller(struct connmgr *mgr, const char *target, uint8_t dscp,
ofconn = ofconn_create(mgr, rconn_create(5, 8, dscp, allowed_versions),
OFCONN_PRIMARY, true);
ofconn->pktbuf = pktbuf_create();
rconn_connect(ofconn->rconn, target, name);
hmap_insert(&mgr->controllers, &ofconn->hmap_node, hash_string(target, 0));
@ -1113,14 +1109,6 @@ ofconn_send_error(const struct ofconn *ofconn,
ofconn_send_reply(ofconn, reply);
}
/* Same as pktbuf_retrieve(), using the pktbuf owned by 'ofconn'. */
enum ofperr
ofconn_pktbuf_retrieve(struct ofconn *ofconn, uint32_t id,
struct dp_packet **bufferp, ofp_port_t *in_port)
{
return pktbuf_retrieve(ofconn->pktbuf, id, bufferp, in_port);
}
/* Reports that a flow_mod operation of the type specified by 'command' was
* successfully executed by 'ofconn', so that the connmgr can log it. */
void
@ -1261,10 +1249,6 @@ ofconn_flush(struct ofconn *ofconn)
ofconn->schedulers[i] = pinsched_create(rate, burst);
}
}
if (ofconn->pktbuf) {
pktbuf_destroy(ofconn->pktbuf);
ofconn->pktbuf = pktbuf_create();
}
ofconn->miss_send_len = (ofconn->type == OFCONN_PRIMARY
? OFP_DEFAULT_MISS_SEND_LEN
: 0);
@ -1308,7 +1292,6 @@ ofconn_destroy(struct ofconn *ofconn)
rconn_destroy(ofconn->rconn);
rconn_packet_counter_destroy(ofconn->packet_in_counter);
rconn_packet_counter_destroy(ofconn->reply_counter);
pktbuf_destroy(ofconn->pktbuf);
rconn_packet_counter_destroy(ofconn->monitor_counter);
free(ofconn);
}
@ -1695,9 +1678,7 @@ connmgr_send_async_msg(struct connmgr *mgr,
}
struct ofpbuf *msg = ofputil_encode_packet_in_private(
&am->pin.up, protocol, ofconn->packet_in_format,
am->pin.max_len >= 0 ? am->pin.max_len : ofconn->miss_send_len,
ofconn->pktbuf);
&am->pin.up, protocol, ofconn->packet_in_format);
struct ovs_list txq;
bool is_miss = (am->pin.up.public.reason == OFPR_NO_MATCH ||

View File

@ -128,9 +128,6 @@ void ofconn_send_replies(const struct ofconn *, struct ovs_list *);
void ofconn_send_error(const struct ofconn *, const struct ofp_header *request,
enum ofperr);
enum ofperr ofconn_pktbuf_retrieve(struct ofconn *, uint32_t id,
struct dp_packet **bufferp, ofp_port_t *in_port);
struct ofp_bundle;
struct ofp_bundle *ofconn_get_bundle(struct ofconn *, uint32_t id);

View File

@ -31,7 +31,6 @@
#include "openvswitch/vlog.h"
#include "ofproto.h"
#include "ofproto-provider.h"
#include "pktbuf.h"
#include "poll-loop.h"
#include "rconn.h"
#include "timeval.h"

View File

@ -4247,24 +4247,6 @@ rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes,
ovs_mutex_unlock(&rule->stats_mutex);
}
static void
rule_dpif_execute(struct rule_dpif *rule, const struct flow *flow,
struct dp_packet *packet)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
ofproto_dpif_execute_actions(ofproto, flow, rule, NULL, 0, packet);
}
static enum ofperr
rule_execute(struct rule *rule, const struct flow *flow,
struct dp_packet *packet)
{
rule_dpif_execute(rule_dpif_cast(rule), flow, packet);
dp_packet_delete(packet);
return 0;
}
static struct group_dpif *group_dpif_cast(const struct ofgroup *group)
{
return group ? CONTAINER_OF(group, struct group_dpif, up) : NULL;
@ -5542,6 +5524,7 @@ ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto,
int error;
fm = (struct ofputil_flow_mod) {
.buffer_id = UINT32_MAX,
.match = *match,
.priority = priority,
.table_id = TBL_INTERNAL,
@ -5580,6 +5563,7 @@ ofproto_dpif_delete_internal_flow(struct ofproto_dpif *ofproto,
int error;
fm = (struct ofputil_flow_mod) {
.buffer_id = UINT32_MAX,
.match = *match,
.priority = priority,
.table_id = TBL_INTERNAL,
@ -5648,7 +5632,6 @@ const struct ofproto_class ofproto_dpif_class = {
rule_destruct,
rule_dealloc,
rule_get_stats,
rule_execute,
set_frag_handling,
packet_out,
nxt_resume,

View File

@ -115,14 +115,6 @@ struct ofproto {
/* OpenFlow connections. */
struct connmgr *connmgr;
/* Delayed rule executions.
*
* We delay calls to ->ofproto_class->rule_execute() past releasing
* ofproto_mutex during a flow_mod, because otherwise a "learn" action
* triggered by the executing the packet would try to recursively modify
* the flow table and reacquire the global lock. */
struct guarded_list rule_executes; /* Contains "struct rule_execute"s. */
int min_mtu; /* Current MTU of non-internal ports. */
/* Groups. */
@ -1285,25 +1277,6 @@ struct ofproto_class {
uint64_t *byte_count, long long int *used)
/* OVS_EXCLUDED(ofproto_mutex) */;
/* Applies the actions in 'rule' to 'packet'. (This implements sending
* buffered packets for OpenFlow OFPT_FLOW_MOD commands.)
*
* Takes ownership of 'packet' (so it should eventually free it, with
* ofpbuf_delete()).
*
* 'flow' reflects the flow information for 'packet'. All of the
* information in 'flow' is extracted from 'packet', except for
* flow->tunnel and flow->in_port, which are assigned the correct values
* for the incoming packet. The register values are zeroed. 'packet''s
* header pointers and offsets (e.g. packet->l3) are appropriately
* initialized. packet->l3 is aligned on a 32-bit boundary.
*
* The implementation should add the statistics for 'packet' into 'rule'.
*
* Returns 0 if successful, otherwise an OpenFlow error code. */
enum ofperr (*rule_execute)(struct rule *rule, const struct flow *flow,
struct dp_packet *packet);
/* Changes the OpenFlow IP fragment handling policy to 'frag_handling',
* which takes one of the following values, with the corresponding
* meanings:
@ -1880,7 +1853,6 @@ struct ofproto_flow_mod {
/* Replicate needed fields from ofputil_flow_mod to not need it after the
* flow has been created. */
uint16_t command;
uint32_t buffer_id;
bool modify_cookie;
/* Fields derived from ofputil_flow_mod. */
bool modify_may_add_flow;

View File

@ -50,7 +50,6 @@
#include "ovs-rcu.h"
#include "packets.h"
#include "pinsched.h"
#include "pktbuf.h"
#include "poll-loop.h"
#include "random.h"
#include "seq.h"
@ -142,20 +141,6 @@ static enum ofperr collect_rules_loose(struct ofproto *,
const struct rule_criteria *,
struct rule_collection *);
/* A packet that needs to be passed to rule_execute().
*
* (We can't do this immediately from ofopgroup_complete() because that holds
* ofproto_mutex, which rule_execute() needs released.) */
struct rule_execute {
struct ovs_list list_node; /* In struct ofproto's "rule_executes" list. */
struct rule *rule; /* Owns a reference to the rule. */
ofp_port_t in_port;
struct dp_packet *packet; /* Owns the packet. */
};
static void run_rule_executes(struct ofproto *) OVS_EXCLUDED(ofproto_mutex);
static void destroy_rule_executes(struct ofproto *);
struct learned_cookie {
union {
/* In struct ofproto's 'learned_cookies' hmap. */
@ -264,10 +249,6 @@ static void delete_flows__(struct rule_collection *,
const struct openflow_mod_requester *)
OVS_REQUIRES(ofproto_mutex);
static void send_buffered_packet(const struct openflow_mod_requester *,
uint32_t buffer_id, struct rule *)
OVS_REQUIRES(ofproto_mutex);
static bool ofproto_group_exists(const struct ofproto *, uint32_t group_id);
static void handle_openflow(struct ofconn *, const struct ofpbuf *);
static enum ofperr ofproto_flow_mod_init(struct ofproto *,
@ -533,7 +514,6 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
hmap_init(&ofproto->learned_cookies);
ovs_list_init(&ofproto->expirable);
ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
guarded_list_init(&ofproto->rule_executes);
ofproto->min_mtu = INT_MAX;
cmap_init(&ofproto->groups);
ovs_mutex_unlock(&ofproto_mutex);
@ -1556,9 +1536,6 @@ ofproto_destroy__(struct ofproto *ofproto)
{
struct oftable *table;
destroy_rule_executes(ofproto);
guarded_list_destroy(&ofproto->rule_executes);
cmap_destroy(&ofproto->groups);
hmap_remove(&all_ofprotos, &ofproto->hmap_node);
@ -1703,8 +1680,6 @@ ofproto_run(struct ofproto *p)
VLOG_ERR_RL(&rl, "%s: run failed (%s)", p->name, ovs_strerror(error));
}
run_rule_executes(p);
/* Restore the eviction group heap invariant occasionally. */
if (p->eviction_group_timer < time_msec()) {
size_t i;
@ -3069,50 +3044,6 @@ ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id)
}
}
static void
rule_execute_destroy(struct rule_execute *e)
{
ofproto_rule_unref(e->rule);
ovs_list_remove(&e->list_node);
free(e);
}
/* Executes all "rule_execute" operations queued up in ofproto->rule_executes,
* by passing them to the ofproto provider. */
static void
run_rule_executes(struct ofproto *ofproto)
OVS_EXCLUDED(ofproto_mutex)
{
struct rule_execute *e, *next;
struct ovs_list executes;
guarded_list_pop_all(&ofproto->rule_executes, &executes);
LIST_FOR_EACH_SAFE (e, next, list_node, &executes) {
struct flow flow;
flow_extract(e->packet, &flow);
flow.in_port.ofp_port = e->in_port;
ofproto->ofproto_class->rule_execute(e->rule, &flow, e->packet);
rule_execute_destroy(e);
}
}
/* Destroys and discards all "rule_execute" operations queued up in
* ofproto->rule_executes. */
static void
destroy_rule_executes(struct ofproto *ofproto)
{
struct rule_execute *e, *next;
struct ovs_list executes;
guarded_list_pop_all(&ofproto->rule_executes, &executes);
LIST_FOR_EACH_SAFE (e, next, list_node, &executes) {
dp_packet_delete(e->packet);
rule_execute_destroy(e);
}
}
static bool
rule_is_readonly(const struct rule *rule)
{
@ -3353,7 +3284,7 @@ handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
query_switch_features(ofproto, &arp_match_ip, &features.ofpacts);
features.datapath_id = ofproto->datapath_id;
features.n_buffers = pktbuf_capacity();
features.n_buffers = 0;
features.n_tables = ofproto_get_n_visible_tables(ofproto);
features.capabilities = (OFPUTIL_C_FLOW_STATS | OFPUTIL_C_TABLE_STATS |
OFPUTIL_C_PORT_STATS | OFPUTIL_C_QUEUE_STATS |
@ -3502,14 +3433,11 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
/* Get payload. */
if (po.buffer_id != UINT32_MAX) {
error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL);
if (error) {
error = OFPERR_OFPBRC_BUFFER_UNKNOWN;
goto exit_free_ofpacts;
}
} else {
/* Ensure that the L3 header is 32-bit aligned. */
payload = dp_packet_clone_data_with_headroom(po.packet, po.packet_len, 2);
}
/* Verify actions against packet, then send packet if successful. */
flow_extract(payload, &flow);
@ -4807,8 +4735,6 @@ add_flow_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
/* Send Vacancy Events for OF1.4+. */
send_table_status(ofproto, new_rule->table_id);
}
send_buffered_packet(req, ofm->buffer_id, new_rule);
}
/* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */
@ -5107,10 +5033,7 @@ modify_flows_init_loose(struct ofproto *ofproto,
}
/* Implements OFPFC_MODIFY. Returns 0 on success or an OpenFlow error code on
* failure.
*
* 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
* if any. */
* failure. */
static enum ofperr
modify_flows_start_loose(struct ofproto *ofproto, struct ofproto_flow_mod *ofm)
OVS_REQUIRES(ofproto_mutex)
@ -5173,9 +5096,6 @@ modify_flows_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
}
learned_cookies_flush(ofproto, &dead_cookies);
remove_rules_postponed(old_rules);
send_buffered_packet(req, ofm->buffer_id,
rule_collection_rules(new_rules)[0]);
rule_collection_destroy(new_rules);
}
}
@ -5520,7 +5440,6 @@ handle_flow_mod__(struct ofproto *ofproto, const struct ofputil_flow_mod *fm,
ofmonitor_flush(ofproto->connmgr);
ovs_mutex_unlock(&ofproto_mutex);
run_rule_executes(ofproto);
return error;
}
@ -7110,7 +7029,6 @@ ofproto_flow_mod_init(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
/* Forward flow mod fields we need later. */
ofm->command = fm->command;
ofm->buffer_id = fm->buffer_id;
ofm->modify_cookie = fm->modify_cookie;
ofm->modify_may_add_flow = (fm->new_cookie != OVS_BE64_MAX
@ -7122,14 +7040,19 @@ ofproto_flow_mod_init(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
ofm->conjs = NULL;
ofm->n_conjs = 0;
bool check_buffer_id = false;
switch (ofm->command) {
case OFPFC_ADD:
check_buffer_id = true;
error = add_flow_init(ofproto, ofm, fm);
break;
case OFPFC_MODIFY:
check_buffer_id = true;
error = modify_flows_init_loose(ofproto, ofm, fm);
break;
case OFPFC_MODIFY_STRICT:
check_buffer_id = true;
error = modify_flow_init_strict(ofproto, ofm, fm);
break;
case OFPFC_DELETE:
@ -7142,6 +7065,10 @@ ofproto_flow_mod_init(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
error = OFPERR_OFPFMFC_BAD_COMMAND;
break;
}
if (!error && check_buffer_id && fm->buffer_id != UINT32_MAX) {
error = OFPERR_OFPBRC_BUFFER_UNKNOWN;
}
if (error) {
ofproto_flow_mod_uninit(ofm);
}
@ -7381,8 +7308,6 @@ do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
ofmonitor_flush(ofproto->connmgr);
ovs_mutex_unlock(&ofproto_mutex);
run_rule_executes(ofproto);
}
/* The bundle is discarded regardless the outcome. */
@ -7751,43 +7676,6 @@ handle_openflow(struct ofconn *ofconn, const struct ofpbuf *ofp_msg)
COVERAGE_INC(ofproto_recv_openflow);
}
/* Asynchronous operations. */
static void
send_buffered_packet(const struct openflow_mod_requester *req,
uint32_t buffer_id, struct rule *rule)
OVS_REQUIRES(ofproto_mutex)
{
if (req && req->ofconn && buffer_id != UINT32_MAX) {
struct ofproto *ofproto = ofconn_get_ofproto(req->ofconn);
struct dp_packet *packet;
ofp_port_t in_port;
enum ofperr error;
error = ofconn_pktbuf_retrieve(req->ofconn, buffer_id, &packet,
&in_port);
if (packet) {
struct rule_execute *re;
ofproto_rule_ref(rule);
re = xmalloc(sizeof *re);
re->rule = rule;
re->in_port = in_port;
re->packet = packet;
if (!guarded_list_push_back(&ofproto->rule_executes,
&re->list_node, 1024)) {
ofproto_rule_unref(rule);
dp_packet_delete(re->packet);
free(re);
}
} else {
ofconn_send_error(req->ofconn, req->request, error);
}
}
}
static uint64_t
pick_datapath_id(const struct ofproto *ofproto)
{

View File

@ -34,7 +34,7 @@ OVS_VSWITCHD_START
AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout])
AT_CHECK([strip_xids < stdout], [0], [dnl
OFPT_FEATURES_REPLY: dpid:fedcba9876543210
n_tables:254, n_buffers:256
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
LOCAL(br0): addr:aa:55:aa:55:00:00
@ -56,7 +56,7 @@ s/ (xid=0x[0-9a-fA-F]*)//
s/00:0.$/00:0x/' < stdout]],
[0], [dnl
OFPT_FEATURES_REPLY: dpid:fedcba9876543210
n_tables:254, n_buffers:256
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
1(p1): addr:aa:55:aa:55:00:0x
@ -1174,7 +1174,7 @@ do
AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout])
AT_CHECK_UNQUOTED([strip_xids < stdout], [0], [dnl
OFPT_FEATURES_REPLY: dpid:fedcba9876543210
n_tables:254, n_buffers:256
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
LOCAL(br0): addr:aa:55:aa:55:00:00
@ -1206,7 +1206,7 @@ do
AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn show br0], [0], [stdout])
AT_CHECK_UNQUOTED([strip_xids < stdout], [0], [dnl
OFPT_FEATURES_REPLY (OF1.2): dpid:fedcba9876543210
n_tables:254, n_buffers:256
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS GROUP_STATS QUEUE_STATS
LOCAL(br0): addr:aa:55:aa:55:00:00
config: $config
@ -1237,7 +1237,7 @@ do
AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn show br0], [0], [stdout])
AT_CHECK_UNQUOTED([strip_xids < stdout], [0], [dnl
OFPT_FEATURES_REPLY (OF1.4): dpid:fedcba9876543210
n_tables:254, n_buffers:256
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS GROUP_STATS QUEUE_STATS
OFPST_PORT_DESC reply (OF1.4):
LOCAL(br0): addr:aa:55:aa:55:00:00

View File

@ -144,7 +144,7 @@ OpenFlow port number of `1`. Similarly, `lport2` has an OpenFlow port number of
$ ovs-ofctl show br-int
OFPT_FEATURES_REPLY (xid=0x2): dpid:00003e1ba878364d
n_tables:254, n_buffers:256
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
1(lport1): addr:aa:55:aa:55:00:07
@ -624,7 +624,7 @@ We get those port numbers using `ovs-ofctl`:
$ ovs-ofctl show br-int
OFPT_FEATURES_REPLY (xid=0x2): dpid:00002a84824b0d40
n_tables:254, n_buffers:256
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst
1(ovn-fakech-0): addr:aa:55:aa:55:00:0e

View File

@ -285,10 +285,7 @@ documentation, both the amount of available buffering, and the length
of time before buffers may be reused.
.
.PP
Open vSwitch maintains a separate set of 256 packet buffers for each
OpenFlow connection. Any given packet buffer is preserved until it is
referenced by an \fBOFPT_FLOW_MOD\fR or \fBOFPT_PACKET_OUT\fR request
or for 5 seconds, whichever comes first.
Open vSwitch does not maintains any packet buffers.
.
.SH "LIMITS"
.