/*** * * stack/stack_mgr.c - Stack-Manager * * Copyright (C) 2002 Ulrich Marx * Copyright (C) 2003-2006 Jan Kiszka * Copyright (C) 2006 Jorge Almeida * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include static unsigned int stack_mgr_prio = RTNET_DEF_STACK_PRIORITY; module_param(stack_mgr_prio, uint, 0444); MODULE_PARM_DESC(stack_mgr_prio, "Priority of the stack manager task"); #if (CONFIG_XENO_DRIVERS_NET_RX_FIFO_SIZE & \ (CONFIG_XENO_DRIVERS_NET_RX_FIFO_SIZE - 1)) != 0 #error CONFIG_XENO_DRIVERS_NET_RX_FIFO_SIZE must be power of 2! #endif static DECLARE_RTSKB_FIFO(rx, CONFIG_XENO_DRIVERS_NET_RX_FIFO_SIZE); struct list_head rt_packets[RTPACKET_HASH_TBL_SIZE]; #ifdef CONFIG_XENO_DRIVERS_NET_ETH_P_ALL struct list_head rt_packets_all; #endif /* CONFIG_XENO_DRIVERS_NET_ETH_P_ALL */ DEFINE_RTDM_LOCK(rt_packets_lock); /*** * rtdev_add_pack: add protocol (Layer 3) * @pt: the new protocol */ int __rtdev_add_pack(struct rtpacket_type *pt, struct module *module) { int ret = 0; rtdm_lockctx_t context; INIT_LIST_HEAD(&pt->list_entry); pt->refcount = 0; if (pt->trylock == NULL) pt->trylock = rtdev_lock_pack; if (pt->unlock == NULL) pt->unlock = rtdev_unlock_pack; pt->owner = module; rtdm_lock_get_irqsave(&rt_packets_lock, context); if (pt->type == htons(ETH_P_ALL)) #ifdef CONFIG_XENO_DRIVERS_NET_ETH_P_ALL list_add_tail(&pt->list_entry, &rt_packets_all); #else /* !CONFIG_XENO_DRIVERS_NET_ETH_P_ALL */ ret = -EINVAL; #endif /* CONFIG_XENO_DRIVERS_NET_ETH_P_ALL */ else list_add_tail( &pt->list_entry, &rt_packets[ntohs(pt->type) & RTPACKET_HASH_KEY_MASK]); rtdm_lock_put_irqrestore(&rt_packets_lock, context); return ret; } EXPORT_SYMBOL_GPL(__rtdev_add_pack); /*** * rtdev_remove_pack: remove protocol (Layer 3) * @pt: protocol */ void rtdev_remove_pack(struct rtpacket_type *pt) { rtdm_lockctx_t context; RTNET_ASSERT(pt != NULL, return;); rtdm_lock_get_irqsave(&rt_packets_lock, context); list_del(&pt->list_entry); rtdm_lock_put_irqrestore(&rt_packets_lock, context); } EXPORT_SYMBOL_GPL(rtdev_remove_pack); /*** * rtnetif_rx: will be called from the driver interrupt handler * (IRQs disabled!) and send a message to rtdev-owned stack-manager * * @skb - the packet */ void rtnetif_rx(struct rtskb *skb) { RTNET_ASSERT(skb != NULL, return;); RTNET_ASSERT(skb->rtdev != NULL, return;); if (unlikely(rtskb_fifo_insert_inirq(&rx.fifo, skb) < 0)) { rtdm_printk("RTnet: dropping packet in %s()\n", __FUNCTION__); kfree_rtskb(skb); } } EXPORT_SYMBOL_GPL(rtnetif_rx); #if IS_ENABLED(CONFIG_XENO_DRIVERS_NET_DRV_LOOPBACK) #define __DELIVER_PREFIX #else /* !CONFIG_XENO_DRIVERS_NET_DRV_LOOPBACK */ #define __DELIVER_PREFIX static inline #endif /* CONFIG_XENO_DRIVERS_NET_DRV_LOOPBACK */ __DELIVER_PREFIX void rt_stack_deliver(struct rtskb *rtskb) { unsigned short hash; struct rtpacket_type *pt_entry; rtdm_lockctx_t context; struct rtnet_device *rtdev = rtskb->rtdev; int err; int eth_p_all_hit = 0; rtcap_report_incoming(rtskb); rtskb->nh.raw = rtskb->data; rtdm_lock_get_irqsave(&rt_packets_lock, context); #ifdef CONFIG_XENO_DRIVERS_NET_ETH_P_ALL eth_p_all_hit = 0; list_for_each_entry (pt_entry, &rt_packets_all, list_entry) { if (!pt_entry->trylock(pt_entry)) continue; rtdm_lock_put_irqrestore(&rt_packets_lock, context); pt_entry->handler(rtskb, pt_entry); rtdm_lock_get_irqsave(&rt_packets_lock, context); pt_entry->unlock(pt_entry); eth_p_all_hit = 1; } #endif /* CONFIG_XENO_DRIVERS_NET_ETH_P_ALL */ hash = ntohs(rtskb->protocol) & RTPACKET_HASH_KEY_MASK; list_for_each_entry (pt_entry, &rt_packets[hash], list_entry) if (pt_entry->type == rtskb->protocol) { if (!pt_entry->trylock(pt_entry)) continue; rtdm_lock_put_irqrestore(&rt_packets_lock, context); err = pt_entry->handler(rtskb, pt_entry); rtdm_lock_get_irqsave(&rt_packets_lock, context); pt_entry->unlock(pt_entry); if (likely(!err)) { rtdm_lock_put_irqrestore(&rt_packets_lock, context); return; } } rtdm_lock_put_irqrestore(&rt_packets_lock, context); /* Don't warn if ETH_P_ALL listener were present or when running in promiscuous mode (RTcap). */ if (unlikely(!eth_p_all_hit && !(rtdev->flags & IFF_PROMISC))) rtdm_printk("RTnet: no one cared for packet with layer 3 " "protocol type 0x%04x\n", ntohs(rtskb->protocol)); kfree_rtskb(rtskb); } #if IS_ENABLED(CONFIG_XENO_DRIVERS_NET_DRV_LOOPBACK) EXPORT_SYMBOL_GPL(rt_stack_deliver); #endif /* CONFIG_XENO_DRIVERS_NET_DRV_LOOPBACK */ static void rt_stack_mgr_task(void *arg) { rtdm_event_t *mgr_event = &((struct rtnet_mgr *)arg)->event; struct rtskb *rtskb; while (!rtdm_task_should_stop()) { if (rtdm_event_wait(mgr_event) < 0) break; /* we are the only reader => no locking required */ while ((rtskb = __rtskb_fifo_remove(&rx.fifo))) rt_stack_deliver(rtskb); } } /*** * rt_stack_connect */ void rt_stack_connect(struct rtnet_device *rtdev, struct rtnet_mgr *mgr) { rtdev->stack_event = &mgr->event; } EXPORT_SYMBOL_GPL(rt_stack_connect); /*** * rt_stack_disconnect */ void rt_stack_disconnect(struct rtnet_device *rtdev) { rtdev->stack_event = NULL; } EXPORT_SYMBOL_GPL(rt_stack_disconnect); /*** * rt_stack_mgr_init */ int rt_stack_mgr_init(struct rtnet_mgr *mgr) { int i; rtskb_fifo_init(&rx.fifo, CONFIG_XENO_DRIVERS_NET_RX_FIFO_SIZE); for (i = 0; i < RTPACKET_HASH_TBL_SIZE; i++) INIT_LIST_HEAD(&rt_packets[i]); #ifdef CONFIG_XENO_DRIVERS_NET_ETH_P_ALL INIT_LIST_HEAD(&rt_packets_all); #endif /* CONFIG_XENO_DRIVERS_NET_ETH_P_ALL */ rtdm_event_init(&mgr->event, 0); return rtdm_task_init(&mgr->task, "rtnet-stack", rt_stack_mgr_task, mgr, stack_mgr_prio, 0); } /*** * rt_stack_mgr_delete */ void rt_stack_mgr_delete(struct rtnet_mgr *mgr) { rtdm_event_destroy(&mgr->event); rtdm_task_destroy(&mgr->task); }