X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=0c5ba8b85c81fa969601b8e047648a9483dcc64a;hb=a23aab1fc2f66b63ba9b7e4b9c9a8f6d58c367d0;hp=e6036bfc5f725746e99f549d86ff754a6b60dec1;hpb=d295e8e97acae13552a5b220d3fbcff8201064a2;p=openvswitch diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index e6036bfc..0c5ba8b8 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -48,6 +48,8 @@ #include "coverage.h" #include "dynamic-string.h" #include "fatal-signal.h" +#include "hash.h" +#include "hmap.h" #include "netdev-provider.h" #include "netdev-vport.h" #include "netlink.h" @@ -55,14 +57,13 @@ #include "openflow/openflow.h" #include "packets.h" #include "poll-loop.h" -#include "port-array.h" #include "rtnetlink.h" #include "socket-util.h" #include "shash.h" #include "svec.h" #include "vlog.h" -VLOG_DEFINE_THIS_MODULE(netdev_linux) +VLOG_DEFINE_THIS_MODULE(netdev_linux); /* These were introduced in Linux 2.6.14, so they might be missing if we have * old headers. */ @@ -102,19 +103,24 @@ struct tap_state { /* Traffic control. */ /* An instance of a traffic control class. Always associated with a particular - * network device. */ + * network device. + * + * Each TC implementation subclasses this with whatever additional data it + * needs. */ struct tc { const struct tc_ops *ops; + struct hmap queues; /* Contains "struct tc_queue"s. + * Read by generic TC layer. + * Written only by TC implementation. */ +}; - /* Maps from queue ID to tc-specific data. - * - * The generic netdev TC layer uses this to the following extent: if an - * entry is nonnull, then the queue whose ID is the index is assumed to - * exist; if an entry is null, then that queue is assumed not to exist. - * Implementations must adhere to this scheme, although they may store - * whatever they like as data. - */ - struct port_array queues; +/* One traffic control queue. + * + * Each TC implementation subclasses this with whatever additional data it + * needs. */ +struct tc_queue { + struct hmap_node hmap_node; /* In struct tc's "queues" hmap. */ + unsigned int queue_id; /* OpenFlow queue ID. */ }; /* A particular kind of traffic control. Each implementation generally maps to @@ -204,8 +210,8 @@ struct tc_ops { */ int (*qdisc_set)(struct netdev *, const struct shash *details); - /* Retrieves details of 'queue_id' on 'netdev->tc' into 'details'. The - * caller ensures that 'queues' has a nonnull value for index 'queue_id. + /* Retrieves details of 'queue' on 'netdev->tc' into 'details'. 'queue' is + * one of the 'struct tc_queue's within 'netdev->tc->queues'. * * The contents of 'details' should be documented as valid for 'ovs_name' * in the "other_config" column in the "Queue" table in @@ -217,7 +223,7 @@ struct tc_ops { * * This function may be null if 'tc' does not have queues ('n_queues' is * 0). */ - int (*class_get)(const struct netdev *netdev, unsigned int queue_id, + int (*class_get)(const struct netdev *netdev, const struct tc_queue *queue, struct shash *details); /* Configures or reconfigures 'queue_id' on 'netdev->tc' according to @@ -234,21 +240,22 @@ struct tc_ops { int (*class_set)(struct netdev *, unsigned int queue_id, const struct shash *details); - /* Deletes 'queue_id' from 'netdev->tc'. The caller ensures that 'queues' - * has a nonnull value for index 'queue_id. + /* Deletes 'queue' from 'netdev->tc'. 'queue' is one of the 'struct + * tc_queue's within 'netdev->tc->queues'. * * This function may be null if 'tc' does not have queues or its queues * cannot be deleted. */ - int (*class_delete)(struct netdev *, unsigned int queue_id); + int (*class_delete)(struct netdev *, struct tc_queue *queue); - /* Obtains stats for 'queue' from 'netdev->tc'. The caller ensures that - * 'queues' has a nonnull value for index 'queue_id. + /* Obtains stats for 'queue' from 'netdev->tc'. 'queue' is one of the + * 'struct tc_queue's within 'netdev->tc->queues'. * * On success, initializes '*stats'. * * This function may be null if 'tc' does not have queues or if it cannot * report queue statistics. */ - int (*class_get_stats)(const struct netdev *netdev, unsigned int queue_id, + int (*class_get_stats)(const struct netdev *netdev, + const struct tc_queue *queue, struct netdev_queue_stats *stats); /* Extracts queue stats from 'nlmsg', which is a response to a @@ -265,13 +272,13 @@ static void tc_init(struct tc *tc, const struct tc_ops *ops) { tc->ops = ops; - port_array_init(&tc->queues); + hmap_init(&tc->queues); } static void tc_destroy(struct tc *tc) { - port_array_destroy(&tc->queues); + hmap_destroy(&tc->queues); } static const struct tc_ops tc_ops_htb; @@ -480,8 +487,9 @@ netdev_linux_cache_cb(const struct rtnetlink_change *change, /* Creates the netdev device of 'type' with 'name'. */ static int -netdev_linux_create_system(const char *name, const char *type OVS_UNUSED, - const struct shash *args, struct netdev_dev **netdev_devp) +netdev_linux_create_system(const struct netdev_class *class OVS_UNUSED, + const char *name, const struct shash *args, + struct netdev_dev **netdev_devp) { struct netdev_dev_linux *netdev_dev; int error; @@ -513,8 +521,9 @@ netdev_linux_create_system(const char *name, const char *type OVS_UNUSED, * buffers, across all readers. Therefore once data is read it will * be unavailable to other reads for tap devices. */ static int -netdev_linux_create_tap(const char *name, const char *type OVS_UNUSED, - const struct shash *args, struct netdev_dev **netdev_devp) +netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED, + const char *name, const struct shash *args, + struct netdev_dev **netdev_devp) { struct netdev_dev_linux *netdev_dev; struct tap_state *state; @@ -1360,6 +1369,9 @@ netdev_linux_remove_policing(struct netdev *netdev) int error; tcmsg = tc_make_request(netdev, RTM_DELQDISC, 0, &request); + if (!tcmsg) { + return ENODEV; + } tcmsg->tcm_handle = tc_make_handle(0xffff, 0); tcmsg->tcm_parent = TC_H_INGRESS; nl_msg_put_string(&request, TCA_KIND, "ingress"); @@ -1468,6 +1480,28 @@ tc_lookup_linux_name(const char *name) return NULL; } +static struct tc_queue * +tc_find_queue__(const struct netdev *netdev, unsigned int queue_id, + size_t hash) +{ + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev)); + struct tc_queue *queue; + + HMAP_FOR_EACH_IN_BUCKET (queue, hmap_node, hash, &netdev_dev->tc->queues) { + if (queue->queue_id == queue_id) { + return queue; + } + } + return NULL; +} + +static struct tc_queue * +tc_find_queue(const struct netdev *netdev, unsigned int queue_id) +{ + return tc_find_queue__(netdev, queue_id, hash_int(queue_id, 0)); +} + static int netdev_linux_get_qos_capabilities(const struct netdev *netdev OVS_UNUSED, const char *type, @@ -1548,12 +1582,12 @@ netdev_linux_get_queue(const struct netdev *netdev, error = tc_query_qdisc(netdev); if (error) { return error; - } else if (queue_id > UINT16_MAX - || !port_array_get(&netdev_dev->tc->queues, queue_id)) { - return ENOENT; + } else { + struct tc_queue *queue = tc_find_queue(netdev, queue_id); + return (queue + ? netdev_dev->tc->ops->class_get(netdev, queue, details) + : ENOENT); } - - return netdev_dev->tc->ops->class_get(netdev, queue_id, details); } static int @@ -1587,12 +1621,12 @@ netdev_linux_delete_queue(struct netdev *netdev, unsigned int queue_id) return error; } else if (!netdev_dev->tc->ops->class_delete) { return EINVAL; - } else if (queue_id > UINT16_MAX - || !port_array_get(&netdev_dev->tc->queues, queue_id)) { - return ENOENT; + } else { + struct tc_queue *queue = tc_find_queue(netdev, queue_id); + return (queue + ? netdev_dev->tc->ops->class_delete(netdev, queue) + : ENOENT); } - - return netdev_dev->tc->ops->class_delete(netdev, queue_id); } static int @@ -1607,26 +1641,30 @@ netdev_linux_get_queue_stats(const struct netdev *netdev, error = tc_query_qdisc(netdev); if (error) { return error; - } else if (queue_id > UINT16_MAX - || !port_array_get(&netdev_dev->tc->queues, queue_id)) { - return ENOENT; } else if (!netdev_dev->tc->ops->class_get_stats) { return EOPNOTSUPP; + } else { + const struct tc_queue *queue = tc_find_queue(netdev, queue_id); + return (queue + ? netdev_dev->tc->ops->class_get_stats(netdev, queue, stats) + : ENOENT); } - - return netdev_dev->tc->ops->class_get_stats(netdev, queue_id, stats); } -static void +static bool start_queue_dump(const struct netdev *netdev, struct nl_dump *dump) { struct ofpbuf request; struct tcmsg *tcmsg; tcmsg = tc_make_request(netdev, RTM_GETTCLASS, 0, &request); + if (!tcmsg) { + return false; + } tcmsg->tcm_parent = 0; nl_dump_start(dump, rtnl_sock, &request); ofpbuf_uninit(&request); + return true; } static int @@ -1635,10 +1673,9 @@ netdev_linux_dump_queues(const struct netdev *netdev, { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); - unsigned int queue_id; + struct tc_queue *queue; struct shash details; int last_error; - void *queue; int error; error = tc_query_qdisc(netdev); @@ -1650,12 +1687,12 @@ netdev_linux_dump_queues(const struct netdev *netdev, last_error = 0; shash_init(&details); - PORT_ARRAY_FOR_EACH (queue, &netdev_dev->tc->queues, queue_id) { + HMAP_FOR_EACH (queue, hmap_node, &netdev_dev->tc->queues) { shash_clear(&details); - error = netdev_dev->tc->ops->class_get(netdev, queue_id, &details); + error = netdev_dev->tc->ops->class_get(netdev, queue, &details); if (!error) { - (*cb)(queue_id, &details, aux); + (*cb)(queue->queue_id, &details, aux); } else { last_error = error; } @@ -1684,7 +1721,9 @@ netdev_linux_dump_queue_stats(const struct netdev *netdev, } last_error = 0; - start_queue_dump(netdev, &dump); + if (!start_queue_dump(netdev, &dump)) { + return ENODEV; + } while (nl_dump_next(&dump, &msg)) { error = netdev_dev->tc->ops->class_dump_stats(netdev, &msg, cb, aux); if (error) { @@ -1779,12 +1818,12 @@ netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6) if (file != NULL) { const char *name = netdev_get_name(netdev_); while (fgets(line, sizeof line, file)) { - struct in6_addr in6; + struct in6_addr in6_tmp; char ifname[16 + 1]; - if (parse_if_inet6_line(line, &in6, ifname) + if (parse_if_inet6_line(line, &in6_tmp, ifname) && !strcmp(name, ifname)) { - netdev_dev->in6 = in6; + netdev_dev->in6 = in6_tmp; break; } } @@ -1982,7 +2021,7 @@ static void poll_notify(struct list *list) { struct netdev_linux_notifier *notifier; - LIST_FOR_EACH (notifier, struct netdev_linux_notifier, node, list) { + LIST_FOR_EACH (notifier, node, list) { struct netdev_notifier *n = ¬ifier->notifier; n->cb(n); } @@ -2191,6 +2230,7 @@ struct htb { }; struct htb_class { + struct tc_queue tc_queue; unsigned int min_rate; /* In bytes/s. */ unsigned int max_rate; /* In bytes/s. */ unsigned int burst; /* In bytes. */ @@ -2237,6 +2277,9 @@ htb_setup_qdisc__(struct netdev *netdev) tcmsg = tc_make_request(netdev, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE, &request); + if (!tcmsg) { + return ENODEV; + } tcmsg->tcm_handle = tc_make_handle(1, 0); tcmsg->tcm_parent = TC_H_ROOT; @@ -2245,7 +2288,7 @@ htb_setup_qdisc__(struct netdev *netdev) memset(&opt, 0, sizeof opt); opt.rate2quantum = 10; opt.version = 3; - opt.defcls = 0; + opt.defcls = 1; opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS); nl_msg_put_unspec(&request, TCA_HTB_INIT, &opt, sizeof opt); @@ -2277,6 +2320,9 @@ htb_setup_class__(struct netdev *netdev, unsigned int handle, opt.prio = class->priority; tcmsg = tc_make_request(netdev, RTM_NEWTCLASS, NLM_F_CREATE, &request); + if (!tcmsg) { + return ENODEV; + } tcmsg->tcm_handle = handle; tcmsg->tcm_parent = parent; @@ -2384,13 +2430,13 @@ htb_parse_class_details__(struct netdev *netdev, const char *priority_s = shash_find_data(details, "priority"); int mtu; - /* min-rate */ + /* min-rate. Don't allow a min-rate below 1500 bytes/s. */ if (!min_rate_s) { /* min-rate is required. */ return EINVAL; } hc->min_rate = strtoull(min_rate_s, NULL, 10) / 8; - hc->min_rate = MAX(hc->min_rate, 0); + hc->min_rate = MAX(hc->min_rate, 1500); hc->min_rate = MIN(hc->min_rate, htb->max_rate); /* max-rate */ @@ -2454,25 +2500,40 @@ htb_tc_install(struct netdev *netdev, const struct shash *details) return error; } +static struct htb_class * +htb_class_cast__(const struct tc_queue *queue) +{ + return CONTAINER_OF(queue, struct htb_class, tc_queue); +} + static void htb_update_queue__(struct netdev *netdev, unsigned int queue_id, const struct htb_class *hc) { struct htb *htb = htb_get__(netdev); + size_t hash = hash_int(queue_id, 0); + struct tc_queue *queue; struct htb_class *hcp; - hcp = port_array_get(&htb->tc.queues, queue_id); - if (!hcp) { + queue = tc_find_queue__(netdev, queue_id, hash); + if (queue) { + hcp = htb_class_cast__(queue); + } else { hcp = xmalloc(sizeof *hcp); - port_array_set(&htb->tc.queues, queue_id, hcp); + queue = &hcp->tc_queue; + queue->queue_id = queue_id; + hmap_insert(&htb->tc.queues, &queue->hmap_node, hash); } - *hcp = *hc; + + hcp->min_rate = hc->min_rate; + hcp->max_rate = hc->max_rate; + hcp->burst = hc->burst; + hcp->priority = hc->priority; } static int htb_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED) { - struct shash details = SHASH_INITIALIZER(&details); struct ofpbuf msg; struct nl_dump dump; struct htb_class hc; @@ -2484,8 +2545,9 @@ htb_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED) htb = htb_install__(netdev, hc.max_rate); /* Get queues. */ - start_queue_dump(netdev, &dump); - shash_init(&details); + if (!start_queue_dump(netdev, &dump)) { + return ENODEV; + } while (nl_dump_next(&dump, &msg)) { unsigned int queue_id; @@ -2502,10 +2564,10 @@ static void htb_tc_destroy(struct tc *tc) { struct htb *htb = CONTAINER_OF(tc, struct htb, tc); - unsigned int queue_id; - struct htb_class *hc; + struct htb_class *hc, *next; - PORT_ARRAY_FOR_EACH (hc, &htb->tc.queues, queue_id) { + HMAP_FOR_EACH_SAFE (hc, next, tc_queue.hmap_node, &htb->tc.queues) { + hmap_remove(&htb->tc.queues, &hc->tc_queue.hmap_node); free(hc); } tc_destroy(tc); @@ -2536,14 +2598,10 @@ htb_qdisc_set(struct netdev *netdev, const struct shash *details) } static int -htb_class_get(const struct netdev *netdev, unsigned int queue_id, - struct shash *details) +htb_class_get(const struct netdev *netdev OVS_UNUSED, + const struct tc_queue *queue, struct shash *details) { - const struct htb *htb = htb_get__(netdev); - const struct htb_class *hc; - - hc = port_array_get(&htb->tc.queues, queue_id); - assert(hc != NULL); + const struct htb_class *hc = htb_class_cast__(queue); shash_add(details, "min-rate", xasprintf("%llu", 8ULL * hc->min_rate)); if (hc->min_rate != hc->max_rate) { @@ -2579,28 +2637,25 @@ htb_class_set(struct netdev *netdev, unsigned int queue_id, } static int -htb_class_delete(struct netdev *netdev, unsigned int queue_id) +htb_class_delete(struct netdev *netdev, struct tc_queue *queue) { + struct htb_class *hc = htb_class_cast__(queue); struct htb *htb = htb_get__(netdev); - struct htb_class *hc; int error; - hc = port_array_get(&htb->tc.queues, queue_id); - assert(hc != NULL); - - error = tc_delete_class(netdev, tc_make_handle(1, queue_id + 1)); + error = tc_delete_class(netdev, tc_make_handle(1, queue->queue_id + 1)); if (!error) { + hmap_remove(&htb->tc.queues, &hc->tc_queue.hmap_node); free(hc); - port_array_delete(&htb->tc.queues, queue_id); } return error; } static int -htb_class_get_stats(const struct netdev *netdev, unsigned int queue_id, +htb_class_get_stats(const struct netdev *netdev, const struct tc_queue *queue, struct netdev_queue_stats *stats) { - return htb_query_class__(netdev, tc_make_handle(1, queue_id + 1), + return htb_query_class__(netdev, tc_make_handle(1, queue->queue_id + 1), tc_make_handle(1, 0xfffe), NULL, stats); } @@ -2621,7 +2676,7 @@ htb_class_dump_stats(const struct netdev *netdev OVS_UNUSED, major = tc_get_major(handle); minor = tc_get_minor(handle); if (major == 1 && minor > 0 && minor <= HTB_N_QUEUES) { - (*cb)(tc_get_minor(handle), &stats, aux); + (*cb)(minor - 1, &stats, aux); } return 0; } @@ -2898,7 +2953,7 @@ tc_bytes_to_ticks(unsigned int rate, unsigned int size) if (!buffer_hz) { read_psched(); } - return ((unsigned long long int) ticks_per_s * size) / rate; + return rate ? ((unsigned long long int) ticks_per_s * size) / rate : 0; } /* Returns the number of bytes that need to be reserved for qdisc buffering at @@ -3044,6 +3099,9 @@ tc_query_class(const struct netdev *netdev, int error; tcmsg = tc_make_request(netdev, RTM_GETTCLASS, NLM_F_ECHO, &request); + if (!tcmsg) { + return ENODEV; + } tcmsg->tcm_handle = handle; tcmsg->tcm_parent = parent; @@ -3067,6 +3125,9 @@ tc_delete_class(const struct netdev *netdev, unsigned int handle) int error; tcmsg = tc_make_request(netdev, RTM_DELTCLASS, 0, &request); + if (!tcmsg) { + return ENODEV; + } tcmsg->tcm_handle = handle; tcmsg->tcm_parent = 0; @@ -3091,6 +3152,9 @@ tc_del_qdisc(struct netdev *netdev) int error; tcmsg = tc_make_request(netdev, RTM_DELQDISC, 0, &request); + if (!tcmsg) { + return ENODEV; + } tcmsg->tcm_handle = tc_make_handle(1, 0); tcmsg->tcm_parent = TC_H_ROOT; @@ -3142,6 +3206,9 @@ tc_query_qdisc(const struct netdev *netdev) * We could check for Linux 2.6.35+ and use a more straightforward method * there. */ tcmsg = tc_make_request(netdev, RTM_GETQDISC, NLM_F_ECHO, &request); + if (!tcmsg) { + return ENODEV; + } tcmsg->tcm_handle = tc_make_handle(1, 0); tcmsg->tcm_parent = 0; @@ -3244,9 +3311,7 @@ tc_put_rtab(struct ofpbuf *msg, uint16_t type, const struct tc_ratespec *rate) /* Calculates the proper value of 'buffer' or 'cbuffer' in HTB options given a * rate of 'Bps' bytes per second, the specified 'mtu', and a user-requested * burst size of 'burst_bytes'. (If no value was requested, a 'burst_bytes' of - * 0 is fine.) - * - * This */ + * 0 is fine.) */ static int tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes) {