📄 touserdevice.cc
字号:
/*Programmer: Roman ChertovAll files in this distribution of are Copyright 2006 by the PurdueResearch Foundation of Purdue University. All rights reserved.Redistribution and use in source and binary forms are permittedprovided that this entire copyright notice is duplicated in all suchcopies, and that any documentation, announcements, and other materialsrelated to such distribution and use acknowledge that the software wasdeveloped at Purdue University, West Lafayette, IN. No charge may bemade for copies, derivations, or distributions of this materialwithout the express written consent of the copyright holder. Neitherthe name of the University nor the name of the author may be used toendorse or promote products derived from this material withoutspecific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUTLIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FORANY PARTICULAR PURPOSE.*/#include <click/config.h>#include <click/glue.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 <linux/mm.h>#include <linux/module.h>#include <linux/version.h>#include <linux/poll.h>#include <linux/fs.h>#include <asm/types.h>#include <asm/uaccess.h>#include <linux/ip.h>#include <linux/inetdevice.h>#include <net/route.h>CLICK_CXX_UNPROTECT#include <click/cxxunprotect.h>#include "touserdevice.hh"#include <click/hashmap.hh>#include <click/llrpc.h>static char DEV_NAME[] = "touser";static int DEV_MAJOR = 241;static int DEV_MINOR = 0;static int DEV_NUM = 0;struct file_operations *ToUserDevice::dev_fops;static volatile ToUserDevice *elem[20] = {0};ToUserDevice::ToUserDevice() : _task(this){ _exit = false; _size = 0; _capacity = default_capacity; _devname = DEV_NAME; _q = 0; _r_slot = 0; _w_slot = 0; _dev_major = DEV_MAJOR; _dev_minor = DEV_MINOR; _read_count = 0; _drop_count = 0; _sleep_proc = 0; _pkt_count = 0; _block_count = 0; _pkt_read_count = 0; _failed_count = 0;}ToUserDevice::~ToUserDevice(){}extern struct file_operations *click_new_file_operations();void ToUserDevice::static_initialize(){ if ((dev_fops = click_new_file_operations())) { dev_fops->read = dev_read; dev_fops->poll = dev_poll; dev_fops->open = dev_open; dev_fops->release = dev_release; dev_fops->ioctl = dev_ioctl; }}void ToUserDevice::static_cleanup(){ dev_fops = 0; // proclikefs will free eventually}#define GETELEM(filp) ((ToUserDevice *) ((uintptr_t) filp->private_data & ~(uintptr_t) 1))// open function - called when the "file" /dev/toclick is opened in userspaceint ToUserDevice::dev_open(struct inode *inode, struct file *filp){ int num = MINOR(inode->i_rdev); // low order number click_chatter("**ToUserDevice_open %d\n", num); if (!elem[num]) { click_chatter("No ToUserDevice element for this device: %d\n", num); return -EIO; } filp->private_data = (void *) elem[num]; return 0;}// close function - called when the "file" /dev/toclick is closed in userspaceint ToUserDevice::dev_release(struct inode *inode, struct file *filp){ ToUserDevice *elem = GETELEM(filp); if (!elem) { click_chatter("Empty private struct!\n"); return -EIO; } click_chatter("ToUserDevice_release ReadCounter: %lu\n", elem->_read_count); return 0;}int ToUserDevice::dev_ioctl(struct inode *inode, struct file *filp, unsigned command, unsigned long address){ ToUserDevice *elem = GETELEM(filp); if (elem->_exit) return -EIO; else if (command == CLICK_IOC_TOUSERDEVICE_GET_MULTI) return ((uintptr_t) filp->private_data) & 1; else if (command == CLICK_IOC_TOUSERDEVICE_SET_MULTI) { if ((int) address != 0 && (int) address != 1) return -EINVAL; filp->private_data = (void *) ((uintptr_t) elem | (int) address); return 0; } else return -EINVAL;}ssize_t ToUserDevice::dev_read(struct file *filp, char *buff, size_t len, loff_t *ppos){ ToUserDevice *elem = GETELEM(filp); int multi = ((uintptr_t) filp->private_data) & 1; int err; // there is a private field and that doesn't compile in C++ so we can't use DEFINE_WAIT macro wait_queue_t wq;#include <click/cxxprotect.h> init_wait(&wq);#include <click/cxxunprotect.h> spin_lock(&elem->_lock); // LOCK elem->_read_count++; while (!elem->_size && !elem->_exit) { // need to put the process to sleep elem->_block_count++; elem->_sleep_proc++; prepare_to_wait(&elem->_proc_queue, &wq, TASK_INTERRUPTIBLE); spin_unlock(&elem->_lock); // UNLOCK schedule(); finish_wait(&elem->_proc_queue, &wq); if (elem->_exit) return -EIO; spin_lock(&elem->_lock); // LOCK elem->_sleep_proc--; } ssize_t nread = 0; unsigned nfetched = 0; while (elem->_size && (nfetched == 0 || multi) && (nfetched < elem->_max_burst || elem->_max_burst == 0) && nread < len && !elem->_exit) { Packet *p = elem->_q[elem->_r_slot]; // user buffer is full if (nfetched > 0 && nread + sizeof(int) + p->length() > len) break; elem->_r_slot++; if (elem->_r_slot == elem->_capacity) elem->_r_slot = 0; elem->_size--; spin_unlock(&elem->_lock); // unlock as don't need the lock, and can't have a lock and copy_to_user // copy packet to user level if (multi) { int len = p->length(); if (copy_to_user(buff + nread, &len, sizeof(int))) { elem->_failed_count++; click_chatter("Read Fault"); p->kill(); return -EFAULT; } nread += sizeof(int); } ssize_t to_copy = p->length(); if (to_copy > len - nread) to_copy = len - nread; if (copy_to_user(buff + nread, p->data(), to_copy)) { elem->_failed_count++; click_chatter("Read Fault"); p->kill(); return -EFAULT; } nread += to_copy; // to get int alignment if (multi && nread % sizeof(int)) nread += sizeof(int) - nread % sizeof(int); p->kill(); spin_lock(&elem->_lock); nfetched++; elem->_pkt_read_count++; } if (nread == 0 && elem->_exit) nread = -EIO; spin_unlock(&elem->_lock); return nread;}uint ToUserDevice::dev_poll(struct file *filp, struct poll_table_struct *pt){ ToUserDevice *elem = GETELEM(filp); uint mask = 0; //click_chatter("ToUserDevice_poll\n"); if (!elem || elem->_exit) return mask; poll_wait(filp, &elem->_proc_queue, pt); spin_lock(&elem->_lock); // LOCK if (elem->_size) { mask |= POLLIN | POLLRDNORM; /* readable */ //click_chatter("ToUserDevice_poll Readable\n"); } spin_unlock(&elem->_lock); // UNLOCK return mask;}int ToUserDevice::configure(Vector<String> &conf, ErrorHandler *errh){ int res; dev_t dev; //click_chatter("CONFIGURE\n"); if (!dev_fops) return errh->error("file operations missing"); _max_burst = 0; if (cp_va_kparse(conf, this, errh, "DEV_MINOR", cpkP+cpkM, cpUnsigned, &_dev_minor, "CAPACITY", 0, cpUnsigned, &_capacity, "BURST", 0, cpUnsigned, &_max_burst, cpEnd) < 0) return -1; spin_lock_init(&_lock); // LOCK INIT if (!(_q = (Packet **)click_lalloc(_capacity * sizeof(Packet *)))) { click_chatter("ToUserDevice Failed to alloc %lu slots\n", _capacity); return -1; } _dev_major = DEV_MAJOR; DEV_NUM++; if (DEV_NUM == 1) { // time to associate the devname with this class //register the device now. this will register 255 minor numbers res = register_chrdev(_dev_major, DEV_NAME, dev_fops); if (res < 0) { click_chatter("Failed to Register Dev:%s Major:%d Minor:%d\n", DEV_NAME, _dev_major, _dev_minor); click_lfree((char*)_q, _capacity * sizeof(Packet *)); _q = 0; return - EIO; } } elem[_dev_minor] = this; init_waitqueue_head(&_proc_queue); click_chatter("Dev:%s Major: %d Minor: %d Size:%d\n", _devname.c_str(), _dev_major, _dev_minor, _size); return 0;}int ToUserDevice::initialize(ErrorHandler *errh){ click_chatter("**ToUserDevice init\n"); if (input_is_pull(0)) { ScheduleInfo::initialize_task(this, &_task, errh); _signal = Notifier::upstream_empty_signal(this, 0, &_task); } return 0;}//cleanupvoid ToUserDevice::cleanup(CleanupStage stage){ //click_chatter("cleanup..."); if (stage < CLEANUP_CONFIGURED) return; // have to quit, as configure was never called spin_lock(&_lock); // LOCK DEV_NUM--; _exit = true; // signal for exit spin_unlock(&_lock); // UNLOCK if (_q) { //click_chatter(" Start "); wake_up_interruptible(&_proc_queue); while (waitqueue_active(&_proc_queue)) schedule(); // I guess after this, god knows what will happen to procs that are // blocked if (!DEV_NUM) unregister_chrdev(_dev_major, DEV_NAME); // now clear out the memory while (_size > 0) { _q[_r_slot]->kill(); _r_slot++; if (_r_slot == _capacity) _r_slot = 0; _size--; } click_lfree((char*)_q, _capacity * sizeof(Packet *)); } //click_chatter("cleanup Done. ");}bool ToUserDevice::process(Packet *p){ spin_lock(&_lock); _pkt_count++; if (_size >= _capacity) { _drop_count++; p->kill(); spin_unlock(&_lock); return false; } _q[_w_slot] = p; _w_slot++; if (_w_slot == _capacity) _w_slot = 0; _size++; spin_unlock(&_lock); // UNLOCK // wake up procs if any are sleeping wake_up_interruptible(&_proc_queue); return true;}void ToUserDevice::push(int, Packet *p){ if (p) process(p);}// something is upstream so we can runbool ToUserDevice::run_task(Task *){ Packet *p = input(0).pull(); int res; if (p) process(p); else if (!_signal) return false; _task.fast_reschedule(); return p != 0;}enum { H_COUNT, H_DROPS, H_READ_CALLS, H_CAPACITY, H_READ_COUNT, H_SIZE, H_BLOCKS, H_FAILED};String ToUserDevice::read_handler(Element *e, void *thunk){ ToUserDevice *c = (ToUserDevice *)e; switch ((intptr_t)thunk) { case H_COUNT: return String(c->_pkt_count); case H_FAILED: return String(c->_failed_count); case H_BLOCKS: return String(c->_block_count); case H_SIZE: return String(c->_size); case H_DROPS: return String(c->_drop_count); case H_READ_CALLS: return String(c->_read_count); case H_READ_COUNT: return String(c->_pkt_read_count); case H_CAPACITY: return String(c->_capacity); default: return "<error>"; }}void ToUserDevice::add_handlers(){ add_read_handler("count", read_handler, (void *)H_COUNT); add_read_handler("failed", read_handler, (void *)H_FAILED); add_read_handler("blocks", read_handler, (void *)H_BLOCKS); add_read_handler("size", read_handler, (void *)H_SIZE); add_read_handler("drops", read_handler, (void *)H_DROPS); add_read_handler("reads", read_handler, (void *)H_READ_CALLS); add_read_handler("pkt_reads", read_handler, (void *)H_READ_COUNT); add_read_handler("capacity", read_handler, (void *)H_CAPACITY); if (input_is_pull(0)) add_task_handlers(&_task);}ELEMENT_REQUIRES(linuxmodule experimental)EXPORT_ELEMENT(ToUserDevice)ELEMENT_MT_SAFE(ToUserDevice)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -