};
+THREAD SAFETY ANNOTATIONS
+
+ Use the macros in lib/compiler.h to annotate locking requirements.
+For example:
+
+ static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
+ static struct ovs_rwlock rwlock = OVS_RWLOCK_INITIALIZER;
+
+ void function_require_plain_mutex(void) OVS_REQUIRES(mutex);
+ void function_require_rwlock(void) OVS_REQ_RDLOCK(rwlock);
+
+ Pass lock objects, not their addresses, to the annotation macros.
+(Thus we have OVS_REQUIRES(mutex) above, not OVS_REQUIRES(&mutex).)
+
+
SOURCE FILES
Each source file should state its license in a comment at the very
int err, start;
ovs_match_init(&match, &key, NULL);
- err = ipv4_tun_from_nlattr(nla_data(attr), &match, false);
+ err = ovs_ipv4_tun_from_nlattr(nla_data(attr), &match, false);
if (err)
return err;
if (!start)
return -EMSGSIZE;
- err = ipv4_tun_to_nlattr(skb,
- nla_data(ovs_key), nla_data(ovs_key));
+ err = ovs_ipv4_tun_to_nlattr(skb, nla_data(ovs_key),
+ nla_data(ovs_key));
if (err)
return err;
nla_nest_end(skb, start);
/* Always allowed mask fields. */
mask_allowed |= ((1ULL << OVS_KEY_ATTR_TUNNEL)
| (1ULL << OVS_KEY_ATTR_IN_PORT)
- | (11ULL << OVS_KEY_ATTR_ETHERTYPE));
+ | (1ULL << OVS_KEY_ATTR_ETHERTYPE));
/* Check key attributes. */
if (match->key->eth.type == htons(ETH_P_ARP)
return __parse_flow_nlattrs(attr, a, attrsp, false);
}
-int ipv4_tun_from_nlattr(const struct nlattr *attr,
- struct sw_flow_match *match, bool is_mask)
+int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr,
+ struct sw_flow_match *match, bool is_mask)
{
struct nlattr *a;
int rem;
return 0;
}
-int ipv4_tun_to_nlattr(struct sk_buff *skb,
- const struct ovs_key_ipv4_tunnel *tun_key,
- const struct ovs_key_ipv4_tunnel *output)
+int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb,
+ const struct ovs_key_ipv4_tunnel *tun_key,
+ const struct ovs_key_ipv4_tunnel *output)
{
struct nlattr *nla;
*attrs &= ~(1ULL << OVS_KEY_ATTR_SKB_MARK);
}
if (*attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
- if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match,
+ if (ovs_ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match,
is_mask))
return -EINVAL;
*attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL);
goto nla_put_failure;
if ((swkey->tun_key.ipv4_dst || is_mask) &&
- ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key))
+ ovs_ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key))
goto nla_put_failure;
if (swkey->phy.in_port == DP_MAX_PORTS) {
struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *idx);
extern const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1];
-int ipv4_tun_from_nlattr(const struct nlattr *attr,
- struct sw_flow_match *match, bool is_mask);
-int ipv4_tun_to_nlattr(struct sk_buff *skb,
- const struct ovs_key_ipv4_tunnel *tun_key,
- const struct ovs_key_ipv4_tunnel *output);
+int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr,
+ struct sw_flow_match *match, bool is_mask);
+int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb,
+ const struct ovs_key_ipv4_tunnel *tun_key,
+ const struct ovs_key_ipv4_tunnel *output);
bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
const struct sw_flow_key *key, int key_len);
static struct hmap all_bfds__ = HMAP_INITIALIZER(&all_bfds__);
static struct hmap *const all_bfds OVS_GUARDED_BY(mutex) = &all_bfds__;
-static bool bfd_forwarding__(const struct bfd *) OVS_REQ_WRLOCK(mutex);
-static bool bfd_in_poll(const struct bfd *) OVS_REQ_WRLOCK(&mutex);
-static void bfd_poll(struct bfd *bfd) OVS_REQ_WRLOCK(&mutex);
-static const char *bfd_diag_str(enum diag) OVS_REQ_WRLOCK(&mutex);
-static const char *bfd_state_str(enum state) OVS_REQ_WRLOCK(&mutex);
-static long long int bfd_min_tx(const struct bfd *) OVS_REQ_WRLOCK(&mutex);
+static bool bfd_forwarding__(const struct bfd *) OVS_REQUIRES(mutex);
+static bool bfd_in_poll(const struct bfd *) OVS_REQUIRES(mutex);
+static void bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex);
+static const char *bfd_diag_str(enum diag) OVS_REQUIRES(mutex);
+static const char *bfd_state_str(enum state) OVS_REQUIRES(mutex);
+static long long int bfd_min_tx(const struct bfd *) OVS_REQUIRES(mutex);
static long long int bfd_tx_interval(const struct bfd *)
- OVS_REQ_WRLOCK(&mutex);
+ OVS_REQUIRES(mutex);
static long long int bfd_rx_interval(const struct bfd *)
- OVS_REQ_WRLOCK(&mutex);
-static void bfd_set_next_tx(struct bfd *) OVS_REQ_WRLOCK(&mutex);
+ OVS_REQUIRES(mutex);
+static void bfd_set_next_tx(struct bfd *) OVS_REQUIRES(mutex);
static void bfd_set_state(struct bfd *, enum state, enum diag)
- OVS_REQ_WRLOCK(&mutex);
-static uint32_t generate_discriminator(void) OVS_REQ_WRLOCK(&mutex);
+ OVS_REQUIRES(mutex);
+static uint32_t generate_discriminator(void) OVS_REQUIRES(mutex);
static void bfd_put_details(struct ds *, const struct bfd *)
- OVS_REQ_WRLOCK(&mutex);
+ OVS_REQUIRES(mutex);
static void bfd_unixctl_show(struct unixctl_conn *, int argc,
const char *argv[], void *aux OVS_UNUSED);
static void bfd_unixctl_set_forwarding_override(struct unixctl_conn *,
int argc, const char *argv[],
void *aux OVS_UNUSED);
static void log_msg(enum vlog_level, const struct msg *, const char *message,
- const struct bfd *) OVS_REQ_WRLOCK(&mutex);
+ const struct bfd *) OVS_REQUIRES(mutex);
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 20);
}
\f
static bool
-bfd_forwarding__(const struct bfd *bfd) OVS_REQ_WRLOCK(mutex)
+bfd_forwarding__(const struct bfd *bfd) OVS_REQUIRES(mutex)
{
if (bfd->forwarding_override != -1) {
return bfd->forwarding_override == 1;
/* Helpers. */
static bool
-bfd_in_poll(const struct bfd *bfd) OVS_REQ_WRLOCK(mutex)
+bfd_in_poll(const struct bfd *bfd) OVS_REQUIRES(mutex)
{
return (bfd->flags & FLAG_POLL) != 0;
}
static void
-bfd_poll(struct bfd *bfd) OVS_REQ_WRLOCK(mutex)
+bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex)
{
if (bfd->state > STATE_DOWN && !bfd_in_poll(bfd)
&& !(bfd->flags & FLAG_FINAL)) {
}
static long long int
-bfd_min_tx(const struct bfd *bfd) OVS_REQ_WRLOCK(mutex)
+bfd_min_tx(const struct bfd *bfd) OVS_REQUIRES(mutex)
{
/* RFC 5880 Section 6.8.3
* When bfd.SessionState is not Up, the system MUST set
}
static long long int
-bfd_tx_interval(const struct bfd *bfd) OVS_REQ_WRLOCK(mutex)
+bfd_tx_interval(const struct bfd *bfd) OVS_REQUIRES(mutex)
{
long long int interval = bfd_min_tx(bfd);
return MAX(interval, bfd->rmt_min_rx);
}
static long long int
-bfd_rx_interval(const struct bfd *bfd) OVS_REQ_WRLOCK(mutex)
+bfd_rx_interval(const struct bfd *bfd) OVS_REQUIRES(mutex)
{
return MAX(bfd->min_rx, bfd->rmt_min_tx);
}
static void
-bfd_set_next_tx(struct bfd *bfd) OVS_REQ_WRLOCK(mutex)
+bfd_set_next_tx(struct bfd *bfd) OVS_REQUIRES(mutex)
{
long long int interval = bfd_tx_interval(bfd);
interval -= interval * random_range(26) / 100;
static void
log_msg(enum vlog_level level, const struct msg *p, const char *message,
- const struct bfd *bfd) OVS_REQ_WRLOCK(mutex)
+ const struct bfd *bfd) OVS_REQUIRES(mutex)
{
struct ds ds = DS_EMPTY_INITIALIZER;
static void
bfd_set_state(struct bfd *bfd, enum state state, enum diag diag)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
if (diag == DIAG_NONE && bfd->cpath_down) {
diag = DIAG_CPATH_DOWN;
}
static struct bfd *
-bfd_find_by_name(const char *name) OVS_REQ_WRLOCK(mutex)
+bfd_find_by_name(const char *name) OVS_REQUIRES(mutex)
{
struct bfd *bfd;
}
static void
-bfd_put_details(struct ds *ds, const struct bfd *bfd) OVS_REQ_WRLOCK(mutex)
+bfd_put_details(struct ds *ds, const struct bfd *bfd) OVS_REQUIRES(mutex)
{
ds_put_format(ds, "\tForwarding: %s\n",
bfd_forwarding__(bfd) ? "true" : "false");
static unixctl_cb_func cfm_unixctl_set_fault;
static uint64_t
-cfm_rx_packets(const struct cfm *cfm) OVS_REQ_WRLOCK(mutex)
+cfm_rx_packets(const struct cfm *cfm) OVS_REQUIRES(mutex)
{
struct netdev_stats stats;
}
static void
-cfm_generate_maid(struct cfm *cfm) OVS_REQ_WRLOCK(mutex)
+cfm_generate_maid(struct cfm *cfm) OVS_REQUIRES(mutex)
{
const char *ovs_md_name = "ovs";
const char *ovs_ma_name = "ovs";
}
static long long int
-cfm_fault_interval(struct cfm *cfm) OVS_REQ_WRLOCK(mutex)
+cfm_fault_interval(struct cfm *cfm) OVS_REQUIRES(mutex)
{
/* According to the 802.1ag specification we should assume every other MP
* with the same MAID has the same transmission interval that we have. If
}
static struct remote_mp *
-lookup_remote_mp(const struct cfm *cfm, uint64_t mpid) OVS_REQ_WRLOCK(mutex)
+lookup_remote_mp(const struct cfm *cfm, uint64_t mpid) OVS_REQUIRES(mutex)
{
struct remote_mp *rmp;
}
static int
-cfm_get_fault__(const struct cfm *cfm) OVS_REQ_WRLOCK(mutex)
+cfm_get_fault__(const struct cfm *cfm) OVS_REQUIRES(mutex)
{
if (cfm->fault_override >= 0) {
return cfm->fault_override ? CFM_FAULT_OVERRIDE : 0;
}
static struct cfm *
-cfm_find(const char *name) OVS_REQ_WRLOCK(&mutex)
+cfm_find(const char *name) OVS_REQUIRES(mutex)
{
struct cfm *cfm;
}
static void
-cfm_print_details(struct ds *ds, const struct cfm *cfm) OVS_REQ_WRLOCK(&mutex)
+cfm_print_details(struct ds *ds, const struct cfm *cfm) OVS_REQUIRES(mutex)
{
struct remote_mp *rmp;
bool extended;
#include "odp-util.h"
#include "ofp-util.h"
#include "packets.h"
+#include "ovs-thread.h"
static struct cls_table *find_table(const struct classifier *,
const struct minimask *);
cls->n_rules = 0;
hmap_init(&cls->tables);
list_init(&cls->tables_priority);
+ ovs_rwlock_init(&cls->rwlock);
}
/* Destroys 'cls'. Rules within 'cls', if any, are not freed; this is the
destroy_table(cls, table);
}
hmap_destroy(&cls->tables);
+ ovs_rwlock_destroy(&cls->rwlock);
}
}
* a hash map from fixed field values to "struct cls_rule",
* which can contain a list of otherwise identical rules
* with lower priorities.
- */
+ *
+ * Thread-safety
+ * =============
+ *
+ * When locked properly, the classifier is thread safe as long as the following
+ * conditions are satisfied.
+ * - Only the main thread calls functions requiring a write lock.
+ * - Only the main thread is allowed to iterate over rules. */
#include "flow.h"
#include "hmap.h"
#include "match.h"
#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
+#include "ovs-thread.h"
+#include "util.h"
#ifdef __cplusplus
extern "C" {
int n_rules; /* Total number of rules. */
struct hmap tables; /* Contains "struct cls_table"s. */
struct list tables_priority; /* Tables in descending priority order */
+ struct ovs_rwlock rwlock;
};
/* A set of rules that all have the same fields wildcarded. */
bool cls_rule_is_loose_match(const struct cls_rule *rule,
const struct minimatch *criteria);
-void classifier_init(struct classifier *);
+void classifier_init(struct classifier *cls);
void classifier_destroy(struct classifier *);
-bool classifier_is_empty(const struct classifier *);
-int classifier_count(const struct classifier *);
-void classifier_insert(struct classifier *, struct cls_rule *);
-struct cls_rule *classifier_replace(struct classifier *, struct cls_rule *);
-void classifier_remove(struct classifier *, struct cls_rule *);
-struct cls_rule *classifier_lookup(const struct classifier *,
+bool classifier_is_empty(const struct classifier *cls)
+ OVS_REQ_RDLOCK(cls->rwlock);
+int classifier_count(const struct classifier *cls)
+ OVS_REQ_RDLOCK(cls->rwlock);
+void classifier_insert(struct classifier *cls, struct cls_rule *)
+ OVS_REQ_WRLOCK(cls->rwlock);
+struct cls_rule *classifier_replace(struct classifier *cls, struct cls_rule *)
+ OVS_REQ_WRLOCK(cls->rwlock);
+void classifier_remove(struct classifier *cls, struct cls_rule *)
+ OVS_REQ_WRLOCK(cls->rwlock);
+struct cls_rule *classifier_lookup(const struct classifier *cls,
const struct flow *,
- struct flow_wildcards *);
-bool classifier_rule_overlaps(const struct classifier *,
- const struct cls_rule *);
+ struct flow_wildcards *)
+ OVS_REQ_RDLOCK(cls->rwlock);
+bool classifier_rule_overlaps(const struct classifier *cls,
+ const struct cls_rule *)
+ OVS_REQ_RDLOCK(cls->rwlock);
typedef void cls_cb_func(struct cls_rule *, void *aux);
-struct cls_rule *classifier_find_rule_exactly(const struct classifier *,
- const struct cls_rule *);
-struct cls_rule *classifier_find_match_exactly(const struct classifier *,
+struct cls_rule *classifier_find_rule_exactly(const struct classifier *cls,
+ const struct cls_rule *)
+ OVS_REQ_RDLOCK(cls->rwlock);
+struct cls_rule *classifier_find_match_exactly(const struct classifier *cls,
const struct match *,
- unsigned int priority);
+ unsigned int priority)
+ OVS_REQ_RDLOCK(cls->rwlock);
\f
/* Iteration. */
const struct cls_rule *target;
};
-void cls_cursor_init(struct cls_cursor *, const struct classifier *,
- const struct cls_rule *match);
-struct cls_rule *cls_cursor_first(struct cls_cursor *);
-struct cls_rule *cls_cursor_next(struct cls_cursor *, struct cls_rule *);
+void cls_cursor_init(struct cls_cursor *cursor, const struct classifier *cls,
+ const struct cls_rule *match) OVS_REQ_RDLOCK(cls->rwlock);
+struct cls_rule *cls_cursor_first(struct cls_cursor *cursor);
+struct cls_rule *cls_cursor_next(struct cls_cursor *cursor, struct cls_rule *);
#define CLS_CURSOR_FOR_EACH(RULE, MEMBER, CURSOR) \
for (ASSIGN_CONTAINER(RULE, cls_cursor_first(CURSOR), MEMBER); \
* while the specific MUTEX is held.
*
*
+ * On a variable A of mutex type:
+ *
+ * - OVS_ACQ_BEFORE(B), where B is a mutex or a comma-separated list of
+ * mutexes, declare that if both A and B are acquired at the same time,
+ * then A must be acquired before B. That is, B nests inside A.
+ *
+ * - OVS_ACQ_AFTER(B) is the opposite of OVS_ACQ_BEFORE(B), that is, it
+ * declares that A nests inside B.
+ *
+ *
* On a function, the following attributes apply to mutexes:
*
* - OVS_ACQUIRES(MUTEX) indicate that the function must be called without
* - OVS_LOCKS_EXCLUDED(MUTEX) indicates that the function may only be
* called when MUTEX is not held.
*
+ *
* The following variants, with the same syntax, apply to reader-writer locks:
*
* mutex rwlock, for reading rwlock, for writing
#define OVS_GUARDED_BY(...) __attribute__((guarded_by(__VA_ARGS__)))
#define OVS_RELEASES(...) __attribute__((unlock_function(__VA_ARGS__)))
#define OVS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__)))
+#define OVS_ACQ_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
+#define OVS_ACQ_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
#elif __CHECKER__
/* "sparse" annotations for mutexes and mutex-like constructs.
*
#define OVS_GUARDED_BY(...)
#define OVS_EXCLUDED(...)
#define OVS_RELEASES(...) __attribute__((context(MUTEX, 1, 0)))
+#define OVS_ACQ_BEFORE(...)
+#define OVS_ACQ_AFTER(...)
#define OVS_MACRO_LOCK(...) __context__(MUTEX, 0, 1)
#define OVS_MACRO_RELEASE(...) __context__(MUTEX, 1, 0)
#else
#define OVS_GUARDED_BY(...)
#define OVS_EXCLUDED(...)
#define OVS_RELEASES(...)
+#define OVS_ACQ_BEFORE(...)
+#define OVS_ACQ_AFTER(...)
#define OVS_MACRO_LOCK(...)
#define OVS_MACRO_RELEASE(...)
#endif
static struct list all_lacps__ = LIST_INITIALIZER(&all_lacps__);
static struct list *const all_lacps OVS_GUARDED_BY(mutex) = &all_lacps__;
-static void lacp_update_attached(struct lacp *) OVS_REQ_WRLOCK(mutex);
+static void lacp_update_attached(struct lacp *) OVS_REQUIRES(mutex);
-static void slave_destroy(struct slave *) OVS_REQ_WRLOCK(mutex);
-static void slave_set_defaulted(struct slave *) OVS_REQ_WRLOCK(mutex);
-static void slave_set_expired(struct slave *) OVS_REQ_WRLOCK(mutex);
+static void slave_destroy(struct slave *) OVS_REQUIRES(mutex);
+static void slave_set_defaulted(struct slave *) OVS_REQUIRES(mutex);
+static void slave_set_expired(struct slave *) OVS_REQUIRES(mutex);
static void slave_get_actor(struct slave *, struct lacp_info *actor)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static void slave_get_priority(struct slave *, struct lacp_info *priority)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static bool slave_may_tx(const struct slave *)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static struct slave *slave_lookup(const struct lacp *, const void *slave)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static bool info_tx_equal(struct lacp_info *, struct lacp_info *)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static unixctl_cb_func lacp_unixctl_show;
}
static bool
-slave_may_enable__(struct slave *slave) OVS_REQ_WRLOCK(mutex)
+slave_may_enable__(struct slave *slave) OVS_REQUIRES(mutex)
{
/* The slave may be enabled if it's attached to an aggregator and its
* partner is synchronized.*/
/* Updates the attached status of all slaves controlled by 'lacp' and sets its
* negotiated parameter to true if any slaves are attachable. */
static void
-lacp_update_attached(struct lacp *lacp) OVS_REQ_WRLOCK(mutex)
+lacp_update_attached(struct lacp *lacp) OVS_REQUIRES(mutex)
{
struct slave *lead, *slave;
struct lacp_info lead_pri;
}
static void
-slave_destroy(struct slave *slave) OVS_REQ_WRLOCK(mutex)
+slave_destroy(struct slave *slave) OVS_REQUIRES(mutex)
{
if (slave) {
struct lacp *lacp = slave->lacp;
}
static void
-slave_set_defaulted(struct slave *slave) OVS_REQ_WRLOCK(mutex)
+slave_set_defaulted(struct slave *slave) OVS_REQUIRES(mutex)
{
memset(&slave->partner, 0, sizeof slave->partner);
}
static void
-slave_set_expired(struct slave *slave) OVS_REQ_WRLOCK(mutex)
+slave_set_expired(struct slave *slave) OVS_REQUIRES(mutex)
{
slave->status = LACP_EXPIRED;
slave->partner.state |= LACP_STATE_TIME;
static void
slave_get_actor(struct slave *slave, struct lacp_info *actor)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
struct lacp *lacp = slave->lacp;
uint16_t key;
* link. */
static void
slave_get_priority(struct slave *slave, struct lacp_info *priority)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
uint16_t partner_priority, actor_priority;
}
static bool
-slave_may_tx(const struct slave *slave) OVS_REQ_WRLOCK(mutex)
+slave_may_tx(const struct slave *slave) OVS_REQUIRES(mutex)
{
return slave->lacp->active || slave->status != LACP_DEFAULTED;
}
static struct slave *
-slave_lookup(const struct lacp *lacp, const void *slave_) OVS_REQ_WRLOCK(mutex)
+slave_lookup(const struct lacp *lacp, const void *slave_) OVS_REQUIRES(mutex)
{
struct slave *slave;
}
\f
static struct lacp *
-lacp_find(const char *name) OVS_REQ_WRLOCK(&mutex)
+lacp_find(const char *name) OVS_REQUIRES(mutex)
{
struct lacp *lacp;
}
static void
-lacp_print_details(struct ds *ds, struct lacp *lacp) OVS_REQ_WRLOCK(&mutex)
+lacp_print_details(struct ds *ds, struct lacp *lacp) OVS_REQUIRES(mutex)
{
struct shash slave_shash = SHASH_INITIALIZER(&slave_shash);
const struct shash_node **sorted_slaves = NULL;
VALID_CARRIER = 1 << 5
};
-/* An AF_INET socket (used for ioctl operations). */
-static int af_inet_sock = -1;
-
#if defined(__NetBSD__)
/* AF_LINK socket used for netdev_bsd_get_stats and set_etheraddr */
static int af_link_sock = -1;
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
-static int netdev_bsd_do_ioctl(const char *, struct ifreq *, unsigned long cmd,
- const char *cmd_name);
static void destroy_tap(int fd, const char *name);
static int get_flags(const struct netdev *, int *flagsp);
static int set_flags(const char *, int flags);
static int
netdev_bsd_init(void)
{
+#if defined(__NetBSD__)
static int status = -1;
if (status >= 0) { /* already initialized */
return status;
}
- af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
- status = af_inet_sock >= 0 ? 0 : errno;
- if (status) {
- VLOG_ERR("failed to create inet socket: %s", ovs_strerror(status));
- return status;
- }
-
-#if defined(__NetBSD__)
af_link_sock = socket(AF_LINK, SOCK_DGRAM, 0);
status = af_link_sock >= 0 ? 0 : errno;
if (status) {
VLOG_ERR("failed to create link socket: %s", ovs_strerror(status));
- close(af_inet_sock);
- af_inet_sock = -1;
}
-#endif /* defined(__NetBSD__) */
return status;
+#else
+ return 0;
+#endif /* defined(__NetBSD__) */
}
/*
/* Change the name of the tap device */
#if defined(SIOCSIFNAME)
ifr.ifr_data = (void *)name;
- if (ioctl(af_inet_sock, SIOCSIFNAME, &ifr) == -1) {
- error = errno;
+ error = af_inet_ioctl(SIOCSIFNAME, &ifr);
+ if (error) {
destroy_tap(netdev->tap_fd, ifr.ifr_name);
goto error_unref_notifier;
}
/* Turn device UP */
ifr_set_flags(&ifr, IFF_UP);
strncpy(ifr.ifr_name, kernel_name, sizeof ifr.ifr_name);
- if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) {
- error = errno;
+ error = af_inet_ioctl(SIOCSIFFLAGS, &ifr);
+ if (error) {
destroy_tap(netdev->tap_fd, kernel_name);
goto error_unref_notifier;
}
struct ifreq ifr;
int error;
- error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
+ error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr,
SIOCGIFMTU, "SIOCGIFMTU");
if (error) {
return error;
if (!(netdev->cache_valid & VALID_CARRIER)) {
struct ifmediareq ifmr;
+ int error;
memset(&ifmr, 0, sizeof(ifmr));
strncpy(ifmr.ifm_name, netdev_get_kernel_name(netdev_),
sizeof ifmr.ifm_name);
- if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+ error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
+ if (error) {
VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
- netdev_get_name(netdev_), ovs_strerror(errno));
- return errno;
+ netdev_get_name(netdev_), ovs_strerror(error));
+ return error;
}
netdev->carrier = (ifmr.ifm_status & IFM_ACTIVE) == IFM_ACTIVE;
/* We make two SIOCGIFMEDIA ioctl calls. The first to determine the
* number of supported modes, and a second with a buffer to retrieve
* them. */
- if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+ error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
+ if (error) {
VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
- netdev_get_name(netdev), ovs_strerror(errno));
- return errno;
+ netdev_get_name(netdev), ovs_strerror(error));
+ return error;
}
media_list = xcalloc(ifmr.ifm_count, sizeof(int));
goto cleanup;
}
- if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+ error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
+ if (error) {
VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
- netdev_get_name(netdev), ovs_strerror(errno));
- error = errno;
+ netdev_get_name(netdev), ovs_strerror(error));
goto cleanup;
}
int error;
ifr.ifr_addr.sa_family = AF_INET;
- error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
+ error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr,
SIOCGIFADDR, "SIOCGIFADDR");
if (error) {
return error;
sin = (struct sockaddr_in *) &ifr.ifr_addr;
netdev->in4 = sin->sin_addr;
- error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
+ error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr,
SIOCGIFNETMASK, "SIOCGIFNETMASK");
if (error) {
return error;
{
struct ifreq ifr;
make_in4_sockaddr(&ifr.ifr_addr, addr);
- return netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev), &ifr, ioctl_nr,
+ return af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev), &ifr, ioctl_nr,
ioctl_name);
}
close(fd);
strcpy(ifr.ifr_name, name);
/* XXX What to do if this call fails? */
- ioctl(af_inet_sock, SIOCIFDESTROY, &ifr);
+ af_inet_ioctl(SIOCIFDESTROY, &ifr);
}
static int
struct ifreq ifr;
int error;
- error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev), &ifr,
+ error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev), &ifr,
SIOCGIFFLAGS, "SIOCGIFFLAGS");
*flags = ifr_get_flags(&ifr);
ifr_set_flags(&ifr, flags);
- return netdev_bsd_do_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
+ return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
}
static int
{
#if defined(__FreeBSD__)
struct ifreq ifr;
+ int error;
memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
ifr.ifr_addr.sa_family = hwaddr_family;
ifr.ifr_addr.sa_len = hwaddr_len;
memcpy(ifr.ifr_addr.sa_data, mac, hwaddr_len);
- if (ioctl(af_inet_sock, SIOCSIFLLADDR, &ifr) < 0) {
+ error = af_inet_ioctl(SIOCSIFLLADDR, &ifr);
+ if (error) {
VLOG_ERR("ioctl(SIOCSIFLLADDR) on %s device failed: %s",
- netdev_name, ovs_strerror(errno));
- return errno;
+ netdev_name, ovs_strerror(error));
+ return error;
}
return 0;
#elif defined(__NetBSD__)
#endif
}
-static int
-netdev_bsd_do_ioctl(const char *name, struct ifreq *ifr, unsigned long cmd,
- const char *cmd_name)
-{
- strncpy(ifr->ifr_name, name, sizeof ifr->ifr_name);
- if (ioctl(af_inet_sock, cmd, ifr) == -1) {
- VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name,
- ovs_strerror(errno));
- return errno;
- }
- return 0;
-}
-
static int
ifr_get_flags(const struct ifreq *ifr)
{
struct list rxes; /* List of child "netdev_rx_dummy"s. */
};
+static const struct netdev_class dummy_class;
+
/* Max 'recv_queue_len' in struct netdev_dummy. */
#define NETDEV_DUMMY_MAX_QUEUE 100
bool listening;
};
-static struct shash dummy_netdevs = SHASH_INITIALIZER(&dummy_netdevs);
-
static const struct netdev_rx_class netdev_rx_dummy_class;
static unixctl_cb_func netdev_dummy_set_admin_state;
static void
netdev_dummy_run(void)
{
+ struct shash dummy_netdevs;
struct shash_node *node;
+ shash_init(&dummy_netdevs);
+ netdev_get_devices(&dummy_class, &dummy_netdevs);
SHASH_FOR_EACH (node, &dummy_netdevs) {
struct netdev_dummy *dev = node->data;
size_t i;
dev->streams[i] = dev->streams[--dev->n_streams];
}
}
+
+ netdev_close(&dev->up);
}
+ shash_destroy(&dummy_netdevs);
}
static void
static void
netdev_dummy_wait(void)
{
+ struct shash dummy_netdevs;
struct shash_node *node;
+ shash_init(&dummy_netdevs);
+ netdev_get_devices(&dummy_class, &dummy_netdevs);
SHASH_FOR_EACH (node, &dummy_netdevs) {
struct netdev_dummy *dev = node->data;
size_t i;
}
stream_recv_wait(s->stream);
}
+ netdev_close(&dev->up);
}
+ shash_destroy(&dummy_netdevs);
}
static int
list_init(&netdev->rxes);
- shash_add(&dummy_netdevs, name, netdev);
-
*netdevp = &netdev->up;
return 0;
struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
size_t i;
- shash_find_and_delete(&dummy_netdevs,
- netdev_get_name(netdev_));
pstream_close(netdev->pstream);
for (i = 0; i < netdev->n_streams; i++) {
dummy_stream_close(&netdev->streams[i]);
int argc, const char *argv[], void *aux OVS_UNUSED)
{
struct netdev_dummy *dummy_dev;
+ struct netdev *netdev;
int i;
- dummy_dev = shash_find_data(&dummy_netdevs, argv[1]);
- if (!dummy_dev) {
+ netdev = netdev_from_name(argv[1]);
+ if (!netdev || !is_dummy_class(netdev->netdev_class)) {
unixctl_command_reply_error(conn, "no such dummy netdev");
- return;
+ goto exit;
}
+ dummy_dev = netdev_dummy_cast(netdev);
for (i = 2; i < argc; i++) {
struct ofpbuf *packet;
packet = eth_from_packet_or_flow(argv[i]);
if (!packet) {
unixctl_command_reply_error(conn, "bad packet syntax");
- return;
+ goto exit;
}
dummy_dev->stats.rx_packets++;
}
unixctl_command_reply(conn, NULL);
+
+exit:
+ netdev_close(netdev);
}
static void
}
if (argc > 2) {
- struct netdev_dummy *dummy_dev;
+ struct netdev *netdev = netdev_from_name(argv[1]);
+ if (netdev && is_dummy_class(netdev->netdev_class)) {
+ struct netdev_dummy *dummy_dev = netdev_dummy_cast(netdev);
- dummy_dev = shash_find_data(&dummy_netdevs, argv[1]);
- if (dummy_dev) {
netdev_dummy_set_admin_state__(dummy_dev, up);
+ netdev_close(netdev);
} else {
unixctl_command_reply_error(conn, "Unknown Dummy Interface");
+ netdev_close(netdev);
return;
}
} else {
+ struct shash dummy_netdevs;
struct shash_node *node;
+ shash_init(&dummy_netdevs);
+ netdev_get_devices(&dummy_class, &dummy_netdevs);
SHASH_FOR_EACH (node, &dummy_netdevs) {
- netdev_dummy_set_admin_state__(node->data, up);
+ struct netdev *netdev = node->data;
+ netdev_dummy_set_admin_state__(netdev_dummy_cast(netdev), up);
+ netdev_close(netdev);
}
+ shash_destroy(&dummy_netdevs);
}
unixctl_command_reply(conn, "OK");
}
static const struct netdev_rx_class netdev_rx_linux_class;
-/* Sockets used for ioctl operations. */
-static int af_inet_sock = -1; /* AF_INET, SOCK_DGRAM. */
-
/* This is set pretty low because we probably won't learn anything from the
* additional log messages. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
-static int netdev_linux_init(void);
+static void netdev_linux_run(void);
static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *,
int cmd, const char *cmd_name);
-static int netdev_linux_do_ioctl(const char *name, struct ifreq *, int cmd,
- const char *cmd_name);
static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *,
int cmd, const char *cmd_name);
static int get_flags(const struct netdev *, unsigned int *flags);
static bool
is_netdev_linux_class(const struct netdev_class *netdev_class)
{
- return netdev_class->init == netdev_linux_init;
+ return netdev_class->run == netdev_linux_run;
}
static bool
return CONTAINER_OF(rx, struct netdev_rx_linux, up);
}
\f
-static int
-netdev_linux_init(void)
-{
- static int status = -1;
- if (status < 0) {
- /* Create AF_INET socket. */
- af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
- status = af_inet_sock >= 0 ? 0 : errno;
- if (status) {
- VLOG_ERR("failed to create inet socket: %s", ovs_strerror(status));
- }
- }
- return status;
-}
-
static void
netdev_linux_run(void)
{
struct sockaddr_ll sll;
int ifindex;
/* Result of tcpdump -dd inbound */
- static struct sock_filter filt[] = {
+ static const struct sock_filter filt[] = {
{ 0x28, 0, 0, 0xfffff004 }, /* ldh [0] */
{ 0x15, 0, 1, 0x00000004 }, /* jeq #4 jt 2 jf 3 */
{ 0x6, 0, 0, 0x00000000 }, /* ret #0 */
{ 0x6, 0, 0, 0x0000ffff } /* ret #65535 */
};
- static struct sock_fprog fprog = { ARRAY_SIZE(filt), filt };
+ static const struct sock_fprog fprog = {
+ ARRAY_SIZE(filt), (struct sock_filter *) filt
+ };
/* Create file descriptor. */
fd = socket(PF_PACKET, SOCK_RAW, 0);
sizeof fprog);
if (error) {
error = errno;
- VLOG_ERR("%s: failed attach filter (%s)",
+ VLOG_ERR("%s: failed to attach filter (%s)",
netdev_get_name(netdev_), ovs_strerror(error));
goto error;
}
struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
if (rx->is_tap) {
struct ifreq ifr;
- int error = netdev_linux_do_ioctl(netdev_rx_get_name(rx_), &ifr,
- SIOCGIFTXQLEN, "SIOCGIFTXQLEN");
+ int error = af_inet_ifreq_ioctl(netdev_rx_get_name(rx_), &ifr,
+ SIOCGIFTXQLEN, "SIOCGIFTXQLEN");
if (error) {
return error;
}
struct ifreq ifr;
int error;
- error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr,
- SIOCGIFMTU, "SIOCGIFMTU");
+ error = af_inet_ifreq_ioctl(netdev_get_name(netdev_), &ifr,
+ SIOCGIFMTU, "SIOCGIFMTU");
netdev->netdev_mtu_error = error;
netdev->mtu = ifr.ifr_mtu;
netdev->cache_valid &= ~VALID_MTU;
}
ifr.ifr_mtu = mtu;
- error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr,
- SIOCSIFMTU, "SIOCSIFMTU");
+ error = af_inet_ifreq_ioctl(netdev_get_name(netdev_), &ifr,
+ SIOCSIFMTU, "SIOCSIFMTU");
if (!error || error == ENODEV) {
netdev->netdev_mtu_error = error;
netdev->mtu = ifr.ifr_mtu;
memset(&ifr, 0, sizeof ifr);
memcpy(&ifr.ifr_data, data, sizeof *data);
- error = netdev_linux_do_ioctl(name, &ifr, cmd, cmd_name);
+ error = af_inet_ifreq_ioctl(name, &ifr, cmd, cmd_name);
memcpy(data, &ifr.ifr_data, sizeof *data);
return error;
int ioctl_nr, const char *ioctl_name, struct in_addr addr)
{
struct ifreq ifr;
- ovs_strzcpy(ifr.ifr_name, netdev_get_name(netdev), sizeof ifr.ifr_name);
- make_in4_sockaddr(&ifr.ifr_addr, addr);
- return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr,
- ioctl_name);
+ make_in4_sockaddr(&ifr.ifr_addr, addr);
+ return af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr,
+ ioctl_name);
}
/* Adds 'router' as a default IP gateway. */
make_in4_sockaddr(&rt.rt_gateway, router);
make_in4_sockaddr(&rt.rt_genmask, any);
rt.rt_flags = RTF_UP | RTF_GATEWAY;
- error = ioctl(af_inet_sock, SIOCADDRT, &rt) < 0 ? errno : 0;
+ error = af_inet_ioctl(SIOCADDRT, &rt);
if (error) {
VLOG_WARN("ioctl(SIOCADDRT): %s", ovs_strerror(error));
}
r.arp_flags = 0;
ovs_strzcpy(r.arp_dev, netdev_get_name(netdev), sizeof r.arp_dev);
COVERAGE_INC(netdev_arp_lookup);
- retval = ioctl(af_inet_sock, SIOCGARP, &r) < 0 ? errno : 0;
+ retval = af_inet_ioctl(SIOCGARP, &r);
if (!retval) {
memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN);
} else if (retval != ENXIO) {
{ \
NAME, \
\
- netdev_linux_init, \
+ NULL, \
netdev_linux_run, \
netdev_linux_wait, \
\
\f
/* Linux-only functions declared in netdev-linux.h */
-/* Returns a fd for an AF_INET socket or a negative errno value. */
-int
-netdev_linux_get_af_inet_sock(void)
-{
- int error = netdev_linux_init();
- return error ? -error : af_inet_sock;
-}
-
/* Modifies the 'flag' bit in ethtool's flags field for 'netdev'. If
* 'enable' is true, the bit is set. Otherwise, it is cleared. */
int
int error;
*flags = 0;
- error = netdev_linux_do_ioctl(dev->name, &ifr, SIOCGIFFLAGS,
- "SIOCGIFFLAGS");
+ error = af_inet_ifreq_ioctl(dev->name, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS");
if (!error) {
*flags = ifr.ifr_flags;
}
struct ifreq ifr;
ifr.ifr_flags = flags;
- return netdev_linux_do_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
+ return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
}
static int
do_get_ifindex(const char *netdev_name)
{
struct ifreq ifr;
+ int error;
ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
COVERAGE_INC(netdev_get_ifindex);
- if (ioctl(af_inet_sock, SIOCGIFINDEX, &ifr) < 0) {
+
+ error = af_inet_ioctl(SIOCGIFINDEX, &ifr);
+ if (error) {
VLOG_WARN_RL(&rl, "ioctl(SIOCGIFINDEX) on %s device failed: %s",
- netdev_name, ovs_strerror(errno));
- return -errno;
+ netdev_name, ovs_strerror(error));
+ return -error;
}
return ifr.ifr_ifindex;
}
{
struct ifreq ifr;
int hwaddr_family;
+ int error;
memset(&ifr, 0, sizeof ifr);
ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
COVERAGE_INC(netdev_get_hwaddr);
- if (ioctl(af_inet_sock, SIOCGIFHWADDR, &ifr) < 0) {
+ error = af_inet_ioctl(SIOCGIFHWADDR, &ifr);
+ if (error) {
/* ENODEV probably means that a vif disappeared asynchronously and
* hasn't been removed from the database yet, so reduce the log level
* to INFO for that case. */
- VLOG(errno == ENODEV ? VLL_INFO : VLL_ERR,
+ VLOG(error == ENODEV ? VLL_INFO : VLL_ERR,
"ioctl(SIOCGIFHWADDR) on %s device failed: %s",
- netdev_name, ovs_strerror(errno));
- return errno;
+ netdev_name, ovs_strerror(error));
+ return error;
}
hwaddr_family = ifr.ifr_hwaddr.sa_family;
if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) {
const uint8_t mac[ETH_ADDR_LEN])
{
struct ifreq ifr;
+ int error;
memset(&ifr, 0, sizeof ifr);
ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ADDR_LEN);
COVERAGE_INC(netdev_set_hwaddr);
- if (ioctl(af_inet_sock, SIOCSIFHWADDR, &ifr) < 0) {
+ error = af_inet_ioctl(SIOCSIFHWADDR, &ifr);
+ if (error) {
VLOG_ERR("ioctl(SIOCSIFHWADDR) on %s device failed: %s",
- netdev_name, ovs_strerror(errno));
- return errno;
+ netdev_name, ovs_strerror(error));
}
- return 0;
+ return error;
}
static int
int cmd, const char *cmd_name)
{
struct ifreq ifr;
+ int error;
memset(&ifr, 0, sizeof ifr);
ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
ifr.ifr_data = (caddr_t) ecmd;
ecmd->cmd = cmd;
- if (ioctl(af_inet_sock, SIOCETHTOOL, &ifr) == 0) {
- return 0;
- } else {
- if (errno != EOPNOTSUPP) {
+ error = af_inet_ioctl(SIOCETHTOOL, &ifr);
+ if (error) {
+ if (error != EOPNOTSUPP) {
VLOG_WARN_RL(&rl, "ethtool command %s on network device %s "
- "failed: %s", cmd_name, name, ovs_strerror(errno));
+ "failed: %s", cmd_name, name, ovs_strerror(error));
} else {
/* The device doesn't support this operation. That's pretty
* common, so there's no point in logging anything. */
}
- return errno;
- }
-}
-
-static int
-netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd,
- const char *cmd_name)
-{
- ovs_strzcpy(ifr->ifr_name, name, sizeof ifr->ifr_name);
- if (ioctl(af_inet_sock, cmd, ifr) == -1) {
- VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name,
- ovs_strerror(errno));
- return errno;
}
- return 0;
+ return error;
}
static int
int error;
ifr.ifr_addr.sa_family = AF_INET;
- error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name);
+ error = af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name);
if (!error) {
const struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *,
&ifr.ifr_addr);
/*
- * Copyright (c) 2011 Nicira, Inc.
+ * Copyright (c) 2011, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
const char *flag_name, bool enable);
-int netdev_linux_get_af_inet_sock(void);
#endif /* netdev-linux.h */
/* Abstract packet-in message. */
struct ofputil_packet_in {
+ struct list list_node; /* For queueing packet_ins. */
+
const void *packet;
size_t packet_len;
XPTHREAD_FUNC1(pthread_cond_signal, pthread_cond_t *);
XPTHREAD_FUNC1(pthread_cond_broadcast, pthread_cond_t *);
+XPTHREAD_FUNC2(pthread_join, pthread_t, void **);
+
typedef void destructor_func(void *);
XPTHREAD_FUNC2(pthread_key_create, pthread_key_t *, destructor_func *);
+XPTHREAD_FUNC2(pthread_setspecific, pthread_key_t, const void *);
void
ovs_mutex_init(const struct ovs_mutex *l_, int type)
ovs_abort(error, "pthread_cond_wait failed");
}
}
+\f
+DEFINE_EXTERN_PER_THREAD_DATA(ovsthread_id, 0);
+
+struct ovsthread_aux {
+ void *(*start)(void *);
+ void *arg;
+};
+
+static void *
+ovsthread_wrapper(void *aux_)
+{
+ static atomic_uint next_id = ATOMIC_VAR_INIT(1);
+
+ struct ovsthread_aux *auxp = aux_;
+ struct ovsthread_aux aux;
+ unsigned int id;
+
+ atomic_add(&next_id, 1, &id);
+ *ovsthread_id_get() = id;
+
+ aux = *auxp;
+ free(auxp);
+
+ return aux.start(aux.arg);
+}
void
xpthread_create(pthread_t *threadp, pthread_attr_t *attr,
void *(*start)(void *), void *arg)
{
+ struct ovsthread_aux *aux;
pthread_t thread;
int error;
forbid_forking("multiple threads exist");
multithreaded = true;
- error = pthread_create(threadp ? threadp : &thread, attr, start, arg);
+ aux = xmalloc(sizeof *aux);
+ aux->start = start;
+ aux->arg = arg;
+
+ error = pthread_create(threadp ? threadp : &thread, attr,
+ ovsthread_wrapper, aux);
if (error) {
ovs_abort(error, "pthread_create failed");
}
void ovs_rwlock_wrlock_at(const struct ovs_rwlock *rwlock, const char *where)
OVS_ACQ_WRLOCK(rwlock);
#define ovs_rwlock_wrlock(rwlock) \
- ovs_rwlock_wrlock_at(rwlock, SOURCE_LOCATOR);
+ ovs_rwlock_wrlock_at(rwlock, SOURCE_LOCATOR)
int ovs_rwlock_trywrlock_at(const struct ovs_rwlock *rwlock, const char *where)
OVS_TRY_WRLOCK(0, rwlock);
void ovs_rwlock_rdlock_at(const struct ovs_rwlock *rwlock, const char *where)
OVS_ACQ_RDLOCK(rwlock);
#define ovs_rwlock_rdlock(rwlock) \
- ovs_rwlock_rdlock_at(rwlock, SOURCE_LOCATOR);
+ ovs_rwlock_rdlock_at(rwlock, SOURCE_LOCATOR)
int ovs_rwlock_tryrdlock_at(const struct ovs_rwlock *rwlock, const char *where)
OVS_TRY_RDLOCK(0, rwlock);
#endif
void xpthread_key_create(pthread_key_t *, void (*destructor)(void *));
+void xpthread_setspecific(pthread_key_t, const void *);
void xpthread_create(pthread_t *, pthread_attr_t *, void *(*)(void *), void *);
+void xpthread_join(pthread_t, void **);
\f
/* Per-thread data.
*
* cross-thread access? yes no yes
*/
-/* DEFINE_PER_THREAD_DATA(TYPE, NAME, INITIALIZER).
+/* For static data, use this macro in a source file:
+ *
+ * DEFINE_STATIC_PER_THREAD_DATA(TYPE, NAME, INITIALIZER).
+ *
+ * For global data, "declare" the data in the header and "define" it in
+ * the source file, with:
+ *
+ * DECLARE_EXTERN_PER_THREAD_DATA(TYPE, NAME).
+ * DEFINE_EXTERN_PER_THREAD_DATA(NAME, INITIALIZER).
*
* One should prefer to use POSIX per-thread data, via pthread_key_t, when its
* performance is acceptable, because of its portability (see the table above).
#error
#endif
-#define DEFINE_PER_THREAD_DATA(TYPE, NAME, ...) \
- typedef TYPE NAME##_type; \
- static thread_local NAME##_type NAME##_var = __VA_ARGS__; \
- \
- static NAME##_type * \
- NAME##_get_unsafe(void) \
- { \
- return &NAME##_var; \
- } \
- \
- static NAME##_type * \
- NAME##_get(void) \
- { \
- return NAME##_get_unsafe(); \
+#define DEFINE_STATIC_PER_THREAD_DATA(TYPE, NAME, ...) \
+ typedef TYPE NAME##_type; \
+ \
+ static NAME##_type * \
+ NAME##_get_unsafe(void) \
+ { \
+ static thread_local NAME##_type var = __VA_ARGS__; \
+ return &var; \
+ } \
+ \
+ static NAME##_type * \
+ NAME##_get(void) \
+ { \
+ return NAME##_get_unsafe(); \
+ }
+#define DECLARE_EXTERN_PER_THREAD_DATA(TYPE, NAME) \
+ typedef TYPE NAME##_type; \
+ extern thread_local NAME##_type NAME##_var; \
+ \
+ static inline NAME##_type * \
+ NAME##_get_unsafe(void) \
+ { \
+ return &NAME##_var; \
+ } \
+ \
+ static inline NAME##_type * \
+ NAME##_get(void) \
+ { \
+ return NAME##_get_unsafe(); \
}
+#define DEFINE_EXTERN_PER_THREAD_DATA(NAME, ...) \
+ thread_local NAME##_type NAME##_var = __VA_ARGS__;
#else /* no C implementation support for thread-local storage */
-#define DEFINE_PER_THREAD_DATA(TYPE, NAME, ...) \
+#define DEFINE_STATIC_PER_THREAD_DATA(TYPE, NAME, ...) \
typedef TYPE NAME##_type; \
static pthread_key_t NAME##_key; \
\
\
value = xmalloc(sizeof *value); \
*value = initial_value; \
- pthread_setspecific(NAME##_key, value); \
+ xpthread_setspecific(NAME##_key, value); \
+ } \
+ return value; \
+ }
+#define DECLARE_EXTERN_PER_THREAD_DATA(TYPE, NAME) \
+ typedef TYPE NAME##_type; \
+ static pthread_key_t NAME##_key; \
+ \
+ static inline NAME##_type * \
+ NAME##_get_unsafe(void) \
+ { \
+ return pthread_getspecific(NAME##_key); \
+ } \
+ \
+ NAME##_type *NAME##_get(void);
+#define DEFINE_EXTERN_PER_THREAD_DATA(NAME, ...) \
+ static void \
+ NAME##_once_init(void) \
+ { \
+ if (pthread_key_create(&NAME##_key, free)) { \
+ abort(); \
+ } \
+ } \
+ \
+ NAME##_type * \
+ NAME##_get(void) \
+ { \
+ static pthread_once_t once = PTHREAD_ONCE_INIT; \
+ NAME##_type *value; \
+ \
+ pthread_once(&once, NAME##_once_init); \
+ value = NAME##_get_unsafe(); \
+ if (!value) { \
+ static const NAME##_type initial_value = __VA_ARGS__; \
+ \
+ value = xmalloc(sizeof *value); \
+ *value = initial_value; \
+ xpthread_setspecific(NAME##_key, value); \
} \
return value; \
}
NAME##_set_unsafe(TYPE value) \
{ \
TYPE old_value = NAME##_get_unsafe(); \
- pthread_setspecific(NAME##_key, value); \
+ xpthread_setspecific(NAME##_key, value); \
return old_value; \
} \
\
((ONCE)->done ? false : ({ OVS_MACRO_LOCK((&ONCE->mutex)); true; }))
#endif
\f
+/* Thread ID.
+ *
+ * pthread_t isn't so nice for some purposes. Its size and representation are
+ * implementation dependent, which means that there is no way to hash it.
+ * This thread ID avoids the problem.
+ */
+
+DECLARE_EXTERN_PER_THREAD_DATA(unsigned int, ovsthread_id);
+
+/* Returns a per-thread identifier unique within the lifetime of the
+ * process. */
+static inline unsigned int
+ovsthread_id_self(void)
+{
+ return *ovsthread_id_get();
+}
+\f
void assert_single_threaded_at(const char *where);
#define assert_single_threaded() assert_single_threaded_at(SOURCE_LOCATOR)
loop = pthread_getspecific(key);
if (!loop) {
loop = xzalloc(sizeof *loop);
- pthread_setspecific(key, loop);
+ xpthread_setspecific(key, loop);
}
return loop;
}
* cryptographic-quality randomness. */
/* Current random state. */
-DEFINE_PER_THREAD_DATA(uint32_t, seed, 0);
+DEFINE_STATIC_PER_THREAD_DATA(uint32_t, seed, 0);
static uint32_t random_next(void);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
*n_fdsp = 0;
return EPROTO;
}
+
+/* Calls ioctl() on an AF_INET sock, passing the specified 'command' and
+ * 'arg'. Returns 0 if successful, otherwise a positive errno value. */
+int
+af_inet_ioctl(unsigned long int command, const void *arg)
+{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ static int sock;
+
+ if (ovsthread_once_start(&once)) {
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ sock = -errno;
+ VLOG_ERR("failed to create inet socket: %s", ovs_strerror(errno));
+ }
+ ovsthread_once_done(&once);
+ }
+
+ return (sock < 0 ? -sock
+ : ioctl(sock, command, arg) == -1 ? errno
+ : 0);
+}
+
+int
+af_inet_ifreq_ioctl(const char *name, struct ifreq *ifr, unsigned long int cmd,
+ const char *cmd_name)
+{
+ int error;
+
+ ovs_strzcpy(ifr->ifr_name, name, sizeof ifr->ifr_name);
+ error = af_inet_ioctl(cmd, ifr);
+ if (error) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+ VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name,
+ ovs_strerror(error));
+ }
+ return error;
+}
+
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void *data, size_t size,
int fds[SOUTIL_MAX_FDS], size_t *n_fdsp);
+/* Helpers for calling ioctl() on an AF_INET socket. */
+struct ifreq;
+int af_inet_ioctl(unsigned long int command, const void *arg);
+int af_inet_ifreq_ioctl(const char *name, struct ifreq *,
+ unsigned long int cmd, const char *cmd_name);
+
#endif /* socket-util.h */
static struct ovs_mutex mutex;
static struct list all_stps__ = LIST_INITIALIZER(&all_stps__);
-static struct list *const all_stps OVS_GUARDED_BY(&mutex) = &all_stps__;
+static struct list *const all_stps OVS_GUARDED_BY(mutex) = &all_stps__;
#define FOR_EACH_ENABLED_PORT(PORT, STP) \
for ((PORT) = stp_next_enabled_port((STP), (STP)->ports); \
(PORT) = stp_next_enabled_port((STP), (PORT) + 1))
static struct stp_port *
stp_next_enabled_port(const struct stp *stp, const struct stp_port *port)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
for (; port < &stp->ports[ARRAY_SIZE(stp->ports)]; port++) {
if (port->state != STP_DISABLED) {
#define MESSAGE_AGE_INCREMENT 1
-static void stp_transmit_config(struct stp_port *) OVS_REQ_WRLOCK(mutex);
+static void stp_transmit_config(struct stp_port *) OVS_REQUIRES(mutex);
static bool stp_supersedes_port_info(const struct stp_port *,
const struct stp_config_bpdu *)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static void stp_record_config_information(struct stp_port *,
const struct stp_config_bpdu *)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static void stp_record_config_timeout_values(struct stp *,
const struct stp_config_bpdu *)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static bool stp_is_designated_port(const struct stp_port *)
- OVS_REQ_WRLOCK(mutex);
-static void stp_config_bpdu_generation(struct stp *) OVS_REQ_WRLOCK(mutex);
-static void stp_transmit_tcn(struct stp *) OVS_REQ_WRLOCK(mutex);
-static void stp_configuration_update(struct stp *) OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
+static void stp_config_bpdu_generation(struct stp *) OVS_REQUIRES(mutex);
+static void stp_transmit_tcn(struct stp *) OVS_REQUIRES(mutex);
+static void stp_configuration_update(struct stp *) OVS_REQUIRES(mutex);
static bool stp_supersedes_root(const struct stp_port *root,
- const struct stp_port *) OVS_REQ_WRLOCK(mutex);
-static void stp_root_selection(struct stp *) OVS_REQ_WRLOCK(mutex);
-static void stp_designated_port_selection(struct stp *) OVS_REQ_WRLOCK(mutex);
+ const struct stp_port *) OVS_REQUIRES(mutex);
+static void stp_root_selection(struct stp *) OVS_REQUIRES(mutex);
+static void stp_designated_port_selection(struct stp *) OVS_REQUIRES(mutex);
static void stp_become_designated_port(struct stp_port *)
- OVS_REQ_WRLOCK(mutex);
-static void stp_port_state_selection(struct stp *) OVS_REQ_WRLOCK(mutex);
-static void stp_make_forwarding(struct stp_port *) OVS_REQ_WRLOCK(mutex);
-static void stp_make_blocking(struct stp_port *) OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
+static void stp_port_state_selection(struct stp *) OVS_REQUIRES(mutex);
+static void stp_make_forwarding(struct stp_port *) OVS_REQUIRES(mutex);
+static void stp_make_blocking(struct stp_port *) OVS_REQUIRES(mutex);
static void stp_set_port_state(struct stp_port *, enum stp_state)
- OVS_REQ_WRLOCK(mutex);
-static void stp_topology_change_detection(struct stp *) OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
+static void stp_topology_change_detection(struct stp *) OVS_REQUIRES(mutex);
static void stp_topology_change_acknowledged(struct stp *)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static void stp_acknowledge_topology_change(struct stp_port *)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static void stp_received_config_bpdu(struct stp *, struct stp_port *,
const struct stp_config_bpdu *)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static void stp_received_tcn_bpdu(struct stp *, struct stp_port *)
- OVS_REQ_WRLOCK(mutex);
-static void stp_hello_timer_expiry(struct stp *) OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
+static void stp_hello_timer_expiry(struct stp *) OVS_REQUIRES(mutex);
static void stp_message_age_timer_expiry(struct stp_port *)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static bool stp_is_designated_for_some_port(const struct stp *)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static void stp_forward_delay_timer_expiry(struct stp_port *)
- OVS_REQ_WRLOCK(mutex);
-static void stp_tcn_timer_expiry(struct stp *) OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
+static void stp_tcn_timer_expiry(struct stp *) OVS_REQUIRES(mutex);
static void stp_topology_change_timer_expiry(struct stp *)
- OVS_REQ_WRLOCK(mutex);
-static void stp_hold_timer_expiry(struct stp_port *) OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
+static void stp_hold_timer_expiry(struct stp_port *) OVS_REQUIRES(mutex);
static void stp_initialize_port(struct stp_port *, enum stp_state)
- OVS_REQ_WRLOCK(mutex);
-static void stp_become_root_bridge(struct stp *) OVS_REQ_WRLOCK(mutex);
-static void stp_update_bridge_timers(struct stp *) OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
+static void stp_become_root_bridge(struct stp *) OVS_REQUIRES(mutex);
+static void stp_update_bridge_timers(struct stp *) OVS_REQUIRES(mutex);
static int clamp(int x, int min, int max);
static int ms_to_timer(int ms);
static bool stp_timer_expired(struct stp_timer *, int elapsed, int timeout);
static void stp_send_bpdu(struct stp_port *, const void *, size_t)
- OVS_REQ_WRLOCK(mutex);
+ OVS_REQUIRES(mutex);
static void stp_unixctl_tcn(struct unixctl_conn *, int argc,
const char *argv[], void *aux);
static void
set_bridge_id(struct stp *stp, stp_identifier new_bridge_id)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
if (new_bridge_id != stp->bridge_id) {
bool root;
}
\f
static void
-stp_transmit_config(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_transmit_config(struct stp_port *p) OVS_REQUIRES(mutex)
{
struct stp *stp = p->stp;
bool root = stp_is_root_bridge(stp);
static bool
stp_supersedes_port_info(const struct stp_port *p,
const struct stp_config_bpdu *config)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
if (ntohll(config->root_id) != p->designated_root) {
return ntohll(config->root_id) < p->designated_root;
static void
stp_record_config_information(struct stp_port *p,
const struct stp_config_bpdu *config)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
p->designated_root = ntohll(config->root_id);
p->designated_cost = ntohl(config->root_path_cost);
static void
stp_record_config_timeout_values(struct stp *stp,
const struct stp_config_bpdu *config)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
stp->max_age = ntohs(config->max_age);
stp->hello_time = ntohs(config->hello_time);
}
static bool
-stp_is_designated_port(const struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_is_designated_port(const struct stp_port *p) OVS_REQUIRES(mutex)
{
return (p->designated_bridge == p->stp->bridge_id
&& p->designated_port == p->port_id);
}
static void
-stp_config_bpdu_generation(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_config_bpdu_generation(struct stp *stp) OVS_REQUIRES(mutex)
{
struct stp_port *p;
}
static void
-stp_transmit_tcn(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_transmit_tcn(struct stp *stp) OVS_REQUIRES(mutex)
{
struct stp_port *p = stp->root_port;
struct stp_tcn_bpdu tcn_bpdu;
}
static void
-stp_configuration_update(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_configuration_update(struct stp *stp) OVS_REQUIRES(mutex)
{
stp_root_selection(stp);
stp_designated_port_selection(stp);
static bool
stp_supersedes_root(const struct stp_port *root, const struct stp_port *p)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
int p_cost = p->designated_cost + p->path_cost;
int root_cost = root->designated_cost + root->path_cost;
}
static void
-stp_root_selection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_root_selection(struct stp *stp) OVS_REQUIRES(mutex)
{
struct stp_port *p, *root;
}
static void
-stp_designated_port_selection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_designated_port_selection(struct stp *stp) OVS_REQUIRES(mutex)
{
struct stp_port *p;
}
static void
-stp_become_designated_port(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_become_designated_port(struct stp_port *p) OVS_REQUIRES(mutex)
{
struct stp *stp = p->stp;
p->designated_root = stp->designated_root;
}
static void
-stp_port_state_selection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_port_state_selection(struct stp *stp) OVS_REQUIRES(mutex)
{
struct stp_port *p;
}
static void
-stp_make_forwarding(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_make_forwarding(struct stp_port *p) OVS_REQUIRES(mutex)
{
if (p->state == STP_BLOCKING) {
stp_set_port_state(p, STP_LISTENING);
}
static void
-stp_make_blocking(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_make_blocking(struct stp_port *p) OVS_REQUIRES(mutex)
{
if (!(p->state & (STP_DISABLED | STP_BLOCKING))) {
if (p->state & (STP_FORWARDING | STP_LEARNING)) {
static void
stp_set_port_state(struct stp_port *p, enum stp_state state)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
if (state != p->state && !p->state_changed) {
p->state_changed = true;
}
static void
-stp_topology_change_detection(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_topology_change_detection(struct stp *stp) OVS_REQUIRES(mutex)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
}
static void
-stp_topology_change_acknowledged(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_topology_change_acknowledged(struct stp *stp) OVS_REQUIRES(mutex)
{
stp->topology_change_detected = false;
stp_stop_timer(&stp->tcn_timer);
}
static void
-stp_acknowledge_topology_change(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_acknowledge_topology_change(struct stp_port *p) OVS_REQUIRES(mutex)
{
p->topology_change_ack = true;
stp_transmit_config(p);
static void
stp_received_config_bpdu(struct stp *stp, struct stp_port *p,
const struct stp_config_bpdu *config)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
if (ntohs(config->message_age) >= ntohs(config->max_age)) {
VLOG_WARN("%s: received config BPDU with message age (%u) greater "
static void
stp_received_tcn_bpdu(struct stp *stp, struct stp_port *p)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
if (p->state != STP_DISABLED) {
if (stp_is_designated_port(p)) {
}
static void
-stp_hello_timer_expiry(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_hello_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex)
{
stp_config_bpdu_generation(stp);
stp_start_timer(&stp->hello_timer, 0);
}
static void
-stp_message_age_timer_expiry(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_message_age_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
{
struct stp *stp = p->stp;
bool root = stp_is_root_bridge(stp);
}
static bool
-stp_is_designated_for_some_port(const struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_is_designated_for_some_port(const struct stp *stp) OVS_REQUIRES(mutex)
{
const struct stp_port *p;
}
static void
-stp_forward_delay_timer_expiry(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_forward_delay_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
{
if (p->state == STP_LISTENING) {
stp_set_port_state(p, STP_LEARNING);
}
static void
-stp_tcn_timer_expiry(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_tcn_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex)
{
stp_transmit_tcn(stp);
stp_start_timer(&stp->tcn_timer, 0);
}
static void
-stp_topology_change_timer_expiry(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_topology_change_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex)
{
stp->topology_change_detected = false;
stp->topology_change = false;
}
static void
-stp_hold_timer_expiry(struct stp_port *p) OVS_REQ_WRLOCK(mutex)
+stp_hold_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
{
if (p->config_pending) {
stp_transmit_config(p);
static void
stp_initialize_port(struct stp_port *p, enum stp_state state)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
ovs_assert(state & (STP_DISABLED | STP_BLOCKING));
stp_become_designated_port(p);
}
static void
-stp_become_root_bridge(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_become_root_bridge(struct stp *stp) OVS_REQUIRES(mutex)
{
stp->max_age = stp->bridge_max_age;
stp->hello_time = stp->bridge_hello_time;
}
static void
-stp_start_timer(struct stp_timer *timer, int value) OVS_REQ_WRLOCK(mutex)
+stp_start_timer(struct stp_timer *timer, int value) OVS_REQUIRES(mutex)
{
timer->value = value;
timer->active = true;
}
static void
-stp_stop_timer(struct stp_timer *timer) OVS_REQ_WRLOCK(mutex)
+stp_stop_timer(struct stp_timer *timer) OVS_REQUIRES(mutex)
{
timer->active = false;
}
static bool
stp_timer_expired(struct stp_timer *timer, int elapsed, int timeout)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
if (timer->active) {
timer->value += elapsed;
}
static void
-stp_update_bridge_timers(struct stp *stp) OVS_REQ_WRLOCK(mutex)
+stp_update_bridge_timers(struct stp *stp) OVS_REQUIRES(mutex)
{
int ht, ma, fd;
static void
stp_send_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
struct eth_header *eth;
struct llc_header *llc;
/* Unixctl. */
static struct stp *
-stp_find(const char *name) OVS_REQ_WRLOCK(mutex)
+stp_find(const char *name) OVS_REQUIRES(mutex)
{
struct stp *stp;
/* Monotonic time, in milliseconds, at which the last call to time_poll() woke
* up. */
-DEFINE_PER_THREAD_DATA(long long int, last_wakeup, 0);
+DEFINE_STATIC_PER_THREAD_DATA(long long int, last_wakeup, 0);
static void set_up_timer(void);
static void set_up_signal(int flags);
static char *program_version;
/* Buffer used by ovs_strerror(). */
-DEFINE_PER_THREAD_DATA(struct { char s[128]; }, strerror_buffer, { "" });
+DEFINE_STATIC_PER_THREAD_DATA(struct { char s[128]; },
+ strerror_buffer,
+ { "" });
void
ovs_assert_failure(const char *where, const char *function,
#include "dummy.h"
#include "hash.h"
#include "shash.h"
+#include "socket-util.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(vlandev);
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
int error;
- int sock;
via->cmd = cmd;
ovs_strlcpy(via->device1, netdev_name, sizeof via->device1);
- sock = netdev_linux_get_af_inet_sock();
- if (sock < 0) {
- return -sock;
- }
-
- error = ioctl(sock, SIOCSIFVLAN, via) < 0 ? errno : 0;
+ error = af_inet_ioctl(SIOCSIFVLAN, via);
if (error) {
VLOG_WARN_RL(&rl, "%s: VLAN ioctl %s failed (%s)",
netdev_name, cmd_name, ovs_strerror(error));
};
/* Sequence number for the message currently being composed. */
-DEFINE_PER_THREAD_DATA(unsigned int, msg_num, 0);
+DEFINE_STATIC_PER_THREAD_DATA(unsigned int, msg_num, 0);
/* VLF_FILE configuration.
*
fu.event = event;
fu.reason = event == NXFME_DELETED ? reason : 0;
- fu.idle_timeout = rule->idle_timeout;
- fu.hard_timeout = rule->hard_timeout;
fu.table_id = rule->table_id;
fu.cookie = rule->flow_cookie;
minimatch_expand(&rule->cr.match, &match);
fu.match = &match;
fu.priority = rule->cr.priority;
+
+ ovs_mutex_lock(&rule->timeout_mutex);
+ fu.idle_timeout = rule->idle_timeout;
+ fu.hard_timeout = rule->hard_timeout;
+ ovs_mutex_unlock(&rule->timeout_mutex);
+
if (flags & NXFMF_ACTIONS) {
fu.ofpacts = rule->ofpacts;
fu.ofpacts_len = rule->ofpacts_len;
}
static void
-dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQ_WRLOCK(mutex)
+dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex)
{
struct dpif_ipfix_flow_exporter_map_node *node, *next;
static struct dpif_sflow_port *
dpif_sflow_find_port(const struct dpif_sflow *ds, odp_port_t odp_port)
- OVS_REQ_WRLOCK(&mutex)
+ OVS_REQUIRES(mutex)
{
struct dpif_sflow_port *dsp;
static void
sflow_agent_get_counters(void *ds_, SFLPoller *poller,
SFL_COUNTERS_SAMPLE_TYPE *cs)
- OVS_REQ_WRLOCK(&mutex)
+ OVS_REQUIRES(mutex)
{
struct dpif_sflow *ds = ds_;
SFLCounters_sample_element elem;
}
static void
-dpif_sflow_clear__(struct dpif_sflow *ds) OVS_REQ_WRLOCK(mutex)
+dpif_sflow_clear__(struct dpif_sflow *ds) OVS_REQUIRES(mutex)
{
if (ds->sflow_agent) {
sfl_agent_release(ds->sflow_agent);
static void
dpif_sflow_add_poller(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
SFLPoller *poller = sfl_agent_addPoller(ds->sflow_agent, &dsp->dsi, ds,
sflow_agent_get_counters);
static void
dpif_sflow_del_port__(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
- OVS_REQ_WRLOCK(mutex)
+ OVS_REQUIRES(mutex)
{
if (ds->sflow_agent) {
sfl_agent_removePoller(ds->sflow_agent, &dsp->dsi);
* flow translation. */
#define MAX_RESUBMIT_RECURSION 64
+struct ovs_rwlock xlate_rwlock = OVS_RWLOCK_INITIALIZER;
+
struct xbridge {
struct hmap_node hmap_node; /* Node in global 'xbridges' map. */
struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
struct dpif_ipfix *ipfix; /* Ipfix handle, or null. */
struct stp *stp; /* STP or null if disabled. */
+ /* Special rules installed by ofproto-dpif. */
+ struct rule_dpif *miss_rule;
+ struct rule_dpif *no_packet_in_rule;
+
enum ofp_config_flags frag; /* Fragmentation handling. */
bool has_netflow; /* Bridge runs netflow? */
bool has_in_band; /* Bridge has in band control? */
void
xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
- struct dpif *dpif, const struct mac_learning *ml,
- struct stp *stp, const struct mbridge *mbridge,
+ struct dpif *dpif, struct rule_dpif *miss_rule,
+ struct rule_dpif *no_packet_in_rule,
+ const struct mac_learning *ml, struct stp *stp,
+ const struct mbridge *mbridge,
const struct dpif_sflow *sflow,
const struct dpif_ipfix *ipfix, enum ofp_config_flags frag,
bool forward_bpdu, bool has_in_band, bool has_netflow)
xbridge->has_in_band = has_in_band;
xbridge->has_netflow = has_netflow;
xbridge->frag = frag;
+ xbridge->miss_rule = miss_rule;
+ xbridge->no_packet_in_rule = no_packet_in_rule;
}
void
const struct xport *xport;
int error = ENODEV;
+ ovs_rwlock_rdlock(&xlate_rwlock);
fitness = odp_flow_key_to_flow(key, key_len, flow);
if (fitness == ODP_FIT_ERROR) {
error = EINVAL;
if (fitnessp) {
*fitnessp = fitness;
}
+ ovs_rwlock_unlock(&xlate_rwlock);
return error;
}
}
}
-static void
-update_learning_table(const struct xbridge *xbridge,
- const struct flow *flow, struct flow_wildcards *wc,
- int vlan, struct xbundle *in_xbundle)
+/* Checks whether a MAC learning update is necessary for MAC learning table
+ * 'ml' given that a packet matching 'flow' was received on 'in_xbundle' in
+ * 'vlan'.
+ *
+ * Most packets processed through the MAC learning table do not actually
+ * change it in any way. This function requires only a read lock on the MAC
+ * learning table, so it is much cheaper in this common case.
+ *
+ * Keep the code here synchronized with that in update_learning_table__()
+ * below. */
+static bool
+is_mac_learning_update_needed(const struct mac_learning *ml,
+ const struct flow *flow,
+ struct flow_wildcards *wc,
+ int vlan, struct xbundle *in_xbundle)
+ OVS_REQ_RDLOCK(ml->rwlock)
{
struct mac_entry *mac;
- /* Don't learn the OFPP_NONE port. */
- if (in_xbundle == &ofpp_none_bundle) {
- return;
+ if (!mac_learning_may_learn(ml, flow->dl_src, vlan)) {
+ return false;
}
- ovs_rwlock_wrlock(&xbridge->ml->rwlock);
+ mac = mac_learning_lookup(ml, flow->dl_src, vlan);
+ if (!mac || mac_entry_age(ml, mac)) {
+ return true;
+ }
+
+ if (is_gratuitous_arp(flow, wc)) {
+ /* We don't want to learn from gratuitous ARP packets that are
+ * reflected back over bond slaves so we lock the learning table. */
+ if (!in_xbundle->bond) {
+ return true;
+ } else if (mac_entry_is_grat_arp_locked(mac)) {
+ return false;
+ }
+ }
+
+ return mac->port.p != in_xbundle->ofbundle;
+}
+
+
+/* Updates MAC learning table 'ml' given that a packet matching 'flow' was
+ * received on 'in_xbundle' in 'vlan'.
+ *
+ * This code repeats all the checks in is_mac_learning_update_needed() because
+ * the lock was released between there and here and thus the MAC learning state
+ * could have changed.
+ *
+ * Keep the code here synchronized with that in is_mac_learning_update_needed()
+ * above. */
+static void
+update_learning_table__(const struct xbridge *xbridge,
+ const struct flow *flow, struct flow_wildcards *wc,
+ int vlan, struct xbundle *in_xbundle)
+ OVS_REQ_WRLOCK(xbridge->ml->rwlock)
+{
+ struct mac_entry *mac;
+
if (!mac_learning_may_learn(xbridge->ml, flow->dl_src, vlan)) {
- goto out;
+ return;
}
mac = mac_learning_insert(xbridge->ml, flow->dl_src, vlan);
if (!in_xbundle->bond) {
mac_entry_set_grat_arp_lock(mac);
} else if (mac_entry_is_grat_arp_locked(mac)) {
- goto out;
+ return;
}
}
/* The log messages here could actually be useful in debugging,
* so keep the rate limit relatively high. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
+
VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
"on port %s in VLAN %d",
xbridge->name, ETH_ADDR_ARGS(flow->dl_src),
mac->port.p = in_xbundle->ofbundle;
mac_learning_changed(xbridge->ml);
}
-out:
+}
+
+static void
+update_learning_table(const struct xbridge *xbridge,
+ const struct flow *flow, struct flow_wildcards *wc,
+ int vlan, struct xbundle *in_xbundle)
+{
+ bool need_update;
+
+ /* Don't learn the OFPP_NONE port. */
+ if (in_xbundle == &ofpp_none_bundle) {
+ return;
+ }
+
+ /* First try the common case: no change to MAC learning table. */
+ ovs_rwlock_rdlock(&xbridge->ml->rwlock);
+ need_update = is_mac_learning_update_needed(xbridge->ml, flow, wc, vlan,
+ in_xbundle);
ovs_rwlock_unlock(&xbridge->ml->rwlock);
+
+ if (need_update) {
+ /* Slow path: MAC learning table might need an update. */
+ ovs_rwlock_wrlock(&xbridge->ml->rwlock);
+ update_learning_table__(xbridge, flow, wc, vlan, in_xbundle);
+ ovs_rwlock_unlock(&xbridge->ml->rwlock);
+ }
}
/* Determines whether packets in 'flow' within 'xbridge' should be forwarded or
}
ctx->xin->flow = old_flow;
- ctx->xbridge = xport->xbundle->xbridge;
+ ctx->xbridge = xport->xbridge;
if (ctx->xin->resubmit_stats) {
netdev_vport_inc_tx(xport->netdev, ctx->xin->resubmit_stats);
ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse);
}
if (rule == NULL && may_packet_in) {
+ struct xport *xport;
+
/* XXX
* check if table configuration flags
* OFPTC_TABLE_MISS_CONTROLLER, default.
* OFPTC_TABLE_MISS_CONTINUE,
* OFPTC_TABLE_MISS_DROP
- * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do?
- */
- rule = rule_dpif_miss_rule(ctx->xbridge->ofproto, &ctx->xin->flow);
+ * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do? */
+ xport = get_ofp_port(ctx->xbridge, ctx->xin->flow.in_port.ofp_port);
+ rule = choose_miss_rule(xport ? xport->config : 0,
+ ctx->xbridge->miss_rule,
+ ctx->xbridge->no_packet_in_rule);
}
if (rule && ctx->xin->resubmit_stats) {
rule_credit_stats(rule, ctx->xin->resubmit_stats);
enum ofp_packet_in_reason reason,
uint16_t controller_id)
{
- struct ofputil_packet_in pin;
+ struct ofputil_packet_in *pin;
struct ofpbuf *packet;
struct flow key;
odp_execute_actions(NULL, packet, &key, ctx->xout->odp_actions.data,
ctx->xout->odp_actions.size, NULL, NULL);
- pin.packet = packet->data;
- pin.packet_len = packet->size;
- pin.reason = reason;
- pin.controller_id = controller_id;
- pin.table_id = ctx->table_id;
- pin.cookie = ctx->rule ? ctx->rule->up.flow_cookie : 0;
+ pin = xmalloc(sizeof *pin);
+ pin->packet_len = packet->size;
+ pin->packet = ofpbuf_steal_data(packet);
+ pin->reason = reason;
+ pin->controller_id = controller_id;
+ pin->table_id = ctx->table_id;
+ pin->cookie = ctx->rule ? ctx->rule->up.flow_cookie : 0;
- pin.send_len = len;
- flow_get_metadata(&ctx->xin->flow, &pin.fmd);
+ pin->send_len = len;
+ flow_get_metadata(&ctx->xin->flow, &pin->fmd);
- ofproto_dpif_send_packet_in(ctx->xbridge->ofproto, &pin);
+ ofproto_dpif_send_packet_in(ctx->xbridge->ofproto, pin);
ofpbuf_delete(packet);
}
if (ctx->xin->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
struct rule_dpif *rule = ctx->rule;
- if (list_is_empty(&rule->up.expirable)) {
- list_insert(&rule->up.ofproto->expirable, &rule->up.expirable);
- }
+ ovs_mutex_lock(&rule->up.ofproto->expirable_mutex);
+ if (list_is_empty(&rule->up.expirable)) {
+ list_insert(&rule->up.ofproto->expirable, &rule->up.expirable);
+ }
+ ovs_mutex_unlock(&rule->up.ofproto->expirable_mutex);
+ ovs_mutex_lock(&rule->up.timeout_mutex);
reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
+ ovs_mutex_unlock(&rule->up.timeout_mutex);
}
}
COVERAGE_INC(xlate_actions);
+ ovs_rwlock_rdlock(&xlate_rwlock);
+
/* Flow initialization rules:
* - 'base_flow' must match the kernel's view of the packet at the
* time that action processing starts. 'flow' represents any
ctx.xbridge = xbridge_lookup(xin->ofproto);
if (!ctx.xbridge) {
- return;
+ goto out;
}
ctx.rule = xin->rule;
break;
case OFPC_FRAG_DROP:
- return;
+ goto out;
case OFPC_FRAG_REASM:
NOT_REACHED();
* use non-header fields as part of the cache. */
memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
+
+out:
+ ovs_rwlock_unlock(&xlate_rwlock);
}
const struct dpif_flow_stats *resubmit_stats;
};
+extern struct ovs_rwlock xlate_rwlock;
+
void xlate_ofproto_set(struct ofproto_dpif *, const char *name,
- struct dpif *, const struct mac_learning *,
- struct stp *, const struct mbridge *,
- const struct dpif_sflow *, const struct dpif_ipfix *,
- enum ofp_config_flags, bool forward_bpdu,
- bool has_in_band, bool has_netflow);
-void xlate_remove_ofproto(struct ofproto_dpif *);
+ struct dpif *, struct rule_dpif *miss_rule,
+ struct rule_dpif *no_packet_in_rule,
+ const struct mac_learning *, struct stp *,
+ const struct mbridge *, const struct dpif_sflow *,
+ const struct dpif_ipfix *, enum ofp_config_flags,
+ bool forward_bpdu, bool has_in_band, bool has_netflow)
+ OVS_REQ_WRLOCK(xlate_rwlock);
+void xlate_remove_ofproto(struct ofproto_dpif *) OVS_REQ_WRLOCK(xlate_rwlock);
void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *,
const char *name, enum port_vlan_mode, int vlan,
unsigned long *trunks, bool use_priority_tags,
const struct bond *, const struct lacp *,
- bool floodable);
-void xlate_bundle_remove(struct ofbundle *);
+ bool floodable) OVS_REQ_WRLOCK(xlate_rwlock);
+void xlate_bundle_remove(struct ofbundle *) OVS_REQ_WRLOCK(xlate_rwlock);
void xlate_ofport_set(struct ofproto_dpif *, struct ofbundle *,
struct ofport_dpif *, ofp_port_t, odp_port_t,
const struct bfd *, struct ofport_dpif *peer,
int stp_port_no, const struct ofproto_port_queue *qdscp,
size_t n_qdscp, enum ofputil_port_config, bool is_tunnel,
- bool may_enable);
-void xlate_ofport_remove(struct ofport_dpif *);
+ bool may_enable) OVS_REQ_WRLOCK(xlate_rwlock);
+void xlate_ofport_remove(struct ofport_dpif *) OVS_REQ_WRLOCK(xlate_rwlock);
int xlate_receive(const struct dpif_backer *, struct ofpbuf *packet,
const struct nlattr *key, size_t key_len,
struct flow *, enum odp_key_fitness *,
- struct ofproto_dpif **, odp_port_t *odp_in_port);
+ struct ofproto_dpif **, odp_port_t *odp_in_port)
+ OVS_EXCLUDED(xlate_rwlock);
-void xlate_actions(struct xlate_in *, struct xlate_out *);
+void xlate_actions(struct xlate_in *, struct xlate_out *)
+ OVS_EXCLUDED(xlate_rwlock);
void xlate_in_init(struct xlate_in *, struct ofproto_dpif *,
const struct flow *, struct rule_dpif *,
uint8_t tcp_flags, const struct ofpbuf *packet);
COVERAGE_DEFINE(facet_unexpected);
COVERAGE_DEFINE(facet_suppress);
COVERAGE_DEFINE(subfacet_install_fail);
+COVERAGE_DEFINE(packet_in_overflow);
COVERAGE_DEFINE(flow_mod_overflow);
/* Number of implemented OpenFlow tables. */
struct ovs_mutex flow_mod_mutex;
struct list flow_mods OVS_GUARDED;
size_t n_flow_mods OVS_GUARDED;
+
+ struct ovs_mutex pin_mutex;
+ struct list pins OVS_GUARDED;
+ size_t n_pins OVS_GUARDED;
};
/* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only
ofproto_dpif_send_packet_in(struct ofproto_dpif *ofproto,
struct ofputil_packet_in *pin)
{
- connmgr_send_packet_in(ofproto->up.connmgr, pin);
+ ovs_mutex_lock(&ofproto->pin_mutex);
+ if (ofproto->n_pins > 1024) {
+ ovs_mutex_unlock(&ofproto->pin_mutex);
+ COVERAGE_INC(packet_in_overflow);
+ free(CONST_CAST(void *, pin->packet));
+ free(pin);
+ return;
+ }
+
+ list_push_back(&ofproto->pins, &pin->list_node);
+ ofproto->n_pins++;
+ ovs_mutex_unlock(&ofproto->pin_mutex);
}
\f
/* Factory functions. */
continue;
}
+ ovs_rwlock_wrlock(&xlate_rwlock);
xlate_ofproto_set(ofproto, ofproto->up.name,
- ofproto->backer->dpif, ofproto->ml,
+ ofproto->backer->dpif, ofproto->miss_rule,
+ ofproto->no_packet_in_rule, ofproto->ml,
ofproto->stp, ofproto->mbridge,
ofproto->sflow, ofproto->ipfix,
ofproto->up.frag_handling,
ofport->up.pp.config, ofport->is_tunnel,
ofport->may_enable);
}
+ ovs_rwlock_unlock(&xlate_rwlock);
+ /* Only ofproto-dpif cares about the facet classifier so we just
+ * lock cls_cursor_init() to appease the thread safety analysis. */
+ ovs_rwlock_rdlock(&ofproto->facets.rwlock);
cls_cursor_init(&cursor, &ofproto->facets, NULL);
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
CLS_CURSOR_FOR_EACH_SAFE (facet, next, cr, &cursor) {
facet_revalidate(facet);
run_fast_rl();
ofproto->n_flow_mods = 0;
ovs_mutex_unlock(&ofproto->flow_mod_mutex);
+ ovs_mutex_init(&ofproto->pin_mutex, PTHREAD_MUTEX_NORMAL);
+ ovs_mutex_lock(&ofproto->pin_mutex);
+ list_init(&ofproto->pins);
+ ofproto->n_pins = 0;
+ ovs_mutex_unlock(&ofproto->pin_mutex);
+
ofproto_dpif_unixctl_init();
hmap_init(&ofproto->vlandev_map);
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct rule_dpif *rule, *next_rule;
+ struct ofputil_flow_mod *pin, *next_pin;
struct ofputil_flow_mod *fm, *next_fm;
struct oftable *table;
ofproto->backer->need_revalidate = REV_RECONFIGURE;
+ ovs_rwlock_wrlock(&xlate_rwlock);
xlate_remove_ofproto(ofproto);
+ ovs_rwlock_unlock(&xlate_rwlock);
hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node);
complete_operations(ofproto);
OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
struct cls_cursor cursor;
+ ovs_rwlock_wrlock(&table->cls.rwlock);
cls_cursor_init(&cursor, &table->cls, NULL);
CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
- ofproto_rule_destroy(&rule->up);
+ ofproto_rule_destroy(&ofproto->up, &table->cls, &rule->up);
}
+ ovs_rwlock_unlock(&table->cls.rwlock);
}
ovs_mutex_lock(&ofproto->flow_mod_mutex);
ovs_mutex_unlock(&ofproto->flow_mod_mutex);
ovs_mutex_destroy(&ofproto->flow_mod_mutex);
+ ovs_mutex_lock(&ofproto->pin_mutex);
+ LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &ofproto->pins) {
+ list_remove(&pin->list_node);
+ ofproto->n_pins--;
+ free(pin->ofpacts);
+ free(pin);
+ }
+ ovs_mutex_unlock(&ofproto->pin_mutex);
+ ovs_mutex_destroy(&ofproto->pin_mutex);
+
mbridge_unref(ofproto->mbridge);
netflow_destroy(ofproto->netflow);
run_fast(struct ofproto *ofproto_)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- struct ofputil_flow_mod *fm, *next;
+ struct ofputil_packet_in *pin, *next_pin;
+ struct ofputil_flow_mod *fm, *next_fm;
+ struct list flow_mods, pins;
struct ofport_dpif *ofport;
- struct list flow_mods;
/* Do not perform any periodic activity required by 'ofproto' while
* waiting for flow restore to complete. */
}
ovs_mutex_unlock(&ofproto->flow_mod_mutex);
- LIST_FOR_EACH_SAFE (fm, next, list_node, &flow_mods) {
+ LIST_FOR_EACH_SAFE (fm, next_fm, list_node, &flow_mods) {
int error = ofproto_flow_mod(&ofproto->up, fm);
if (error && !VLOG_DROP_WARN(&rl)) {
VLOG_WARN("learning action failed to modify flow table (%s)",
free(fm);
}
+ ovs_mutex_lock(&ofproto->pin_mutex);
+ if (ofproto->n_pins) {
+ pins = ofproto->pins;
+ list_moved(&pins);
+ list_init(&ofproto->pins);
+ ofproto->n_pins = 0;
+ } else {
+ list_init(&pins);
+ }
+ ovs_mutex_unlock(&ofproto->pin_mutex);
+
+ LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) {
+ connmgr_send_packet_in(ofproto->up.connmgr, pin);
+ list_remove(&pin->list_node);
+ free(CONST_CAST(void *, pin->packet));
+ free(pin);
+ }
+
HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
port_run_fast(ofport);
}
ovs_rwlock_unlock(&ofproto->ml->rwlock);
/* Check the consistency of a random facet, to aid debugging. */
+ ovs_rwlock_rdlock(&ofproto->facets.rwlock);
if (time_msec() >= ofproto->consistency_rl
&& !classifier_is_empty(&ofproto->facets)
&& !ofproto->backer->need_revalidate) {
ofproto->backer->need_revalidate = REV_INCONSISTENCY;
}
}
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
return 0;
}
size_t n_subfacets = 0;
struct facet *facet;
+ ovs_rwlock_rdlock(&ofproto->facets.rwlock);
simap_increase(usage, "facets", classifier_count(&ofproto->facets));
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
+ ovs_rwlock_rdlock(&ofproto->facets.rwlock);
cls_cursor_init(&cursor, &ofproto->facets, NULL);
CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
n_subfacets += list_size(&facet->subfacets);
}
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
simap_increase(usage, "subfacets", n_subfacets);
}
const char *dp_port_name;
ofproto->backer->need_revalidate = REV_RECONFIGURE;
+ ovs_rwlock_wrlock(&xlate_rwlock);
xlate_ofport_remove(port);
+ ovs_rwlock_unlock(&xlate_rwlock);
dp_port_name = netdev_vport_get_dpif_port(port->up.netdev, namebuf,
sizeof namebuf);
ofproto = bundle->ofproto;
mbridge_unregister_bundle(ofproto->mbridge, bundle->aux);
+ ovs_rwlock_wrlock(&xlate_rwlock);
xlate_bundle_remove(bundle);
+ ovs_rwlock_unlock(&xlate_rwlock);
LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) {
bundle_del_port(port);
/* Expire OpenFlow flows whose idle_timeout or hard_timeout
* has passed. */
+ ovs_mutex_lock(&ofproto->up.expirable_mutex);
LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
&ofproto->up.expirable) {
rule_expire(rule_dpif_cast(rule));
}
+ ovs_mutex_unlock(&ofproto->up.expirable_mutex);
/* All outstanding data in existing flows has been accounted, so it's a
* good time to do bond rebalancing. */
static void
rule_expire(struct rule_dpif *rule)
{
+ uint16_t idle_timeout, hard_timeout;
long long int now;
uint8_t reason;
return;
}
+ ovs_mutex_lock(&rule->up.timeout_mutex);
+ hard_timeout = rule->up.hard_timeout;
+ idle_timeout = rule->up.idle_timeout;
+ ovs_mutex_unlock(&rule->up.timeout_mutex);
+
/* Has 'rule' expired? */
now = time_msec();
- if (rule->up.hard_timeout
- && now > rule->up.modified + rule->up.hard_timeout * 1000) {
+ if (hard_timeout && now > rule->up.modified + hard_timeout * 1000) {
reason = OFPRR_HARD_TIMEOUT;
- } else if (rule->up.idle_timeout
- && now > rule->up.used + rule->up.idle_timeout * 1000) {
+ } else if (idle_timeout && now > rule->up.used + idle_timeout * 1000) {
reason = OFPRR_IDLE_TIMEOUT;
} else {
return;
match_init(&match, &facet->flow, &facet->xout.wc);
cls_rule_init(&facet->cr, &match, OFP_DEFAULT_PRIORITY);
+ ovs_rwlock_wrlock(&ofproto->facets.rwlock);
classifier_insert(&ofproto->facets, &facet->cr);
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
facet->nf_flow.output_iface = facet->xout.nf_output_iface;
facet->fail_open = rule->up.cr.priority == FAIL_OPEN_PRIORITY;
&facet->subfacets) {
subfacet_destroy__(subfacet);
}
+ ovs_rwlock_wrlock(&facet->ofproto->facets.rwlock);
classifier_remove(&facet->ofproto->facets, &facet->cr);
+ ovs_rwlock_unlock(&facet->ofproto->facets.rwlock);
cls_rule_destroy(&facet->cr);
facet_free(facet);
}
static struct facet *
facet_find(struct ofproto_dpif *ofproto, const struct flow *flow)
{
- struct cls_rule *cr = classifier_lookup(&ofproto->facets, flow, NULL);
+ struct cls_rule *cr;
+
+ ovs_rwlock_rdlock(&ofproto->facets.rwlock);
+ cr = classifier_lookup(&ofproto->facets, flow, NULL);
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
return cr ? CONTAINER_OF(cr, struct facet, cr) : NULL;
}
struct cls_cursor cursor;
struct facet *facet;
+ ovs_rwlock_rdlock(&ofproto->facets.rwlock);
cls_cursor_init(&cursor, &ofproto->facets, NULL);
CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
facet_push_stats(facet, false);
run_fast_rl();
}
}
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
}
rl = time_msec() + 100;
rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow,
struct flow_wildcards *wc)
{
+ struct ofport_dpif *port;
struct rule_dpif *rule;
rule = rule_dpif_lookup_in_table(ofproto, flow, wc, 0);
if (rule) {
return rule;
}
+ port = get_ofp_port(ofproto, flow->in_port.ofp_port);
+ if (!port) {
+ VLOG_WARN_RL(&rl, "packet-in on unknown OpenFlow port %"PRIu16,
+ flow->in_port.ofp_port);
+ }
- return rule_dpif_miss_rule(ofproto, flow);
+ return choose_miss_rule(port ? port->up.pp.config : 0, ofproto->miss_rule,
+ ofproto->no_packet_in_rule);
}
struct rule_dpif *
struct flow ofpc_normal_flow = *flow;
ofpc_normal_flow.tp_src = htons(0);
ofpc_normal_flow.tp_dst = htons(0);
+ ovs_rwlock_rdlock(&cls->rwlock);
cls_rule = classifier_lookup(cls, &ofpc_normal_flow, wc);
+ ovs_rwlock_unlock(&cls->rwlock);
} else if (frag && ofproto->up.frag_handling == OFPC_FRAG_DROP) {
cls_rule = &ofproto->drop_frags_rule->up.cr;
if (wc) {
flow_wildcards_init_exact(wc);
}
} else {
+ ovs_rwlock_rdlock(&cls->rwlock);
cls_rule = classifier_lookup(cls, flow, wc);
+ ovs_rwlock_unlock(&cls->rwlock);
}
return rule_dpif_cast(rule_from_cls_rule(cls_rule));
}
+/* Given a port configuration (specified as zero if there's no port), chooses
+ * which of 'miss_rule' and 'no_packet_in_rule' should be used in case of a
+ * flow table miss. */
struct rule_dpif *
-rule_dpif_miss_rule(struct ofproto_dpif *ofproto, const struct flow *flow)
+choose_miss_rule(enum ofputil_port_config config, struct rule_dpif *miss_rule,
+ struct rule_dpif *no_packet_in_rule)
{
- struct ofport_dpif *port;
-
- port = get_ofp_port(ofproto, flow->in_port.ofp_port);
- if (!port) {
- VLOG_WARN_RL(&rl, "packet-in on unknown OpenFlow port %"PRIu16,
- flow->in_port.ofp_port);
- return ofproto->miss_rule;
- }
-
- if (port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN) {
- return ofproto->no_packet_in_rule;
- }
- return ofproto->miss_rule;
+ return config & OFPUTIL_PC_NO_PACKET_IN ? no_packet_in_rule : miss_rule;
}
static void
struct cls_cursor cursor;
struct facet *facet;
+ ovs_rwlock_rdlock(&ofproto->facets.rwlock);
cls_cursor_init(&cursor, &ofproto->facets, NULL);
CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
send_active_timeout(ofproto, facet);
}
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
}
\f
static struct ofproto_dpif *
int errors;
errors = 0;
+ ovs_rwlock_rdlock(&ofproto->facets.rwlock);
cls_cursor_init(&cursor, &ofproto->facets, NULL);
CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
if (!facet_check_consistency(facet)) {
errors++;
}
}
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
if (errors) {
ofproto->backer->need_revalidate = REV_INCONSISTENCY;
}
return;
}
+ ovs_rwlock_rdlock(&ofproto->facets.rwlock);
cls_cursor_init(&cursor, &ofproto->facets, NULL);
CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
cls_rule_format(&facet->cr, &ds);
}
ds_put_cstr(&ds, "\n");
}
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
ds_chomp(&ds, '\n');
unixctl_command_reply(conn, ds_cstr(&ds));
static ofp_port_t
vsp_vlandev_to_realdev(const struct ofproto_dpif *ofproto,
ofp_port_t vlandev_ofp_port, int *vid)
- OVS_REQ_WRLOCK(ofproto->vsp_mutex)
+ OVS_REQUIRES(ofproto->vsp_mutex)
{
if (!hmap_is_empty(&ofproto->vlandev_map)) {
const struct vlan_splinter *vsp;
#include "ovs-thread.h"
#include "timer.h"
#include "util.h"
+#include "ovs-thread.h"
union user_action_cookie;
struct ofproto_dpif;
struct flow_wildcards *,
uint8_t table_id);
-struct rule_dpif *rule_dpif_miss_rule(struct ofproto_dpif *ofproto,
- const struct flow *);
-
void rule_credit_stats(struct rule_dpif *, const struct dpif_flow_stats *);
+struct rule_dpif *choose_miss_rule(enum ofputil_port_config,
+ struct rule_dpif *miss_rule,
+ struct rule_dpif *no_packet_in_rule);
+
bool ofproto_has_vlan_splinters(const struct ofproto_dpif *);
ofp_port_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
ofp_port_t realdev_ofp_port,
/* Definitions for use within ofproto. */
-#include "ofproto/ofproto.h"
#include "cfm.h"
#include "classifier.h"
#include "heap.h"
#include "list.h"
#include "ofp-errors.h"
#include "ofp-util.h"
+#include "ofproto/ofproto.h"
+#include "ovs-thread.h"
#include "shash.h"
#include "simap.h"
#include "timeval.h"
/* Optimisation for flow expiry.
* These flows should all be present in tables. */
- struct list expirable; /* Expirable 'struct rule"s in all tables. */
+ struct ovs_mutex expirable_mutex;
+ struct list expirable OVS_GUARDED; /* Expirable 'struct rule"s in all
+ tables. */
/* Meter table.
* OpenFlow meters start at 1. To avoid confusion we leave the first
long long int created; /* Creation time. */
long long int modified; /* Time of last modification. */
long long int used; /* Last use; time created if never used. */
- uint16_t hard_timeout; /* In seconds from ->modified. */
- uint16_t idle_timeout; /* In seconds from ->used. */
uint8_t table_id; /* Index in ofproto's 'tables' array. */
bool send_flow_removed; /* Send a flow removed message? */
+ struct ovs_mutex timeout_mutex;
+ uint16_t hard_timeout OVS_GUARDED; /* In seconds from ->modified. */
+ uint16_t idle_timeout OVS_GUARDED; /* In seconds from ->used. */
+
/* Eviction groups. */
bool evictable; /* If false, prevents eviction. */
struct heap_node evg_node; /* In eviction_group's "rules" heap. */
void ofproto_rule_update_used(struct rule *, long long int used);
void ofproto_rule_expire(struct rule *, uint8_t reason);
-void ofproto_rule_destroy(struct rule *);
+void ofproto_rule_destroy(struct ofproto *, struct classifier *cls,
+ struct rule *) OVS_REQ_WRLOCK(cls->rwlock);
bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t out_port);
size_t n_fields);
static void oftable_remove_rule(struct rule *);
+static void oftable_remove_rule__(struct ofproto *ofproto,
+ struct classifier *cls, struct rule *rule)
+ OVS_REQ_WRLOCK(cls->rwlock);
static struct rule *oftable_replace_rule(struct rule *);
static void oftable_substitute_rule(struct rule *old, struct rule *new);
ofproto->n_tables = 0;
hindex_init(&ofproto->cookies);
list_init(&ofproto->expirable);
+ ovs_mutex_init(&ofproto->expirable_mutex, PTHREAD_MUTEX_RECURSIVE);
ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
ofproto->state = S_OPENFLOW;
list_init(&ofproto->pending);
}
table->max_flows = s->max_flows;
+ ovs_rwlock_rdlock(&table->cls.rwlock);
if (classifier_count(&table->cls) > table->max_flows
&& table->eviction_fields) {
/* 'table' contains more flows than allowed. We might not be able to
break;
}
}
+ ovs_rwlock_unlock(&table->cls.rwlock);
}
\f
bool
continue;
}
+ ovs_rwlock_wrlock(&table->cls.rwlock);
cls_cursor_init(&cursor, &table->cls, NULL);
CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
if (!rule->pending) {
ofoperation_create(group, rule, OFOPERATION_DELETE,
OFPRR_DELETE);
- oftable_remove_rule(rule);
+ oftable_remove_rule__(ofproto, &table->cls, rule);
ofproto->ofproto_class->rule_destruct(rule);
}
}
+ ovs_rwlock_unlock(&table->cls.rwlock);
}
ofopgroup_submit(group);
}
free(ofproto->vlan_bitmap);
+ ovs_mutex_destroy(&ofproto->expirable_mutex);
ofproto->ofproto_class->dealloc(ofproto);
}
n_rules = 0;
OFPROTO_FOR_EACH_TABLE (table, ofproto) {
+ ovs_rwlock_rdlock(&table->cls.rwlock);
n_rules += classifier_count(&table->cls);
+ ovs_rwlock_unlock(&table->cls.rwlock);
}
simap_increase(usage, "rules", n_rules);
{
const struct rule *rule;
+ ovs_rwlock_rdlock(&ofproto->tables[0].cls.rwlock);
rule = rule_from_cls_rule(classifier_find_match_exactly(
&ofproto->tables[0].cls, match, priority));
+ ovs_rwlock_unlock(&ofproto->tables[0].cls.rwlock);
if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len,
ofpacts, ofpacts_len)) {
struct ofputil_flow_mod fm;
{
struct rule *rule;
+ ovs_rwlock_rdlock(&ofproto->tables[0].cls.rwlock);
rule = rule_from_cls_rule(classifier_find_match_exactly(
&ofproto->tables[0].cls, target, priority));
+ ovs_rwlock_unlock(&ofproto->tables[0].cls.rwlock);
if (!rule) {
/* No such rule -> success. */
return true;
if (rule) {
cls_rule_destroy(&rule->cr);
free(rule->ofpacts);
+ ovs_mutex_destroy(&rule->timeout_mutex);
rule->ofproto->ofproto_class->rule_dealloc(rule);
}
}
* This function should only be called from an ofproto implementation's
* ->destruct() function. It is not suitable elsewhere. */
void
-ofproto_rule_destroy(struct rule *rule)
+ofproto_rule_destroy(struct ofproto *ofproto, struct classifier *cls,
+ struct rule *rule) OVS_REQ_WRLOCK(cls->rwlock)
{
ovs_assert(!rule->pending);
- oftable_remove_rule(rule);
+ oftable_remove_rule__(ofproto, cls, rule);
ofproto_rule_destroy__(rule);
}
ots[i].instructions = htonl(OFPIT11_ALL);
ots[i].config = htonl(OFPTC11_TABLE_MISS_MASK);
ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */
+ ovs_rwlock_rdlock(&p->tables[i].cls.rwlock);
ots[i].active_count = htonl(classifier_count(&p->tables[i].cls));
+ ovs_rwlock_unlock(&p->tables[i].cls.rwlock);
}
p->ofproto_class->get_tables(p, ots);
struct cls_cursor cursor;
struct rule *rule;
+ ovs_rwlock_rdlock(&table->cls.rwlock);
cls_cursor_init(&cursor, &table->cls, &cr);
CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
if (rule->pending) {
+ ovs_rwlock_unlock(&table->cls.rwlock);
error = OFPROTO_POSTPONE;
goto exit;
}
list_push_back(rules, &rule->ofproto_node);
}
}
+ ovs_rwlock_unlock(&table->cls.rwlock);
}
exit:
FOR_EACH_MATCHING_TABLE (table, table_id, ofproto) {
struct rule *rule;
+ ovs_rwlock_rdlock(&table->cls.rwlock);
rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls,
&cr));
+ ovs_rwlock_unlock(&table->cls.rwlock);
if (rule) {
if (rule->pending) {
error = OFPROTO_POSTPONE;
fs.cookie = rule->flow_cookie;
fs.table_id = rule->table_id;
calc_duration(rule->created, now, &fs.duration_sec, &fs.duration_nsec);
- fs.idle_timeout = rule->idle_timeout;
- fs.hard_timeout = rule->hard_timeout;
fs.idle_age = age_secs(now - rule->used);
fs.hard_age = age_secs(now - rule->modified);
ofproto->ofproto_class->rule_get_stats(rule, &fs.packet_count,
fs.ofpacts = rule->ofpacts;
fs.ofpacts_len = rule->ofpacts_len;
fs.flags = 0;
+
+ ovs_mutex_lock(&rule->timeout_mutex);
+ fs.idle_timeout = rule->idle_timeout;
+ fs.hard_timeout = rule->hard_timeout;
+ ovs_mutex_unlock(&rule->timeout_mutex);
+
if (rule->send_flow_removed) {
fs.flags |= OFPFF_SEND_FLOW_REM;
/* FIXME: Implement OF 1.3 flags OFPFF13_NO_PKT_COUNTS
struct cls_cursor cursor;
struct rule *rule;
+ ovs_rwlock_rdlock(&table->cls.rwlock);
cls_cursor_init(&cursor, &table->cls, NULL);
CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
flow_stats_ds(rule, results);
}
+ ovs_rwlock_unlock(&table->cls.rwlock);
}
}
struct rule *victim;
struct rule *rule;
uint8_t table_id;
+ bool overlaps;
int error;
error = check_table_id(ofproto, fm->table_id);
}
/* Check for overlap, if requested. */
- if (fm->flags & OFPFF_CHECK_OVERLAP
- && classifier_rule_overlaps(&table->cls, &rule->cr)) {
+ ovs_rwlock_rdlock(&table->cls.rwlock);
+ overlaps = classifier_rule_overlaps(&table->cls, &rule->cr);
+ ovs_rwlock_unlock(&table->cls.rwlock);
+ if (fm->flags & OFPFF_CHECK_OVERLAP && overlaps) {
cls_rule_destroy(&rule->cr);
ofproto->ofproto_class->rule_dealloc(rule);
return OFPERR_OFPFMFC_OVERLAP;
rule->pending = NULL;
rule->flow_cookie = fm->new_cookie;
rule->created = rule->modified = rule->used = time_msec();
+
+ ovs_mutex_init(&rule->timeout_mutex, OVS_MUTEX_ADAPTIVE);
+ ovs_mutex_lock(&rule->timeout_mutex);
rule->idle_timeout = fm->idle_timeout;
rule->hard_timeout = fm->hard_timeout;
+ ovs_mutex_unlock(&rule->timeout_mutex);
+
rule->table_id = table - ofproto->tables;
rule->send_flow_removed = (fm->flags & OFPFF_SEND_FLOW_REM) != 0;
/* FIXME: Implement OF 1.3 flags OFPFF13_NO_PKT_COUNTS
} else {
struct ofoperation *op;
struct rule *evict;
+ size_t n_rules;
- if (classifier_count(&table->cls) > table->max_flows) {
+ ovs_rwlock_rdlock(&table->cls.rwlock);
+ n_rules = classifier_count(&table->cls);
+ ovs_rwlock_unlock(&table->cls.rwlock);
+ if (n_rules > table->max_flows) {
bool was_evictable;
was_evictable = rule->evictable;
fr.table_id = rule->table_id;
calc_duration(rule->created, time_msec(),
&fr.duration_sec, &fr.duration_nsec);
+ ovs_mutex_lock(&rule->timeout_mutex);
fr.idle_timeout = rule->idle_timeout;
fr.hard_timeout = rule->hard_timeout;
+ ovs_mutex_unlock(&rule->timeout_mutex);
rule->ofproto->ofproto_class->rule_get_stats(rule, &fr.packet_count,
&fr.byte_count);
fu.event = (flags & (NXFMF_INITIAL | NXFMF_ADD)
? NXFME_ADDED : NXFME_MODIFIED);
fu.reason = 0;
+ ovs_mutex_lock(&rule->timeout_mutex);
fu.idle_timeout = rule->idle_timeout;
fu.hard_timeout = rule->hard_timeout;
+ ovs_mutex_unlock(&rule->timeout_mutex);
fu.table_id = rule->table_id;
fu.cookie = rule->flow_cookie;
minimatch_expand(&rule->cr.match, &match);
struct cls_cursor cursor;
struct rule *rule;
+ ovs_rwlock_rdlock(&table->cls.rwlock);
cls_cursor_init(&cursor, &table->cls, &target);
CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
ovs_assert(!rule->pending); /* XXX */
ofproto_collect_ofmonitor_refresh_rule(m, rule, seqno, rules);
}
+ ovs_rwlock_unlock(&table->cls.rwlock);
}
HMAP_FOR_EACH (op, hmap_node, &ofproto->deletions) {
group = ofopgroup_create_unattached(ofproto);
OFPROTO_FOR_EACH_TABLE (table, ofproto) {
- while (classifier_count(&table->cls) > table->max_flows
- && table->eviction_fields) {
+ while (table->eviction_fields) {
struct rule *rule;
+ size_t n_rules;
+
+ ovs_rwlock_rdlock(&table->cls.rwlock);
+ n_rules = classifier_count(&table->cls);
+ ovs_rwlock_unlock(&table->cls.rwlock);
+
+ if (n_rules <= table->max_flows) {
+ break;
+ }
rule = choose_rule_to_evict(table);
if (!rule || rule->pending) {
uint32_t expiration_offset;
/* Calculate time of expiration. */
+ ovs_mutex_lock(&rule->timeout_mutex);
hard_expiration = (rule->hard_timeout
? rule->modified + rule->hard_timeout * 1000
: LLONG_MAX);
? rule->used + rule->idle_timeout * 1000
: LLONG_MAX);
expiration = MIN(hard_expiration, idle_expiration);
+ ovs_mutex_unlock(&rule->timeout_mutex);
if (expiration == LLONG_MAX) {
return 0;
}
{
struct ofproto *ofproto = rule->ofproto;
struct oftable *table = &ofproto->tables[rule->table_id];
+ bool has_timeout;
- if (table->eviction_fields
- && (rule->hard_timeout || rule->idle_timeout)) {
+ ovs_mutex_lock(&rule->timeout_mutex);
+ has_timeout = rule->hard_timeout || rule->idle_timeout;
+ ovs_mutex_unlock(&rule->timeout_mutex);
+
+ if (table->eviction_fields && has_timeout) {
struct eviction_group *evg;
evg = eviction_group_find(table, eviction_group_hash_rule(rule));
static void
oftable_destroy(struct oftable *table)
{
+ ovs_rwlock_rdlock(&table->cls.rwlock);
ovs_assert(classifier_is_empty(&table->cls));
+ ovs_rwlock_unlock(&table->cls.rwlock);
oftable_disable_eviction(table);
classifier_destroy(&table->cls);
free(table->name);
hmap_init(&table->eviction_groups_by_id);
heap_init(&table->eviction_groups_by_size);
+ ovs_rwlock_rdlock(&table->cls.rwlock);
cls_cursor_init(&cursor, &table->cls, NULL);
CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
eviction_group_add_rule(rule);
}
+ ovs_rwlock_unlock(&table->cls.rwlock);
}
/* Removes 'rule' from the oftable that contains it. */
static void
-oftable_remove_rule(struct rule *rule)
+oftable_remove_rule__(struct ofproto *ofproto, struct classifier *cls,
+ struct rule *rule) OVS_REQ_WRLOCK(cls->rwlock)
{
- struct ofproto *ofproto = rule->ofproto;
- struct oftable *table = &ofproto->tables[rule->table_id];
-
- classifier_remove(&table->cls, &rule->cr);
+ classifier_remove(cls, &rule->cr);
if (rule->meter_id) {
list_remove(&rule->meter_list_node);
}
cookies_remove(ofproto, rule);
eviction_group_remove_rule(rule);
+ ovs_mutex_lock(&ofproto->expirable_mutex);
if (!list_is_empty(&rule->expirable)) {
list_remove(&rule->expirable);
}
+ ovs_mutex_unlock(&ofproto->expirable_mutex);
if (!list_is_empty(&rule->meter_list_node)) {
list_remove(&rule->meter_list_node);
}
}
+static void
+oftable_remove_rule(struct rule *rule)
+{
+ struct ofproto *ofproto = rule->ofproto;
+ struct oftable *table = &ofproto->tables[rule->table_id];
+
+ ovs_rwlock_wrlock(&table->cls.rwlock);
+ oftable_remove_rule__(ofproto, &table->cls, rule);
+ ovs_rwlock_unlock(&table->cls.rwlock);
+}
+
/* Inserts 'rule' into its oftable. Removes any existing rule from 'rule''s
* oftable that has an identical cls_rule. Returns the rule that was removed,
* if any, and otherwise NULL. */
struct ofproto *ofproto = rule->ofproto;
struct oftable *table = &ofproto->tables[rule->table_id];
struct rule *victim;
- bool may_expire = rule->hard_timeout || rule->idle_timeout;
+ bool may_expire;
+
+ ovs_mutex_lock(&rule->timeout_mutex);
+ may_expire = rule->hard_timeout || rule->idle_timeout;
+ ovs_mutex_unlock(&rule->timeout_mutex);
if (may_expire) {
+ ovs_mutex_lock(&ofproto->expirable_mutex);
list_insert(&ofproto->expirable, &rule->expirable);
+ ovs_mutex_unlock(&ofproto->expirable_mutex);
}
cookies_insert(ofproto, rule);
if (rule->meter_id) {
struct meter *meter = ofproto->meters[rule->meter_id];
list_insert(&meter->rules, &rule->meter_list_node);
}
+ ovs_rwlock_wrlock(&table->cls.rwlock);
victim = rule_from_cls_rule(classifier_replace(&table->cls, &rule->cr));
+ ovs_rwlock_unlock(&table->cls.rwlock);
if (victim) {
if (victim->meter_id) {
list_remove(&victim->meter_list_node);
}
cookies_remove(ofproto, victim);
+ ovs_mutex_lock(&ofproto->expirable_mutex);
if (!list_is_empty(&victim->expirable)) {
list_remove(&victim->expirable);
}
+ ovs_mutex_unlock(&ofproto->expirable_mutex);
eviction_group_remove_rule(victim);
}
eviction_group_add_rule(rule);
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
static struct vlog_rate_limit dbg_rl = VLOG_RATE_LIMIT_INIT(60, 60);
-static struct tnl_port *tnl_find(struct tnl_match *) OVS_REQ_RDLOCK(&rwlock);
+static struct tnl_port *tnl_find(struct tnl_match *) OVS_REQ_RDLOCK(rwlock);
static struct tnl_port *tnl_find_exact(struct tnl_match *)
- OVS_REQ_RDLOCK(&rwlock);
+ OVS_REQ_RDLOCK(rwlock);
static struct tnl_port *tnl_find_ofport(const struct ofport_dpif *)
- OVS_REQ_RDLOCK(&rwlock);
+ OVS_REQ_RDLOCK(rwlock);
static uint32_t tnl_hash(struct tnl_match *);
static void tnl_match_fmt(const struct tnl_match *, struct ds *);
-static char *tnl_port_fmt(const struct tnl_port *) OVS_REQ_RDLOCK(&rwlock);
+static char *tnl_port_fmt(const struct tnl_port *) OVS_REQ_RDLOCK(rwlock);
static void tnl_port_mod_log(const struct tnl_port *, const char *action)
- OVS_REQ_RDLOCK(&rwlock);
+ OVS_REQ_RDLOCK(rwlock);
static const char *tnl_port_get_name(const struct tnl_port *)
- OVS_REQ_RDLOCK(&rwlock);
-static void tnl_port_del__(const struct ofport_dpif *) OVS_REQ_WRLOCK(&rwlock);
+ OVS_REQ_RDLOCK(rwlock);
+static void tnl_port_del__(const struct ofport_dpif *) OVS_REQ_WRLOCK(rwlock);
static bool
tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
odp_port_t odp_port, bool warn)
- OVS_REQ_WRLOCK(&rwlock)
+ OVS_REQ_WRLOCK(rwlock)
{
const struct netdev_tunnel_config *cfg;
struct tnl_port *existing_port;
static void
compare_classifiers(struct classifier *cls, struct tcls *tcls)
+ OVS_REQ_RDLOCK(cls->rwlock)
{
static const int confidence = 500;
unsigned int i;
struct test_rule *rule, *next_rule;
struct cls_cursor cursor;
+ ovs_rwlock_wrlock(&cls->rwlock);
cls_cursor_init(&cursor, cls, NULL);
CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cls_rule, &cursor) {
classifier_remove(cls, &rule->cls_rule);
free_rule(rule);
}
+ ovs_rwlock_unlock(&cls->rwlock);
classifier_destroy(cls);
}
static void
-check_tables(const struct classifier *cls,
- int n_tables, int n_rules, int n_dups)
+check_tables(const struct classifier *cls, int n_tables, int n_rules,
+ int n_dups) OVS_REQ_RDLOCK(cls->rwlock)
{
const struct cls_table *table;
struct test_rule *test_rule;
struct tcls tcls;
classifier_init(&cls);
+ ovs_rwlock_rdlock(&cls.rwlock);
tcls_init(&tcls);
assert(classifier_is_empty(&cls));
assert(tcls_is_empty(&tcls));
compare_classifiers(&cls, &tcls);
+ ovs_rwlock_unlock(&cls.rwlock);
classifier_destroy(&cls);
tcls_destroy(&tcls);
}
hash_bytes(&wc_fields, sizeof wc_fields, 0), 0);
classifier_init(&cls);
+ ovs_rwlock_wrlock(&cls.rwlock);
tcls_init(&tcls);
tcls_rule = tcls_insert(&tcls, rule);
compare_classifiers(&cls, &tcls);
free_rule(rule);
+ ovs_rwlock_unlock(&cls.rwlock);
classifier_destroy(&cls);
tcls_destroy(&tcls);
}
rule2->aux += 5;
classifier_init(&cls);
+ ovs_rwlock_wrlock(&cls.rwlock);
tcls_init(&tcls);
tcls_insert(&tcls, rule1);
classifier_insert(&cls, &rule1->cls_rule);
check_tables(&cls, 1, 1, 0);
compare_classifiers(&cls, &tcls);
tcls_destroy(&tcls);
+ ovs_rwlock_unlock(&cls.rwlock);
destroy_classifier(&cls);
}
}
}
classifier_init(&cls);
+ ovs_rwlock_wrlock(&cls.rwlock);
tcls_init(&tcls);
for (i = 0; i < ARRAY_SIZE(ops); i++) {
compare_classifiers(&cls, &tcls);
}
+ ovs_rwlock_unlock(&cls.rwlock);
classifier_destroy(&cls);
tcls_destroy(&tcls);
} while ((1 << count_ones(value_mask)) < N_RULES);
classifier_init(&cls);
+ ovs_rwlock_wrlock(&cls.rwlock);
tcls_init(&tcls);
for (i = 0; i < N_RULES; i++) {
compare_classifiers(&cls, &tcls);
}
+ ovs_rwlock_unlock(&cls.rwlock);
classifier_destroy(&cls);
tcls_destroy(&tcls);
}
shuffle(priorities, ARRAY_SIZE(priorities));
classifier_init(&cls);
+ ovs_rwlock_wrlock(&cls.rwlock);
tcls_init(&tcls);
for (i = 0; i < MAX_RULES; i++) {
free_rule(target);
}
+ ovs_rwlock_unlock(&cls.rwlock);
destroy_classifier(&cls);
tcls_destroy(&tcls);
}
OPENVSWITCH_SYSCONFIG_SWITCH = '/etc/sysconfig/openvswitch' # RHEL
OPENVSWITCH_DEFAULT_CONTROLLER = '/etc/default/openvswitch-controller'
OPENVSWITCH_CONF_DB = '@DBDIR@/conf.db'
+OPENVSWITCH_COMPACT_DB = '@DBDIR@/bugtool-compact-conf.db'
OPENVSWITCH_VSWITCHD_PID = '@RUNDIR@/ovs-vswitchd.pid'
VAR_LOG_DIR = '/var/log/'
VAR_LOG_CORE_DIR = '/var/log/core'
cap(CAP_MULTIPATH, PII_MAYBE, max_size=20*KB,
max_time=10)
cap(CAP_NETWORK_CONFIG, PII_IF_CUSTOMIZED,
- min_size=0, max_size=40*KB)
+ min_size=0, max_size=5*MB)
cap(CAP_NETWORK_INFO, PII_YES, max_size=50*MB,
max_time=30)
cap(CAP_NETWORK_STATUS, PII_YES, max_size=-1,
tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, ROUTE_RE)
file_output(CAP_NETWORK_CONFIG, [SYSCONFIG_NETWORK, RESOLV_CONF, NSSWITCH_CONF, HOSTS])
file_output(CAP_NETWORK_CONFIG, [NTP_CONF, IPTABLES_CONFIG, HOSTS_ALLOW, HOSTS_DENY])
- file_output(CAP_NETWORK_CONFIG, [OPENVSWITCH_CONF_DB])
+ file_output(CAP_NETWORK_CONFIG, [OPENVSWITCH_DEFAULT_SWITCH,
+ OPENVSWITCH_SYSCONFIG_SWITCH, OPENVSWITCH_DEFAULT_CONTROLLER])
cmd_output(CAP_NETWORK_INFO, [IFCONFIG, '-a'])
cmd_output(CAP_NETWORK_INFO, [ROUTE, '-n'])
tree_output(CAP_NETWORK_INFO, PROC_NET_VLAN_DIR)
cmd_output(CAP_NETWORK_INFO, [TC, '-s', 'qdisc'])
file_output(CAP_NETWORK_INFO, [PROC_NET_SOFTNET_STAT])
+
+ collect_ovsdb()
if os.path.exists(OPENVSWITCH_VSWITCHD_PID):
cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'show', '-s'])
for d in dp_list():
for c in caps.keys():
print >>sys.stderr, " %s (%d, %d)" % (c, caps[c][MAX_SIZE],
cap_sizes[c])
+
+ cleanup_ovsdb()
return 0
def dump_scsi_hosts(cap):
return output.getvalue().splitlines()
return []
+def collect_ovsdb():
+ if not os.path.isfile(OPENVSWITCH_CONF_DB):
+ return
+
+ max_size = 10*MB
+
+ try:
+ if os.path.getsize(OPENVSWITCH_CONF_DB) > max_size:
+ if os.path.isfile(OPENVSWITCH_COMPACT_DB):
+ os.unlink(OPENVSWITCH_COMPACT_DB)
+
+ output = StringIO.StringIO()
+ max_time = 5
+ procs = [ProcOutput(['ovsdb-tool', 'compact',
+ OPENVSWITCH_CONF_DB, OPENVSWITCH_COMPACT_DB],
+ max_time, output)]
+ run_procs([procs])
+ file_output(CAP_NETWORK_STATUS, [OPENVSWITCH_COMPACT_DB])
+ else:
+ file_output(CAP_NETWORK_STATUS, [OPENVSWITCH_CONF_DB])
+ except OSError, e:
+ return
+
+def cleanup_ovsdb():
+ try:
+ if os.path.isfile(OPENVSWITCH_COMPACT_DB):
+ os.unlink(OPENVSWITCH_COMPACT_DB)
+ except:
+ return
+
def fd_usage(cap):
output = ''
fd_dict = {}
struct cls_cursor cursor;
struct fte *fte, *next;
+ ovs_rwlock_wrlock(&cls->rwlock);
cls_cursor_init(&cursor, cls, NULL);
CLS_CURSOR_FOR_EACH_SAFE (fte, next, rule, &cursor) {
classifier_remove(cls, &fte->rule);
fte_free(fte);
}
+ ovs_rwlock_unlock(&cls->rwlock);
classifier_destroy(cls);
}
cls_rule_init(&fte->rule, match, priority);
fte->versions[index] = version;
+ ovs_rwlock_wrlock(&cls->rwlock);
old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
+ ovs_rwlock_unlock(&cls->rwlock);
if (old) {
fte_version_free(old->versions[index]);
fte->versions[!index] = old->versions[!index];
list_init(&requests);
/* Delete flows that exist on the switch but not in the file. */
+ ovs_rwlock_rdlock(&cls.rwlock);
cls_cursor_init(&cursor, &cls, NULL);
CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
struct fte_version *file_ver = fte->versions[FILE_IDX];
fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests);
}
}
+ ovs_rwlock_unlock(&cls.rwlock);
transact_multiple_noreply(vconn, &requests);
vconn_close(vconn);
ds_init(&a_s);
ds_init(&b_s);
+ ovs_rwlock_rdlock(&cls.rwlock);
cls_cursor_init(&cursor, &cls, NULL);
CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
struct fte_version *a = fte->versions[0];
}
}
}
+ ovs_rwlock_unlock(&cls.rwlock);
ds_destroy(&a_s);
ds_destroy(&b_s);
}
static void
-discard_stats(void) OVS_REQUIRES(&mutex)
+discard_stats(void) OVS_REQUIRES(mutex)
{
if (system_stats) {
smap_destroy(system_stats);