diff --git a/include/sched.h b/include/sched.h index 02b0d5e51e94..e03f076febb1 100644 --- a/include/sched.h +++ b/include/sched.h @@ -1,53 +1,49 @@ /*- * Copyright (c) 2021 The FreeBSD Foundation * * This software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __SCHED_H__ #define __SCHED_H__ #include #include #include #if __BSD_VISIBLE -#ifdef _WITH_CPU_SET_T #include struct _cpuset; typedef struct _cpuset cpu_set_t; -#endif /* _WITH_CPU_SET_T */ #endif /* __BSD_VISIBLE */ __BEGIN_DECLS #if __BSD_VISIBLE -#ifdef _WITH_CPU_SET_T int sched_getaffinity(pid_t pid, size_t cpusetsz, cpuset_t *cpuset); int sched_setaffinity(pid_t pid, size_t cpusetsz, const cpuset_t *cpuset); int sched_getcpu(void); -#endif /* _WITH_CPU_SET_T */ #endif /* __BSD_VISIBLE */ __END_DECLS #endif /* __SCHED_H__ */ diff --git a/lib/libmemstat/memstat_uma.c b/lib/libmemstat/memstat_uma.c index b416cbc63297..980a674ab76e 100644 --- a/lib/libmemstat/memstat_uma.c +++ b/lib/libmemstat/memstat_uma.c @@ -1,490 +1,492 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005-2006 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ +#define _WANT_FREEBSD_BITSET + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "memstat.h" #include "memstat_internal.h" static struct nlist namelist[] = { #define X_UMA_KEGS 0 { .n_name = "_uma_kegs" }, #define X_MP_MAXID 1 { .n_name = "_mp_maxid" }, #define X_ALL_CPUS 2 { .n_name = "_all_cpus" }, #define X_VM_NDOMAINS 3 { .n_name = "_vm_ndomains" }, { .n_name = "" }, }; /* * Extract uma(9) statistics from the running kernel, and store all memory * type information in the passed list. For each type, check the list for an * existing entry with the right name/allocator -- if present, update that * entry. Otherwise, add a new entry. On error, the entire list will be * cleared, as entries will be in an inconsistent state. * * To reduce the level of work for a list that starts empty, we keep around a * hint as to whether it was empty when we began, so we can avoid searching * the list for entries to update. Updates are O(n^2) due to searching for * each entry before adding it. */ int memstat_sysctl_uma(struct memory_type_list *list, int flags) { struct uma_stream_header *ushp; struct uma_type_header *uthp; struct uma_percpu_stat *upsp; struct memory_type *mtp; int count, hint_dontsearch, i, j, maxcpus, maxid; char *buffer, *p; size_t size; hint_dontsearch = LIST_EMPTY(&list->mtl_list); /* * Query the number of CPUs, number of malloc types so that we can * guess an initial buffer size. We loop until we succeed or really * fail. Note that the value of maxcpus we query using sysctl is not * the version we use when processing the real data -- that is read * from the header. */ retry: size = sizeof(maxid); if (sysctlbyname("kern.smp.maxid", &maxid, &size, NULL, 0) < 0) { if (errno == EACCES || errno == EPERM) list->mtl_error = MEMSTAT_ERROR_PERMISSION; else list->mtl_error = MEMSTAT_ERROR_DATAERROR; return (-1); } if (size != sizeof(maxid)) { list->mtl_error = MEMSTAT_ERROR_DATAERROR; return (-1); } size = sizeof(count); if (sysctlbyname("vm.zone_count", &count, &size, NULL, 0) < 0) { if (errno == EACCES || errno == EPERM) list->mtl_error = MEMSTAT_ERROR_PERMISSION; else list->mtl_error = MEMSTAT_ERROR_VERSION; return (-1); } if (size != sizeof(count)) { list->mtl_error = MEMSTAT_ERROR_DATAERROR; return (-1); } size = sizeof(*uthp) + count * (sizeof(*uthp) + sizeof(*upsp) * (maxid + 1)); buffer = malloc(size); if (buffer == NULL) { list->mtl_error = MEMSTAT_ERROR_NOMEMORY; return (-1); } if (sysctlbyname("vm.zone_stats", buffer, &size, NULL, 0) < 0) { /* * XXXRW: ENOMEM is an ambiguous return, we should bound the * number of loops, perhaps. */ if (errno == ENOMEM) { free(buffer); goto retry; } if (errno == EACCES || errno == EPERM) list->mtl_error = MEMSTAT_ERROR_PERMISSION; else list->mtl_error = MEMSTAT_ERROR_VERSION; free(buffer); return (-1); } if (size == 0) { free(buffer); return (0); } if (size < sizeof(*ushp)) { list->mtl_error = MEMSTAT_ERROR_VERSION; free(buffer); return (-1); } p = buffer; ushp = (struct uma_stream_header *)p; p += sizeof(*ushp); if (ushp->ush_version != UMA_STREAM_VERSION) { list->mtl_error = MEMSTAT_ERROR_VERSION; free(buffer); return (-1); } /* * For the remainder of this function, we are quite trusting about * the layout of structures and sizes, since we've determined we have * a matching version and acceptable CPU count. */ maxcpus = ushp->ush_maxcpus; count = ushp->ush_count; for (i = 0; i < count; i++) { uthp = (struct uma_type_header *)p; p += sizeof(*uthp); if (hint_dontsearch == 0) { mtp = memstat_mtl_find(list, ALLOCATOR_UMA, uthp->uth_name); } else mtp = NULL; if (mtp == NULL) mtp = _memstat_mt_allocate(list, ALLOCATOR_UMA, uthp->uth_name, maxid + 1); if (mtp == NULL) { _memstat_mtl_empty(list); free(buffer); list->mtl_error = MEMSTAT_ERROR_NOMEMORY; return (-1); } /* * Reset the statistics on a current node. */ _memstat_mt_reset_stats(mtp, maxid + 1); mtp->mt_numallocs = uthp->uth_allocs; mtp->mt_numfrees = uthp->uth_frees; mtp->mt_failures = uthp->uth_fails; mtp->mt_sleeps = uthp->uth_sleeps; mtp->mt_xdomain = uthp->uth_xdomain; for (j = 0; j < maxcpus; j++) { upsp = (struct uma_percpu_stat *)p; p += sizeof(*upsp); mtp->mt_percpu_cache[j].mtp_free = upsp->ups_cache_free; mtp->mt_free += upsp->ups_cache_free; mtp->mt_numallocs += upsp->ups_allocs; mtp->mt_numfrees += upsp->ups_frees; } /* * Values for uth_allocs and uth_frees frees are snap. * It may happen that kernel reports that number of frees * is greater than number of allocs. See counter(9) for * details. */ if (mtp->mt_numallocs < mtp->mt_numfrees) mtp->mt_numallocs = mtp->mt_numfrees; mtp->mt_size = uthp->uth_size; mtp->mt_rsize = uthp->uth_rsize; mtp->mt_memalloced = mtp->mt_numallocs * uthp->uth_size; mtp->mt_memfreed = mtp->mt_numfrees * uthp->uth_size; mtp->mt_bytes = mtp->mt_memalloced - mtp->mt_memfreed; mtp->mt_countlimit = uthp->uth_limit; mtp->mt_byteslimit = uthp->uth_limit * uthp->uth_size; mtp->mt_count = mtp->mt_numallocs - mtp->mt_numfrees; mtp->mt_zonefree = uthp->uth_zone_free; /* * UMA secondary zones share a keg with the primary zone. To * avoid double-reporting of free items, report keg free * items only in the primary zone. */ if (!(uthp->uth_zone_flags & UTH_ZONE_SECONDARY)) { mtp->mt_kegfree = uthp->uth_keg_free; mtp->mt_free += mtp->mt_kegfree; } mtp->mt_free += mtp->mt_zonefree; } free(buffer); return (0); } static int kread(kvm_t *kvm, void *kvm_pointer, void *address, size_t size, size_t offset) { ssize_t ret; ret = kvm_read(kvm, (unsigned long)kvm_pointer + offset, address, size); if (ret < 0) return (MEMSTAT_ERROR_KVM); if ((size_t)ret != size) return (MEMSTAT_ERROR_KVM_SHORTREAD); return (0); } static int kread_string(kvm_t *kvm, const void *kvm_pointer, char *buffer, int buflen) { ssize_t ret; int i; for (i = 0; i < buflen; i++) { ret = kvm_read(kvm, (unsigned long)kvm_pointer + i, &(buffer[i]), sizeof(char)); if (ret < 0) return (MEMSTAT_ERROR_KVM); if ((size_t)ret != sizeof(char)) return (MEMSTAT_ERROR_KVM_SHORTREAD); if (buffer[i] == '\0') return (0); } /* Truncate. */ buffer[i-1] = '\0'; return (0); } static int kread_symbol(kvm_t *kvm, int index, void *address, size_t size, size_t offset) { ssize_t ret; ret = kvm_read(kvm, namelist[index].n_value + offset, address, size); if (ret < 0) return (MEMSTAT_ERROR_KVM); if ((size_t)ret != size) return (MEMSTAT_ERROR_KVM_SHORTREAD); return (0); } /* * memstat_kvm_uma() is similar to memstat_sysctl_uma(), only it extracts * UMA(9) statistics from a kernel core/memory file. */ int memstat_kvm_uma(struct memory_type_list *list, void *kvm_handle) { LIST_HEAD(, uma_keg) uma_kegs; struct memory_type *mtp; struct uma_zone_domain uzd; struct uma_domain ukd; struct uma_bucket *ubp, ub; struct uma_cache *ucp, *ucp_array; struct uma_zone *uzp, uz; struct uma_keg *kzp, kz; uint64_t kegfree; int hint_dontsearch, i, mp_maxid, ndomains, ret; char name[MEMTYPE_MAXNAME]; cpuset_t all_cpus; long cpusetsize; kvm_t *kvm; kvm = (kvm_t *)kvm_handle; hint_dontsearch = LIST_EMPTY(&list->mtl_list); if (kvm_nlist(kvm, namelist) != 0) { list->mtl_error = MEMSTAT_ERROR_KVM; return (-1); } if (namelist[X_UMA_KEGS].n_type == 0 || namelist[X_UMA_KEGS].n_value == 0) { list->mtl_error = MEMSTAT_ERROR_KVM_NOSYMBOL; return (-1); } ret = kread_symbol(kvm, X_MP_MAXID, &mp_maxid, sizeof(mp_maxid), 0); if (ret != 0) { list->mtl_error = ret; return (-1); } ret = kread_symbol(kvm, X_VM_NDOMAINS, &ndomains, sizeof(ndomains), 0); if (ret != 0) { list->mtl_error = ret; return (-1); } ret = kread_symbol(kvm, X_UMA_KEGS, &uma_kegs, sizeof(uma_kegs), 0); if (ret != 0) { list->mtl_error = ret; return (-1); } cpusetsize = sysconf(_SC_CPUSET_SIZE); if (cpusetsize == -1 || (u_long)cpusetsize > sizeof(cpuset_t)) { list->mtl_error = MEMSTAT_ERROR_KVM_NOSYMBOL; return (-1); } CPU_ZERO(&all_cpus); ret = kread_symbol(kvm, X_ALL_CPUS, &all_cpus, cpusetsize, 0); if (ret != 0) { list->mtl_error = ret; return (-1); } ucp_array = malloc(sizeof(struct uma_cache) * (mp_maxid + 1)); if (ucp_array == NULL) { list->mtl_error = MEMSTAT_ERROR_NOMEMORY; return (-1); } for (kzp = LIST_FIRST(&uma_kegs); kzp != NULL; kzp = LIST_NEXT(&kz, uk_link)) { ret = kread(kvm, kzp, &kz, sizeof(kz), 0); if (ret != 0) { free(ucp_array); _memstat_mtl_empty(list); list->mtl_error = ret; return (-1); } for (uzp = LIST_FIRST(&kz.uk_zones); uzp != NULL; uzp = LIST_NEXT(&uz, uz_link)) { ret = kread(kvm, uzp, &uz, sizeof(uz), 0); if (ret != 0) { free(ucp_array); _memstat_mtl_empty(list); list->mtl_error = ret; return (-1); } ret = kread(kvm, uzp, ucp_array, sizeof(struct uma_cache) * (mp_maxid + 1), offsetof(struct uma_zone, uz_cpu[0])); if (ret != 0) { free(ucp_array); _memstat_mtl_empty(list); list->mtl_error = ret; return (-1); } ret = kread_string(kvm, uz.uz_name, name, MEMTYPE_MAXNAME); if (ret != 0) { free(ucp_array); _memstat_mtl_empty(list); list->mtl_error = ret; return (-1); } if (hint_dontsearch == 0) { mtp = memstat_mtl_find(list, ALLOCATOR_UMA, name); } else mtp = NULL; if (mtp == NULL) mtp = _memstat_mt_allocate(list, ALLOCATOR_UMA, name, mp_maxid + 1); if (mtp == NULL) { free(ucp_array); _memstat_mtl_empty(list); list->mtl_error = MEMSTAT_ERROR_NOMEMORY; return (-1); } /* * Reset the statistics on a current node. */ _memstat_mt_reset_stats(mtp, mp_maxid + 1); mtp->mt_numallocs = kvm_counter_u64_fetch(kvm, (unsigned long )uz.uz_allocs); mtp->mt_numfrees = kvm_counter_u64_fetch(kvm, (unsigned long )uz.uz_frees); mtp->mt_failures = kvm_counter_u64_fetch(kvm, (unsigned long )uz.uz_fails); mtp->mt_xdomain = kvm_counter_u64_fetch(kvm, (unsigned long )uz.uz_xdomain); mtp->mt_sleeps = uz.uz_sleeps; /* See comment above in memstat_sysctl_uma(). */ if (mtp->mt_numallocs < mtp->mt_numfrees) mtp->mt_numallocs = mtp->mt_numfrees; if (kz.uk_flags & UMA_ZFLAG_INTERNAL) goto skip_percpu; for (i = 0; i < mp_maxid + 1; i++) { if (!CPU_ISSET(i, &all_cpus)) continue; ucp = &ucp_array[i]; mtp->mt_numallocs += ucp->uc_allocs; mtp->mt_numfrees += ucp->uc_frees; mtp->mt_free += ucp->uc_allocbucket.ucb_cnt; mtp->mt_free += ucp->uc_freebucket.ucb_cnt; mtp->mt_free += ucp->uc_crossbucket.ucb_cnt; } skip_percpu: mtp->mt_size = kz.uk_size; mtp->mt_rsize = kz.uk_rsize; mtp->mt_memalloced = mtp->mt_numallocs * mtp->mt_size; mtp->mt_memfreed = mtp->mt_numfrees * mtp->mt_size; mtp->mt_bytes = mtp->mt_memalloced - mtp->mt_memfreed; mtp->mt_countlimit = uz.uz_max_items; mtp->mt_byteslimit = mtp->mt_countlimit * mtp->mt_size; mtp->mt_count = mtp->mt_numallocs - mtp->mt_numfrees; for (i = 0; i < ndomains; i++) { ret = kread(kvm, ZDOM_GET(uzp, i), &uzd, sizeof(uzd), 0); if (ret != 0) continue; for (ubp = STAILQ_FIRST(&uzd.uzd_buckets); ubp != NULL; ubp = STAILQ_NEXT(&ub, ub_link)) { ret = kread(kvm, ubp, &ub, sizeof(ub), 0); if (ret != 0) continue; mtp->mt_zonefree += ub.ub_cnt; } } if (!((kz.uk_flags & UMA_ZONE_SECONDARY) && LIST_FIRST(&kz.uk_zones) != uzp)) { kegfree = 0; for (i = 0; i < ndomains; i++) { ret = kread(kvm, &kzp->uk_domain[i], &ukd, sizeof(ukd), 0); if (ret != 0) kegfree += ukd.ud_free_items; } mtp->mt_kegfree = kegfree; mtp->mt_free += mtp->mt_kegfree; } mtp->mt_free += mtp->mt_zonefree; } } free(ucp_array); return (0); } diff --git a/sbin/pfctl/pfctl_altq.c b/sbin/pfctl/pfctl_altq.c index 8067b0598361..7b709040f162 100644 --- a/sbin/pfctl/pfctl_altq.c +++ b/sbin/pfctl/pfctl_altq.c @@ -1,1467 +1,1468 @@ /* $OpenBSD: pfctl_altq.c,v 1.93 2007/10/15 02:16:35 deraadt Exp $ */ /* * Copyright (c) 2002 * Sony Computer Science Laboratories Inc. * Copyright (c) 2002, 2003 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #define PFIOC_USE_LATEST +#define _WANT_FREEBSD_BITSET #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pfctl_parser.h" #include "pfctl.h" #define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0)) static STAILQ_HEAD(interfaces, pfctl_altq) interfaces = STAILQ_HEAD_INITIALIZER(interfaces); static struct hsearch_data queue_map; static struct hsearch_data if_map; static struct hsearch_data qid_map; static struct pfctl_altq *pfaltq_lookup(char *ifname); static struct pfctl_altq *qname_to_pfaltq(const char *, const char *); static u_int32_t qname_to_qid(char *); static int eval_pfqueue_cbq(struct pfctl *, struct pf_altq *, struct pfctl_altq *); static int cbq_compute_idletime(struct pfctl *, struct pf_altq *); static int check_commit_cbq(int, int, struct pfctl_altq *); static int print_cbq_opts(const struct pf_altq *); static int print_codel_opts(const struct pf_altq *, const struct node_queue_opt *); static int eval_pfqueue_priq(struct pfctl *, struct pf_altq *, struct pfctl_altq *); static int check_commit_priq(int, int, struct pfctl_altq *); static int print_priq_opts(const struct pf_altq *); static int eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *, struct pfctl_altq *, struct pfctl_altq *); static int check_commit_hfsc(int, int, struct pfctl_altq *); static int print_hfsc_opts(const struct pf_altq *, const struct node_queue_opt *); static int eval_pfqueue_fairq(struct pfctl *, struct pf_altq *, struct pfctl_altq *, struct pfctl_altq *); static int print_fairq_opts(const struct pf_altq *, const struct node_queue_opt *); static int check_commit_fairq(int, int, struct pfctl_altq *); static void gsc_add_sc(struct gen_sc *, struct service_curve *); static int is_gsc_under_sc(struct gen_sc *, struct service_curve *); static struct segment *gsc_getentry(struct gen_sc *, double); static int gsc_add_seg(struct gen_sc *, double, double, double, double); static double sc_x2y(struct service_curve *, double); #ifdef __FreeBSD__ u_int64_t getifspeed(int, char *); #else u_int32_t getifspeed(char *); #endif u_long getifmtu(char *); int eval_queue_opts(struct pf_altq *, struct node_queue_opt *, u_int64_t); u_int64_t eval_bwspec(struct node_queue_bw *, u_int64_t); void print_hfsc_sc(const char *, u_int, u_int, u_int, const struct node_hfsc_sc *); void print_fairq_sc(const char *, u_int, u_int, u_int, const struct node_fairq_sc *); static __attribute__((constructor)) void pfctl_altq_init(void) { /* * As hdestroy() will never be called on these tables, it will be * safe to use references into the stored data as keys. */ if (hcreate_r(0, &queue_map) == 0) err(1, "Failed to create altq queue map"); if (hcreate_r(0, &if_map) == 0) err(1, "Failed to create altq interface map"); if (hcreate_r(0, &qid_map) == 0) err(1, "Failed to create altq queue id map"); } void pfaltq_store(struct pf_altq *a) { struct pfctl_altq *altq; ENTRY item; ENTRY *ret_item; size_t key_size; if ((altq = malloc(sizeof(*altq))) == NULL) err(1, "queue malloc"); memcpy(&altq->pa, a, sizeof(struct pf_altq)); memset(&altq->meta, 0, sizeof(altq->meta)); if (a->qname[0] == 0) { item.key = altq->pa.ifname; item.data = altq; if (hsearch_r(item, ENTER, &ret_item, &if_map) == 0) err(1, "interface map insert"); STAILQ_INSERT_TAIL(&interfaces, altq, meta.link); } else { key_size = sizeof(a->ifname) + sizeof(a->qname); if ((item.key = malloc(key_size)) == NULL) err(1, "queue map key malloc"); snprintf(item.key, key_size, "%s:%s", a->ifname, a->qname); item.data = altq; if (hsearch_r(item, ENTER, &ret_item, &queue_map) == 0) err(1, "queue map insert"); item.key = altq->pa.qname; item.data = &altq->pa.qid; if (hsearch_r(item, ENTER, &ret_item, &qid_map) == 0) err(1, "qid map insert"); } } static struct pfctl_altq * pfaltq_lookup(char *ifname) { ENTRY item; ENTRY *ret_item; item.key = ifname; if (hsearch_r(item, FIND, &ret_item, &if_map) == 0) return (NULL); return (ret_item->data); } static struct pfctl_altq * qname_to_pfaltq(const char *qname, const char *ifname) { ENTRY item; ENTRY *ret_item; char key[IFNAMSIZ + PF_QNAME_SIZE]; item.key = key; snprintf(item.key, sizeof(key), "%s:%s", ifname, qname); if (hsearch_r(item, FIND, &ret_item, &queue_map) == 0) return (NULL); return (ret_item->data); } static u_int32_t qname_to_qid(char *qname) { ENTRY item; ENTRY *ret_item; uint32_t qid; /* * We guarantee that same named queues on different interfaces * have the same qid. */ item.key = qname; if (hsearch_r(item, FIND, &ret_item, &qid_map) == 0) return (0); qid = *(uint32_t *)ret_item->data; return (qid); } void print_altq(const struct pf_altq *a, unsigned int level, struct node_queue_bw *bw, struct node_queue_opt *qopts) { if (a->qname[0] != 0) { print_queue(a, level, bw, 1, qopts); return; } #ifdef __FreeBSD__ if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) printf("INACTIVE "); #endif printf("altq on %s ", a->ifname); switch (a->scheduler) { case ALTQT_CBQ: if (!print_cbq_opts(a)) printf("cbq "); break; case ALTQT_PRIQ: if (!print_priq_opts(a)) printf("priq "); break; case ALTQT_HFSC: if (!print_hfsc_opts(a, qopts)) printf("hfsc "); break; case ALTQT_FAIRQ: if (!print_fairq_opts(a, qopts)) printf("fairq "); break; case ALTQT_CODEL: if (!print_codel_opts(a, qopts)) printf("codel "); break; } if (bw != NULL && bw->bw_percent > 0) { if (bw->bw_percent < 100) printf("bandwidth %u%% ", bw->bw_percent); } else printf("bandwidth %s ", rate2str((double)a->ifbandwidth)); if (a->qlimit != DEFAULT_QLIMIT) printf("qlimit %u ", a->qlimit); printf("tbrsize %u ", a->tbrsize); } void print_queue(const struct pf_altq *a, unsigned int level, struct node_queue_bw *bw, int print_interface, struct node_queue_opt *qopts) { unsigned int i; #ifdef __FreeBSD__ if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) printf("INACTIVE "); #endif printf("queue "); for (i = 0; i < level; ++i) printf(" "); printf("%s ", a->qname); if (print_interface) printf("on %s ", a->ifname); if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC || a->scheduler == ALTQT_FAIRQ) { if (bw != NULL && bw->bw_percent > 0) { if (bw->bw_percent < 100) printf("bandwidth %u%% ", bw->bw_percent); } else printf("bandwidth %s ", rate2str((double)a->bandwidth)); } if (a->priority != DEFAULT_PRIORITY) printf("priority %u ", a->priority); if (a->qlimit != DEFAULT_QLIMIT) printf("qlimit %u ", a->qlimit); switch (a->scheduler) { case ALTQT_CBQ: print_cbq_opts(a); break; case ALTQT_PRIQ: print_priq_opts(a); break; case ALTQT_HFSC: print_hfsc_opts(a, qopts); break; case ALTQT_FAIRQ: print_fairq_opts(a, qopts); break; } } /* * eval_pfaltq computes the discipline parameters. */ int eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, struct node_queue_opt *opts) { u_int64_t rate; u_int size, errors = 0; if (bw->bw_absolute > 0) pa->ifbandwidth = bw->bw_absolute; else #ifdef __FreeBSD__ if ((rate = getifspeed(pf->dev, pa->ifname)) == 0) { #else if ((rate = getifspeed(pa->ifname)) == 0) { #endif fprintf(stderr, "interface %s does not know its bandwidth, " "please specify an absolute bandwidth\n", pa->ifname); errors++; } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0) pa->ifbandwidth = rate; /* * Limit bandwidth to UINT_MAX for schedulers that aren't 64-bit ready. */ if ((pa->scheduler != ALTQT_HFSC) && (pa->ifbandwidth > UINT_MAX)) { pa->ifbandwidth = UINT_MAX; warnx("interface %s bandwidth limited to %" PRIu64 " bps " "because selected scheduler is 32-bit limited\n", pa->ifname, pa->ifbandwidth); } errors += eval_queue_opts(pa, opts, pa->ifbandwidth); /* if tbrsize is not specified, use heuristics */ if (pa->tbrsize == 0) { rate = pa->ifbandwidth; if (rate <= 1 * 1000 * 1000) size = 1; else if (rate <= 10 * 1000 * 1000) size = 4; else if (rate <= 200 * 1000 * 1000) size = 8; else if (rate <= 2500 * 1000 * 1000ULL) size = 24; else size = 128; size = size * getifmtu(pa->ifname); pa->tbrsize = size; } return (errors); } /* * check_commit_altq does consistency check for each interface */ int check_commit_altq(int dev, int opts) { struct pfctl_altq *if_ppa; int error = 0; /* call the discipline check for each interface. */ STAILQ_FOREACH(if_ppa, &interfaces, meta.link) { switch (if_ppa->pa.scheduler) { case ALTQT_CBQ: error = check_commit_cbq(dev, opts, if_ppa); break; case ALTQT_PRIQ: error = check_commit_priq(dev, opts, if_ppa); break; case ALTQT_HFSC: error = check_commit_hfsc(dev, opts, if_ppa); break; case ALTQT_FAIRQ: error = check_commit_fairq(dev, opts, if_ppa); break; default: break; } } return (error); } /* * eval_pfqueue computes the queue parameters. */ int eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, struct node_queue_opt *opts) { /* should be merged with expand_queue */ struct pfctl_altq *if_ppa, *parent; int error = 0; /* find the corresponding interface and copy fields used by queues */ if ((if_ppa = pfaltq_lookup(pa->ifname)) == NULL) { fprintf(stderr, "altq not defined on %s\n", pa->ifname); return (1); } pa->scheduler = if_ppa->pa.scheduler; pa->ifbandwidth = if_ppa->pa.ifbandwidth; if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) { fprintf(stderr, "queue %s already exists on interface %s\n", pa->qname, pa->ifname); return (1); } pa->qid = qname_to_qid(pa->qname); parent = NULL; if (pa->parent[0] != 0) { parent = qname_to_pfaltq(pa->parent, pa->ifname); if (parent == NULL) { fprintf(stderr, "parent %s not found for %s\n", pa->parent, pa->qname); return (1); } pa->parent_qid = parent->pa.qid; } if (pa->qlimit == 0) pa->qlimit = DEFAULT_QLIMIT; if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC || pa->scheduler == ALTQT_FAIRQ) { pa->bandwidth = eval_bwspec(bw, parent == NULL ? pa->ifbandwidth : parent->pa.bandwidth); if (pa->bandwidth > pa->ifbandwidth) { fprintf(stderr, "bandwidth for %s higher than " "interface\n", pa->qname); return (1); } /* * If not HFSC, then check that the sum of the child * bandwidths is less than the parent's bandwidth. For * HFSC, the equivalent concept is to check that the sum of * the child linkshare service curves are under the parent's * linkshare service curve, and that check is performed by * eval_pfqueue_hfsc(). */ if ((parent != NULL) && (pa->scheduler != ALTQT_HFSC)) { if (pa->bandwidth > parent->pa.bandwidth) { warnx("bandwidth for %s higher than parent", pa->qname); return (1); } parent->meta.bwsum += pa->bandwidth; if (parent->meta.bwsum > parent->pa.bandwidth) { warnx("the sum of the child bandwidth (%" PRIu64 ") higher than parent \"%s\" (%" PRIu64 ")", parent->meta.bwsum, parent->pa.qname, parent->pa.bandwidth); } } } if (eval_queue_opts(pa, opts, parent == NULL ? pa->ifbandwidth : parent->pa.bandwidth)) return (1); if (parent != NULL) parent->meta.children++; switch (pa->scheduler) { case ALTQT_CBQ: error = eval_pfqueue_cbq(pf, pa, if_ppa); break; case ALTQT_PRIQ: error = eval_pfqueue_priq(pf, pa, if_ppa); break; case ALTQT_HFSC: error = eval_pfqueue_hfsc(pf, pa, if_ppa, parent); break; case ALTQT_FAIRQ: error = eval_pfqueue_fairq(pf, pa, if_ppa, parent); break; default: break; } return (error); } /* * CBQ support functions */ #define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */ #define RM_NS_PER_SEC (1000000000) static int eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa, struct pfctl_altq *if_ppa) { struct cbq_opts *opts; u_int ifmtu; if (pa->priority >= CBQ_MAXPRI) { warnx("priority out of range: max %d", CBQ_MAXPRI - 1); return (-1); } ifmtu = getifmtu(pa->ifname); opts = &pa->pq_u.cbq_opts; if (opts->pktsize == 0) { /* use default */ opts->pktsize = ifmtu; if (opts->pktsize > MCLBYTES) /* do what TCP does */ opts->pktsize &= ~MCLBYTES; } else if (opts->pktsize > ifmtu) opts->pktsize = ifmtu; if (opts->maxpktsize == 0) /* use default */ opts->maxpktsize = ifmtu; else if (opts->maxpktsize > ifmtu) opts->pktsize = ifmtu; if (opts->pktsize > opts->maxpktsize) opts->pktsize = opts->maxpktsize; if (pa->parent[0] == 0) opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR); if (pa->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS) if_ppa->meta.root_classes++; if (pa->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS) if_ppa->meta.default_classes++; cbq_compute_idletime(pf, pa); return (0); } /* * compute ns_per_byte, maxidle, minidle, and offtime */ static int cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa) { struct cbq_opts *opts; double maxidle_s, maxidle, minidle; double offtime, nsPerByte, ifnsPerByte, ptime, cptime; double z, g, f, gton, gtom; u_int minburst, maxburst; opts = &pa->pq_u.cbq_opts; ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8; minburst = opts->minburst; maxburst = opts->maxburst; if (pa->bandwidth == 0) f = 0.0001; /* small enough? */ else f = ((double) pa->bandwidth / (double) pa->ifbandwidth); nsPerByte = ifnsPerByte / f; ptime = (double)opts->pktsize * ifnsPerByte; cptime = ptime * (1.0 - f) / f; if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) { /* * this causes integer overflow in kernel! * (bandwidth < 6Kbps when max_pkt_size=1500) */ if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0) { warnx("queue bandwidth must be larger than %s", rate2str(ifnsPerByte * (double)opts->maxpktsize / (double)INT_MAX * (double)pa->ifbandwidth)); fprintf(stderr, "cbq: queue %s is too slow!\n", pa->qname); } nsPerByte = (double)(INT_MAX / opts->maxpktsize); } if (maxburst == 0) { /* use default */ if (cptime > 10.0 * 1000000) maxburst = 4; else maxburst = 16; } if (minburst == 0) /* use default */ minburst = 2; if (minburst > maxburst) minburst = maxburst; z = (double)(1 << RM_FILTER_GAIN); g = (1.0 - 1.0 / z); gton = pow(g, (double)maxburst); gtom = pow(g, (double)(minburst-1)); maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton)); maxidle_s = (1.0 - g); if (maxidle > maxidle_s) maxidle = ptime * maxidle; else maxidle = ptime * maxidle_s; offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom); minidle = -((double)opts->maxpktsize * (double)nsPerByte); /* scale parameters */ maxidle = ((maxidle * 8.0) / nsPerByte) * pow(2.0, (double)RM_FILTER_GAIN); offtime = (offtime * 8.0) / nsPerByte * pow(2.0, (double)RM_FILTER_GAIN); minidle = ((minidle * 8.0) / nsPerByte) * pow(2.0, (double)RM_FILTER_GAIN); maxidle = maxidle / 1000.0; offtime = offtime / 1000.0; minidle = minidle / 1000.0; opts->minburst = minburst; opts->maxburst = maxburst; opts->ns_per_byte = (u_int)nsPerByte; opts->maxidle = (u_int)fabs(maxidle); opts->minidle = (int)minidle; opts->offtime = (u_int)fabs(offtime); return (0); } static int check_commit_cbq(int dev, int opts, struct pfctl_altq *if_ppa) { int error = 0; /* * check if cbq has one root queue and one default queue * for this interface */ if (if_ppa->meta.root_classes != 1) { warnx("should have one root queue on %s", if_ppa->pa.ifname); error++; } if (if_ppa->meta.default_classes != 1) { warnx("should have one default queue on %s", if_ppa->pa.ifname); error++; } return (error); } static int print_cbq_opts(const struct pf_altq *a) { const struct cbq_opts *opts; opts = &a->pq_u.cbq_opts; if (opts->flags) { printf("cbq("); if (opts->flags & CBQCLF_RED) printf(" red"); if (opts->flags & CBQCLF_ECN) printf(" ecn"); if (opts->flags & CBQCLF_RIO) printf(" rio"); if (opts->flags & CBQCLF_CODEL) printf(" codel"); if (opts->flags & CBQCLF_CLEARDSCP) printf(" cleardscp"); if (opts->flags & CBQCLF_FLOWVALVE) printf(" flowvalve"); if (opts->flags & CBQCLF_BORROW) printf(" borrow"); if (opts->flags & CBQCLF_WRR) printf(" wrr"); if (opts->flags & CBQCLF_EFFICIENT) printf(" efficient"); if (opts->flags & CBQCLF_ROOTCLASS) printf(" root"); if (opts->flags & CBQCLF_DEFCLASS) printf(" default"); printf(" ) "); return (1); } else return (0); } /* * PRIQ support functions */ static int eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa, struct pfctl_altq *if_ppa) { if (pa->priority >= PRIQ_MAXPRI) { warnx("priority out of range: max %d", PRIQ_MAXPRI - 1); return (-1); } if (BIT_ISSET(QPRI_BITSET_SIZE, pa->priority, &if_ppa->meta.qpris)) { warnx("%s does not have a unique priority on interface %s", pa->qname, pa->ifname); return (-1); } else BIT_SET(QPRI_BITSET_SIZE, pa->priority, &if_ppa->meta.qpris); if (pa->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS) if_ppa->meta.default_classes++; return (0); } static int check_commit_priq(int dev, int opts, struct pfctl_altq *if_ppa) { /* * check if priq has one default class for this interface */ if (if_ppa->meta.default_classes != 1) { warnx("should have one default queue on %s", if_ppa->pa.ifname); return (1); } return (0); } static int print_priq_opts(const struct pf_altq *a) { const struct priq_opts *opts; opts = &a->pq_u.priq_opts; if (opts->flags) { printf("priq("); if (opts->flags & PRCF_RED) printf(" red"); if (opts->flags & PRCF_ECN) printf(" ecn"); if (opts->flags & PRCF_RIO) printf(" rio"); if (opts->flags & PRCF_CODEL) printf(" codel"); if (opts->flags & PRCF_CLEARDSCP) printf(" cleardscp"); if (opts->flags & PRCF_DEFAULTCLASS) printf(" default"); printf(" ) "); return (1); } else return (0); } /* * HFSC support functions */ static int eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa, struct pfctl_altq *if_ppa, struct pfctl_altq *parent) { struct hfsc_opts_v1 *opts; struct service_curve sc; opts = &pa->pq_u.hfsc_opts; if (parent == NULL) { /* root queue */ opts->lssc_m1 = pa->ifbandwidth; opts->lssc_m2 = pa->ifbandwidth; opts->lssc_d = 0; return (0); } /* First child initializes the parent's service curve accumulators. */ if (parent->meta.children == 1) { LIST_INIT(&parent->meta.rtsc); LIST_INIT(&parent->meta.lssc); } if (parent->pa.pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) { warnx("adding %s would make default queue %s not a leaf", pa->qname, pa->parent); return (-1); } if (pa->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) if_ppa->meta.default_classes++; /* if link_share is not specified, use bandwidth */ if (opts->lssc_m2 == 0) opts->lssc_m2 = pa->bandwidth; if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) || (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) || (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) { warnx("m2 is zero for %s", pa->qname); return (-1); } if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) || (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) || (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) { warnx("m1 must be zero for convex curve: %s", pa->qname); return (-1); } /* * admission control: * for the real-time service curve, the sum of the service curves * should not exceed 80% of the interface bandwidth. 20% is reserved * not to over-commit the actual interface bandwidth. * for the linkshare service curve, the sum of the child service * curve should not exceed the parent service curve. * for the upper-limit service curve, the assigned bandwidth should * be smaller than the interface bandwidth, and the upper-limit should * be larger than the real-time service curve when both are defined. */ /* check the real-time service curve. reserve 20% of interface bw */ if (opts->rtsc_m2 != 0) { /* add this queue to the sum */ sc.m1 = opts->rtsc_m1; sc.d = opts->rtsc_d; sc.m2 = opts->rtsc_m2; gsc_add_sc(&parent->meta.rtsc, &sc); /* compare the sum with 80% of the interface */ sc.m1 = 0; sc.d = 0; sc.m2 = pa->ifbandwidth / 100 * 80; if (!is_gsc_under_sc(&parent->meta.rtsc, &sc)) { warnx("real-time sc exceeds 80%% of the interface " "bandwidth (%s)", rate2str((double)sc.m2)); return (-1); } } /* check the linkshare service curve. */ if (opts->lssc_m2 != 0) { /* add this queue to the child sum */ sc.m1 = opts->lssc_m1; sc.d = opts->lssc_d; sc.m2 = opts->lssc_m2; gsc_add_sc(&parent->meta.lssc, &sc); /* compare the sum of the children with parent's sc */ sc.m1 = parent->pa.pq_u.hfsc_opts.lssc_m1; sc.d = parent->pa.pq_u.hfsc_opts.lssc_d; sc.m2 = parent->pa.pq_u.hfsc_opts.lssc_m2; if (!is_gsc_under_sc(&parent->meta.lssc, &sc)) { warnx("linkshare sc exceeds parent's sc"); return (-1); } } /* check the upper-limit service curve. */ if (opts->ulsc_m2 != 0) { if (opts->ulsc_m1 > pa->ifbandwidth || opts->ulsc_m2 > pa->ifbandwidth) { warnx("upper-limit larger than interface bandwidth"); return (-1); } if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) { warnx("upper-limit sc smaller than real-time sc"); return (-1); } } return (0); } /* * FAIRQ support functions */ static int eval_pfqueue_fairq(struct pfctl *pf __unused, struct pf_altq *pa, struct pfctl_altq *if_ppa, struct pfctl_altq *parent) { struct fairq_opts *opts; struct service_curve sc; opts = &pa->pq_u.fairq_opts; if (parent == NULL) { /* root queue */ opts->lssc_m1 = pa->ifbandwidth; opts->lssc_m2 = pa->ifbandwidth; opts->lssc_d = 0; return (0); } /* First child initializes the parent's service curve accumulator. */ if (parent->meta.children == 1) LIST_INIT(&parent->meta.lssc); if (parent->pa.pq_u.fairq_opts.flags & FARF_DEFAULTCLASS) { warnx("adding %s would make default queue %s not a leaf", pa->qname, pa->parent); return (-1); } if (pa->pq_u.fairq_opts.flags & FARF_DEFAULTCLASS) if_ppa->meta.default_classes++; /* if link_share is not specified, use bandwidth */ if (opts->lssc_m2 == 0) opts->lssc_m2 = pa->bandwidth; /* * admission control: * for the real-time service curve, the sum of the service curves * should not exceed 80% of the interface bandwidth. 20% is reserved * not to over-commit the actual interface bandwidth. * for the link-sharing service curve, the sum of the child service * curve should not exceed the parent service curve. * for the upper-limit service curve, the assigned bandwidth should * be smaller than the interface bandwidth, and the upper-limit should * be larger than the real-time service curve when both are defined. */ /* check the linkshare service curve. */ if (opts->lssc_m2 != 0) { /* add this queue to the child sum */ sc.m1 = opts->lssc_m1; sc.d = opts->lssc_d; sc.m2 = opts->lssc_m2; gsc_add_sc(&parent->meta.lssc, &sc); /* compare the sum of the children with parent's sc */ sc.m1 = parent->pa.pq_u.fairq_opts.lssc_m1; sc.d = parent->pa.pq_u.fairq_opts.lssc_d; sc.m2 = parent->pa.pq_u.fairq_opts.lssc_m2; if (!is_gsc_under_sc(&parent->meta.lssc, &sc)) { warnx("link-sharing sc exceeds parent's sc"); return (-1); } } return (0); } static int check_commit_hfsc(int dev, int opts, struct pfctl_altq *if_ppa) { /* check if hfsc has one default queue for this interface */ if (if_ppa->meta.default_classes != 1) { warnx("should have one default queue on %s", if_ppa->pa.ifname); return (1); } return (0); } static int check_commit_fairq(int dev __unused, int opts __unused, struct pfctl_altq *if_ppa) { /* check if fairq has one default queue for this interface */ if (if_ppa->meta.default_classes != 1) { warnx("should have one default queue on %s", if_ppa->pa.ifname); return (1); } return (0); } static int print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) { const struct hfsc_opts_v1 *opts; const struct node_hfsc_sc *rtsc, *lssc, *ulsc; opts = &a->pq_u.hfsc_opts; if (qopts == NULL) rtsc = lssc = ulsc = NULL; else { rtsc = &qopts->data.hfsc_opts.realtime; lssc = &qopts->data.hfsc_opts.linkshare; ulsc = &qopts->data.hfsc_opts.upperlimit; } if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 || (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || opts->lssc_d != 0))) { printf("hfsc("); if (opts->flags & HFCF_RED) printf(" red"); if (opts->flags & HFCF_ECN) printf(" ecn"); if (opts->flags & HFCF_RIO) printf(" rio"); if (opts->flags & HFCF_CODEL) printf(" codel"); if (opts->flags & HFCF_CLEARDSCP) printf(" cleardscp"); if (opts->flags & HFCF_DEFAULTCLASS) printf(" default"); if (opts->rtsc_m2 != 0) print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d, opts->rtsc_m2, rtsc); if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || opts->lssc_d != 0)) print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d, opts->lssc_m2, lssc); if (opts->ulsc_m2 != 0) print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d, opts->ulsc_m2, ulsc); printf(" ) "); return (1); } else return (0); } static int print_codel_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) { const struct codel_opts *opts; opts = &a->pq_u.codel_opts; if (opts->target || opts->interval || opts->ecn) { printf("codel("); if (opts->target) printf(" target %d", opts->target); if (opts->interval) printf(" interval %d", opts->interval); if (opts->ecn) printf("ecn"); printf(" ) "); return (1); } return (0); } static int print_fairq_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) { const struct fairq_opts *opts; const struct node_fairq_sc *loc_lssc; opts = &a->pq_u.fairq_opts; if (qopts == NULL) loc_lssc = NULL; else loc_lssc = &qopts->data.fairq_opts.linkshare; if (opts->flags || (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || opts->lssc_d != 0))) { printf("fairq("); if (opts->flags & FARF_RED) printf(" red"); if (opts->flags & FARF_ECN) printf(" ecn"); if (opts->flags & FARF_RIO) printf(" rio"); if (opts->flags & FARF_CODEL) printf(" codel"); if (opts->flags & FARF_CLEARDSCP) printf(" cleardscp"); if (opts->flags & FARF_DEFAULTCLASS) printf(" default"); if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || opts->lssc_d != 0)) print_fairq_sc("linkshare", opts->lssc_m1, opts->lssc_d, opts->lssc_m2, loc_lssc); printf(" ) "); return (1); } else return (0); } /* * admission control using generalized service curve */ /* add a new service curve to a generalized service curve */ static void gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc) { if (is_sc_null(sc)) return; if (sc->d != 0) gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1); gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2); } /* * check whether all points of a generalized service curve have * their y-coordinates no larger than a given two-piece linear * service curve. */ static int is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc) { struct segment *s, *last, *end; double y; if (is_sc_null(sc)) { if (LIST_EMPTY(gsc)) return (1); LIST_FOREACH(s, gsc, _next) { if (s->m != 0) return (0); } return (1); } /* * gsc has a dummy entry at the end with x = INFINITY. * loop through up to this dummy entry. */ end = gsc_getentry(gsc, INFINITY); if (end == NULL) return (1); last = NULL; for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) { if (s->y > sc_x2y(sc, s->x)) return (0); last = s; } /* last now holds the real last segment */ if (last == NULL) return (1); if (last->m > sc->m2) return (0); if (last->x < sc->d && last->m > sc->m1) { y = last->y + (sc->d - last->x) * last->m; if (y > sc_x2y(sc, sc->d)) return (0); } return (1); } /* * return a segment entry starting at x. * if gsc has no entry starting at x, a new entry is created at x. */ static struct segment * gsc_getentry(struct gen_sc *gsc, double x) { struct segment *new, *prev, *s; prev = NULL; LIST_FOREACH(s, gsc, _next) { if (s->x == x) return (s); /* matching entry found */ else if (s->x < x) prev = s; else break; } /* we have to create a new entry */ if ((new = calloc(1, sizeof(struct segment))) == NULL) return (NULL); new->x = x; if (x == INFINITY || s == NULL) new->d = 0; else if (s->x == INFINITY) new->d = INFINITY; else new->d = s->x - x; if (prev == NULL) { /* insert the new entry at the head of the list */ new->y = 0; new->m = 0; LIST_INSERT_HEAD(gsc, new, _next); } else { /* * the start point intersects with the segment pointed by * prev. divide prev into 2 segments */ if (x == INFINITY) { prev->d = INFINITY; if (prev->m == 0) new->y = prev->y; else new->y = INFINITY; } else { prev->d = x - prev->x; new->y = prev->d * prev->m + prev->y; } new->m = prev->m; LIST_INSERT_AFTER(prev, new, _next); } return (new); } /* add a segment to a generalized service curve */ static int gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m) { struct segment *start, *end, *s; double x2; if (d == INFINITY) x2 = INFINITY; else x2 = x + d; start = gsc_getentry(gsc, x); end = gsc_getentry(gsc, x2); if (start == NULL || end == NULL) return (-1); for (s = start; s != end; s = LIST_NEXT(s, _next)) { s->m += m; s->y += y + (s->x - x) * m; } end = gsc_getentry(gsc, INFINITY); for (; s != end; s = LIST_NEXT(s, _next)) { s->y += m * d; } return (0); } /* get y-projection of a service curve */ static double sc_x2y(struct service_curve *sc, double x) { double y; if (x <= (double)sc->d) /* y belongs to the 1st segment */ y = x * (double)sc->m1; else /* y belongs to the 2nd segment */ y = (double)sc->d * (double)sc->m1 + (x - (double)sc->d) * (double)sc->m2; return (y); } /* * misc utilities */ #define R2S_BUFS 8 #define RATESTR_MAX 16 char * rate2str(double rate) { char *buf; static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */ static int idx = 0; int i; static const char unit[] = " KMG"; buf = r2sbuf[idx++]; if (idx == R2S_BUFS) idx = 0; for (i = 0; rate >= 1000 && i <= 3; i++) rate /= 1000; if ((int)(rate * 100) % 100) snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]); else snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]); return (buf); } #ifdef __FreeBSD__ /* * XXX * FreeBSD does not have SIOCGIFDATA. * To emulate this, DIOCGIFSPEED ioctl added to pf. */ u_int64_t getifspeed(int pfdev, char *ifname) { struct pf_ifspeed io; bzero(&io, sizeof io); if (strlcpy(io.ifname, ifname, IFNAMSIZ) >= sizeof(io.ifname)) errx(1, "getifspeed: strlcpy"); if (ioctl(pfdev, DIOCGIFSPEED, &io) == -1) err(1, "DIOCGIFSPEED"); return (io.baudrate); } #else u_int32_t getifspeed(char *ifname) { int s; struct ifreq ifr; struct if_data ifrdat; s = get_query_socket(); bzero(&ifr, sizeof(ifr)); if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= sizeof(ifr.ifr_name)) errx(1, "getifspeed: strlcpy"); ifr.ifr_data = (caddr_t)&ifrdat; if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) err(1, "SIOCGIFDATA"); return ((u_int32_t)ifrdat.ifi_baudrate); } #endif u_long getifmtu(char *ifname) { int s; struct ifreq ifr; s = get_query_socket(); bzero(&ifr, sizeof(ifr)); if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= sizeof(ifr.ifr_name)) errx(1, "getifmtu: strlcpy"); if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1) #ifdef __FreeBSD__ ifr.ifr_mtu = 1500; #else err(1, "SIOCGIFMTU"); #endif if (ifr.ifr_mtu > 0) return (ifr.ifr_mtu); else { warnx("could not get mtu for %s, assuming 1500", ifname); return (1500); } } int eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts, u_int64_t ref_bw) { int errors = 0; switch (pa->scheduler) { case ALTQT_CBQ: pa->pq_u.cbq_opts = opts->data.cbq_opts; break; case ALTQT_PRIQ: pa->pq_u.priq_opts = opts->data.priq_opts; break; case ALTQT_HFSC: pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags; if (opts->data.hfsc_opts.linkshare.used) { pa->pq_u.hfsc_opts.lssc_m1 = eval_bwspec(&opts->data.hfsc_opts.linkshare.m1, ref_bw); pa->pq_u.hfsc_opts.lssc_m2 = eval_bwspec(&opts->data.hfsc_opts.linkshare.m2, ref_bw); pa->pq_u.hfsc_opts.lssc_d = opts->data.hfsc_opts.linkshare.d; } if (opts->data.hfsc_opts.realtime.used) { pa->pq_u.hfsc_opts.rtsc_m1 = eval_bwspec(&opts->data.hfsc_opts.realtime.m1, ref_bw); pa->pq_u.hfsc_opts.rtsc_m2 = eval_bwspec(&opts->data.hfsc_opts.realtime.m2, ref_bw); pa->pq_u.hfsc_opts.rtsc_d = opts->data.hfsc_opts.realtime.d; } if (opts->data.hfsc_opts.upperlimit.used) { pa->pq_u.hfsc_opts.ulsc_m1 = eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1, ref_bw); pa->pq_u.hfsc_opts.ulsc_m2 = eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2, ref_bw); pa->pq_u.hfsc_opts.ulsc_d = opts->data.hfsc_opts.upperlimit.d; } break; case ALTQT_FAIRQ: pa->pq_u.fairq_opts.flags = opts->data.fairq_opts.flags; pa->pq_u.fairq_opts.nbuckets = opts->data.fairq_opts.nbuckets; pa->pq_u.fairq_opts.hogs_m1 = eval_bwspec(&opts->data.fairq_opts.hogs_bw, ref_bw); if (opts->data.fairq_opts.linkshare.used) { pa->pq_u.fairq_opts.lssc_m1 = eval_bwspec(&opts->data.fairq_opts.linkshare.m1, ref_bw); pa->pq_u.fairq_opts.lssc_m2 = eval_bwspec(&opts->data.fairq_opts.linkshare.m2, ref_bw); pa->pq_u.fairq_opts.lssc_d = opts->data.fairq_opts.linkshare.d; } break; case ALTQT_CODEL: pa->pq_u.codel_opts.target = opts->data.codel_opts.target; pa->pq_u.codel_opts.interval = opts->data.codel_opts.interval; pa->pq_u.codel_opts.ecn = opts->data.codel_opts.ecn; break; default: warnx("eval_queue_opts: unknown scheduler type %u", opts->qtype); errors++; break; } return (errors); } /* * If absolute bandwidth if set, return the lesser of that value and the * reference bandwidth. Limiting to the reference bandwidth allows simple * limiting of configured bandwidth parameters for schedulers that are * 32-bit limited, as the root/interface bandwidth (top-level reference * bandwidth) will be properly limited in that case. * * Otherwise, if the absolute bandwidth is not set, return given percentage * of reference bandwidth. */ u_int64_t eval_bwspec(struct node_queue_bw *bw, u_int64_t ref_bw) { if (bw->bw_absolute > 0) return (MIN(bw->bw_absolute, ref_bw)); if (bw->bw_percent > 0) return (ref_bw / 100 * bw->bw_percent); return (0); } void print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2, const struct node_hfsc_sc *sc) { printf(" %s", scname); if (d != 0) { printf("("); if (sc != NULL && sc->m1.bw_percent > 0) printf("%u%%", sc->m1.bw_percent); else printf("%s", rate2str((double)m1)); printf(" %u", d); } if (sc != NULL && sc->m2.bw_percent > 0) printf(" %u%%", sc->m2.bw_percent); else printf(" %s", rate2str((double)m2)); if (d != 0) printf(")"); } void print_fairq_sc(const char *scname, u_int m1, u_int d, u_int m2, const struct node_fairq_sc *sc) { printf(" %s", scname); if (d != 0) { printf("("); if (sc != NULL && sc->m1.bw_percent > 0) printf("%u%%", sc->m1.bw_percent); else printf("%s", rate2str((double)m1)); printf(" %u", d); } if (sc != NULL && sc->m2.bw_percent > 0) printf(" %u%%", sc->m2.bw_percent); else printf(" %s", rate2str((double)m2)); if (d != 0) printf(")"); } diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h index 4e144b97567b..0cd19a560f8d 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -1,356 +1,356 @@ /* $OpenBSD: pfctl_parser.h,v 1.86 2006/10/31 23:46:25 mcbride Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2001 Daniel Hartmeier * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PFCTL_PARSER_H_ #define _PFCTL_PARSER_H_ #include #define PF_OSFP_FILE "/etc/pf.os" #define PF_OPT_DISABLE 0x0001 #define PF_OPT_ENABLE 0x0002 #define PF_OPT_VERBOSE 0x0004 #define PF_OPT_NOACTION 0x0008 #define PF_OPT_QUIET 0x0010 #define PF_OPT_CLRRULECTRS 0x0020 #define PF_OPT_USEDNS 0x0040 #define PF_OPT_VERBOSE2 0x0080 #define PF_OPT_DUMMYACTION 0x0100 #define PF_OPT_DEBUG 0x0200 #define PF_OPT_SHOWALL 0x0400 #define PF_OPT_OPTIMIZE 0x0800 #define PF_OPT_NUMERIC 0x1000 #define PF_OPT_MERGE 0x2000 #define PF_OPT_RECURSE 0x4000 #define PF_OPT_KILLMATCH 0x8000 #define PF_TH_ALL 0xFF #define PF_NAT_PROXY_PORT_LOW 50001 #define PF_NAT_PROXY_PORT_HIGH 65535 #define PF_OPTIMIZE_BASIC 0x0001 #define PF_OPTIMIZE_PROFILE 0x0002 #define FCNT_NAMES { \ "searches", \ "inserts", \ "removals", \ NULL \ } struct pfr_buffer; /* forward definition */ struct pfctl { int dev; int opts; int optimize; int loadopt; int asd; /* anchor stack depth */ int bn; /* brace number */ int tdirty; /* kernel dirty */ #define PFCTL_ANCHOR_STACK_DEPTH 64 struct pfctl_anchor *astack[PFCTL_ANCHOR_STACK_DEPTH]; struct pfioc_pooladdr paddr; struct pfioc_altq *paltq; struct pfioc_queue *pqueue; struct pfr_buffer *trans; struct pfctl_anchor *anchor, *alast; const char *ruleset; /* 'set foo' options */ u_int32_t timeout[PFTM_MAX]; u_int32_t limit[PF_LIMIT_MAX]; u_int32_t debug; u_int32_t hostid; char *ifname; bool keep_counters; u_int8_t syncookies; u_int8_t syncookieswat[2]; /* lowat, highwat, in % */ u_int8_t syncookieswat_set; u_int8_t timeout_set[PFTM_MAX]; u_int8_t limit_set[PF_LIMIT_MAX]; u_int8_t debug_set; u_int8_t hostid_set; u_int8_t ifname_set; }; struct node_if { char ifname[IFNAMSIZ]; u_int8_t not; u_int8_t dynamic; /* antispoof */ u_int ifa_flags; struct node_if *next; struct node_if *tail; }; struct node_host { struct pf_addr_wrap addr; struct pf_addr bcast; struct pf_addr peer; sa_family_t af; u_int8_t not; u_int32_t ifindex; /* link-local IPv6 addrs */ char *ifname; u_int ifa_flags; struct node_host *next; struct node_host *tail; }; struct node_os { char *os; pf_osfp_t fingerprint; struct node_os *next; struct node_os *tail; }; struct node_queue_bw { u_int64_t bw_absolute; u_int16_t bw_percent; }; struct node_hfsc_sc { struct node_queue_bw m1; /* slope of 1st segment; bps */ u_int d; /* x-projection of m1; msec */ struct node_queue_bw m2; /* slope of 2nd segment; bps */ u_int8_t used; }; struct node_hfsc_opts { struct node_hfsc_sc realtime; struct node_hfsc_sc linkshare; struct node_hfsc_sc upperlimit; int flags; }; struct node_fairq_sc { struct node_queue_bw m1; /* slope of 1st segment; bps */ u_int d; /* x-projection of m1; msec */ struct node_queue_bw m2; /* slope of 2nd segment; bps */ u_int8_t used; }; struct node_fairq_opts { struct node_fairq_sc linkshare; struct node_queue_bw hogs_bw; u_int nbuckets; int flags; }; struct node_queue_opt { int qtype; union { struct cbq_opts cbq_opts; struct codel_opts codel_opts; struct priq_opts priq_opts; struct node_hfsc_opts hfsc_opts; struct node_fairq_opts fairq_opts; } data; }; #define QPRI_BITSET_SIZE 256 -BITSET_DEFINE(qpri_bitset, QPRI_BITSET_SIZE); +__BITSET_DEFINE(qpri_bitset, QPRI_BITSET_SIZE); LIST_HEAD(gen_sc, segment); struct pfctl_altq { struct pf_altq pa; struct { STAILQ_ENTRY(pfctl_altq) link; u_int64_t bwsum; struct qpri_bitset qpris; int children; int root_classes; int default_classes; struct gen_sc lssc; struct gen_sc rtsc; } meta; }; struct pfctl_watermarks { uint32_t hi; uint32_t lo; }; #ifdef __FreeBSD__ /* * XXX * Absolutely this is not correct location to define this. * Should we use an another sperate header file? */ #define SIMPLEQ_HEAD STAILQ_HEAD #define SIMPLEQ_HEAD_INITIALIZER STAILQ_HEAD_INITIALIZER #define SIMPLEQ_ENTRY STAILQ_ENTRY #define SIMPLEQ_FIRST STAILQ_FIRST #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY STAILQ_EMPTY #define SIMPLEQ_NEXT STAILQ_NEXT /*#define SIMPLEQ_FOREACH STAILQ_FOREACH*/ #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) #define SIMPLEQ_INIT STAILQ_INIT #define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD #define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL #define SIMPLEQ_INSERT_AFTER STAILQ_INSERT_AFTER #define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD #endif SIMPLEQ_HEAD(node_tinithead, node_tinit); struct node_tinit { /* table initializer */ SIMPLEQ_ENTRY(node_tinit) entries; struct node_host *host; char *file; }; /* optimizer created tables */ struct pf_opt_tbl { char pt_name[PF_TABLE_NAME_SIZE]; int pt_rulecount; int pt_generated; struct node_tinithead pt_nodes; struct pfr_buffer *pt_buf; }; #define PF_OPT_TABLE_PREFIX "__automatic_" /* optimizer pf_rule container */ struct pf_opt_rule { struct pfctl_rule por_rule; struct pf_opt_tbl *por_src_tbl; struct pf_opt_tbl *por_dst_tbl; u_int64_t por_profile_count; TAILQ_ENTRY(pf_opt_rule) por_entry; TAILQ_ENTRY(pf_opt_rule) por_skip_entry[PF_SKIP_COUNT]; }; TAILQ_HEAD(pf_opt_queue, pf_opt_rule); int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *); int pfctl_optimize_ruleset(struct pfctl *, struct pfctl_ruleset *); int pfctl_append_rule(struct pfctl *, struct pfctl_rule *, const char *); int pfctl_add_altq(struct pfctl *, struct pf_altq *); int pfctl_add_pool(struct pfctl *, struct pfctl_pool *, sa_family_t); void pfctl_move_pool(struct pfctl_pool *, struct pfctl_pool *); void pfctl_clear_pool(struct pfctl_pool *); int pfctl_set_timeout(struct pfctl *, const char *, int, int); int pfctl_set_optimization(struct pfctl *, const char *); int pfctl_set_limit(struct pfctl *, const char *, unsigned int); int pfctl_set_logif(struct pfctl *, char *); int pfctl_set_hostid(struct pfctl *, u_int32_t); int pfctl_set_debug(struct pfctl *, char *); int pfctl_set_interface_flags(struct pfctl *, char *, int, int); int pfctl_cfg_syncookies(struct pfctl *, uint8_t, struct pfctl_watermarks *); int parse_config(char *, struct pfctl *); int parse_flags(char *); int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *); void print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, sa_family_t, int); void print_src_node(struct pf_src_node *, int); void print_rule(struct pfctl_rule *, const char *, int, int); void print_tabledef(const char *, int, int, struct node_tinithead *); void print_status(struct pfctl_status *, struct pfctl_syncookies *, int); void print_running(struct pfctl_status *); int eval_pfaltq(struct pfctl *, struct pf_altq *, struct node_queue_bw *, struct node_queue_opt *); int eval_pfqueue(struct pfctl *, struct pf_altq *, struct node_queue_bw *, struct node_queue_opt *); void print_altq(const struct pf_altq *, unsigned, struct node_queue_bw *, struct node_queue_opt *); void print_queue(const struct pf_altq *, unsigned, struct node_queue_bw *, int, struct node_queue_opt *); int pfctl_define_table(char *, int, int, const char *, struct pfr_buffer *, u_int32_t); void pfctl_clear_fingerprints(int, int); int pfctl_file_fingerprints(int, int, const char *); pf_osfp_t pfctl_get_fingerprint(const char *); int pfctl_load_fingerprints(int, int); char *pfctl_lookup_fingerprint(pf_osfp_t, char *, size_t); void pfctl_show_fingerprints(int); struct icmptypeent { const char *name; u_int8_t type; }; struct icmpcodeent { const char *name; u_int8_t type; u_int8_t code; }; const struct icmptypeent *geticmptypebynumber(u_int8_t, u_int8_t); const struct icmptypeent *geticmptypebyname(char *, u_int8_t); const struct icmpcodeent *geticmpcodebynumber(u_int8_t, u_int8_t, u_int8_t); const struct icmpcodeent *geticmpcodebyname(u_long, char *, u_int8_t); struct pf_timeout { const char *name; int timeout; }; #define PFCTL_FLAG_FILTER 0x02 #define PFCTL_FLAG_NAT 0x04 #define PFCTL_FLAG_OPTION 0x08 #define PFCTL_FLAG_ALTQ 0x10 #define PFCTL_FLAG_TABLE 0x20 extern const struct pf_timeout pf_timeouts[]; void set_ipmask(struct node_host *, u_int8_t); int check_netmask(struct node_host *, sa_family_t); int unmask(struct pf_addr *, sa_family_t); void ifa_load(void); int get_query_socket(void); struct node_host *ifa_exists(char *); struct node_host *ifa_grouplookup(char *ifa_name, int flags); struct node_host *ifa_lookup(char *, int); struct node_host *host(const char *); int append_addr(struct pfr_buffer *, char *, int); int append_addr_host(struct pfr_buffer *, struct node_host *, int, int); #endif /* _PFCTL_PARSER_H_ */ diff --git a/share/man/man9/bitset.9 b/share/man/man9/bitset.9 index 1a5ec05b01c6..e6ce3d9b4f58 100644 --- a/share/man/man9/bitset.9 +++ b/share/man/man9/bitset.9 @@ -1,586 +1,611 @@ .\" Copyright (c) 2015 Conrad Meyer .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' .\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED .\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR .\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE .\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR .\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd September 20, 2021 .Dt BITSET 9 .Os .Sh NAME .Nm bitset(9) \(em .Nm BITSET_DEFINE , .Nm BITSET_T_INITIALIZER , .Nm BITSET_FSET , .Nm BIT_CLR , .Nm BIT_COPY , .Nm BIT_ISSET , .Nm BIT_SET , .Nm BIT_ZERO , .Nm BIT_FILL , .Nm BIT_SETOF , .Nm BIT_EMPTY , .Nm BIT_ISFULLSET , .Nm BIT_FFS , .Nm BIT_FFS_AT , .Nm BIT_FLS , .Nm BIT_FOREACH_ISSET , .Nm BIT_FOREACH_ISCLR , .Nm BIT_COUNT , .Nm BIT_SUBSET , .Nm BIT_OVERLAP , .Nm BIT_CMP , .Nm BIT_OR , .Nm BIT_OR2 , .Nm BIT_AND , .Nm BIT_AND2 , .Nm BIT_ANDNOT , .Nm BIT_ANDNOT2 , .Nm BIT_XOR , .Nm BIT_XOR2 , .Nm BIT_CLR_ATOMIC , .Nm BIT_SET_ATOMIC , .Nm BIT_SET_ATOMIC_ACQ , .Nm BIT_TEST_SET_ATOMIC , .Nm BIT_TEST_CLR_ATOMIC , .Nm BIT_AND_ATOMIC , .Nm BIT_OR_ATOMIC , .Nm BIT_COPY_STORE_REL .Nd bitset manipulation macros .Sh SYNOPSIS .In sys/_bitset.h .In sys/bitset.h .\" .Fn BITSET_DEFINE "STRUCTNAME" "const SETSIZE" .Fn BITSET_T_INITIALIZER "ARRAY_CONTENTS" .Fn BITSET_FSET "N_WORDS" .\" .Fn BIT_CLR "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" .Fn BIT_COPY "const SETSIZE" "struct STRUCTNAME *from" "struct STRUCTNAME *to" .Ft bool .Fn BIT_ISSET "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" .Fn BIT_SET "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" .Fn BIT_ZERO "const SETSIZE" "struct STRUCTNAME *bitset" .Fn BIT_FILL "const SETSIZE" "struct STRUCTNAME *bitset" .Fn BIT_SETOF "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" .Ft bool .Fn BIT_EMPTY "const SETSIZE" "struct STRUCTNAME *bitset" .Ft bool .Fn BIT_ISFULLSET "const SETSIZE" "struct STRUCTNAME *bitset" .Ft long .Fn BIT_FFS "const SETSIZE" "struct STRUCTNAME *bitset" .Ft long .Fn BIT_FFS_AT "const SETSIZE" "struct STRUCTNAME *bitset" "long start" .Ft long .Fn BIT_FLS "const SETSIZE" "struct STRUCTNAME *bitset" .Fo BIT_FOREACH_ISSET .Fa "const SETSIZE" .Fa "size_t bit" .Fa "const struct STRUCTNAME *bitset" .Fc .Fo BIT_FOREACH_ISCLR .Fa "const SETSIZE" .Fa "size_t bit" .Fa "const struct STRUCTNAME *bitset" .Fc .Ft long .Fn BIT_COUNT "const SETSIZE" "struct STRUCTNAME *bitset" .Ft bool .Fo BIT_SUBSET .Fa "const SETSIZE" "struct STRUCTNAME *haystack" "struct STRUCTNAME *needle" .Fc .Ft bool .Fo BIT_OVERLAP .Fa "const SETSIZE" "struct STRUCTNAME *bitset1" "struct STRUCTNAME *bitset2" .Fc .Ft bool .Fo BIT_CMP .Fa "const SETSIZE" "struct STRUCTNAME *bitset1" "struct STRUCTNAME *bitset2" .Fc .Fn BIT_OR "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" .Fo BIT_OR2 .Fa "const SETSIZE" .Fa "struct STRUCTNAME *dst" .Fa "struct STRUCTNAME *src1" .Fa "struct STRUCTNAME *src2" .Fc .Fn BIT_AND "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" .Fo BIT_AND2 .Fa "const SETSIZE" .Fa "struct STRUCTNAME *dst" .Fa "struct STRUCTNAME *src1" .Fa "struct STRUCTNAME *src2" .Fc .Fn BIT_ANDNOT "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" .Fo BIT_ANDNOT2 .Fa "const SETSIZE" .Fa "struct STRUCTNAME *dst" .Fa "struct STRUCTNAME *src1" .Fa "struct STRUCTNAME *src2" .Fc .Fn BIT_XOR "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" .Fo BIT_XOR2 .Fa "const SETSIZE" .Fa "struct STRUCTNAME *dst" .Fa "struct STRUCTNAME *src1" .Fa "struct STRUCTNAME *src2" .Fc .\" .Fn BIT_CLR_ATOMIC "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" .Fn BIT_SET_ATOMIC "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" .Fn BIT_SET_ATOMIC_ACQ "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" .Ft bool .Fn BIT_TEST_SET_ATOMIC "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" .Ft bool .Fn BIT_TEST_CLR_ATOMIC "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" .\" .Fo BIT_AND_ATOMIC .Fa "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" .Fc .Fo BIT_OR_ATOMIC .Fa "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" .Fc .Fo BIT_COPY_STORE_REL .Fa "const SETSIZE" "struct STRUCTNAME *from" "struct STRUCTNAME *to" .Fc +.Fd #define _WANT_FREEBSD_BITSET .Sh DESCRIPTION The .Nm family of macros provide a flexible and efficient bitset implementation if the maximum size of the set is known at compilation. Throughout this manual page, the name .Fa SETSIZE refers to the size of the bitset in bits. Individual bits in bitsets are referenced with indices zero through .Fa SETSIZE - 1 . One example use of .In sys/bitset.h is .In sys/cpuset.h . .Pp +These macros are meant to be used in the kernel and are visible if +.Dv _KERNEL is defined when +.In sys/_bitset.h +or +.In sys/bitset.h +are included in a program. +Userland programs must define +.Dv _WANT_FREEBSD_BITSET +before including these files to make the macros visible. +.Pp The .Fn BITSET_DEFINE macro defines a bitset struct .Fa STRUCTNAME with room to represent .Fa SETSIZE bits. .Pp The .Fn BITSET_T_INITIALIZER macro allows one to initialize a bitset struct with a compile time literal value. .Pp The .Fn BITSET_FSET macro generates a compile time literal, usable by .Fn BITSET_T_INITIALIZER , representing a full bitset (all bits set). For examples of .Fn BITSET_T_INITIALIZER and .Fn BITSET_FSET usage, see the .Sx BITSET_T_INITIALIZER EXAMPLE section. The .Fa N_WORDS parameter to .Fn BITSET_FSET should be: .Bd -literal -offset indent __bitset_words(SETSIZE) .Ed .Pp The .Fn BIT_CLR macro clears bit .Fa bit in the bitset pointed to by .Fa bitset . The .Fn BIT_CLR_ATOMIC macro is identical, but the bit is cleared atomically. The .Fn BIT_TEST_CLR_ATOMIC macro atomically clears the bit and returns whether it was set. .Pp The .Fn BIT_COPY macro copies the contents of the bitset .Fa from to the bitset .Fa to . .Fn BIT_COPY_STORE_REL is similar, but copies component machine words from .Fa from and writes them to .Fa to with atomic store with release semantics. (That is, if .Fa to is composed of multiple machine words, .Fn BIT_COPY_STORE_REL performs multiple individually atomic operations.) .Pp The .Fn BIT_SET macro sets bit .Fa bit in the bitset pointed to by .Fa bitset . The .Fn BIT_SET_ATOMIC macro is identical, but the bit is set atomically. The .Fn BIT_SET_ATOMIC_ACQ macro sets the bit with acquire semantics. The .Fn BIT_TEST_SET_ATOMIC macro atomically sets the bit and returns whether it was set. .Pp The .Fn BIT_ZERO macro clears all bits in .Fa bitset . .Pp The .Fn BIT_FILL macro sets all bits in .Fa bitset . .Pp The .Fn BIT_SETOF macro clears all bits in .Fa bitset before setting only bit .Fa bit . .Pp The .Fn BIT_EMPTY macro returns .Dv true if .Fa bitset is empty. .Pp The .Fn BIT_ISFULLSET macro returns .Dv true if .Fa bitset is full (all bits set). .Pp The .Fn BIT_FFS macro returns the 1-index of the first (lowest) set bit in .Fa bitset , or zero if .Fa bitset is empty. Like with .Xr ffs 3 , to use the non-zero result of .Fn BIT_FFS as a .Fa bit index parameter to any other .Nm macro, you must subtract one from the result. .Pp The .Fn BIT_FFS_AT macro returns the 1-index of the first (lowest) set bit in .Fa bitset , which is greater than the given 1-indexed .Fa start , or zero if no bits in .Fa bitset greater than .Fa start are set. .Pp The .Fn BIT_FLS macro returns the 1-index of the last (highest) set bit in .Fa bitset , or zero if .Fa bitset is empty. Like with .Xr fls 3 , to use the non-zero result of .Fn BIT_FLS as a .Fa bit index parameter to any other .Nm macro, you must subtract one from the result. .Pp The .Fn BIT_FOREACH_ISSET macro can be used to iterate over all set bits in .Fa bitset . The index variable .Fa bit must have been declared with type .Ft int , and upon each iteration .Fa bit is set to the index of successive set bits. The value of .Fa bit after the loop terminates is undefined. Similarly, .Fn BIT_FOREACH_ISCLR iterates over all clear bits in .Fa bitset . In the loop body, the currently indexed bit may be set or cleared. However, setting or clearing bits other than the currently indexed bit does not guarantee that they will or will not be returned in subsequent iterations of the same loop. .Pp The .Fn BIT_COUNT macro returns the total number of set bits in .Fa bitset . .Pp The .Fn BIT_SUBSET macro returns .Dv true if .Fa needle is a subset of .Fa haystack . .Pp The .Fn BIT_OVERLAP macro returns .Dv true if .Fa bitset1 and .Fa bitset2 have any common bits. (That is, if .Fa bitset1 AND .Fa bitset2 is not the empty set.) .Pp The .Fn BIT_CMP macro returns .Dv true if .Fa bitset1 is NOT equal to .Fa bitset2 . .Pp The .Fn BIT_OR macro sets bits present in .Fa src in .Fa dst . (It is the .Nm equivalent of the scalar: .Fa dst |= .Fa src . ) .Fn BIT_OR_ATOMIC is similar, but sets bits in the component machine words in .Fa dst atomically. (That is, if .Fa dst is composed of multiple machine words, .Fn BIT_OR_ATOMIC performs multiple individually atomic operations.) .Pp The .Fn BIT_OR2 macro computes .Fa src1 bitwise or .Fa src2 and assigns the result to .Fa dst . (It is the .Nm equivalent of the scalar: .Fa dst = .Fa src1 | .Fa src2 . ) .Pp The .Fn BIT_AND macro clears bits absent from .Fa src from .Fa dst . (It is the .Nm equivalent of the scalar: .Fa dst &= .Fa src . ) .Fn BIT_AND_ATOMIC is similar, with the same atomic semantics as .Fn BIT_OR_ATOMIC . .Pp The .Fn BIT_AND2 macro computes .Fa src1 bitwise and .Fa src2 and assigns the result to .Fa dst . (It is the .Nm equivalent of the scalar: .Fa dst = .Fa src1 & .Fa src2 . ) .Pp The .Fn BIT_ANDNOT macro clears bits set in .Fa src from .Fa dst . (It is the .Nm equivalent of the scalar: .Fa dst &= .Fa ~ src . ) .Pp The .Fn BIT_ANDNOT2 macro computes .Fa src1 bitwise and not .Fa src2 and assigns the result to .Fa dst . (It is the .Nm equivalent of the scalar: .Fa dst = .Fa src1 & ~ .Fa src2 . ) .Pp The .Fn BIT_XOR macro toggles bits set in .Fa src in .Fa dst . (It is the .Nm equivalent of the scalar: .Fa dst ^= .Fa src . ) .Pp The .Fn BIT_XOR2 macro computes .Fa src1 bitwise exclusive or .Fa src2 and assigns the result to .Fa dst . (It is the .Nm equivalent of the scalar: .Fa dst = .Fa src1 ^ .Fa src2 . ) .Sh BITSET_T_INITIALIZER EXAMPLE .Bd -literal BITSET_DEFINE(_myset, MYSETSIZE); struct _myset myset; /* Initialize myset to filled (all bits set) */ myset = BITSET_T_INITIALIZER(BITSET_FSET(__bitset_words(MYSETSIZE))); /* Initialize myset to only the lowest bit set */ myset = BITSET_T_INITIALIZER(0x1); .Ed .Sh SEE ALSO .Xr bitstring 3 , .Xr cpuset 9 .Sh HISTORY The .Nm macros first appeared in .Fx 10.0 in January 2014. They were MFCed to .Fx 9.3 , released in July 2014. .Pp This manual page first appeared in .Fx 11.0 . .Sh AUTHORS .An -nosplit The .Nm macros were generalized and pulled out of .In sys/cpuset.h as .In sys/_bitset.h and .In sys/bitset.h by .An Attilio Rao Aq Mt attilio@FreeBSD.org . This manual page was written by .An Conrad Meyer Aq Mt cem@FreeBSD.org . .Sh CAVEATS The .Fa SETSIZE argument to all of these macros must match the value given to .Fn BITSET_DEFINE . .Pp Unlike every other reference to individual set members, which are zero-indexed, .Fn BIT_FFS , .Fn BIT_FFS_AT and .Fn BIT_FLS return a one-indexed result (or zero if the set is empty). +.Pp +In order to use the macros defined in +.In sys/bitset.h +and +.In sys/_bitset.h +in userland programs, +.Dv _WANT_FREEBSD_BITSET +has to be defined before including the header files. +This requirements exists to prevent a name space pollution due to macros defined in +.Nm +in programs that include +.In sys/cpuset.h +or +.In sched.h . diff --git a/sys/sys/_bitset.h b/sys/sys/_bitset.h index e54f04cbeee7..1c167daf3f09 100644 --- a/sys/sys/_bitset.h +++ b/sys/sys/_bitset.h @@ -1,66 +1,71 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008, Jeffrey Roberson * All rights reserved. * * Copyright (c) 2008 Nokia Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS__BITSET_H_ #define _SYS__BITSET_H_ /* * Macros addressing word and bit within it, tuned to make compiler * optimize cases when SETSIZE fits into single machine word. */ #define _BITSET_BITS (sizeof(long) * 8) #define __howmany(x, y) (((x) + ((y) - 1)) / (y)) #define __bitset_words(_s) (__howmany(_s, _BITSET_BITS)) -#define BITSET_DEFINE(t, _s) \ -struct t { \ +#define __BITSET_DEFINE(_t, _s) \ +struct _t { \ long __bits[__bitset_words((_s))]; \ } /* * Helper to declare a bitset without it's size being a constant. * * Sadly we cannot declare a bitset struct with '__bits[]', because it's * the only member of the struct and the compiler complains. */ -#define BITSET_DEFINE_VAR(t) BITSET_DEFINE(t, 1) +#define __BITSET_DEFINE_VAR(_t) __BITSET_DEFINE(_t, 1) /* * Define a default type that can be used while manually specifying size * to every call. */ -BITSET_DEFINE(bitset, 1); +__BITSET_DEFINE(bitset, 1); + +#if defined(_KERNEL) || defined(_WANT_FREEBSD_BITSET) +#define BITSET_DEFINE(_t, _s) __BITSET_DEFINE(_t, _s) +#define BITSET_DEFINE_VAR(_t) __BITSET_DEFINE_VAR(_t) +#endif #endif /* !_SYS__BITSET_H_ */ diff --git a/sys/sys/_cpuset.h b/sys/sys/_cpuset.h index cb3c0f9d586e..9033acb9ba14 100644 --- a/sys/sys/_cpuset.h +++ b/sys/sys/_cpuset.h @@ -1,52 +1,52 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008, Jeffrey Roberson * All rights reserved. * * Copyright (c) 2008 Nokia Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS__CPUSET_H_ #define _SYS__CPUSET_H_ #include #ifdef _KERNEL #define CPU_SETSIZE MAXCPU #endif #define CPU_MAXSIZE 256 #ifndef CPU_SETSIZE #define CPU_SETSIZE CPU_MAXSIZE #endif -BITSET_DEFINE(_cpuset, CPU_SETSIZE); +__BITSET_DEFINE(_cpuset, CPU_SETSIZE); typedef struct _cpuset cpuset_t; #endif /* !_SYS__CPUSET_H_ */ diff --git a/sys/sys/_domainset.h b/sys/sys/_domainset.h index 5685d532a9e3..443c68fd331b 100644 --- a/sys/sys/_domainset.h +++ b/sys/sys/_domainset.h @@ -1,60 +1,60 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017, Jeffrey Roberson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS__DOMAINSET_H_ #define _SYS__DOMAINSET_H_ #include #ifdef _KERNEL #define DOMAINSET_SETSIZE MAXMEMDOM #endif #define DOMAINSET_MAXSIZE 256 #ifndef DOMAINSET_SETSIZE #define DOMAINSET_SETSIZE DOMAINSET_MAXSIZE #endif -BITSET_DEFINE(_domainset, DOMAINSET_SETSIZE); +__BITSET_DEFINE(_domainset, DOMAINSET_SETSIZE); typedef struct _domainset domainset_t; /* * This structure is intended to be embedded in objects which have policy * attributes. Each object keeps its own iterator so round-robin is * synchronized and accurate. */ struct domainset; struct domainset_ref { struct domainset * volatile dr_policy; unsigned int dr_iter; }; #endif /* !_SYS__DOMAINSET_H_ */ diff --git a/sys/sys/bitset.h b/sys/sys/bitset.h index 1c154d5601ab..91a5e6e37db3 100644 --- a/sys/sys/bitset.h +++ b/sys/sys/bitset.h @@ -1,316 +1,360 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008, Jeffrey Roberson * All rights reserved. * * Copyright (c) 2008 Nokia Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_BITSET_H_ #define _SYS_BITSET_H_ /* * Whether expr is both constant and true. Result is itself constant. * Used to enable optimizations for sets with a known small size. */ #define __constexpr_cond(expr) (__builtin_constant_p((expr)) && (expr)) #define __bitset_mask(_s, n) \ (1UL << (__constexpr_cond(__bitset_words((_s)) == 1) ? \ (__size_t)(n) : ((n) % _BITSET_BITS))) #define __bitset_word(_s, n) \ (__constexpr_cond(__bitset_words((_s)) == 1) ? \ 0 : ((n) / _BITSET_BITS)) -#define BIT_CLR(_s, n, p) \ +#define __BIT_CLR(_s, n, p) \ ((p)->__bits[__bitset_word(_s, n)] &= ~__bitset_mask((_s), (n))) -#define BIT_COPY(_s, f, t) (void)(*(t) = *(f)) +#define __BIT_COPY(_s, f, t) (void)(*(t) = *(f)) -#define BIT_ISSET(_s, n, p) \ +#define __BIT_ISSET(_s, n, p) \ ((((p)->__bits[__bitset_word(_s, n)] & __bitset_mask((_s), (n))) != 0)) -#define BIT_SET(_s, n, p) \ +#define __BIT_SET(_s, n, p) \ ((p)->__bits[__bitset_word(_s, n)] |= __bitset_mask((_s), (n))) -#define BIT_ZERO(_s, p) do { \ +#define __BIT_ZERO(_s, p) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ (p)->__bits[__i] = 0L; \ } while (0) -#define BIT_FILL(_s, p) do { \ +#define __BIT_FILL(_s, p) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ (p)->__bits[__i] = -1L; \ } while (0) -#define BIT_SETOF(_s, n, p) do { \ - BIT_ZERO(_s, p); \ +#define __BIT_SETOF(_s, n, p) do { \ + __BIT_ZERO(_s, p); \ (p)->__bits[__bitset_word(_s, n)] = __bitset_mask((_s), (n)); \ } while (0) /* Is p empty. */ -#define BIT_EMPTY(_s, p) __extension__ ({ \ +#define __BIT_EMPTY(_s, p) __extension__ ({ \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ if ((p)->__bits[__i]) \ break; \ __i == __bitset_words((_s)); \ }) /* Is p full set. */ -#define BIT_ISFULLSET(_s, p) __extension__ ({ \ +#define __BIT_ISFULLSET(_s, p) __extension__ ({ \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ if ((p)->__bits[__i] != (long)-1) \ break; \ __i == __bitset_words((_s)); \ }) /* Is c a subset of p. */ -#define BIT_SUBSET(_s, p, c) __extension__ ({ \ +#define __BIT_SUBSET(_s, p, c) __extension__ ({ \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ if (((c)->__bits[__i] & \ (p)->__bits[__i]) != \ (c)->__bits[__i]) \ break; \ __i == __bitset_words((_s)); \ }) /* Are there any common bits between b & c? */ -#define BIT_OVERLAP(_s, p, c) __extension__ ({ \ +#define __BIT_OVERLAP(_s, p, c) __extension__ ({ \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ if (((c)->__bits[__i] & \ (p)->__bits[__i]) != 0) \ break; \ __i != __bitset_words((_s)); \ }) /* Compare two sets, returns 0 if equal 1 otherwise. */ -#define BIT_CMP(_s, p, c) __extension__ ({ \ +#define __BIT_CMP(_s, p, c) __extension__ ({ \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ if (((c)->__bits[__i] != \ (p)->__bits[__i])) \ break; \ __i != __bitset_words((_s)); \ }) -#define BIT_OR(_s, d, s) do { \ +#define __BIT_OR(_s, d, s) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ (d)->__bits[__i] |= (s)->__bits[__i]; \ } while (0) -#define BIT_OR2(_s, d, s1, s2) do { \ +#define __BIT_OR2(_s, d, s1, s2) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ (d)->__bits[__i] = (s1)->__bits[__i] | (s2)->__bits[__i];\ } while (0) -#define BIT_AND(_s, d, s) do { \ +#define __BIT_AND(_s, d, s) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ (d)->__bits[__i] &= (s)->__bits[__i]; \ } while (0) -#define BIT_AND2(_s, d, s1, s2) do { \ +#define __BIT_AND2(_s, d, s1, s2) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ (d)->__bits[__i] = (s1)->__bits[__i] & (s2)->__bits[__i];\ } while (0) -#define BIT_ANDNOT(_s, d, s) do { \ +#define __BIT_ANDNOT(_s, d, s) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ (d)->__bits[__i] &= ~(s)->__bits[__i]; \ } while (0) -#define BIT_ANDNOT2(_s, d, s1, s2) do { \ +#define __BIT_ANDNOT2(_s, d, s1, s2) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ (d)->__bits[__i] = (s1)->__bits[__i] & ~(s2)->__bits[__i];\ } while (0) -#define BIT_XOR(_s, d, s) do { \ +#define __BIT_XOR(_s, d, s) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ (d)->__bits[__i] ^= (s)->__bits[__i]; \ } while (0) -#define BIT_XOR2(_s, d, s1, s2) do { \ +#define __BIT_XOR2(_s, d, s1, s2) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ (d)->__bits[__i] = (s1)->__bits[__i] ^ (s2)->__bits[__i];\ } while (0) /* * Note, the atomic(9) API is not consistent between clear/set and * testandclear/testandset in whether the value argument is a mask * or a bit index. */ -#define BIT_CLR_ATOMIC(_s, n, p) \ +#define __BIT_CLR_ATOMIC(_s, n, p) \ atomic_clear_long(&(p)->__bits[__bitset_word(_s, n)], \ __bitset_mask((_s), n)) -#define BIT_SET_ATOMIC(_s, n, p) \ +#define __BIT_SET_ATOMIC(_s, n, p) \ atomic_set_long(&(p)->__bits[__bitset_word(_s, n)], \ __bitset_mask((_s), n)) -#define BIT_SET_ATOMIC_ACQ(_s, n, p) \ +#define __BIT_SET_ATOMIC_ACQ(_s, n, p) \ atomic_set_acq_long(&(p)->__bits[__bitset_word(_s, n)], \ __bitset_mask((_s), n)) -#define BIT_TEST_CLR_ATOMIC(_s, n, p) \ +#define __BIT_TEST_CLR_ATOMIC(_s, n, p) \ (atomic_testandclear_long( \ &(p)->__bits[__bitset_word((_s), (n))], (n)) != 0) -#define BIT_TEST_SET_ATOMIC(_s, n, p) \ +#define __BIT_TEST_SET_ATOMIC(_s, n, p) \ (atomic_testandset_long( \ &(p)->__bits[__bitset_word((_s), (n))], (n)) != 0) /* Convenience functions catering special cases. */ -#define BIT_AND_ATOMIC(_s, d, s) do { \ +#define __BIT_AND_ATOMIC(_s, d, s) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ atomic_clear_long(&(d)->__bits[__i], \ ~(s)->__bits[__i]); \ } while (0) -#define BIT_OR_ATOMIC(_s, d, s) do { \ +#define __BIT_OR_ATOMIC(_s, d, s) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ atomic_set_long(&(d)->__bits[__i], \ (s)->__bits[__i]); \ } while (0) -#define BIT_COPY_STORE_REL(_s, f, t) do { \ +#define __BIT_COPY_STORE_REL(_s, f, t) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ atomic_store_rel_long(&(t)->__bits[__i], \ (f)->__bits[__i]); \ } while (0) /* - * Note that `start` and the returned value from BIT_FFS_AT are + * Note that `start` and the returned value from __BIT_FFS_AT are * 1-based bit indices. */ -#define BIT_FFS_AT(_s, p, start) __extension__ ({ \ +#define __BIT_FFS_AT(_s, p, start) __extension__ ({ \ __size_t __i; \ long __bit, __mask; \ \ __mask = ~0UL << ((start) % _BITSET_BITS); \ __bit = 0; \ for (__i = __bitset_word((_s), (start)); \ __i < __bitset_words((_s)); \ __i++) { \ if (((p)->__bits[__i] & __mask) != 0) { \ __bit = ffsl((p)->__bits[__i] & __mask); \ __bit += __i * _BITSET_BITS; \ break; \ } \ __mask = ~0UL; \ } \ __bit; \ }) -#define BIT_FFS(_s, p) BIT_FFS_AT((_s), (p), 0) +#define __BIT_FFS(_s, p) __BIT_FFS_AT((_s), (p), 0) -#define BIT_FLS(_s, p) __extension__ ({ \ +#define __BIT_FLS(_s, p) __extension__ ({ \ __size_t __i; \ long __bit; \ \ __bit = 0; \ for (__i = __bitset_words((_s)); __i > 0; __i--) { \ if ((p)->__bits[__i - 1] != 0) { \ __bit = flsl((p)->__bits[__i - 1]); \ __bit += (__i - 1) * _BITSET_BITS; \ break; \ } \ } \ __bit; \ }) -#define BIT_COUNT(_s, p) __extension__ ({ \ +#define __BIT_COUNT(_s, p) __extension__ ({ \ __size_t __i; \ long __count; \ \ __count = 0; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ __count += __bitcountl((p)->__bits[__i]); \ __count; \ }) -#define _BIT_FOREACH_ADVANCE(_s, i, p, op) __extension__ ({ \ +#define __BIT_FOREACH_ADVANCE(_s, i, p, op) __extension__ ({ \ int __found; \ for (;;) { \ if (__bits != 0) { \ int __bit = ffsl(__bits) - 1; \ __bits &= ~(1ul << __bit); \ (i) = __i * _BITSET_BITS + __bit; \ __found = 1; \ break; \ } \ if (++__i == __bitset_words(_s)) { \ __found = 0; \ break; \ } \ __bits = op((p)->__bits[__i]); \ } \ __found != 0; \ }) /* * Non-destructively loop over all set or clear bits in the set. */ -#define _BIT_FOREACH(_s, i, p, op) \ +#define __BIT_FOREACH(_s, i, p, op) \ for (long __i = -1, __bits = 0; \ - _BIT_FOREACH_ADVANCE(_s, i, p, op); ) + __BIT_FOREACH_ADVANCE(_s, i, p, op); ) -#define BIT_FOREACH_ISSET(_s, i, p) _BIT_FOREACH(_s, i, p, ) -#define BIT_FOREACH_ISCLR(_s, i, p) _BIT_FOREACH(_s, i, p, ~) +#define __BIT_FOREACH_ISSET(_s, i, p) __BIT_FOREACH(_s, i, p, ) +#define __BIT_FOREACH_ISCLR(_s, i, p) __BIT_FOREACH(_s, i, p, ~) -#define BITSET_T_INITIALIZER(x) \ +#define __BITSET_T_INITIALIZER(x) \ { .__bits = { x } } -#define BITSET_FSET(n) \ +#define __BITSET_FSET(n) \ [ 0 ... ((n) - 1) ] = (-1L) -#define BITSET_SIZE(_s) (__bitset_words((_s)) * sizeof(long)) +#define __BITSET_SIZE(_s) (__bitset_words((_s)) * sizeof(long)) /* * Dynamically allocate a bitset. */ -#define BITSET_ALLOC(_s, mt, mf) malloc(BITSET_SIZE((_s)), mt, (mf)) +#define __BITSET_ALLOC(_s, mt, mf) malloc(__BITSET_SIZE((_s)), mt, (mf)) + +#if defined(_KERNEL) || defined(_WANT_FREEBSD_BITSET) +#define BIT_AND(_s, d, s) __BIT_AND(_s, d, s) +#define BIT_AND2(_s, d, s1, s2) __BIT_AND2(_s, d, s1, s2) +#define BIT_ANDNOT(_s, d, s) __BIT_ANDNOT(_s, d, s) +#define BIT_ANDNOT2(_s, d, s1, s2) __BIT_ANDNOT2(_s, d, s1, s2) +#define BIT_AND_ATOMIC(_s, d, s) __BIT_AND_ATOMIC(_s, d, s) +#define BIT_CLR(_s, n, p) __BIT_CLR(_s, n, p) +#define BIT_CLR_ATOMIC(_s, n, p) __BIT_CLR_ATOMIC(_s, n, p) +#define BIT_CMP(_s, p, c) __BIT_CMP(_s, p, c) +#define BIT_COPY(_s, f, t) __BIT_COPY(_s, f, t) +#define BIT_COPY_STORE_REL(_s, f, t) __BIT_COPY_STORE_REL(_s, f, t) +#define BIT_COUNT(_s, p) __BIT_COUNT(_s, p) +#define BIT_EMPTY(_s, p) __BIT_EMPTY(_s, p) +#define BIT_FFS(_s, p) __BIT_FFS(_s, p) +#define BIT_FFS_AT(_s, p, start) __BIT_FFS_AT(_s, p, start) +#define BIT_FILL(_s, p) __BIT_FILL(_s, p) +#define BIT_FLS(_s, p) __BIT_FLS(_s, p) +#define BIT_FOREACH(_s, i, p, op) __BIT_FOREACH(_s, i, p, op) +#define BIT_FOREACH_ADVANCE(_s, i, p, op) __BIT_FOREACH_ADVANCE(_s, i, p, op) +#define BIT_FOREACH_ISCLR(_s, i, p) __BIT_FOREACH_ISCLR(_s, i, p) +#define BIT_FOREACH_ISSET(_s, i, p) __BIT_FOREACH_ISSET(_s, i, p) +#define BIT_ISFULLSET(_s, p) __BIT_ISFULLSET(_s, p) +#define BIT_ISSET(_s, n, p) __BIT_ISSET(_s, n, p) +#define BIT_OR(_s, d, s) __BIT_OR(_s, d, s) +#define BIT_OR2(_s, d, s1, s2) __BIT_OR2(_s, d, s1, s2) +#define BIT_OR_ATOMIC(_s, d, s) __BIT_OR_ATOMIC(_s, d, s) +#define BIT_OVERLAP(_s, p, c) __BIT_OVERLAP(_s, p, c) +#define BIT_SET(_s, n, p) __BIT_SET(_s, n, p) +#define BIT_SETOF(_s, n, p) __BIT_SETOF(_s, n, p) +#define BIT_SET_ATOMIC(_s, n, p) __BIT_SET_ATOMIC(_s, n, p) +#define BIT_SET_ATOMIC_ACQ(_s, n, p) __BIT_SET_ATOMIC_ACQ(_s, n, p) +#define BIT_SUBSET(_s, p, c) __BIT_SUBSET(_s, p, c) +#define BIT_TEST_CLR_ATOMIC(_s, n, p) __BIT_TEST_CLR_ATOMIC(_s, n, p) +#define BIT_TEST_SET_ATOMIC(_s, n, p) __BIT_TEST_SET_ATOMIC(_s, n, p) +#define BIT_XOR(_s, d, s) __BIT_XOR(_s, d, s) +#define BIT_XOR2(_s, d, s1, s2) __BIT_XOR2(_s, d, s1, s2) +#define BIT_ZERO(_s, p) __BIT_ZERO(_s, p) + +#define BITSET_ALLOC(_s, mt, mf) __BITSET_ALLOC(_s, mt, mf) +#define BITSET_FSET(n) __BITSET_FSET(n) +#define BITSET_SIZE(_s) __BITSET_SIZE(_s) +#define BITSET_T_INITIALIZER(x) __BITSET_T_INITIALIZER(x) +#endif #endif /* !_SYS_BITSET_H_ */ diff --git a/sys/sys/cpuset.h b/sys/sys/cpuset.h index 602f73fbe5e4..71317670c593 100644 --- a/sys/sys/cpuset.h +++ b/sys/sys/cpuset.h @@ -1,163 +1,163 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008, Jeffrey Roberson * All rights reserved. * * Copyright (c) 2008 Nokia Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_CPUSET_H_ #define _SYS_CPUSET_H_ #include #include #define _NCPUBITS _BITSET_BITS #define _NCPUWORDS __bitset_words(CPU_SETSIZE) #define CPUSETBUFSIZ ((2 + sizeof(long) * 2) * _NCPUWORDS) -#define CPU_CLR(n, p) BIT_CLR(CPU_SETSIZE, n, p) -#define CPU_COPY(f, t) BIT_COPY(CPU_SETSIZE, f, t) -#define CPU_ISSET(n, p) BIT_ISSET(CPU_SETSIZE, n, p) -#define CPU_SET(n, p) BIT_SET(CPU_SETSIZE, n, p) -#define CPU_ZERO(p) BIT_ZERO(CPU_SETSIZE, p) -#define CPU_FILL(p) BIT_FILL(CPU_SETSIZE, p) -#define CPU_SETOF(n, p) BIT_SETOF(CPU_SETSIZE, n, p) -#define CPU_EQUAL(p, c) (BIT_CMP(CPU_SETSIZE, p, c) == 0) -#define CPU_EMPTY(p) BIT_EMPTY(CPU_SETSIZE, p) -#define CPU_ISFULLSET(p) BIT_ISFULLSET(CPU_SETSIZE, p) -#define CPU_SUBSET(p, c) BIT_SUBSET(CPU_SETSIZE, p, c) -#define CPU_OVERLAP(p, c) BIT_OVERLAP(CPU_SETSIZE, p, c) -#define CPU_CMP(p, c) BIT_CMP(CPU_SETSIZE, p, c) -#define CPU_OR(d, s) BIT_OR(CPU_SETSIZE, d, s) -#define CPU_AND(d, s) BIT_AND(CPU_SETSIZE, d, s) -#define CPU_ANDNOT(d, s) BIT_ANDNOT(CPU_SETSIZE, d, s) -#define CPU_CLR_ATOMIC(n, p) BIT_CLR_ATOMIC(CPU_SETSIZE, n, p) -#define CPU_SET_ATOMIC(n, p) BIT_SET_ATOMIC(CPU_SETSIZE, n, p) -#define CPU_SET_ATOMIC_ACQ(n, p) BIT_SET_ATOMIC_ACQ(CPU_SETSIZE, n, p) -#define CPU_AND_ATOMIC(n, p) BIT_AND_ATOMIC(CPU_SETSIZE, n, p) -#define CPU_OR_ATOMIC(d, s) BIT_OR_ATOMIC(CPU_SETSIZE, d, s) -#define CPU_COPY_STORE_REL(f, t) BIT_COPY_STORE_REL(CPU_SETSIZE, f, t) -#define CPU_FFS(p) BIT_FFS(CPU_SETSIZE, p) -#define CPU_FLS(p) BIT_FLS(CPU_SETSIZE, p) -#define CPU_FOREACH_ISSET(i, p) BIT_FOREACH_ISSET(CPU_SETSIZE, i, p) -#define CPU_FOREACH_ISCLR(i, p) BIT_FOREACH_ISCLR(CPU_SETSIZE, i, p) -#define CPU_COUNT(p) ((int)BIT_COUNT(CPU_SETSIZE, p)) -#define CPUSET_FSET BITSET_FSET(_NCPUWORDS) -#define CPUSET_T_INITIALIZER BITSET_T_INITIALIZER +#define CPU_CLR(n, p) __BIT_CLR(CPU_SETSIZE, n, p) +#define CPU_COPY(f, t) __BIT_COPY(CPU_SETSIZE, f, t) +#define CPU_ISSET(n, p) __BIT_ISSET(CPU_SETSIZE, n, p) +#define CPU_SET(n, p) __BIT_SET(CPU_SETSIZE, n, p) +#define CPU_ZERO(p) __BIT_ZERO(CPU_SETSIZE, p) +#define CPU_FILL(p) __BIT_FILL(CPU_SETSIZE, p) +#define CPU_SETOF(n, p) __BIT_SETOF(CPU_SETSIZE, n, p) +#define CPU_EQUAL(p, c) (__BIT_CMP(CPU_SETSIZE, p, c) == 0) +#define CPU_EMPTY(p) __BIT_EMPTY(CPU_SETSIZE, p) +#define CPU_ISFULLSET(p) __BIT_ISFULLSET(CPU_SETSIZE, p) +#define CPU_SUBSET(p, c) __BIT_SUBSET(CPU_SETSIZE, p, c) +#define CPU_OVERLAP(p, c) __BIT_OVERLAP(CPU_SETSIZE, p, c) +#define CPU_CMP(p, c) __BIT_CMP(CPU_SETSIZE, p, c) +#define CPU_OR(d, s) __BIT_OR(CPU_SETSIZE, d, s) +#define CPU_AND(d, s) __BIT_AND(CPU_SETSIZE, d, s) +#define CPU_ANDNOT(d, s) __BIT_ANDNOT(CPU_SETSIZE, d, s) +#define CPU_CLR_ATOMIC(n, p) __BIT_CLR_ATOMIC(CPU_SETSIZE, n, p) +#define CPU_SET_ATOMIC(n, p) __BIT_SET_ATOMIC(CPU_SETSIZE, n, p) +#define CPU_SET_ATOMIC_ACQ(n, p) __BIT_SET_ATOMIC_ACQ(CPU_SETSIZE, n, p) +#define CPU_AND_ATOMIC(n, p) __BIT_AND_ATOMIC(CPU_SETSIZE, n, p) +#define CPU_OR_ATOMIC(d, s) __BIT_OR_ATOMIC(CPU_SETSIZE, d, s) +#define CPU_COPY_STORE_REL(f, t) __BIT_COPY_STORE_REL(CPU_SETSIZE, f, t) +#define CPU_FFS(p) __BIT_FFS(CPU_SETSIZE, p) +#define CPU_FLS(p) __BIT_FLS(CPU_SETSIZE, p) +#define CPU_FOREACH_ISSET(i, p) __BIT_FOREACH_ISSET(CPU_SETSIZE, i, p) +#define CPU_FOREACH_ISCLR(i, p) __BIT_FOREACH_ISCLR(CPU_SETSIZE, i, p) +#define CPU_COUNT(p) ((int)__BIT_COUNT(CPU_SETSIZE, p)) +#define CPUSET_FSET __BITSET_FSET(_NCPUWORDS) +#define CPUSET_T_INITIALIZER(x) __BITSET_T_INITIALIZER(x) /* * Valid cpulevel_t values. */ #define CPU_LEVEL_ROOT 1 /* All system cpus. */ #define CPU_LEVEL_CPUSET 2 /* Available cpus for which. */ #define CPU_LEVEL_WHICH 3 /* Actual mask/id for which. */ /* * Valid cpuwhich_t values. */ #define CPU_WHICH_TID 1 /* Specifies a thread id. */ #define CPU_WHICH_PID 2 /* Specifies a process id. */ #define CPU_WHICH_CPUSET 3 /* Specifies a set id. */ #define CPU_WHICH_IRQ 4 /* Specifies an irq #. */ #define CPU_WHICH_JAIL 5 /* Specifies a jail id. */ #define CPU_WHICH_DOMAIN 6 /* Specifies a NUMA domain id. */ #define CPU_WHICH_INTRHANDLER 7 /* Specifies an irq # (not ithread). */ #define CPU_WHICH_ITHREAD 8 /* Specifies an irq's ithread. */ /* * Reserved cpuset identifiers. */ #define CPUSET_INVALID -1 #define CPUSET_DEFAULT 0 #ifdef _KERNEL #include LIST_HEAD(setlist, cpuset); /* * cpusets encapsulate cpu binding information for one or more threads. * * a - Accessed with atomics. * s - Set at creation, never modified. Only a ref required to read. * c - Locked internally by a cpuset lock. * * The bitmask is only modified while holding the cpuset lock. It may be * read while only a reference is held but the consumer must be prepared * to deal with inconsistent results. */ struct cpuset { volatile u_int cs_ref; /* (a) Reference count. */ int cs_flags; /* (s) Flags from below. */ LIST_ENTRY(cpuset) cs_link; /* (c) All identified sets. */ LIST_ENTRY(cpuset) cs_siblings; /* (c) Sibling set link. */ struct setlist cs_children; /* (c) List of children. */ struct domainset *cs_domain; /* (c) NUMA policy. */ cpusetid_t cs_id; /* (s) Id or INVALID. */ struct cpuset *cs_parent; /* (s) Pointer to our parent. */ cpuset_t cs_mask; /* bitmask of valid cpus. */ }; #define CPU_SET_ROOT 0x0001 /* Set is a root set. */ #define CPU_SET_RDONLY 0x0002 /* No modification allowed. */ extern cpuset_t *cpuset_root; struct prison; struct proc; struct thread; struct cpuset *cpuset_thread0(void); struct cpuset *cpuset_ref(struct cpuset *); void cpuset_rel(struct cpuset *); int cpuset_setthread(lwpid_t id, cpuset_t *); int cpuset_setithread(lwpid_t id, int cpu); int cpuset_create_root(struct prison *, struct cpuset **); int cpuset_setproc_update_set(struct proc *, struct cpuset *); int cpuset_which(cpuwhich_t, id_t, struct proc **, struct thread **, struct cpuset **); void cpuset_kernthread(struct thread *); char *cpusetobj_strprint(char *, const cpuset_t *); int cpusetobj_strscan(cpuset_t *, const char *); #ifdef DDB void ddb_display_cpuset(const cpuset_t *); #endif #else __BEGIN_DECLS int cpuset(cpusetid_t *); int cpuset_setid(cpuwhich_t, id_t, cpusetid_t); int cpuset_getid(cpulevel_t, cpuwhich_t, id_t, cpusetid_t *); int cpuset_getaffinity(cpulevel_t, cpuwhich_t, id_t, size_t, cpuset_t *); int cpuset_setaffinity(cpulevel_t, cpuwhich_t, id_t, size_t, const cpuset_t *); __END_DECLS #endif #endif /* !_SYS_CPUSET_H_ */ diff --git a/sys/sys/domainset.h b/sys/sys/domainset.h index 2113196d8d26..51b5011fcb4e 100644 --- a/sys/sys/domainset.h +++ b/sys/sys/domainset.h @@ -1,130 +1,130 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017, Jeffrey Roberson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_DOMAINSET_H_ #define _SYS_DOMAINSET_H_ #include #include #include #define _NDOMAINSETBITS _BITSET_BITS #define _NDOMAINSETWORDS __bitset_words(DOMAINSET_SETSIZE) #define DOMAINSETBUFSIZ \ (((2 + sizeof(long) * 2) * _NDOMAINSETWORDS) + \ sizeof("::") + sizeof(__XSTRING(DOMAINSET_POLICY_MAX)) + \ sizeof(__XSTRING(MAXMEMDOM))) -#define DOMAINSET_CLR(n, p) BIT_CLR(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_COPY(f, t) BIT_COPY(DOMAINSET_SETSIZE, f, t) -#define DOMAINSET_ISSET(n, p) BIT_ISSET(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_SET(n, p) BIT_SET(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_ZERO(p) BIT_ZERO(DOMAINSET_SETSIZE, p) -#define DOMAINSET_FILL(p) BIT_FILL(DOMAINSET_SETSIZE, p) -#define DOMAINSET_SETOF(n, p) BIT_SETOF(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_EMPTY(p) BIT_EMPTY(DOMAINSET_SETSIZE, p) -#define DOMAINSET_ISFULLSET(p) BIT_ISFULLSET(DOMAINSET_SETSIZE, p) -#define DOMAINSET_SUBSET(p, c) BIT_SUBSET(DOMAINSET_SETSIZE, p, c) -#define DOMAINSET_OVERLAP(p, c) BIT_OVERLAP(DOMAINSET_SETSIZE, p, c) -#define DOMAINSET_CMP(p, c) BIT_CMP(DOMAINSET_SETSIZE, p, c) -#define DOMAINSET_OR(d, s) BIT_OR(DOMAINSET_SETSIZE, d, s) -#define DOMAINSET_AND(d, s) BIT_AND(DOMAINSET_SETSIZE, d, s) -#define DOMAINSET_ANDNOT(d, s) BIT_ANDNOT(DOMAINSET_SETSIZE, d, s) -#define DOMAINSET_CLR_ATOMIC(n, p) BIT_CLR_ATOMIC(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_SET_ATOMIC(n, p) BIT_SET_ATOMIC(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_CLR(n, p) __BIT_CLR(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_COPY(f, t) __BIT_COPY(DOMAINSET_SETSIZE, f, t) +#define DOMAINSET_ISSET(n, p) __BIT_ISSET(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_SET(n, p) __BIT_SET(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_ZERO(p) __BIT_ZERO(DOMAINSET_SETSIZE, p) +#define DOMAINSET_FILL(p) __BIT_FILL(DOMAINSET_SETSIZE, p) +#define DOMAINSET_SETOF(n, p) __BIT_SETOF(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_EMPTY(p) __BIT_EMPTY(DOMAINSET_SETSIZE, p) +#define DOMAINSET_ISFULLSET(p) __BIT_ISFULLSET(DOMAINSET_SETSIZE, p) +#define DOMAINSET_SUBSET(p, c) __BIT_SUBSET(DOMAINSET_SETSIZE, p, c) +#define DOMAINSET_OVERLAP(p, c) __BIT_OVERLAP(DOMAINSET_SETSIZE, p, c) +#define DOMAINSET_CMP(p, c) __BIT_CMP(DOMAINSET_SETSIZE, p, c) +#define DOMAINSET_OR(d, s) __BIT_OR(DOMAINSET_SETSIZE, d, s) +#define DOMAINSET_AND(d, s) __BIT_AND(DOMAINSET_SETSIZE, d, s) +#define DOMAINSET_ANDNOT(d, s) __BIT_ANDNOT(DOMAINSET_SETSIZE, d, s) +#define DOMAINSET_CLR_ATOMIC(n, p) __BIT_CLR_ATOMIC(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_SET_ATOMIC(n, p) __BIT_SET_ATOMIC(DOMAINSET_SETSIZE, n, p) #define DOMAINSET_SET_ATOMIC_ACQ(n, p) \ - BIT_SET_ATOMIC_ACQ(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_AND_ATOMIC(n, p) BIT_AND_ATOMIC(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_OR_ATOMIC(d, s) BIT_OR_ATOMIC(DOMAINSET_SETSIZE, d, s) + __BIT_SET_ATOMIC_ACQ(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_AND_ATOMIC(n, p) __BIT_AND_ATOMIC(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_OR_ATOMIC(d, s) __BIT_OR_ATOMIC(DOMAINSET_SETSIZE, d, s) #define DOMAINSET_COPY_STORE_REL(f, t) \ - BIT_COPY_STORE_REL(DOMAINSET_SETSIZE, f, t) -#define DOMAINSET_FFS(p) BIT_FFS(DOMAINSET_SETSIZE, p) -#define DOMAINSET_FLS(p) BIT_FLS(DOMAINSET_SETSIZE, p) -#define DOMAINSET_COUNT(p) ((int)BIT_COUNT(DOMAINSET_SETSIZE, p)) -#define DOMAINSET_FSET BITSET_FSET(_NDOMAINSETWORDS) -#define DOMAINSET_T_INITIALIZER BITSET_T_INITIALIZER + __BIT_COPY_STORE_REL(DOMAINSET_SETSIZE, f, t) +#define DOMAINSET_FFS(p) __BIT_FFS(DOMAINSET_SETSIZE, p) +#define DOMAINSET_FLS(p) __BIT_FLS(DOMAINSET_SETSIZE, p) +#define DOMAINSET_COUNT(p) ((int)__BIT_COUNT(DOMAINSET_SETSIZE, p)) +#define DOMAINSET_FSET __BITSET_FSET(_NDOMAINSETWORDS) +#define DOMAINSET_T_INITIALIZER(x) __BITSET_T_INITIALIZER(x) #define DOMAINSET_POLICY_INVALID 0 #define DOMAINSET_POLICY_ROUNDROBIN 1 #define DOMAINSET_POLICY_FIRSTTOUCH 2 #define DOMAINSET_POLICY_PREFER 3 #define DOMAINSET_POLICY_INTERLEAVE 4 #define DOMAINSET_POLICY_MAX DOMAINSET_POLICY_INTERLEAVE #ifdef _KERNEL #if MAXMEMDOM < 256 typedef uint8_t domainid_t; #else typedef uint16_t domainid_t; #endif struct domainset { LIST_ENTRY(domainset) ds_link; domainset_t ds_mask; /* allowed domains. */ uint16_t ds_policy; /* Policy type. */ domainid_t ds_prefer; /* Preferred domain or -1. */ domainid_t ds_cnt; /* popcnt from above. */ domainid_t ds_order[MAXMEMDOM]; /* nth domain table. */ }; extern struct domainset domainset_firsttouch; #define DOMAINSET_FT() (&domainset_firsttouch) extern struct domainset domainset_interleave; #define DOMAINSET_IL() (&domainset_interleave) extern struct domainset domainset_fixed[MAXMEMDOM], domainset_prefer[MAXMEMDOM]; #define DOMAINSET_FIXED(domain) (&domainset_fixed[(domain)]) #define DOMAINSET_PREF(domain) (&domainset_prefer[(domain)]) extern struct domainset domainset_roundrobin; #define DOMAINSET_RR() (&domainset_roundrobin) void domainset_init(void); void domainset_zero(void); /* * Add a domainset to the system based on a key initializing policy, prefer, * and mask. Do not create and directly use domainset structures. The * returned value will not match the key pointer. */ struct domainset *domainset_create(const struct domainset *); #ifdef _SYS_SYSCTL_H_ int sysctl_handle_domainset(SYSCTL_HANDLER_ARGS); #endif #else __BEGIN_DECLS int cpuset_getdomain(cpulevel_t, cpuwhich_t, id_t, size_t, domainset_t *, int *); int cpuset_setdomain(cpulevel_t, cpuwhich_t, id_t, size_t, const domainset_t *, int); __END_DECLS #endif #endif /* !_SYS_DOMAINSET_H_ */ diff --git a/tests/sys/sys/bitset_test.c b/tests/sys/sys/bitset_test.c index 781b523dae97..d193d2e3014c 100644 --- a/tests/sys/sys/bitset_test.c +++ b/tests/sys/sys/bitset_test.c @@ -1,88 +1,90 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2021 The FreeBSD Foundation * * This software was developed by Mark Johnston under sponsorship from * the FreeBSD Foundation. */ +#define _WANT_FREEBSD_BITSET + #include #include #include #include #include #include BITSET_DEFINE(bs256, 256); ATF_TC_WITHOUT_HEAD(bit_foreach); ATF_TC_BODY(bit_foreach, tc) { struct bs256 bs0, bs1, bsrand; int setc, clrc, i; #define _BIT_FOREACH_COUNT(s, bs) do { \ int prev = -1; \ setc = clrc = 0; \ BIT_FOREACH_ISSET((s), i, (bs)) { \ ATF_REQUIRE_MSG(prev < i, "incorrect bit ordering"); \ ATF_REQUIRE_MSG(BIT_ISSET((s), i, (bs)), \ "bit %d is not set", i); \ setc++; \ prev = i; \ } \ prev = -1; \ BIT_FOREACH_ISCLR((s), i, (bs)) { \ ATF_REQUIRE_MSG(prev < i, "incorrect bit ordering"); \ ATF_REQUIRE_MSG(!BIT_ISSET((s), i, (bs)), \ "bit %d is set", i); \ clrc++; \ prev = i; \ } \ } while (0) /* * Create several bitsets, and for each one count the number * of set and clear bits and make sure they match what we expect. */ BIT_FILL(256, &bs1); _BIT_FOREACH_COUNT(256, &bs1); ATF_REQUIRE_MSG(setc == 256, "incorrect set count %d", setc); ATF_REQUIRE_MSG(clrc == 0, "incorrect clear count %d", clrc); BIT_ZERO(256, &bs0); _BIT_FOREACH_COUNT(256, &bs0); ATF_REQUIRE_MSG(setc == 0, "incorrect set count %d", setc); ATF_REQUIRE_MSG(clrc == 256, "incorrect clear count %d", clrc); BIT_ZERO(256, &bsrand); for (i = 0; i < 256; i++) if (random() % 2 != 0) BIT_SET(256, i, &bsrand); _BIT_FOREACH_COUNT(256, &bsrand); ATF_REQUIRE_MSG(setc + clrc == 256, "incorrect counts %d, %d", setc, clrc); /* * Try to verify that we can safely clear bits in the set while * iterating. */ BIT_FOREACH_ISSET(256, i, &bsrand) { ATF_REQUIRE(setc-- > 0); BIT_CLR(256, i, &bsrand); } _BIT_FOREACH_COUNT(256, &bsrand); ATF_REQUIRE_MSG(setc == 0, "incorrect set count %d", setc); ATF_REQUIRE_MSG(clrc == 256, "incorrect clear count %d", clrc); #undef _BIT_FOREACH_COUNT } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, bit_foreach); return (atf_no_error()); } diff --git a/usr.bin/cpuset/cpuset.c b/usr.bin/cpuset/cpuset.c index e039030415bb..79c6c2b6ca79 100644 --- a/usr.bin/cpuset/cpuset.c +++ b/usr.bin/cpuset/cpuset.c @@ -1,473 +1,475 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2007, 2008 Jeffrey Roberson * All rights reserved. * * Copyright (c) 2008 Nokia Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); +#define _WANT_FREEBSD_BITSET + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int Cflag; static int cflag; static int dflag; static int gflag; static int iflag; static int jflag; static int lflag; static int nflag; static int pflag; static int rflag; static int sflag; static int tflag; static int xflag; static id_t id; static cpulevel_t level; static cpuwhich_t which; static void usage(void); struct numa_policy { const char *name; int policy; }; static struct numa_policy policies[] = { { "round-robin", DOMAINSET_POLICY_ROUNDROBIN }, { "rr", DOMAINSET_POLICY_ROUNDROBIN }, { "first-touch", DOMAINSET_POLICY_FIRSTTOUCH }, { "ft", DOMAINSET_POLICY_FIRSTTOUCH }, { "prefer", DOMAINSET_POLICY_PREFER }, { "interleave", DOMAINSET_POLICY_INTERLEAVE}, { "il", DOMAINSET_POLICY_INTERLEAVE}, { NULL, DOMAINSET_POLICY_INVALID } }; static void printset(struct bitset *mask, int size); static void parselist(char *list, struct bitset *mask, int size) { enum { NONE, NUM, DASH } state; int lastnum; int curnum; char *l; state = NONE; curnum = lastnum = 0; for (l = list; *l != '\0';) { if (isdigit(*l)) { curnum = atoi(l); if (curnum >= size) errx(EXIT_FAILURE, "List entry %d exceeds maximum of %d", curnum, size - 1); while (isdigit(*l)) l++; switch (state) { case NONE: lastnum = curnum; state = NUM; break; case DASH: for (; lastnum <= curnum; lastnum++) BIT_SET(size, lastnum, mask); state = NONE; break; case NUM: default: goto parserr; } continue; } switch (*l) { case ',': switch (state) { case NONE: break; case NUM: BIT_SET(size, curnum, mask); state = NONE; break; case DASH: goto parserr; break; } break; case '-': if (state != NUM) goto parserr; state = DASH; break; default: goto parserr; } l++; } switch (state) { case NONE: break; case NUM: BIT_SET(size, curnum, mask); break; case DASH: goto parserr; } return; parserr: errx(EXIT_FAILURE, "Malformed list %s", list); } static void parsecpulist(char *list, cpuset_t *mask) { if (strcasecmp(list, "all") == 0) { if (cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, sizeof(*mask), mask) != 0) err(EXIT_FAILURE, "getaffinity"); return; } parselist(list, (struct bitset *)mask, CPU_SETSIZE); } /* * permissively parse policy:domain list * allow: * round-robin:0-4 explicit * round-robin:all explicit root domains * 0-4 implicit root policy * round-robin implicit root domains * all explicit root domains and implicit policy */ static void parsedomainlist(char *list, domainset_t *mask, int *policyp) { domainset_t rootmask; struct numa_policy *policy; char *l; int p; /* * Use the rootset's policy as the default for unspecified policies. */ if (cpuset_getdomain(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, sizeof(rootmask), &rootmask, &p) != 0) err(EXIT_FAILURE, "getdomain"); l = list; for (policy = &policies[0]; policy->name != NULL; policy++) { if (strncasecmp(l, policy->name, strlen(policy->name)) == 0) { p = policy->policy; l += strlen(policy->name); if (*l != ':' && *l != '\0') errx(EXIT_FAILURE, "Malformed list %s", list); if (*l == ':') l++; break; } } *policyp = p; if (strcasecmp(l, "all") == 0 || *l == '\0') { DOMAINSET_COPY(&rootmask, mask); return; } parselist(l, (struct bitset *)mask, DOMAINSET_SETSIZE); } static void printset(struct bitset *mask, int size) { int once; int bit; for (once = 0, bit = 0; bit < size; bit++) { if (BIT_ISSET(size, bit, mask)) { if (once == 0) { printf("%d", bit); once = 1; } else printf(", %d", bit); } } printf("\n"); } static const char *whichnames[] = { NULL, "tid", "pid", "cpuset", "irq", "jail", "domain" }; static const char *levelnames[] = { NULL, " root", " cpuset", "" }; static const char *policynames[] = { "invalid", "round-robin", "first-touch", "prefer", "interleave" }; static void printaffinity(void) { domainset_t domain; cpuset_t mask; int policy; if (cpuset_getaffinity(level, which, id, sizeof(mask), &mask) != 0) err(EXIT_FAILURE, "getaffinity"); printf("%s %jd%s mask: ", whichnames[which], (intmax_t)id, levelnames[level]); printset((struct bitset *)&mask, CPU_SETSIZE); if (dflag || xflag) goto out; if (cpuset_getdomain(level, which, id, sizeof(domain), &domain, &policy) != 0) err(EXIT_FAILURE, "getdomain"); printf("%s %jd%s domain policy: %s mask: ", whichnames[which], (intmax_t)id, levelnames[level], policynames[policy]); printset((struct bitset *)&domain, DOMAINSET_SETSIZE); out: exit(EXIT_SUCCESS); } static void printsetid(void) { cpusetid_t setid; /* * Only LEVEL_WHICH && WHICH_CPUSET has a numbered id. */ if (level == CPU_LEVEL_WHICH && !sflag) level = CPU_LEVEL_CPUSET; if (cpuset_getid(level, which, id, &setid)) err(errno, "getid"); printf("%s %jd%s id: %d\n", whichnames[which], (intmax_t)id, levelnames[level], setid); } int main(int argc, char *argv[]) { domainset_t domains; cpusetid_t setid; cpuset_t mask; int policy; lwpid_t tid; pid_t pid; int ch; CPU_ZERO(&mask); DOMAINSET_ZERO(&domains); policy = DOMAINSET_POLICY_INVALID; level = CPU_LEVEL_WHICH; which = CPU_WHICH_PID; id = pid = tid = setid = -1; while ((ch = getopt(argc, argv, "Ccd:gij:l:n:p:rs:t:x:")) != -1) { switch (ch) { case 'C': Cflag = 1; break; case 'c': cflag = 1; level = CPU_LEVEL_CPUSET; break; case 'd': dflag = 1; which = CPU_WHICH_DOMAIN; id = atoi(optarg); break; case 'g': gflag = 1; break; case 'i': iflag = 1; break; case 'j': jflag = 1; which = CPU_WHICH_JAIL; id = jail_getid(optarg); if (id < 0) errx(EXIT_FAILURE, "%s", jail_errmsg); break; case 'l': lflag = 1; parsecpulist(optarg, &mask); break; case 'n': nflag = 1; parsedomainlist(optarg, &domains, &policy); break; case 'p': pflag = 1; which = CPU_WHICH_PID; id = pid = atoi(optarg); break; case 'r': level = CPU_LEVEL_ROOT; rflag = 1; break; case 's': sflag = 1; which = CPU_WHICH_CPUSET; id = setid = atoi(optarg); break; case 't': tflag = 1; which = CPU_WHICH_TID; id = tid = atoi(optarg); break; case 'x': xflag = 1; which = CPU_WHICH_IRQ; id = atoi(optarg); break; default: usage(); } } argc -= optind; argv += optind; if (gflag) { if (argc || Cflag || lflag || nflag) usage(); /* Only one identity specifier. */ if (dflag + jflag + xflag + sflag + pflag + tflag > 1) usage(); if (iflag) printsetid(); else printaffinity(); exit(EXIT_SUCCESS); } if (dflag || iflag || rflag) usage(); /* * The user wants to run a command with a set and possibly cpumask. */ if (argc) { if (Cflag || pflag || tflag || xflag || jflag) usage(); if (sflag) { if (cpuset_setid(CPU_WHICH_PID, -1, setid)) err(argc, "setid"); } else { if (cpuset(&setid)) err(argc, "newid"); } if (lflag) { if (cpuset_setaffinity(level, CPU_WHICH_PID, -1, sizeof(mask), &mask) != 0) err(EXIT_FAILURE, "setaffinity"); } if (nflag) { if (cpuset_setdomain(level, CPU_WHICH_PID, -1, sizeof(domains), &domains, policy) != 0) err(EXIT_FAILURE, "setdomain"); } errno = 0; execvp(*argv, argv); err(errno == ENOENT ? 127 : 126, "%s", *argv); } /* * We're modifying something that presently exists. */ if (Cflag && (jflag || !pflag || sflag || tflag || xflag)) usage(); if ((!lflag && !nflag) && cflag) usage(); if ((!lflag && !nflag) && !(Cflag || sflag)) usage(); /* You can only set a mask on a thread. */ if (tflag && (sflag | pflag | xflag | jflag)) usage(); /* You can only set a mask on an irq. */ if (xflag && (jflag | pflag | sflag | tflag)) usage(); if (Cflag) { /* * Create a new cpuset and move the specified process * into the set. */ if (cpuset(&setid) < 0) err(EXIT_FAILURE, "newid"); sflag = 1; } if (pflag && sflag) { if (cpuset_setid(CPU_WHICH_PID, pid, setid)) err(EXIT_FAILURE, "setid"); /* * If the user specifies a set and a list we want the mask * to effect the pid and not the set. */ which = CPU_WHICH_PID; id = pid; } if (lflag) { if (cpuset_setaffinity(level, which, id, sizeof(mask), &mask) != 0) err(EXIT_FAILURE, "setaffinity"); } if (nflag) { if (cpuset_setdomain(level, which, id, sizeof(domains), &domains, policy) != 0) err(EXIT_FAILURE, "setdomain"); } exit(EXIT_SUCCESS); } static void usage(void) { fprintf(stderr, "usage: cpuset [-l cpu-list] [-n policy:domain-list] [-s setid] cmd ...\n"); fprintf(stderr, " cpuset [-l cpu-list] [-n policy:domain-list] [-s setid] -p pid\n"); fprintf(stderr, " cpuset [-c] [-l cpu-list] [-n policy:domain-list] -C -p pid\n"); fprintf(stderr, " cpuset [-c] [-l cpu-list] [-n policy:domain-list]\n" " [-j jailid | -p pid | -t tid | -s setid | -x irq]\n"); fprintf(stderr, " cpuset -g [-cir]\n" " [-d domain | -j jailid | -p pid | -t tid | -s setid | -x irq]\n"); exit(1); }