mirror of
https://github.com/openvswitch/ovs
synced 2025-09-01 06:45:17 +00:00
lib/flow: add dp_hash and recirc_id to struct flow
Signed-off-by: Andy Zhou <azhou@nicira.com> Acked-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
@@ -1803,6 +1803,35 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
|
|||||||
#define NXM_NX_TCP_FLAGS NXM_HEADER (0x0001, 34, 2)
|
#define NXM_NX_TCP_FLAGS NXM_HEADER (0x0001, 34, 2)
|
||||||
#define NXM_NX_TCP_FLAGS_W NXM_HEADER_W(0x0001, 34, 2)
|
#define NXM_NX_TCP_FLAGS_W NXM_HEADER_W(0x0001, 34, 2)
|
||||||
|
|
||||||
|
/* Metadata dp_hash.
|
||||||
|
*
|
||||||
|
* Internal use only, not programable from controller.
|
||||||
|
*
|
||||||
|
* The dp_hash is used to carry the flow hash computed in the
|
||||||
|
* datapath.
|
||||||
|
*
|
||||||
|
* Prereqs: None.
|
||||||
|
*
|
||||||
|
* Format: 32-bit integer in network byte order.
|
||||||
|
*
|
||||||
|
* Masking: Fully maskable. */
|
||||||
|
#define NXM_NX_DP_HASH NXM_HEADER (0x0001, 35, 4)
|
||||||
|
#define NXM_NX_DP_HASH_W NXM_HEADER_W(0x0001, 35, 4)
|
||||||
|
|
||||||
|
/* Metadata recirc_id.
|
||||||
|
*
|
||||||
|
* Internal use only, not programable from controller.
|
||||||
|
*
|
||||||
|
* The recirc_id used for recirculation. 0 is reserved
|
||||||
|
* for initially received packet.
|
||||||
|
*
|
||||||
|
* Prereqs: None.
|
||||||
|
*
|
||||||
|
* Format: 32-bit integer in network byte order.
|
||||||
|
*
|
||||||
|
* Masking: not maskable. */
|
||||||
|
#define NXM_NX_RECIRC_ID NXM_HEADER (0x0001, 36, 4)
|
||||||
|
|
||||||
/* ## --------------------- ## */
|
/* ## --------------------- ## */
|
||||||
/* ## Requests and replies. ## */
|
/* ## Requests and replies. ## */
|
||||||
/* ## --------------------- ## */
|
/* ## --------------------- ## */
|
||||||
|
@@ -530,8 +530,10 @@ flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc)
|
|||||||
void
|
void
|
||||||
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
|
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
|
||||||
{
|
{
|
||||||
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
|
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 25);
|
||||||
|
|
||||||
|
fmd->dp_hash = flow->dp_hash;
|
||||||
|
fmd->recirc_id = flow->recirc_id;
|
||||||
fmd->tun_id = flow->tunnel.tun_id;
|
fmd->tun_id = flow->tunnel.tun_id;
|
||||||
fmd->tun_src = flow->tunnel.ip_src;
|
fmd->tun_src = flow->tunnel.ip_src;
|
||||||
fmd->tun_dst = flow->tunnel.ip_dst;
|
fmd->tun_dst = flow->tunnel.ip_dst;
|
||||||
@@ -1194,7 +1196,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
|
|||||||
flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));
|
flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));
|
||||||
|
|
||||||
/* Clear all L3 and L4 fields. */
|
/* Clear all L3 and L4 fields. */
|
||||||
BUILD_ASSERT(FLOW_WC_SEQ == 24);
|
BUILD_ASSERT(FLOW_WC_SEQ == 25);
|
||||||
memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
|
memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
|
||||||
sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
|
sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
|
||||||
}
|
}
|
||||||
|
13
lib/flow.h
13
lib/flow.h
@@ -37,7 +37,7 @@ struct pkt_metadata;
|
|||||||
/* This sequence number should be incremented whenever anything involving flows
|
/* This sequence number should be incremented whenever anything involving flows
|
||||||
* or the wildcarding of flows changes. This will cause build assertion
|
* or the wildcarding of flows changes. This will cause build assertion
|
||||||
* failures in places which likely need to be updated. */
|
* failures in places which likely need to be updated. */
|
||||||
#define FLOW_WC_SEQ 24
|
#define FLOW_WC_SEQ 25
|
||||||
|
|
||||||
#define FLOW_N_REGS 8
|
#define FLOW_N_REGS 8
|
||||||
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
|
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
|
||||||
@@ -97,6 +97,11 @@ union flow_in_port {
|
|||||||
* be looked at. This enables better wildcarding for datapath flows.
|
* be looked at. This enables better wildcarding for datapath flows.
|
||||||
*/
|
*/
|
||||||
struct flow {
|
struct flow {
|
||||||
|
/* Recirculation */
|
||||||
|
uint32_t dp_hash; /* Datapath computed hash value. The exact
|
||||||
|
computation is opaque to the user space.*/
|
||||||
|
uint32_t recirc_id; /* Must be exact match. */
|
||||||
|
|
||||||
/* L1 */
|
/* L1 */
|
||||||
struct flow_tnl tunnel; /* Encapsulating tunnel parameters. */
|
struct flow_tnl tunnel; /* Encapsulating tunnel parameters. */
|
||||||
ovs_be64 metadata; /* OpenFlow Metadata. */
|
ovs_be64 metadata; /* OpenFlow Metadata. */
|
||||||
@@ -139,8 +144,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
|
|||||||
|
|
||||||
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
|
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
|
||||||
BUILD_ASSERT_DECL(offsetof(struct flow, tp_dst) + 2
|
BUILD_ASSERT_DECL(offsetof(struct flow, tp_dst) + 2
|
||||||
== sizeof(struct flow_tnl) + 164
|
== sizeof(struct flow_tnl) + 172
|
||||||
&& FLOW_WC_SEQ == 24);
|
&& FLOW_WC_SEQ == 25);
|
||||||
|
|
||||||
/* Incremental points at which flow classification may be performed in
|
/* Incremental points at which flow classification may be performed in
|
||||||
* segments.
|
* segments.
|
||||||
@@ -165,6 +170,8 @@ extern const uint8_t flow_segment_u32s[];
|
|||||||
|
|
||||||
/* Represents the metadata fields of struct flow. */
|
/* Represents the metadata fields of struct flow. */
|
||||||
struct flow_metadata {
|
struct flow_metadata {
|
||||||
|
uint32_t dp_hash; /* Datapath computed hash field. */
|
||||||
|
uint32_t recirc_id; /* Recirculation ID. */
|
||||||
ovs_be64 tun_id; /* Encapsulating tunnel ID. */
|
ovs_be64 tun_id; /* Encapsulating tunnel ID. */
|
||||||
ovs_be32 tun_src; /* Tunnel outer IPv4 src addr */
|
ovs_be32 tun_src; /* Tunnel outer IPv4 src addr */
|
||||||
ovs_be32 tun_dst; /* Tunnel outer IPv4 dst addr */
|
ovs_be32 tun_dst; /* Tunnel outer IPv4 dst addr */
|
||||||
|
32
lib/match.c
32
lib/match.c
@@ -164,6 +164,26 @@ match_zero_wildcarded_fields(struct match *match)
|
|||||||
flow_zero_wildcards(&match->flow, &match->wc);
|
flow_zero_wildcards(&match->flow, &match->wc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
match_set_dp_hash(struct match *match, uint32_t value)
|
||||||
|
{
|
||||||
|
match_set_dp_hash_masked(match, value, UINT32_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
match_set_dp_hash_masked(struct match *match, uint32_t value, uint32_t mask)
|
||||||
|
{
|
||||||
|
match->wc.masks.dp_hash = mask;
|
||||||
|
match->flow.dp_hash = value & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
match_set_recirc_id(struct match *match, uint32_t value)
|
||||||
|
{
|
||||||
|
match->flow.recirc_id = value;
|
||||||
|
match->wc.masks.recirc_id = UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
match_set_reg(struct match *match, unsigned int reg_idx, uint32_t value)
|
match_set_reg(struct match *match, unsigned int reg_idx, uint32_t value)
|
||||||
{
|
{
|
||||||
@@ -895,7 +915,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
|
|||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
|
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 25);
|
||||||
|
|
||||||
if (priority != OFP_DEFAULT_PRIORITY) {
|
if (priority != OFP_DEFAULT_PRIORITY) {
|
||||||
ds_put_format(s, "priority=%u,", priority);
|
ds_put_format(s, "priority=%u,", priority);
|
||||||
@@ -903,6 +923,16 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
|
|||||||
|
|
||||||
format_uint32_masked(s, "pkt_mark", f->pkt_mark, wc->masks.pkt_mark);
|
format_uint32_masked(s, "pkt_mark", f->pkt_mark, wc->masks.pkt_mark);
|
||||||
|
|
||||||
|
if (wc->masks.recirc_id) {
|
||||||
|
format_uint32_masked(s, "recirc_id", f->recirc_id,
|
||||||
|
wc->masks.recirc_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f->dp_hash && wc->masks.dp_hash) {
|
||||||
|
format_uint32_masked(s, "dp_hash", f->dp_hash,
|
||||||
|
wc->masks.dp_hash);
|
||||||
|
}
|
||||||
|
|
||||||
if (wc->masks.skb_priority) {
|
if (wc->masks.skb_priority) {
|
||||||
ds_put_format(s, "skb_priority=%#"PRIx32",", f->skb_priority);
|
ds_put_format(s, "skb_priority=%#"PRIx32",", f->skb_priority);
|
||||||
}
|
}
|
||||||
|
@@ -41,6 +41,12 @@ void match_init_catchall(struct match *);
|
|||||||
|
|
||||||
void match_zero_wildcarded_fields(struct match *);
|
void match_zero_wildcarded_fields(struct match *);
|
||||||
|
|
||||||
|
void match_set_dp_hash(struct match *, uint32_t value);
|
||||||
|
void match_set_dp_hash_masked(struct match *, uint32_t value, uint32_t mask);
|
||||||
|
|
||||||
|
void match_set_recirc_id(struct match *, uint32_t value);
|
||||||
|
void match_set_recirc_id_masked(struct match *, uint32_t value, uint32_t mask);
|
||||||
|
|
||||||
void match_set_reg(struct match *, unsigned int reg_idx, uint32_t value);
|
void match_set_reg(struct match *, unsigned int reg_idx, uint32_t value);
|
||||||
void match_set_reg_masked(struct match *, unsigned int reg_idx,
|
void match_set_reg_masked(struct match *, unsigned int reg_idx,
|
||||||
uint32_t value, uint32_t mask);
|
uint32_t value, uint32_t mask);
|
||||||
|
@@ -52,6 +52,30 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
|
|||||||
/* ## -------- ## */
|
/* ## -------- ## */
|
||||||
|
|
||||||
{
|
{
|
||||||
|
MFF_DP_HASH, "dp_hash", NULL,
|
||||||
|
MF_FIELD_SIZES(be32),
|
||||||
|
MFM_FULLY,
|
||||||
|
MFS_HEXADECIMAL,
|
||||||
|
MFP_NONE,
|
||||||
|
false,
|
||||||
|
NXM_NX_DP_HASH, "NXM_NX_DP_HASH",
|
||||||
|
NXM_NX_DP_HASH, "NXM_NX_DP_HASH",
|
||||||
|
OFPUTIL_P_NXM_OXM_ANY,
|
||||||
|
OFPUTIL_P_NXM_OXM_ANY,
|
||||||
|
-1,
|
||||||
|
}, {
|
||||||
|
MFF_RECIRC_ID, "recirc_id", NULL,
|
||||||
|
MF_FIELD_SIZES(be32),
|
||||||
|
MFM_NONE,
|
||||||
|
MFS_DECIMAL,
|
||||||
|
MFP_NONE,
|
||||||
|
false,
|
||||||
|
NXM_NX_RECIRC_ID, "NXM_NX_RECIRC_ID",
|
||||||
|
NXM_NX_RECIRC_ID, "NXM_NX_RECIRC_ID",
|
||||||
|
OFPUTIL_P_NXM_OXM_ANY,
|
||||||
|
OFPUTIL_P_NXM_OXM_ANY,
|
||||||
|
-1,
|
||||||
|
}, {
|
||||||
MFF_TUN_ID, "tun_id", "tunnel_id",
|
MFF_TUN_ID, "tun_id", "tunnel_id",
|
||||||
MF_FIELD_SIZES(be64),
|
MF_FIELD_SIZES(be64),
|
||||||
MFM_FULLY,
|
MFM_FULLY,
|
||||||
@@ -879,6 +903,10 @@ bool
|
|||||||
mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
|
mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
|
||||||
{
|
{
|
||||||
switch (mf->id) {
|
switch (mf->id) {
|
||||||
|
case MFF_DP_HASH:
|
||||||
|
return !wc->masks.dp_hash;
|
||||||
|
case MFF_RECIRC_ID:
|
||||||
|
return !wc->masks.recirc_id;
|
||||||
case MFF_TUN_SRC:
|
case MFF_TUN_SRC:
|
||||||
return !wc->masks.tunnel.ip_src;
|
return !wc->masks.tunnel.ip_src;
|
||||||
case MFF_TUN_DST:
|
case MFF_TUN_DST:
|
||||||
@@ -1124,6 +1152,8 @@ bool
|
|||||||
mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
|
mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
|
||||||
{
|
{
|
||||||
switch (mf->id) {
|
switch (mf->id) {
|
||||||
|
case MFF_DP_HASH:
|
||||||
|
case MFF_RECIRC_ID:
|
||||||
case MFF_TUN_ID:
|
case MFF_TUN_ID:
|
||||||
case MFF_TUN_SRC:
|
case MFF_TUN_SRC:
|
||||||
case MFF_TUN_DST:
|
case MFF_TUN_DST:
|
||||||
@@ -1217,6 +1247,12 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
|
|||||||
union mf_value *value)
|
union mf_value *value)
|
||||||
{
|
{
|
||||||
switch (mf->id) {
|
switch (mf->id) {
|
||||||
|
case MFF_DP_HASH:
|
||||||
|
value->be32 = htonl(flow->dp_hash);
|
||||||
|
break;
|
||||||
|
case MFF_RECIRC_ID:
|
||||||
|
value->be32 = htonl(flow->recirc_id);
|
||||||
|
break;
|
||||||
case MFF_TUN_ID:
|
case MFF_TUN_ID:
|
||||||
value->be64 = flow->tunnel.tun_id;
|
value->be64 = flow->tunnel.tun_id;
|
||||||
break;
|
break;
|
||||||
@@ -1409,6 +1445,12 @@ mf_set_value(const struct mf_field *mf,
|
|||||||
const union mf_value *value, struct match *match)
|
const union mf_value *value, struct match *match)
|
||||||
{
|
{
|
||||||
switch (mf->id) {
|
switch (mf->id) {
|
||||||
|
case MFF_DP_HASH:
|
||||||
|
match_set_dp_hash(match, ntohl(value->be32));
|
||||||
|
break;
|
||||||
|
case MFF_RECIRC_ID:
|
||||||
|
match_set_recirc_id(match, ntohl(value->be32));
|
||||||
|
break;
|
||||||
case MFF_TUN_ID:
|
case MFF_TUN_ID:
|
||||||
match_set_tun_id(match, value->be64);
|
match_set_tun_id(match, value->be64);
|
||||||
break;
|
break;
|
||||||
@@ -1622,6 +1664,12 @@ mf_set_flow_value(const struct mf_field *mf,
|
|||||||
const union mf_value *value, struct flow *flow)
|
const union mf_value *value, struct flow *flow)
|
||||||
{
|
{
|
||||||
switch (mf->id) {
|
switch (mf->id) {
|
||||||
|
case MFF_DP_HASH:
|
||||||
|
flow->dp_hash = ntohl(value->be32);
|
||||||
|
break;
|
||||||
|
case MFF_RECIRC_ID:
|
||||||
|
flow->recirc_id = ntohl(value->be32);
|
||||||
|
break;
|
||||||
case MFF_TUN_ID:
|
case MFF_TUN_ID:
|
||||||
flow->tunnel.tun_id = value->be64;
|
flow->tunnel.tun_id = value->be64;
|
||||||
break;
|
break;
|
||||||
@@ -1834,6 +1882,14 @@ void
|
|||||||
mf_set_wild(const struct mf_field *mf, struct match *match)
|
mf_set_wild(const struct mf_field *mf, struct match *match)
|
||||||
{
|
{
|
||||||
switch (mf->id) {
|
switch (mf->id) {
|
||||||
|
case MFF_DP_HASH:
|
||||||
|
match->flow.dp_hash = 0;
|
||||||
|
match->wc.masks.dp_hash = 0;
|
||||||
|
break;
|
||||||
|
case MFF_RECIRC_ID:
|
||||||
|
match->flow.recirc_id = 0;
|
||||||
|
match->wc.masks.recirc_id = 0;
|
||||||
|
break;
|
||||||
case MFF_TUN_ID:
|
case MFF_TUN_ID:
|
||||||
match_set_tun_id_masked(match, htonll(0), htonll(0));
|
match_set_tun_id_masked(match, htonll(0), htonll(0));
|
||||||
break;
|
break;
|
||||||
@@ -2046,6 +2102,7 @@ mf_set(const struct mf_field *mf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (mf->id) {
|
switch (mf->id) {
|
||||||
|
case MFF_RECIRC_ID:
|
||||||
case MFF_IN_PORT:
|
case MFF_IN_PORT:
|
||||||
case MFF_IN_PORT_OXM:
|
case MFF_IN_PORT_OXM:
|
||||||
case MFF_SKB_PRIORITY:
|
case MFF_SKB_PRIORITY:
|
||||||
@@ -2068,6 +2125,9 @@ mf_set(const struct mf_field *mf,
|
|||||||
case MFF_ICMPV6_CODE:
|
case MFF_ICMPV6_CODE:
|
||||||
return OFPUTIL_P_NONE;
|
return OFPUTIL_P_NONE;
|
||||||
|
|
||||||
|
case MFF_DP_HASH:
|
||||||
|
match_set_dp_hash_masked(match, ntohl(value->be32), ntohl(mask->be32));
|
||||||
|
break;
|
||||||
case MFF_TUN_ID:
|
case MFF_TUN_ID:
|
||||||
match_set_tun_id_masked(match, value->be64, mask->be64);
|
match_set_tun_id_masked(match, value->be64, mask->be64);
|
||||||
break;
|
break;
|
||||||
|
@@ -33,6 +33,8 @@ struct match;
|
|||||||
* to represent its value. */
|
* to represent its value. */
|
||||||
enum OVS_PACKED_ENUM mf_field_id {
|
enum OVS_PACKED_ENUM mf_field_id {
|
||||||
/* Metadata. */
|
/* Metadata. */
|
||||||
|
MFF_DP_HASH, /* be32 */
|
||||||
|
MFF_RECIRC_ID, /* be32 */
|
||||||
MFF_TUN_ID, /* be64 */
|
MFF_TUN_ID, /* be64 */
|
||||||
MFF_TUN_SRC, /* be32 */
|
MFF_TUN_SRC, /* be32 */
|
||||||
MFF_TUN_DST, /* be32 */
|
MFF_TUN_DST, /* be32 */
|
||||||
|
@@ -572,9 +572,22 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
|
|||||||
int match_len;
|
int match_len;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
|
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 25);
|
||||||
|
|
||||||
/* Metadata. */
|
/* Metadata. */
|
||||||
|
if (match->wc.masks.dp_hash) {
|
||||||
|
if (!oxm) {
|
||||||
|
nxm_put_32m(b, NXM_NX_DP_HASH, htonl(flow->dp_hash),
|
||||||
|
htonl(match->wc.masks.dp_hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match->wc.masks.recirc_id) {
|
||||||
|
if (!oxm) {
|
||||||
|
nxm_put_32(b, NXM_NX_RECIRC_ID, htonl(flow->recirc_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (match->wc.masks.in_port.ofp_port) {
|
if (match->wc.masks.in_port.ofp_port) {
|
||||||
ofp_port_t in_port = flow->in_port.ofp_port;
|
ofp_port_t in_port = flow->in_port.ofp_port;
|
||||||
if (oxm) {
|
if (oxm) {
|
||||||
|
@@ -85,7 +85,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
|
|||||||
void
|
void
|
||||||
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
|
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
|
||||||
{
|
{
|
||||||
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
|
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 25);
|
||||||
|
|
||||||
/* Initialize most of wc. */
|
/* Initialize most of wc. */
|
||||||
flow_wildcards_init_catchall(wc);
|
flow_wildcards_init_catchall(wc);
|
||||||
|
@@ -1693,7 +1693,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
|
|||||||
|
|
||||||
/* If 'struct flow' gets additional metadata, we'll need to zero it out
|
/* If 'struct flow' gets additional metadata, we'll need to zero it out
|
||||||
* before traversing a patch port. */
|
* before traversing a patch port. */
|
||||||
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
|
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 25);
|
||||||
|
|
||||||
if (!xport) {
|
if (!xport) {
|
||||||
xlate_report(ctx, "Nonexistent output port");
|
xlate_report(ctx, "Nonexistent output port");
|
||||||
|
Reference in New Issue
Block a user