Index: head/sys/compat/linuxkpi/common/include/linux/gfp.h =================================================================== --- head/sys/compat/linuxkpi/common/include/linux/gfp.h (revision 293418) +++ head/sys/compat/linuxkpi/common/include/linux/gfp.h (revision 293419) @@ -1,149 +1,149 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013 Mellanox Technologies, Ltd. * 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 _LINUX_GFP_H_ #define _LINUX_GFP_H_ #include #include #include #include #include #include #include #include #include #define __GFP_NOWARN 0 #define __GFP_HIGHMEM 0 #define __GFP_ZERO M_ZERO #define GFP_NOWAIT M_NOWAIT #define GFP_ATOMIC (M_NOWAIT | M_USE_RESERVE) #define GFP_KERNEL M_WAITOK #define GFP_USER M_WAITOK #define GFP_HIGHUSER M_WAITOK #define GFP_HIGHUSER_MOVABLE M_WAITOK #define GFP_IOFS M_NOWAIT static inline void * page_address(struct page *page) { if (page->object != kmem_object && page->object != kernel_object) return (NULL); return ((void *)(uintptr_t)(VM_MIN_KERNEL_ADDRESS + IDX_TO_OFF(page->pindex))); } static inline unsigned long -_get_page(gfp_t mask) +linux_get_page(gfp_t mask) { return kmem_malloc(kmem_arena, PAGE_SIZE, mask); } -#define get_zeroed_page(mask) _get_page((mask) | M_ZERO) -#define alloc_page(mask) virt_to_page(_get_page((mask))) -#define __get_free_page(mask) _get_page((mask)) +#define get_zeroed_page(mask) linux_get_page((mask) | M_ZERO) +#define alloc_page(mask) virt_to_page(linux_get_page((mask))) +#define __get_free_page(mask) linux_get_page((mask)) static inline void free_page(unsigned long page) { if (page == 0) return; kmem_free(kmem_arena, page, PAGE_SIZE); } static inline void __free_page(struct page *m) { if (m->object != kmem_object) panic("__free_page: Freed page %p not allocated via wrappers.", m); kmem_free(kmem_arena, (vm_offset_t)page_address(m), PAGE_SIZE); } static inline void __free_pages(struct page *m, unsigned int order) { size_t size; if (m == NULL) return; size = PAGE_SIZE << order; kmem_free(kmem_arena, (vm_offset_t)page_address(m), size); } static inline void free_pages(uintptr_t addr, unsigned int order) { if (addr == 0) return; __free_pages(virt_to_page((void *)addr), order); } /* * Alloc pages allocates directly from the buddy allocator on linux so * order specifies a power of two bucket of pages and the results * are expected to be aligned on the size as well. */ static inline struct page * alloc_pages(gfp_t gfp_mask, unsigned int order) { unsigned long page; size_t size; size = PAGE_SIZE << order; page = kmem_alloc_contig(kmem_arena, size, gfp_mask, 0, -1, size, 0, VM_MEMATTR_DEFAULT); if (page == 0) return (NULL); return (virt_to_page(page)); } static inline uintptr_t __get_free_pages(gfp_t gfp_mask, unsigned int order) { struct page *page; page = alloc_pages(gfp_mask, order); if (page == NULL) return (0); return ((uintptr_t)page_address(page)); } #define alloc_pages_node(node, mask, order) alloc_pages(mask, order) #define kmalloc_node(chunk, mask, node) kmalloc(chunk, mask) #endif /* _LINUX_GFP_H_ */ Index: head/sys/compat/linuxkpi/common/include/linux/interrupt.h =================================================================== --- head/sys/compat/linuxkpi/common/include/linux/interrupt.h (revision 293418) +++ head/sys/compat/linuxkpi/common/include/linux/interrupt.h (revision 293419) @@ -1,158 +1,151 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013-2015 Mellanox Technologies, Ltd. * 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 _LINUX_INTERRUPT_H_ #define _LINUX_INTERRUPT_H_ #include #include #include #include typedef irqreturn_t (*irq_handler_t)(int, void *); #define IRQ_RETVAL(x) ((x) != IRQ_NONE) #define IRQF_SHARED RF_SHAREABLE struct irq_ent { struct list_head links; struct device *dev; struct resource *res; void *arg; irqreturn_t (*handler)(int, void *); void *tag; int irq; }; static inline int -_irq_rid(struct device *dev, int irq) +linux_irq_rid(struct device *dev, int irq) { if (irq == dev->irq) return (0); return irq - dev->msix + 1; } -static inline void -_irq_handler(void *ent) -{ - struct irq_ent *irqe; +extern void linux_irq_handler(void *); - irqe = ent; - irqe->handler(irqe->irq, irqe->arg); -} - static inline struct irq_ent * -_irq_ent(struct device *dev, int irq) +linux_irq_ent(struct device *dev, int irq) { struct irq_ent *irqe; list_for_each_entry(irqe, &dev->irqents, links) if (irqe->irq == irq) return (irqe); return (NULL); } static inline int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *arg) { struct resource *res; struct irq_ent *irqe; struct device *dev; int error; int rid; dev = _pci_find_irq_dev(irq); if (dev == NULL) return -ENXIO; - rid = _irq_rid(dev, irq); + rid = linux_irq_rid(dev, irq); res = bus_alloc_resource_any(dev->bsddev, SYS_RES_IRQ, &rid, flags | RF_ACTIVE); if (res == NULL) return (-ENXIO); irqe = kmalloc(sizeof(*irqe), GFP_KERNEL); irqe->dev = dev; irqe->res = res; irqe->arg = arg; irqe->handler = handler; irqe->irq = irq; error = bus_setup_intr(dev->bsddev, res, INTR_TYPE_NET | INTR_MPSAFE, - NULL, _irq_handler, irqe, &irqe->tag); + NULL, linux_irq_handler, irqe, &irqe->tag); if (error) { bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res); kfree(irqe); return (-error); } list_add(&irqe->links, &dev->irqents); return 0; } static inline int bind_irq_to_cpu(unsigned int irq, int cpu_id) { struct irq_ent *irqe; struct device *dev; dev = _pci_find_irq_dev(irq); if (dev == NULL) return (-ENOENT); - irqe = _irq_ent(dev, irq); + irqe = linux_irq_ent(dev, irq); if (irqe == NULL) return (-ENOENT); return (-bus_bind_intr(dev->bsddev, irqe->res, cpu_id)); } static inline void free_irq(unsigned int irq, void *device) { struct irq_ent *irqe; struct device *dev; int rid; dev = _pci_find_irq_dev(irq); if (dev == NULL) return; - rid = _irq_rid(dev, irq); - irqe = _irq_ent(dev, irq); + rid = linux_irq_rid(dev, irq); + irqe = linux_irq_ent(dev, irq); if (irqe == NULL) return; bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag); bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res); list_del(&irqe->links); kfree(irqe); } #endif /* _LINUX_INTERRUPT_H_ */ Index: head/sys/compat/linuxkpi/common/include/linux/kthread.h =================================================================== --- head/sys/compat/linuxkpi/common/include/linux/kthread.h (revision 293418) +++ head/sys/compat/linuxkpi/common/include/linux/kthread.h (revision 293419) @@ -1,107 +1,102 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. * 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 _LINUX_KTHREAD_H_ #define _LINUX_KTHREAD_H_ #include #include #include #include #include #include #include #include static inline void -_kthread_fn(void *arg) +linux_kthread_fn(void *arg) { struct task_struct *task; task = arg; task_struct_set(curthread, task); if (task->should_stop == 0) task->task_ret = task->task_fn(task->task_data); PROC_LOCK(task->task_thread->td_proc); task->should_stop = TASK_STOPPED; wakeup(task); PROC_UNLOCK(task->task_thread->td_proc); kthread_exit(); } static inline struct task_struct * -_kthread_create(int (*threadfn)(void *data), void *data) +linux_kthread_create(int (*threadfn)(void *data), void *data) { struct task_struct *task; task = kzalloc(sizeof(*task), GFP_KERNEL); task->task_fn = threadfn; task->task_data = data; return (task); } -struct task_struct *kthread_create(int (*threadfn)(void *data), - void *data, - const char namefmt[], ...) - __attribute__((format(printf, 3, 4))); - #define kthread_run(fn, data, fmt, ...) \ ({ \ struct task_struct *_task; \ \ - _task = _kthread_create((fn), (data)); \ - if (kthread_add(_kthread_fn, _task, NULL, &_task->task_thread, \ + _task = linux_kthread_create((fn), (data)); \ + if (kthread_add(linux_kthread_fn, _task, NULL, &_task->task_thread, \ 0, 0, fmt, ## __VA_ARGS__)) { \ kfree(_task); \ _task = NULL; \ } else \ task_struct_set(_task->task_thread, _task); \ _task; \ }) #define kthread_should_stop() current->should_stop static inline int kthread_stop(struct task_struct *task) { PROC_LOCK(task->task_thread->td_proc); task->should_stop = TASK_SHOULD_STOP; wake_up_process(task); while (task->should_stop != TASK_STOPPED) msleep(task, &task->task_thread->td_proc->p_mtx, PWAIT, "kstop", hz); PROC_UNLOCK(task->task_thread->td_proc); return task->task_ret; } #endif /* _LINUX_KTHREAD_H_ */ Index: head/sys/compat/linuxkpi/common/include/linux/netdevice.h =================================================================== --- head/sys/compat/linuxkpi/common/include/linux/netdevice.h (revision 293418) +++ head/sys/compat/linuxkpi/common/include/linux/netdevice.h (revision 293419) @@ -1,205 +1,112 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. - * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. + * Copyright (c) 2013-2016 Mellanox Technologies, Ltd. * 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 _LINUX_NETDEVICE_H_ #define _LINUX_NETDEVICE_H_ #include #include #include #include #include #include #include #include #include #include #include struct net { }; extern struct net init_net; #define MAX_ADDR_LEN 20 #define net_device ifnet #define dev_get_by_index(n, idx) ifnet_byindex_ref((idx)) #define dev_hold(d) if_ref((d)) #define dev_put(d) if_rele((d)) #define netif_running(dev) !!((dev)->if_drv_flags & IFF_DRV_RUNNING) #define netif_oper_up(dev) !!((dev)->if_flags & IFF_UP) #define netif_carrier_ok(dev) netif_running(dev) static inline void * netdev_priv(const struct net_device *dev) { return (dev->if_softc); } -static inline void -_handle_ifnet_link_event(void *arg, struct ifnet *ifp, int linkstate) -{ - struct notifier_block *nb; - - nb = arg; - if (linkstate == LINK_STATE_UP) - nb->notifier_call(nb, NETDEV_UP, ifp); - else - nb->notifier_call(nb, NETDEV_DOWN, ifp); -} - -static inline void -_handle_ifnet_arrival_event(void *arg, struct ifnet *ifp) -{ - struct notifier_block *nb; - - nb = arg; - nb->notifier_call(nb, NETDEV_REGISTER, ifp); -} - -static inline void -_handle_ifnet_departure_event(void *arg, struct ifnet *ifp) -{ - struct notifier_block *nb; - - nb = arg; - nb->notifier_call(nb, NETDEV_UNREGISTER, ifp); -} - -static inline void -_handle_iflladdr_event(void *arg, struct ifnet *ifp) -{ - struct notifier_block *nb; - - nb = arg; - nb->notifier_call(nb, NETDEV_CHANGEADDR, ifp); -} - -static inline void -_handle_ifaddr_event(void *arg, struct ifnet *ifp) -{ - struct notifier_block *nb; - - nb = arg; - nb->notifier_call(nb, NETDEV_CHANGEIFADDR, ifp); -} - -static inline int -register_netdevice_notifier(struct notifier_block *nb) -{ - - nb->tags[NETDEV_UP] = EVENTHANDLER_REGISTER( - ifnet_link_event, _handle_ifnet_link_event, nb, 0); - nb->tags[NETDEV_REGISTER] = EVENTHANDLER_REGISTER( - ifnet_arrival_event, _handle_ifnet_arrival_event, nb, 0); - nb->tags[NETDEV_UNREGISTER] = EVENTHANDLER_REGISTER( - ifnet_departure_event, _handle_ifnet_departure_event, nb, 0); - nb->tags[NETDEV_CHANGEADDR] = EVENTHANDLER_REGISTER( - iflladdr_event, _handle_iflladdr_event, nb, 0); - - return (0); -} - -static inline int -register_inetaddr_notifier(struct notifier_block *nb) -{ - - nb->tags[NETDEV_CHANGEIFADDR] = EVENTHANDLER_REGISTER( - ifaddr_event, _handle_ifaddr_event, nb, 0); - return (0); -} - -static inline int -unregister_netdevice_notifier(struct notifier_block *nb) -{ - - EVENTHANDLER_DEREGISTER(ifnet_link_event, nb->tags[NETDEV_UP]); - EVENTHANDLER_DEREGISTER(ifnet_arrival_event, nb->tags[NETDEV_REGISTER]); - EVENTHANDLER_DEREGISTER(ifnet_departure_event, - nb->tags[NETDEV_UNREGISTER]); - EVENTHANDLER_DEREGISTER(iflladdr_event, - nb->tags[NETDEV_CHANGEADDR]); - - return (0); -} - -static inline int -unregister_inetaddr_notifier(struct notifier_block *nb) -{ - - EVENTHANDLER_DEREGISTER(ifaddr_event, - nb->tags[NETDEV_CHANGEIFADDR]); - - return (0); -} - +int register_netdevice_notifier(struct notifier_block *); +int register_inetaddr_notifier(struct notifier_block *); +int unregister_netdevice_notifier(struct notifier_block *); +int unregister_inetaddr_notifier(struct notifier_block *); #define rtnl_lock() #define rtnl_unlock() static inline int dev_mc_delete(struct net_device *dev, void *addr, int alen, int all) { struct sockaddr_dl sdl; if (alen > sizeof(sdl.sdl_data)) return (-EINVAL); memset(&sdl, 0, sizeof(sdl)); sdl.sdl_len = sizeof(sdl); sdl.sdl_family = AF_LINK; sdl.sdl_alen = alen; memcpy(&sdl.sdl_data, addr, alen); return -if_delmulti(dev, (struct sockaddr *)&sdl); } static inline int dev_mc_add(struct net_device *dev, void *addr, int alen, int newonly) { struct sockaddr_dl sdl; if (alen > sizeof(sdl.sdl_data)) return (-EINVAL); memset(&sdl, 0, sizeof(sdl)); sdl.sdl_len = sizeof(sdl); sdl.sdl_family = AF_LINK; sdl.sdl_alen = alen; memcpy(&sdl.sdl_data, addr, alen); return -if_addmulti(dev, (struct sockaddr *)&sdl, NULL); } #endif /* _LINUX_NETDEVICE_H_ */ Index: head/sys/compat/linuxkpi/common/src/linux_compat.c =================================================================== --- head/sys/compat/linuxkpi/common/src/linux_compat.c (revision 293418) +++ head/sys/compat/linuxkpi/common/src/linux_compat.c (revision 293419) @@ -1,1190 +1,1299 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013-2016 Mellanox Technologies, Ltd. * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include MALLOC_DEFINE(M_KMALLOC, "linux", "Linux kmalloc compat"); #include /* Undo Linux compat changes. */ #undef RB_ROOT #undef file #undef cdev #define RB_ROOT(head) (head)->rbh_root struct kobject linux_class_root; struct device linux_root_device; struct class linux_class_misc; struct list_head pci_drivers; struct list_head pci_devices; struct net init_net; spinlock_t pci_lock; struct sx linux_global_rcu_lock; unsigned long linux_timer_hz_mask; int panic_cmp(struct rb_node *one, struct rb_node *two) { panic("no cmp"); } RB_GENERATE(linux_root, rb_node, __entry, panic_cmp); int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list args) { va_list tmp_va; int len; char *old; char *name; char dummy; old = kobj->name; if (old && fmt == NULL) return (0); /* compute length of string */ va_copy(tmp_va, args); len = vsnprintf(&dummy, 0, fmt, tmp_va); va_end(tmp_va); /* account for zero termination */ len++; /* check for error */ if (len < 1) return (-EINVAL); /* allocate memory for string */ name = kzalloc(len, GFP_KERNEL); if (name == NULL) return (-ENOMEM); vsnprintf(name, len, fmt, args); kobj->name = name; /* free old string */ kfree(old); /* filter new string */ for (; *name != '\0'; name++) if (*name == '/') *name = '!'; return (0); } int kobject_set_name(struct kobject *kobj, const char *fmt, ...) { va_list args; int error; va_start(args, fmt); error = kobject_set_name_vargs(kobj, fmt, args); va_end(args); return (error); } static int kobject_add_complete(struct kobject *kobj, struct kobject *parent) { const struct kobj_type *t; int error; kobj->parent = parent; error = sysfs_create_dir(kobj); if (error == 0 && kobj->ktype && kobj->ktype->default_attrs) { struct attribute **attr; t = kobj->ktype; for (attr = t->default_attrs; *attr != NULL; attr++) { error = sysfs_create_file(kobj, *attr); if (error) break; } if (error) sysfs_remove_dir(kobj); } return (error); } int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...) { va_list args; int error; va_start(args, fmt); error = kobject_set_name_vargs(kobj, fmt, args); va_end(args); if (error) return (error); return kobject_add_complete(kobj, parent); } void linux_kobject_release(struct kref *kref) { struct kobject *kobj; char *name; kobj = container_of(kref, struct kobject, kref); sysfs_remove_dir(kobj); name = kobj->name; if (kobj->ktype && kobj->ktype->release) kobj->ktype->release(kobj); kfree(name); } static void linux_kobject_kfree(struct kobject *kobj) { kfree(kobj); } static void linux_kobject_kfree_name(struct kobject *kobj) { if (kobj) { kfree(kobj->name); } } const struct kobj_type linux_kfree_type = { .release = linux_kobject_kfree }; static void linux_device_release(struct device *dev) { pr_debug("linux_device_release: %s\n", dev_name(dev)); kfree(dev); } static ssize_t linux_class_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct class_attribute *dattr; ssize_t error; dattr = container_of(attr, struct class_attribute, attr); error = -EIO; if (dattr->show) error = dattr->show(container_of(kobj, struct class, kobj), dattr, buf); return (error); } static ssize_t linux_class_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct class_attribute *dattr; ssize_t error; dattr = container_of(attr, struct class_attribute, attr); error = -EIO; if (dattr->store) error = dattr->store(container_of(kobj, struct class, kobj), dattr, buf, count); return (error); } static void linux_class_release(struct kobject *kobj) { struct class *class; class = container_of(kobj, struct class, kobj); if (class->class_release) class->class_release(class); } static const struct sysfs_ops linux_class_sysfs = { .show = linux_class_show, .store = linux_class_store, }; const struct kobj_type linux_class_ktype = { .release = linux_class_release, .sysfs_ops = &linux_class_sysfs }; static void linux_dev_release(struct kobject *kobj) { struct device *dev; dev = container_of(kobj, struct device, kobj); /* This is the precedence defined by linux. */ if (dev->release) dev->release(dev); else if (dev->class && dev->class->dev_release) dev->class->dev_release(dev); } static ssize_t linux_dev_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct device_attribute *dattr; ssize_t error; dattr = container_of(attr, struct device_attribute, attr); error = -EIO; if (dattr->show) error = dattr->show(container_of(kobj, struct device, kobj), dattr, buf); return (error); } static ssize_t linux_dev_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct device_attribute *dattr; ssize_t error; dattr = container_of(attr, struct device_attribute, attr); error = -EIO; if (dattr->store) error = dattr->store(container_of(kobj, struct device, kobj), dattr, buf, count); return (error); } static const struct sysfs_ops linux_dev_sysfs = { .show = linux_dev_show, .store = linux_dev_store, }; const struct kobj_type linux_dev_ktype = { .release = linux_dev_release, .sysfs_ops = &linux_dev_sysfs }; struct device * device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) { struct device *dev; va_list args; dev = kzalloc(sizeof(*dev), M_WAITOK); dev->parent = parent; dev->class = class; dev->devt = devt; dev->driver_data = drvdata; dev->release = linux_device_release; va_start(args, fmt); kobject_set_name_vargs(&dev->kobj, fmt, args); va_end(args); device_register(dev); return (dev); } int kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...) { va_list args; int error; kobject_init(kobj, ktype); kobj->ktype = ktype; kobj->parent = parent; kobj->name = NULL; va_start(args, fmt); error = kobject_set_name_vargs(kobj, fmt, args); va_end(args); if (error) return (error); return kobject_add_complete(kobj, parent); } static void linux_file_dtor(void *cdp) { struct linux_file *filp; filp = cdp; filp->f_op->release(filp->f_vnode, filp); vdrop(filp->f_vnode); kfree(filp); } static int linux_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (ENODEV); filp = kzalloc(sizeof(*filp), GFP_KERNEL); filp->f_dentry = &filp->f_dentry_store; filp->f_op = ldev->ops; filp->f_flags = file->f_flag; vhold(file->f_vnode); filp->f_vnode = file->f_vnode; if (filp->f_op->open) { error = -filp->f_op->open(file->f_vnode, filp); if (error) { kfree(filp); return (error); } } error = devfs_set_cdevpriv(filp, linux_file_dtor); if (error) { filp->f_op->release(file->f_vnode, filp); kfree(filp); return (error); } return 0; } static int linux_dev_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (0); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; devfs_clear_cdevpriv(); return (0); } static int linux_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (0); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; /* * Linux does not have a generic ioctl copyin/copyout layer. All * linux ioctls must be converted to void ioctls which pass a * pointer to the address of the data. We want the actual user * address so we dereference here. */ data = *(void **)data; if (filp->f_op->unlocked_ioctl) error = -filp->f_op->unlocked_ioctl(filp, cmd, (u_long)data); else error = ENOTTY; return (error); } static int linux_dev_read(struct cdev *dev, struct uio *uio, int ioflag) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; ssize_t bytes; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (0); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; if (uio->uio_iovcnt != 1) panic("linux_dev_read: uio %p iovcnt %d", uio, uio->uio_iovcnt); if (filp->f_op->read) { bytes = filp->f_op->read(filp, uio->uio_iov->iov_base, uio->uio_iov->iov_len, &uio->uio_offset); if (bytes >= 0) { uio->uio_iov->iov_base = ((uint8_t *)uio->uio_iov->iov_base) + bytes; uio->uio_iov->iov_len -= bytes; uio->uio_resid -= bytes; } else error = -bytes; } else error = ENXIO; return (error); } static int linux_dev_write(struct cdev *dev, struct uio *uio, int ioflag) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; ssize_t bytes; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (0); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; if (uio->uio_iovcnt != 1) panic("linux_dev_write: uio %p iovcnt %d", uio, uio->uio_iovcnt); if (filp->f_op->write) { bytes = filp->f_op->write(filp, uio->uio_iov->iov_base, uio->uio_iov->iov_len, &uio->uio_offset); if (bytes >= 0) { uio->uio_iov->iov_base = ((uint8_t *)uio->uio_iov->iov_base) + bytes; uio->uio_iov->iov_len -= bytes; uio->uio_resid -= bytes; } else error = -bytes; } else error = ENXIO; return (error); } static int linux_dev_poll(struct cdev *dev, int events, struct thread *td) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; int revents; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (0); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; if (filp->f_op->poll) revents = filp->f_op->poll(filp, NULL) & events; else revents = 0; return (revents); } static int linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size, struct vm_object **object, int nprot) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; struct vm_area_struct vma; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (ENODEV); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; vma.vm_start = 0; vma.vm_end = size; vma.vm_pgoff = *offset / PAGE_SIZE; vma.vm_pfn = 0; vma.vm_page_prot = 0; if (filp->f_op->mmap) { error = -filp->f_op->mmap(filp, &vma); if (error == 0) { struct sglist *sg; sg = sglist_alloc(1, M_WAITOK); sglist_append_phys(sg, (vm_paddr_t)vma.vm_pfn << PAGE_SHIFT, vma.vm_len); *object = vm_pager_allocate(OBJT_SG, sg, vma.vm_len, nprot, 0, curthread->td_ucred); if (*object == NULL) { sglist_free(sg); return (EINVAL); } *offset = 0; if (vma.vm_page_prot != VM_MEMATTR_DEFAULT) { VM_OBJECT_WLOCK(*object); vm_object_set_memattr(*object, vma.vm_page_prot); VM_OBJECT_WUNLOCK(*object); } } } else error = ENODEV; return (error); } struct cdevsw linuxcdevsw = { .d_version = D_VERSION, .d_flags = D_TRACKCLOSE, .d_open = linux_dev_open, .d_close = linux_dev_close, .d_read = linux_dev_read, .d_write = linux_dev_write, .d_ioctl = linux_dev_ioctl, .d_mmap_single = linux_dev_mmap_single, .d_poll = linux_dev_poll, }; static int linux_file_read(struct file *file, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { struct linux_file *filp; ssize_t bytes; int error; error = 0; filp = (struct linux_file *)file->f_data; filp->f_flags = file->f_flag; if (uio->uio_iovcnt != 1) panic("linux_file_read: uio %p iovcnt %d", uio, uio->uio_iovcnt); if (filp->f_op->read) { bytes = filp->f_op->read(filp, uio->uio_iov->iov_base, uio->uio_iov->iov_len, &uio->uio_offset); if (bytes >= 0) { uio->uio_iov->iov_base = ((uint8_t *)uio->uio_iov->iov_base) + bytes; uio->uio_iov->iov_len -= bytes; uio->uio_resid -= bytes; } else error = -bytes; } else error = ENXIO; return (error); } static int linux_file_poll(struct file *file, int events, struct ucred *active_cred, struct thread *td) { struct linux_file *filp; int revents; filp = (struct linux_file *)file->f_data; filp->f_flags = file->f_flag; if (filp->f_op->poll) revents = filp->f_op->poll(filp, NULL) & events; else revents = 0; return (0); } static int linux_file_close(struct file *file, struct thread *td) { struct linux_file *filp; int error; filp = (struct linux_file *)file->f_data; filp->f_flags = file->f_flag; error = -filp->f_op->release(NULL, filp); funsetown(&filp->f_sigio); kfree(filp); return (error); } static int linux_file_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *cred, struct thread *td) { struct linux_file *filp; int error; filp = (struct linux_file *)fp->f_data; filp->f_flags = fp->f_flag; error = 0; switch (cmd) { case FIONBIO: break; case FIOASYNC: if (filp->f_op->fasync == NULL) break; error = filp->f_op->fasync(0, filp, fp->f_flag & FASYNC); break; case FIOSETOWN: error = fsetown(*(int *)data, &filp->f_sigio); if (error == 0) error = filp->f_op->fasync(0, filp, fp->f_flag & FASYNC); break; case FIOGETOWN: *(int *)data = fgetown(&filp->f_sigio); break; default: error = ENOTTY; break; } return (error); } static int linux_file_stat(struct file *fp, struct stat *sb, struct ucred *active_cred, struct thread *td) { return (EOPNOTSUPP); } static int linux_file_fill_kinfo(struct file *fp, struct kinfo_file *kif, struct filedesc *fdp) { return (0); } struct fileops linuxfileops = { .fo_read = linux_file_read, .fo_write = invfo_rdwr, .fo_truncate = invfo_truncate, .fo_kqfilter = invfo_kqfilter, .fo_stat = linux_file_stat, .fo_fill_kinfo = linux_file_fill_kinfo, .fo_poll = linux_file_poll, .fo_close = linux_file_close, .fo_ioctl = linux_file_ioctl, .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, .fo_sendfile = invfo_sendfile, }; /* * Hash of vmmap addresses. This is infrequently accessed and does not * need to be particularly large. This is done because we must store the * caller's idea of the map size to properly unmap. */ struct vmmap { LIST_ENTRY(vmmap) vm_next; void *vm_addr; unsigned long vm_size; }; struct vmmaphd { struct vmmap *lh_first; }; #define VMMAP_HASH_SIZE 64 #define VMMAP_HASH_MASK (VMMAP_HASH_SIZE - 1) #define VM_HASH(addr) ((uintptr_t)(addr) >> PAGE_SHIFT) & VMMAP_HASH_MASK static struct vmmaphd vmmaphead[VMMAP_HASH_SIZE]; static struct mtx vmmaplock; static void vmmap_add(void *addr, unsigned long size) { struct vmmap *vmmap; vmmap = kmalloc(sizeof(*vmmap), GFP_KERNEL); mtx_lock(&vmmaplock); vmmap->vm_size = size; vmmap->vm_addr = addr; LIST_INSERT_HEAD(&vmmaphead[VM_HASH(addr)], vmmap, vm_next); mtx_unlock(&vmmaplock); } static struct vmmap * vmmap_remove(void *addr) { struct vmmap *vmmap; mtx_lock(&vmmaplock); LIST_FOREACH(vmmap, &vmmaphead[VM_HASH(addr)], vm_next) if (vmmap->vm_addr == addr) break; if (vmmap) LIST_REMOVE(vmmap, vm_next); mtx_unlock(&vmmaplock); return (vmmap); } #if defined(__i386__) || defined(__amd64__) void * _ioremap_attr(vm_paddr_t phys_addr, unsigned long size, int attr) { void *addr; addr = pmap_mapdev_attr(phys_addr, size, attr); if (addr == NULL) return (NULL); vmmap_add(addr, size); return (addr); } #endif void iounmap(void *addr) { struct vmmap *vmmap; vmmap = vmmap_remove(addr); if (vmmap == NULL) return; #if defined(__i386__) || defined(__amd64__) pmap_unmapdev((vm_offset_t)addr, vmmap->vm_size); #endif kfree(vmmap); } void * vmap(struct page **pages, unsigned int count, unsigned long flags, int prot) { vm_offset_t off; size_t size; size = count * PAGE_SIZE; off = kva_alloc(size); if (off == 0) return (NULL); vmmap_add((void *)off, size); pmap_qenter(off, pages, count); return ((void *)off); } void vunmap(void *addr) { struct vmmap *vmmap; vmmap = vmmap_remove(addr); if (vmmap == NULL) return; pmap_qremove((vm_offset_t)addr, vmmap->vm_size / PAGE_SIZE); kva_free((vm_offset_t)addr, vmmap->vm_size); kfree(vmmap); } char * kvasprintf(gfp_t gfp, const char *fmt, va_list ap) { unsigned int len; char *p; va_list aq; va_copy(aq, ap); len = vsnprintf(NULL, 0, fmt, aq); va_end(aq); p = kmalloc(len + 1, gfp); if (p != NULL) vsnprintf(p, len + 1, fmt, ap); return (p); } char * kasprintf(gfp_t gfp, const char *fmt, ...) { va_list ap; char *p; va_start(ap, fmt); p = kvasprintf(gfp, fmt, ap); va_end(ap); return (p); } static int linux_timer_jiffies_until(unsigned long expires) { int delta = expires - jiffies; /* guard against already expired values */ if (delta < 1) delta = 1; return (delta); } static void linux_timer_callback_wrapper(void *context) { struct timer_list *timer; timer = context; timer->function(timer->data); } void mod_timer(struct timer_list *timer, unsigned long expires) { timer->expires = expires; callout_reset(&timer->timer_callout, linux_timer_jiffies_until(expires), &linux_timer_callback_wrapper, timer); } void add_timer(struct timer_list *timer) { callout_reset(&timer->timer_callout, linux_timer_jiffies_until(timer->expires), &linux_timer_callback_wrapper, timer); } static void linux_timer_init(void *arg) { /* * Compute an internal HZ value which can divide 2**32 to * avoid timer rounding problems when the tick value wraps * around 2**32: */ linux_timer_hz_mask = 1; while (linux_timer_hz_mask < (unsigned long)hz) linux_timer_hz_mask *= 2; linux_timer_hz_mask--; } SYSINIT(linux_timer, SI_SUB_DRIVERS, SI_ORDER_FIRST, linux_timer_init, NULL); void linux_complete_common(struct completion *c, int all) { int wakeup_swapper; sleepq_lock(c); c->done++; if (all) wakeup_swapper = sleepq_broadcast(c, SLEEPQ_SLEEP, 0, 0); else wakeup_swapper = sleepq_signal(c, SLEEPQ_SLEEP, 0, 0); sleepq_release(c); if (wakeup_swapper) kick_proc0(); } /* * Indefinite wait for done != 0 with or without signals. */ long linux_wait_for_common(struct completion *c, int flags) { if (flags != 0) flags = SLEEPQ_INTERRUPTIBLE | SLEEPQ_SLEEP; else flags = SLEEPQ_SLEEP; for (;;) { sleepq_lock(c); if (c->done) break; sleepq_add(c, NULL, "completion", flags, 0); if (flags & SLEEPQ_INTERRUPTIBLE) { if (sleepq_wait_sig(c, 0) != 0) return (-ERESTARTSYS); } else sleepq_wait(c, 0); } c->done--; sleepq_release(c); return (0); } /* * Time limited wait for done != 0 with or without signals. */ long linux_wait_for_timeout_common(struct completion *c, long timeout, int flags) { long end = jiffies + timeout; if (flags != 0) flags = SLEEPQ_INTERRUPTIBLE | SLEEPQ_SLEEP; else flags = SLEEPQ_SLEEP; for (;;) { int ret; sleepq_lock(c); if (c->done) break; sleepq_add(c, NULL, "completion", flags, 0); sleepq_set_timeout(c, linux_timer_jiffies_until(end)); if (flags & SLEEPQ_INTERRUPTIBLE) ret = sleepq_timedwait_sig(c, 0); else ret = sleepq_timedwait(c, 0); if (ret != 0) { /* check for timeout or signal */ if (ret == EWOULDBLOCK) return (0); else return (-ERESTARTSYS); } } c->done--; sleepq_release(c); /* return how many jiffies are left */ return (linux_timer_jiffies_until(end)); } int linux_try_wait_for_completion(struct completion *c) { int isdone; isdone = 1; sleepq_lock(c); if (c->done) c->done--; else isdone = 0; sleepq_release(c); return (isdone); } int linux_completion_done(struct completion *c) { int isdone; isdone = 1; sleepq_lock(c); if (c->done == 0) isdone = 0; sleepq_release(c); return (isdone); } void linux_delayed_work_fn(void *arg) { struct delayed_work *work; work = arg; taskqueue_enqueue(work->work.taskqueue, &work->work.work_task); } void linux_work_fn(void *context, int pending) { struct work_struct *work; work = context; work->fn(work); } void linux_flush_fn(void *context, int pending) { } struct workqueue_struct * linux_create_workqueue_common(const char *name, int cpus) { struct workqueue_struct *wq; wq = kmalloc(sizeof(*wq), M_WAITOK); wq->taskqueue = taskqueue_create(name, M_WAITOK, taskqueue_thread_enqueue, &wq->taskqueue); atomic_set(&wq->draining, 0); taskqueue_start_threads(&wq->taskqueue, cpus, PWAIT, "%s", name); return (wq); } void destroy_workqueue(struct workqueue_struct *wq) { taskqueue_free(wq->taskqueue); kfree(wq); } static void linux_cdev_release(struct kobject *kobj) { struct linux_cdev *cdev; struct kobject *parent; cdev = container_of(kobj, struct linux_cdev, kobj); parent = kobj->parent; if (cdev->cdev) destroy_dev(cdev->cdev); kfree(cdev); kobject_put(parent); } static void linux_cdev_static_release(struct kobject *kobj) { struct linux_cdev *cdev; struct kobject *parent; cdev = container_of(kobj, struct linux_cdev, kobj); parent = kobj->parent; if (cdev->cdev) destroy_dev(cdev->cdev); kobject_put(parent); } const struct kobj_type linux_cdev_ktype = { .release = linux_cdev_release, }; const struct kobj_type linux_cdev_static_ktype = { .release = linux_cdev_static_release, }; + +static void +linux_handle_ifnet_link_event(void *arg, struct ifnet *ifp, int linkstate) +{ + struct notifier_block *nb; + + nb = arg; + if (linkstate == LINK_STATE_UP) + nb->notifier_call(nb, NETDEV_UP, ifp); + else + nb->notifier_call(nb, NETDEV_DOWN, ifp); +} + +static void +linux_handle_ifnet_arrival_event(void *arg, struct ifnet *ifp) +{ + struct notifier_block *nb; + + nb = arg; + nb->notifier_call(nb, NETDEV_REGISTER, ifp); +} + +static void +linux_handle_ifnet_departure_event(void *arg, struct ifnet *ifp) +{ + struct notifier_block *nb; + + nb = arg; + nb->notifier_call(nb, NETDEV_UNREGISTER, ifp); +} + +static void +linux_handle_iflladdr_event(void *arg, struct ifnet *ifp) +{ + struct notifier_block *nb; + + nb = arg; + nb->notifier_call(nb, NETDEV_CHANGEADDR, ifp); +} + +static void +linux_handle_ifaddr_event(void *arg, struct ifnet *ifp) +{ + struct notifier_block *nb; + + nb = arg; + nb->notifier_call(nb, NETDEV_CHANGEIFADDR, ifp); +} + +int +register_netdevice_notifier(struct notifier_block *nb) +{ + + nb->tags[NETDEV_UP] = EVENTHANDLER_REGISTER( + ifnet_link_event, linux_handle_ifnet_link_event, nb, 0); + nb->tags[NETDEV_REGISTER] = EVENTHANDLER_REGISTER( + ifnet_arrival_event, linux_handle_ifnet_arrival_event, nb, 0); + nb->tags[NETDEV_UNREGISTER] = EVENTHANDLER_REGISTER( + ifnet_departure_event, linux_handle_ifnet_departure_event, nb, 0); + nb->tags[NETDEV_CHANGEADDR] = EVENTHANDLER_REGISTER( + iflladdr_event, linux_handle_iflladdr_event, nb, 0); + + return (0); +} + +int +register_inetaddr_notifier(struct notifier_block *nb) +{ + + nb->tags[NETDEV_CHANGEIFADDR] = EVENTHANDLER_REGISTER( + ifaddr_event, linux_handle_ifaddr_event, nb, 0); + return (0); +} + +int +unregister_netdevice_notifier(struct notifier_block *nb) +{ + + EVENTHANDLER_DEREGISTER(ifnet_link_event, + nb->tags[NETDEV_UP]); + EVENTHANDLER_DEREGISTER(ifnet_arrival_event, + nb->tags[NETDEV_REGISTER]); + EVENTHANDLER_DEREGISTER(ifnet_departure_event, + nb->tags[NETDEV_UNREGISTER]); + EVENTHANDLER_DEREGISTER(iflladdr_event, + nb->tags[NETDEV_CHANGEADDR]); + + return (0); +} + +int +unregister_inetaddr_notifier(struct notifier_block *nb) +{ + + EVENTHANDLER_DEREGISTER(ifaddr_event, + nb->tags[NETDEV_CHANGEIFADDR]); + + return (0); +} + +void +linux_irq_handler(void *ent) +{ + struct irq_ent *irqe; + + irqe = ent; + irqe->handler(irqe->irq, irqe->arg); +} static void linux_compat_init(void *arg) { struct sysctl_oid *rootoid; int i; sx_init(&linux_global_rcu_lock, "LinuxGlobalRCU"); rootoid = SYSCTL_ADD_ROOT_NODE(NULL, OID_AUTO, "sys", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "sys"); kobject_init(&linux_class_root, &linux_class_ktype); kobject_set_name(&linux_class_root, "class"); linux_class_root.oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(rootoid), OID_AUTO, "class", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "class"); kobject_init(&linux_root_device.kobj, &linux_dev_ktype); kobject_set_name(&linux_root_device.kobj, "device"); linux_root_device.kobj.oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(rootoid), OID_AUTO, "device", CTLFLAG_RD, NULL, "device"); linux_root_device.bsddev = root_bus; linux_class_misc.name = "misc"; class_register(&linux_class_misc); INIT_LIST_HEAD(&pci_drivers); INIT_LIST_HEAD(&pci_devices); spin_lock_init(&pci_lock); mtx_init(&vmmaplock, "IO Map lock", NULL, MTX_DEF); for (i = 0; i < VMMAP_HASH_SIZE; i++) LIST_INIT(&vmmaphead[i]); } SYSINIT(linux_compat, SI_SUB_DRIVERS, SI_ORDER_SECOND, linux_compat_init, NULL); static void linux_compat_uninit(void *arg) { linux_kobject_kfree_name(&linux_class_root); linux_kobject_kfree_name(&linux_root_device.kobj); linux_kobject_kfree_name(&linux_class_misc.kobj); synchronize_rcu(); sx_destroy(&linux_global_rcu_lock); } SYSUNINIT(linux_compat, SI_SUB_DRIVERS, SI_ORDER_SECOND, linux_compat_uninit, NULL); /* * NOTE: Linux frequently uses "unsigned long" for pointer to integer * conversion and vice versa, where in FreeBSD "uintptr_t" would be * used. Assert these types have the same size, else some parts of the * LinuxKPI may not work like expected: */ CTASSERT(sizeof(unsigned long) == sizeof(uintptr_t));