📄 fromdevice.cc
字号:
// -*- mode: c++; c-basic-offset: 4 -*-/* * fromdevice.{cc,hh} -- element steals packets from kernel devices * * Robert Morris * Eddie Kohler: AnyDevice, other changes * Benjie Chen: scheduling, internal queue * Nickolai Zeldovich, Luigi Rizzo, Marko Zec: BSD * * Copyright (c) 1999-2000 Massachusetts Institute of Technology * Copyright (c) 2000 Mazu Networks, Inc. * Copyright (c) 2001-2004 International Computer Science Institute * Copyright (c) 2004 University of Zagreb * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the conditions * listed in the Click LICENSE file. These conditions include: you must * preserve this copyright notice, and you cannot mention the copyright * holders in advertising related to the Software without their permission. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */#include <click/config.h>#include <click/glue.hh>#include "fromdevice.hh"#include "fromhost.hh"#include <click/error.hh>#include <click/confparse.hh>#include <click/router.hh>#include <click/standard/scheduleinfo.hh>#include <click/cxxprotect.h>CLICK_CXX_PROTECT#include <sys/linker.h>#include <net/if_var.h>#include <net/ethernet.h>#include <net/netisr.h>CLICK_CXX_UNPROTECT#include <click/cxxunprotect.h>#define POLL_LIST_LEN 128 // XXX should refernce a proper #includestruct pollrec { poll_handler_t *handler; struct ifnet *ifp;};int *polling; // polling modestatic int *poll_handlers; // # of NICs registered for BSD kernel pollingstatic int *reg_frac; // How often we have to check status registersstatic struct pollrec *pr; // BSD kernel polling handlersstatic AnyDeviceMap from_device_map;static int registered_readers;static int from_device_count;/* * Process incoming packets using the ng_ether_input_p hook. * The if_poll_intren (normally unused) field from the struct ifnet is * used as a pointer to the fromdevice data structure. Call it "xp". * * If xp == NULL, no FromDevice element is registered on this * interface, so return to pass the packet back to FreeBSD. * Otherwise, xp points to the element, which in turn contains * a queue. Append the packet there, clear *mp to grab the pkt from FreeBSD. * call wakeup() to poentially wake up the element. * * Special case: m->m_pkthdr.rcvif == NULL means the packet is coming * from click and directed to this interface on the host. * We are already running at splimp() so we need no further protection. */extern "C"voidclick_ether_input(struct ifnet *ifp, struct mbuf **mp, struct ether_header *eh){ if (ifp->if_poll_intren == NULL) // not for click. return ; struct mbuf *m = *mp; if (m->m_pkthdr.rcvif == NULL) { // Special case: from click to FreeBSD m->m_pkthdr.rcvif = ifp; // Reset rcvif to correct value, and return; // let FreeBSD continue processing. } *mp = NULL; // tell ether_input no further processing needed. FromDevice *me = (FromDevice *)(ifp->if_poll_intren); /* * If sysctl kern.polling.enable == 2 we should take care of polling * this NIC from inside a Click thread, so steal the handler from BSD. */ if (!me->_polling && (ifp->if_ipending & IFF_POLLING) && polling && *polling == 2) { struct pollrec *prp = pr; int i; for (i = 0; i < *poll_handlers; i++, prp++) if (prp->ifp == ifp) { me->_poll_handler = prp->handler; me->_poll_status_tick = ticks; prp->handler = NULL; prp->ifp = NULL; printf("Click FromDevice(%s%d) taking control over NIC driver polling\n", ifp->if_name, ifp->if_unit); me->_polling = -1; // wakeup task thread only once more break; } if (!me->_polling) { printf("Strange, couldn't find polling handler for %s%d\n", ifp->if_name, ifp->if_unit); me->_polling = -2; // Do not bother trying to register again } } // put the ethernet header back into the mbuf. M_PREPEND(m, sizeof(*eh), M_WAIT); bcopy(eh, mtod(m, struct ether_header *), sizeof(*eh)); if (IF_QFULL(me->_inq)) { IF_DROP(me->_inq); m_freem(m); } else IF_ENQUEUE(me->_inq, m); if (me->_polling != 1) me->intr_reschedule(); if (me->_polling == -1) me->_polling = 1; // no need to wakeup task thread any more#ifdef FROMDEVICE_TSTAMP me->_tstamp = rdtsc();#endif}/* * Process outgoing packets using the ng_ether_output_p hook. * If if_poll_xmit == NULL, no FromHost element is registered on this * interface, so return 0 to pass the packet back to FreeBSD. * Otherwise, if_poll_xmit points to the element, which in turn contains * a queue. Append the packet there, clear *mp to grab the pkt from FreeBSD, * and possibly wakeup the element. * * We _need_ splimp()/splx() to avoid races. */extern "C"intclick_ether_output(struct ifnet *ifp, struct mbuf **mp){ int s = splimp(); if (ifp->if_poll_xmit == NULL) { // not for click... splx(s); return 0; } struct mbuf *m = *mp; *mp = NULL; // tell ether_output no further processing needed FromHost *me = (FromHost *)(ifp->if_poll_xmit); if (IF_QFULL(me->_inq)) { IF_DROP(me->_inq); m_freem(m); } else IF_ENQUEUE(me->_inq, m); me->intr_reschedule(); splx(s); return 0;}static voidfromdev_static_initialize(){ linker_file_t kernel_lf = linker_find_file_by_id(1); /* kernel file */ pr = (struct pollrec *) linker_file_lookup_symbol(kernel_lf, "pr", 0); reg_frac = (int *) linker_file_lookup_symbol(kernel_lf, "reg_frac", 0); poll_handlers = (int *) linker_file_lookup_symbol(kernel_lf, "poll_handlers", 0); if (pr && poll_handlers && reg_frac) polling = (int *) linker_file_lookup_symbol(kernel_lf, "polling", 0); else polling = NULL; if (polling) printf("Cool, we are running Click on a polling capable kernel!\n"); if (++from_device_count == 1) from_device_map.initialize();}static voidfromdev_static_cleanup(){ if (--from_device_count <= 0) { if (registered_readers) printf("Warning: registered reader count mismatch!\n"); }}FromDevice::FromDevice(){ _readers = 0; // noone registered so far _polling = 0; // we do not poll until NIC driver registers itself add_output(); fromdev_static_initialize();}FromDevice::~FromDevice(){ fromdev_static_cleanup();}void *FromDevice::cast(const char *n){ if (strcmp(n, "Storage") == 0) return (Storage *)this; else if (strcmp(n, "FromDevice") == 0) return (Element *)this; else return 0;}intFromDevice::configure(Vector<String> &conf, ErrorHandler *errh){ _promisc = false; _inq = NULL; bool allow_nonexistent = false; _burst = 8; if (cp_va_parse(conf, this, errh, cpString, "interface name", &_devname, cpOptional, cpBool, "enter promiscuous mode?", &_promisc, cpUnsigned, "burst size", &_burst, cpKeywords, "PROMISC", cpBool, "enter promiscuous mode?", &_promisc, "PROMISCUOUS", cpBool, "enter promiscuous mode?", &_promisc, "BURST", cpUnsigned, "burst size", &_burst, "ALLOW_NONEXISTENT", cpBool, "allow nonexistent interface?", &allow_nonexistent, cpEnd) < 0) return -1; if (find_device(allow_nonexistent, &from_device_map, errh) < 0) return -1; return 0;}/* * Use a Linux interface added by us, in net/core/dev.c, * to register to grab incoming packets. */intFromDevice::initialize(ErrorHandler *errh){ // check for duplicates if (ifindex() >= 0) for (int fi = 0; fi < router()->nelements(); fi++) { Element *e = router()->element(fi); if (e == this) continue; if (FromDevice *fd = (FromDevice *)(e->cast("FromDevice"))) { if (fd->ifindex() == ifindex()) return errh->error("duplicate FromDevice for `%s'", _devname.cc()); } } from_device_map.insert(this); if (_promisc && device()) ifpromisc(device(), 1); assert(device()); int s = splimp(); if (_inq == NULL) { if (_readers != 0) printf("Warning, _readers mismatch (%d should be 0)\n", _readers); _inq = (struct ifqueue *) malloc(sizeof (struct ifqueue), M_DEVBUF, M_NOWAIT|M_ZERO); assert(_inq); _inq->ifq_maxlen = QSIZE; (FromDevice *)(device()->if_poll_intren) = this; } else { if (_readers == 0) printf("Warning, _readers mismatch (should not be 0)\n"); } _readers++; registered_readers++; splx(s); ScheduleInfo::initialize_task(this, &_task, device() != 0, errh);#ifdef HAVE_STRIDE_SCHED // start out with default number of tickets, inflate up to max set_max_tickets( _task.tickets() ); _task.set_tickets(Task::DEFAULT_TICKETS);#endif#if CLICK_DEVICE_STATS // Initialize performance stats _time_read = 0; _time_push = 0; _perfcnt1_read = 0; _perfcnt2_read = 0; _perfcnt1_push = 0; _perfcnt2_push = 0;#endif _npackets = 0; _capacity = QSIZE; return 0;}voidFromDevice::cleanup(CleanupStage){ if (!device()) return; struct ifqueue *q = NULL; int s = splimp(); registered_readers--; _readers--; if (_readers == 0) { // flush queue q = _inq ; _inq = NULL ; device()->if_poll_intren = NULL ; } if (_polling == 1) { // return polling handler to the kernel struct pollrec *prp = pr; int i; for (i = 0; i < *poll_handlers; i++, prp++) if (prp->ifp == NULL) break; prp->handler = _poll_handler; prp->ifp = _dev; if (*poll_handlers == 0 && (_dev->if_flags & IFF_RUNNING)) *poll_handlers = 1; } splx(s); if (q) { // we do not mutex for this. int i, max = q->ifq_maxlen ; for (i = 0; i < max; i++) { struct mbuf *m; IF_DEQUEUE(q, m); if (!m) break; m_freem(m); } free(q, M_DEVBUF); } from_device_map.remove(this); if (_promisc && device()) ifpromisc(device(), 0);}voidFromDevice::take_state(Element *e, ErrorHandler *errh){ FromDevice *fd = (FromDevice *)e->cast("FromDevice"); if (!fd) return;}boolFromDevice::run_task(){ int npq = 0; // click_chatter("FromDevice::run_task()."); if (_dev && _polling == 1) { if (_dev->if_ipending & IFF_POLLING) { enum poll_cmd cmd; if ( _poll_status_tick <= ticks ) { _poll_status_tick = ticks + *reg_frac; cmd = POLL_AND_CHECK_STATUS; } else cmd = POLL_ONLY; if ( *polling != 2 ) { // Need to return the handler to the kernel struct pollrec *prp = pr; int i; _polling = 0; // No more polling in Click for (i = 0; i < *poll_handlers; i++, prp++) if (prp->ifp == NULL) break; prp->handler = _poll_handler; prp->ifp = _dev; if (*poll_handlers == 0 && (_dev->if_flags & IFF_RUNNING)) *poll_handlers = 1; } else _poll_handler(_dev, cmd, _burst); } else _polling = 0; // No more polling }#ifdef FROMDEVICE_TSTAMP if (_tstamp) { uint64_t now = rdtsc(); click_chatter("FromDevice::run_task() latency=%ld", now - _tstamp); _tstamp = 0; }#endif while (npq <= _burst) { struct mbuf *m = 0; // Try to dequeue a packet from the interrupt input queue. IF_DEQUEUE(_inq, m); if (m == NULL) {#if CLICK_DEVICE_ADJUST_TICKETS adjust_tickets(npq);#endif if (_polling) _task.fast_reschedule(); return npq > 0; } // Got a packet, which includes the MAC header. Make it a real Packet. Packet *p = Packet::make(m); output(0).push(p); npq++; _npackets++; }#if CLICK_DEVICE_ADJUST_TICKETS adjust_tickets(npq);#endif//printf("fromdevice couldn't handle all packets\n"); _task.fast_reschedule(); return true;}intFromDevice::get_inq_drops(){ return (_inq ? _inq->ifq_drops : 0);}static StringFromDevice_read_stats(Element *f, void *){ FromDevice *fd = (FromDevice *)f; return String(fd->_npackets) + " packets received\n" + String(fd->get_inq_drops()) + " input queue drops\n" +#if CLICK_DEVICE_STATS String(fd->_time_read) + " cycles read\n" + String(fd->_time_push) + " cycles push\n" + String(fd->_perfcnt1_read) + " perfcnt1 read\n" + String(fd->_perfcnt2_read) + " perfcnt2 read\n" + String(fd->_perfcnt1_push) + " perfcnt1 push\n" + String(fd->_perfcnt2_push) + " perfcnt2 push\n" +#endif String();}voidFromDevice::add_handlers(){ add_read_handler("stats", FromDevice_read_stats, 0); add_task_handlers(&_task);}ELEMENT_REQUIRES(AnyDevice bsdmodule)EXPORT_ELEMENT(FromDevice)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -