├── .gitignore ├── ChangeLog ├── LICENSE ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac ├── debian ├── .gitignore ├── changelog ├── compat ├── control ├── copyright ├── docs ├── files ├── libmooshika-dev.dirs ├── libmooshika-dev.install ├── libmooshika-dev.substvars ├── libmooshika1.dirs ├── libmooshika1.install ├── libmooshika1.substvars ├── rules └── source │ └── format ├── doc ├── DESIGN ├── doxygen.conf └── man │ ├── rcat.1.txt │ ├── rmitm.1.txt │ └── rreplay.1.txt ├── include └── mooshika.h ├── libmooshika.spec.in ├── m4 └── .gitignore ├── rpm └── .gitignore └── src ├── .gitignore ├── Makefile.am ├── atomics.h ├── libmooshika.pc.in ├── rcat.c ├── rmitm.c ├── rmitm.h ├── rreplay.c ├── tests ├── .gitignore ├── Makefile.am ├── bench_rdma.c ├── mooshika.sigmund ├── multiple_sge.c ├── multithread.sh ├── nfsv4_client.c ├── read_write.c ├── run_tests.sh └── urandom-md5.sh ├── tools ├── .gitignore ├── Makefile.am └── pktdump.c ├── trans_rdma.c └── utils.h /.gitignore: -------------------------------------------------------------------------------- 1 | doxygen 2 | *.o 3 | *.lo 4 | *.la 5 | *.pc 6 | *.loT 7 | *~ 8 | m4 9 | \#*\# 10 | .*.swp 11 | *.pdf 12 | *.html 13 | *.xml 14 | *.1 15 | *.3 16 | libmooshika-*.tar.gz 17 | libmooshika.spec 18 | cscope* 19 | Makefile 20 | Makefile.in 21 | aclocal.m4 22 | autom4te.cache/ 23 | config.guess 24 | config.h 25 | config.h.in 26 | config.log 27 | config.status 28 | config.sub 29 | configure 30 | depcomp 31 | install-sh 32 | libtool 33 | ltmain.sh 34 | missing 35 | .deps 36 | .libs 37 | stamp-h1 38 | compile 39 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinetd/mooshika/938e1518a0da681ba00a16c159f00c8465f91c64/ChangeLog -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | 3 | SUBDIRS = src 4 | 5 | include_HEADERS = include/mooshika.h 6 | 7 | rpm: dist libmooshika.spec 8 | rpmbuild $(RPM_DB_ARGS) $(extra_arg) -tb $(distdir).tar.gz 9 | 10 | srpm: dist libmooshika.spec 11 | rpmbuild $(RPM_DB_ARGS) $(extra_arg) -ts $(distdir).tar.gz 12 | 13 | rpms: rpm srpm 14 | 15 | rpm/SPECS rpm/SOURCES rpm/RPMS rpm/BUILD rpm/SRPMS: 16 | mkdir -p $@ 17 | 18 | rpmdirs: rpm/SPECS rpm/SOURCES rpm/RPMS rpm/BUILD rpm/SRPMS 19 | 20 | clean-local: 21 | rm -rf doc/doxygen 22 | 23 | doc: 24 | doxygen ./doc/doxygen.conf 25 | cd doc/doxygen/latex; make pdf 26 | cd doc; asciidoc DESIGN; a2x -fpdf DESIGN 27 | cd doc/man; asciidoc rcat.1.txt; a2x -fmanpage rcat.1.txt; a2x -fpdf rcat.1.txt 28 | 29 | EXTRA_DIST=libmooshika.spec \ 30 | src/utils.h \ 31 | src/rmitm.h \ 32 | src/atomics.h \ 33 | LICENSE 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mooshika 2 | ======== 3 | 4 | RDMA abstraction layer 5 | 6 | 7 | Dependencies 8 | ============ 9 | 10 | Mooshika depends on libibverbs and librdmacm (with -devel or -dev packages and autoconf/libtool for compiling) 11 | 12 | Compiling 13 | ========= 14 | 15 | Straightforward: 16 | 17 | ./autogen.sh 18 | ./configure 19 | make 20 | make install 21 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | #autoreconf -i -f 6 | rm -f config.cache 7 | aclocal -I m4 8 | libtoolize --force --copy 9 | autoconf 10 | autoheader 11 | automake -a --add-missing -Wall 12 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([libmooshika], [1.1], [dominique.martinet@codewreck.org]) 2 | AM_INIT_AUTOMAKE([foreign]) 3 | 4 | AC_CONFIG_MACRO_DIR([m4]) 5 | 6 | AC_GNU_SOURCE 7 | AC_PROG_CC 8 | LT_INIT([disable-static]) 9 | 10 | # Check for InfiniBand Libs and Headers 11 | AC_SEARCH_LIBS([ibv_create_qp], [ibverbs], [], [ 12 | AC_MSG_ERROR([unable to find libibverbs]) 13 | ]) 14 | AC_CHECK_HEADERS([infiniband/verbs.h],[], [AC_MSG_ERROR(missing infiniband headers)]) 15 | 16 | # Check for RDAM_CM Libs and Headers 17 | AC_SEARCH_LIBS([rdma_create_id], [rdmacm], [], [ 18 | AC_MSG_ERROR([unable to find librdmacm]) 19 | ]) 20 | AC_CHECK_HEADERS([rdma/rdma_cma.h],[], [AC_MSG_ERROR(missing rdma headers)]) 21 | 22 | AC_DEFINE( [VERSION_COMMENT], ["libmooshika and examples"], [No Comment]) 23 | 24 | # Git latest commit 25 | AC_MSG_CHECKING( [Git HEAD] ) 26 | head_commit=`git rev-parse HEAD 2>/dev/null` 27 | 28 | if test "x$head_commit" == "x" ; then 29 | AC_MSG_RESULT( [no git here] ) 30 | AC_DEFINE_UNQUOTED( [_GIT_HEAD_COMMIT], "not compiled within a git repository", [Lastest HEAD at the time configure was run]) 31 | AC_MSG_CHECKING( [Git describe] ) 32 | AC_DEFINE_UNQUOTED( [_GIT_DESCRIBE], "not compiled within a git repository", [Result of git-describe --long]) 33 | AC_SUBST(_GIT_HEAD_TAG, [${VERSION}] ) 34 | AC_SUBST(_GIT_HEAD_DESCRIBE) 35 | else 36 | AC_MSG_RESULT( $head_commit ) 37 | AC_DEFINE_UNQUOTED( [_GIT_HEAD_COMMIT], "$head_commit", [Lastest HEAD at the time configure was run]) 38 | AC_MSG_CHECKING( [Git describe] ) 39 | git_describe=`git describe --long` 40 | AC_MSG_RESULT( $git_describe ) 41 | AC_DEFINE_UNQUOTED( [_GIT_DESCRIBE], "$git_describe", [Result of git-describe --long]) 42 | git_tag=${git_describe%%-*} 43 | git_describe=${git_describe#*-} 44 | if test "x$git_describe" != "x"; then 45 | AC_SUBST(_GIT_HEAD_TAG, [${git_tag}] ) 46 | AC_SUBST(_GIT_HEAD_DESCRIBE, [${git_describe//-/.}] ) 47 | else 48 | AC_SUBST(_GIT_HEAD_TAG, [${VERSION}] ) 49 | AC_SUBST(_GIT_HEAD_DESCRIBE, [1.g${head_commit:0:7}] ) 50 | fi 51 | fi 52 | 53 | 54 | AC_ARG_ENABLE(rmitm, 55 | AC_HELP_STRING([--enable-rmitm], [Enable rmitm compilation, requires pcap development files (auto)])) 56 | 57 | if test x$enable_rmitm != xno ; then 58 | AC_CHECK_HEADERS([pcap/pcap.h], [ enable_rmitm=yes ], [if test x$enable_rmitm != x; then AC_MSG_ERROR(rmitm requires libpcap); else echo "libpcap development files not found, disabling rmitm"; fi]) 59 | fi 60 | AM_CONDITIONAL(ENABLE_RMITM, test x$enable_rmitm = xyes) 61 | 62 | AC_ARG_WITH([valgrind], 63 | AC_HELP_STRING([--with-valgrind], 64 | [Enable Valgrind annotations (small runtime overhead, default NO)])) 65 | if test "x$with_valgrind" != "x" && test "x$with_valgrind" != "xno"; then 66 | if test -d $with_valgrind; then 67 | CFLAGS="$CFLAGS -I$with_valgrind/include" 68 | fi 69 | AC_CHECK_HEADER(valgrind/memcheck.h, 70 | [AC_DEFINE(HAVE_VALGRIND_MEMCHECK_H, 1, 71 | [Define to 1 if you have the header file.])], 72 | [AC_MSG_ERROR([Valgrind memcheck support requested, but not found.])]) 73 | fi 74 | 75 | 76 | AC_CANONICAL_BUILD 77 | 78 | WARNINGS_CFLAGS="-Wall -Wimplicit -Wformat -Wmissing-braces -Werror" 79 | if test "x$build_cpu" == "xarmv7hl"; then 80 | WARNINGS_CFLAGS="$WARNINGS_CFLAGS -Wno-error=cpp" 81 | fi 82 | AC_SUBST([WARNINGS_CFLAGS]) 83 | 84 | #for exporting to spec file 85 | AC_SUBST(ac_configure_args) 86 | 87 | AC_CONFIG_HEADERS([include/config.h]) 88 | AC_CONFIG_FILES([Makefile src/Makefile src/tests/Makefile src/tools/Makefile src/libmooshika.pc libmooshika.spec]) 89 | AC_OUTPUT 90 | -------------------------------------------------------------------------------- /debian/.gitignore: -------------------------------------------------------------------------------- 1 | *.debhelper 2 | *.log 3 | libmooshika1 4 | libmooshika-dev 5 | tmp 6 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | libmooshika (1.1-1) unstable; urgency=low 2 | 3 | * Create release to fix build issue 4 | 5 | -- Dominique Martinet Tue, 15 Dec 2021 06:44:34 +0900 6 | 7 | libmooshika (1.0-1) unstable; urgency=low 8 | 9 | * Initial release 10 | 11 | -- Dominique Martinet Thu, 19 Feb 2015 08:40:01 +0100 12 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: libmooshika 2 | Priority: extra 3 | Maintainer: Dominique Martinet 4 | Build-Depends: debhelper (>= 8.0.0), autotools-dev, librdmacm-dev, libibverbs-dev, libpcap-dev 5 | Standards-Version: 3.9.3 6 | Section: libs 7 | Homepage: https://github.com/martinetd/mooshika 8 | Vcs-Git: git://github.com/martinetd/mooshika.git 9 | 10 | Package: libmooshika-dev 11 | Section: libdevel 12 | Architecture: any 13 | Depends: libmooshika1 (= ${binary:Version}), librdmacm-dev, libibverbs-dev 14 | Description: libmooshika is a RDMA helper lib 15 | libmooshika is a RDMA helper lib 16 | 17 | Package: libmooshika1 18 | Section: libs 19 | Architecture: any 20 | Depends: librdmacm1, libibverbs1, libpcap0.8 21 | Description: libmooshika is a RDMA helper lib 22 | libmooshika is a RDMA helper lib 23 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: mooshika 3 | Source: http://github.com/martinetd/mooshika 4 | 5 | Files: * 6 | Copyright: 2013-2015 Dominique Martinet 7 | License: LGPL-3 8 | This package is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as 10 | published by the Free Software Foundation; either version 3 of the 11 | License, or (at your option) any later version. 12 | . 13 | This package is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | . 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this program. If not, see 20 | . 21 | On Debian systems, the complete text of the GNU Lesser General 22 | Public License version 3 can be found in "/usr/share/common-licenses/LGPL-3". 23 | 24 | # Please also look if there are files or directories which have a 25 | # different copyright/license attached and list them here. 26 | # Please avoid to pick license terms that are more restrictive than the 27 | # packaged work, as it may make Debian's contributions unacceptable upstream. 28 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/files: -------------------------------------------------------------------------------- 1 | libmooshika-dev_1.1-1_amd64.deb libdevel extra 2 | libmooshika1_1.1-1_amd64.deb libs extra 3 | -------------------------------------------------------------------------------- /debian/libmooshika-dev.dirs: -------------------------------------------------------------------------------- 1 | usr/lib 2 | usr/include 3 | -------------------------------------------------------------------------------- /debian/libmooshika-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/* 2 | usr/lib/lib*.a 3 | usr/lib/lib*.so 4 | usr/lib/pkgconfig/* 5 | -------------------------------------------------------------------------------- /debian/libmooshika-dev.substvars: -------------------------------------------------------------------------------- 1 | misc:Depends= 2 | -------------------------------------------------------------------------------- /debian/libmooshika1.dirs: -------------------------------------------------------------------------------- 1 | usr/lib 2 | -------------------------------------------------------------------------------- /debian/libmooshika1.install: -------------------------------------------------------------------------------- 1 | usr/lib/lib*.so.* 2 | -------------------------------------------------------------------------------- /debian/libmooshika1.substvars: -------------------------------------------------------------------------------- 1 | shlibs:Depends=libc6 (>= 2.7), libibverbs1 (>= 1.1.2), librdmacm1 (>= 1.0.15) 2 | misc:Depends= 3 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | #export DH_VERBOSE=1 11 | 12 | %: 13 | dh $@ --with autotools_dev 14 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /doc/DESIGN: -------------------------------------------------------------------------------- 1 | Mooshika design document 2 | ======================== 3 | Dominique Martinet 4 | 5 | == `trans_rdma.c` 6 | 7 | 8 | Threads: 9 | 10 | - `msk_cm_thread` 11 | - `msk_cq_thread` 12 | - (optional) `worker_count` * `msk_worker_thread` 13 | - (optional) `msk_stats_thread` 14 | 15 | 16 | === `msk_cm_thread`: Connection Manager thread 17 | 18 | epoll_wait on event channel fds (one per listenner or client), 19 | processes connection requests/process, disconnect event, errors... 20 | 21 | 22 | === `msk_cq_thread`: Completion Queue thread 23 | 24 | epoll_wait on completion channel fds (one per connection), 25 | processes incoming messages and completion events for outgoing ones. 26 | 27 | Either executes callback directly or notify a worker thread to do it 28 | (lockless work queue with notification through eventfds) 29 | 30 | The actual work is done in `msk_worker_callback` in either case. 31 | 32 | 33 | === Important data structures 34 | ==== `struct msk_trans` 35 | Need to make half of it private somehow (so as not to need to share it 36 | between different transports), but contains everything needed for the 37 | connection to work, a state machine, stats, etc... 38 | 39 | ==== `struct trans_attr` 40 | Parameters for the connection. Mandatory fields are: 41 | 42 | - `server`, 0 for client, connection backlog on a server 43 | - `node` and `port`: strings to use for remote peer's hostname and service 44 | that will be fed to a custom getaddrinfo 45 | 46 | Useful ones: 47 | 48 | - `debug`, a debug mask 49 | - `worker_count` and `worker_queue_size` for worker threads (queue size 50 | will be rounded up to bigger power of 2) 51 | - `stats_prefix` for stats unix socket path 52 | 53 | ==== `struct msk_ctx`: trace back to the request in the completion event 54 | 55 | ib (and portals4) allow for a uint64 to be carried through. 56 | Our's contains a pointer to an `msk_ctx` struct, that is: 57 | 58 | - the processed data 59 | - callbacks pointers (functions and arguments) 60 | - work request 61 | - scatter-gather list 62 | 63 | 64 | -------------------------------------------------------------------------------- /doc/doxygen.conf: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.4.7 2 | 3 | # This file describes the settings to be used by the documentation system 4 | # doxygen (www.doxygen.org) for a project 5 | # 6 | # All text after a hash (#) is considered a comment and will be ignored 7 | # The format is: 8 | # TAG = value [value, ...] 9 | # For lists items can also be appended using: 10 | # TAG += value [value, ...] 11 | # Values that contain spaces should be placed between quotes (" ") 12 | 13 | #--------------------------------------------------------------------------- 14 | # Project related configuration options 15 | #--------------------------------------------------------------------------- 16 | 17 | # The PROJECT_NAME tag is a single word (or a sequence of words surrounded 18 | # by quotes) that should identify the project. 19 | 20 | PROJECT_NAME = libmooshika 21 | 22 | # The PROJECT_NUMBER tag can be used to enter a project or revision number. 23 | # This could be handy for archiving the generated documentation or 24 | # if some version control system is used. 25 | 26 | PROJECT_NUMBER = 27 | 28 | # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 29 | # base path where the generated documentation will be put. 30 | # If a relative path is entered, it will be relative to the location 31 | # where doxygen was started. If left blank the current directory will be used. 32 | 33 | OUTPUT_DIRECTORY = doc/doxygen 34 | 35 | # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 36 | # 4096 sub-directories (in 2 levels) under the output directory of each output 37 | # format and will distribute the generated files over these directories. 38 | # Enabling this option can be useful when feeding doxygen a huge amount of 39 | # source files, where putting all generated files in the same directory would 40 | # otherwise cause performance problems for the file system. 41 | 42 | CREATE_SUBDIRS = NO 43 | 44 | # The OUTPUT_LANGUAGE tag is used to specify the language in which all 45 | # documentation generated by doxygen is written. Doxygen will use this 46 | # information to generate all constant output in the proper language. 47 | # The default language is English, other supported languages are: 48 | # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 49 | # Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 50 | # Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 51 | # Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 52 | # Swedish, and Ukrainian. 53 | 54 | OUTPUT_LANGUAGE = English 55 | 56 | # This tag can be used to specify the encoding used in the generated output. 57 | # The encoding is not always determined by the language that is chosen, 58 | # but also whether or not the output is meant for Windows or non-Windows users. 59 | # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 60 | # forces the Windows encoding (this is the default for the Windows binary), 61 | # whereas setting the tag to NO uses a Unix-style encoding (the default for 62 | # all platforms other than Windows). 63 | 64 | USE_WINDOWS_ENCODING = NO 65 | 66 | # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 67 | # include brief member descriptions after the members that are listed in 68 | # the file and class documentation (similar to JavaDoc). 69 | # Set to NO to disable this. 70 | 71 | BRIEF_MEMBER_DESC = YES 72 | 73 | # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 74 | # the brief description of a member or function before the detailed description. 75 | # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 76 | # brief descriptions will be completely suppressed. 77 | 78 | REPEAT_BRIEF = YES 79 | 80 | # This tag implements a quasi-intelligent brief description abbreviator 81 | # that is used to form the text in various listings. Each string 82 | # in this list, if found as the leading text of the brief description, will be 83 | # stripped from the text and the result after processing the whole list, is 84 | # used as the annotated text. Otherwise, the brief description is used as-is. 85 | # If left blank, the following values are used ("$name" is automatically 86 | # replaced with the name of the entity): "The $name class" "The $name widget" 87 | # "The $name file" "is" "provides" "specifies" "contains" 88 | # "represents" "a" "an" "the" 89 | 90 | ABBREVIATE_BRIEF = 91 | 92 | # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 93 | # Doxygen will generate a detailed section even if there is only a brief 94 | # description. 95 | 96 | ALWAYS_DETAILED_SEC = NO 97 | 98 | # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 99 | # inherited members of a class in the documentation of that class as if those 100 | # members were ordinary class members. Constructors, destructors and assignment 101 | # operators of the base classes will not be shown. 102 | 103 | INLINE_INHERITED_MEMB = NO 104 | 105 | # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 106 | # path before files name in the file list and in the header files. If set 107 | # to NO the shortest path that makes the file name unique will be used. 108 | 109 | FULL_PATH_NAMES = NO 110 | 111 | # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 112 | # can be used to strip a user-defined part of the path. Stripping is 113 | # only done if one of the specified strings matches the left-hand part of 114 | # the path. The tag can be used to show relative paths in the file list. 115 | # If left blank the directory from which doxygen is run is used as the 116 | # path to strip. 117 | 118 | STRIP_FROM_PATH = src/ 119 | 120 | # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 121 | # the path mentioned in the documentation of a class, which tells 122 | # the reader which header file to include in order to use a class. 123 | # If left blank only the name of the header file containing the class 124 | # definition is used. Otherwise one should specify the include paths that 125 | # are normally passed to the compiler using the -I flag. 126 | 127 | STRIP_FROM_INC_PATH = 128 | 129 | # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 130 | # (but less readable) file names. This can be useful is your file systems 131 | # doesn't support long names like on DOS, Mac, or CD-ROM. 132 | 133 | SHORT_NAMES = NO 134 | 135 | # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 136 | # will interpret the first line (until the first dot) of a JavaDoc-style 137 | # comment as the brief description. If set to NO, the JavaDoc 138 | # comments will behave just like the Qt-style comments (thus requiring an 139 | # explicit @brief command for a brief description. 140 | 141 | JAVADOC_AUTOBRIEF = YES 142 | 143 | # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 144 | # treat a multi-line C++ special comment block (i.e. a block of //! or /// 145 | # comments) as a brief description. This used to be the default behaviour. 146 | # The new default is to treat a multi-line C++ comment block as a detailed 147 | # description. Set this tag to YES if you prefer the old behaviour instead. 148 | 149 | MULTILINE_CPP_IS_BRIEF = NO 150 | 151 | # If the DETAILS_AT_TOP tag is set to YES then Doxygen 152 | # will output the detailed description near the top, like JavaDoc. 153 | # If set to NO, the detailed description appears after the member 154 | # documentation. 155 | 156 | DETAILS_AT_TOP = NO 157 | 158 | # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 159 | # member inherits the documentation from any documented member that it 160 | # re-implements. 161 | 162 | INHERIT_DOCS = YES 163 | 164 | # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 165 | # a new page for each member. If set to NO, the documentation of a member will 166 | # be part of the file/class/namespace that contains it. 167 | 168 | SEPARATE_MEMBER_PAGES = NO 169 | 170 | # The TAB_SIZE tag can be used to set the number of spaces in a tab. 171 | # Doxygen uses this value to replace tabs by spaces in code fragments. 172 | 173 | TAB_SIZE = 8 174 | 175 | # This tag can be used to specify a number of aliases that acts 176 | # as commands in the documentation. An alias has the form "name=value". 177 | # For example adding "sideeffect=\par Side Effects:\n" will allow you to 178 | # put the command \sideeffect (or @sideeffect) in the documentation, which 179 | # will result in a user-defined paragraph with heading "Side Effects:". 180 | # You can put \n's in the value part of an alias to insert newlines. 181 | 182 | ALIASES = 183 | 184 | # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 185 | # sources only. Doxygen will then generate output that is more tailored for C. 186 | # For instance, some of the names that are used will be different. The list 187 | # of all members will be omitted, etc. 188 | 189 | OPTIMIZE_OUTPUT_FOR_C = YES 190 | 191 | # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 192 | # sources only. Doxygen will then generate output that is more tailored for Java. 193 | # For instance, namespaces will be presented as packages, qualified scopes 194 | # will look different, etc. 195 | 196 | OPTIMIZE_OUTPUT_JAVA = NO 197 | 198 | # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to 199 | # include (a tag file for) the STL sources as input, then you should 200 | # set this tag to YES in order to let doxygen match functions declarations and 201 | # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 202 | # func(std::string) {}). This also make the inheritance and collaboration 203 | # diagrams that involve STL classes more complete and accurate. 204 | 205 | BUILTIN_STL_SUPPORT = NO 206 | 207 | # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 208 | # tag is set to YES, then doxygen will reuse the documentation of the first 209 | # member in the group (if any) for the other members of the group. By default 210 | # all members of a group must be documented explicitly. 211 | 212 | DISTRIBUTE_GROUP_DOC = NO 213 | 214 | # Set the SUBGROUPING tag to YES (the default) to allow class member groups of 215 | # the same type (for instance a group of public functions) to be put as a 216 | # subgroup of that type (e.g. under the Public Functions section). Set it to 217 | # NO to prevent subgrouping. Alternatively, this can be done per class using 218 | # the \nosubgrouping command. 219 | 220 | SUBGROUPING = YES 221 | 222 | #--------------------------------------------------------------------------- 223 | # Build related configuration options 224 | #--------------------------------------------------------------------------- 225 | 226 | # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 227 | # documentation are documented, even if no documentation was available. 228 | # Private class members and static file members will be hidden unless 229 | # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES 230 | 231 | EXTRACT_ALL = YES 232 | 233 | # If the EXTRACT_PRIVATE tag is set to YES all private members of a class 234 | # will be included in the documentation. 235 | 236 | EXTRACT_PRIVATE = NO 237 | 238 | # If the EXTRACT_STATIC tag is set to YES all static members of a file 239 | # will be included in the documentation. 240 | 241 | EXTRACT_STATIC = NO 242 | 243 | # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 244 | # defined locally in source files will be included in the documentation. 245 | # If set to NO only classes defined in header files are included. 246 | 247 | EXTRACT_LOCAL_CLASSES = NO 248 | 249 | # This flag is only useful for Objective-C code. When set to YES local 250 | # methods, which are defined in the implementation section but not in 251 | # the interface are included in the documentation. 252 | # If set to NO (the default) only methods in the interface are included. 253 | 254 | EXTRACT_LOCAL_METHODS = NO 255 | 256 | # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 257 | # undocumented members of documented classes, files or namespaces. 258 | # If set to NO (the default) these members will be included in the 259 | # various overviews, but no documentation section is generated. 260 | # This option has no effect if EXTRACT_ALL is enabled. 261 | 262 | HIDE_UNDOC_MEMBERS = NO 263 | 264 | # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 265 | # undocumented classes that are normally visible in the class hierarchy. 266 | # If set to NO (the default) these classes will be included in the various 267 | # overviews. This option has no effect if EXTRACT_ALL is enabled. 268 | 269 | HIDE_UNDOC_CLASSES = NO 270 | 271 | # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 272 | # friend (class|struct|union) declarations. 273 | # If set to NO (the default) these declarations will be included in the 274 | # documentation. 275 | 276 | HIDE_FRIEND_COMPOUNDS = NO 277 | 278 | # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 279 | # documentation blocks found inside the body of a function. 280 | # If set to NO (the default) these blocks will be appended to the 281 | # function's detailed documentation block. 282 | 283 | HIDE_IN_BODY_DOCS = NO 284 | 285 | # The INTERNAL_DOCS tag determines if documentation 286 | # that is typed after a \internal command is included. If the tag is set 287 | # to NO (the default) then the documentation will be excluded. 288 | # Set it to YES to include the internal documentation. 289 | 290 | INTERNAL_DOCS = NO 291 | 292 | # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 293 | # file names in lower-case letters. If set to YES upper-case letters are also 294 | # allowed. This is useful if you have classes or files whose names only differ 295 | # in case and if your file system supports case sensitive file names. Windows 296 | # and Mac users are advised to set this option to NO. 297 | 298 | CASE_SENSE_NAMES = YES 299 | 300 | # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 301 | # will show members with their full class and namespace scopes in the 302 | # documentation. If set to YES the scope will be hidden. 303 | 304 | HIDE_SCOPE_NAMES = NO 305 | 306 | # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 307 | # will put a list of the files that are included by a file in the documentation 308 | # of that file. 309 | 310 | SHOW_INCLUDE_FILES = YES 311 | 312 | # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 313 | # is inserted in the documentation for inline members. 314 | 315 | INLINE_INFO = YES 316 | 317 | # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 318 | # will sort the (detailed) documentation of file and class members 319 | # alphabetically by member name. If set to NO the members will appear in 320 | # declaration order. 321 | 322 | SORT_MEMBER_DOCS = YES 323 | 324 | # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 325 | # brief documentation of file, namespace and class members alphabetically 326 | # by member name. If set to NO (the default) the members will appear in 327 | # declaration order. 328 | 329 | SORT_BRIEF_DOCS = NO 330 | 331 | # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 332 | # sorted by fully-qualified names, including namespaces. If set to 333 | # NO (the default), the class list will be sorted only by class name, 334 | # not including the namespace part. 335 | # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 336 | # Note: This option applies only to the class list, not to the 337 | # alphabetical list. 338 | 339 | SORT_BY_SCOPE_NAME = NO 340 | 341 | # The GENERATE_TODOLIST tag can be used to enable (YES) or 342 | # disable (NO) the todo list. This list is created by putting \todo 343 | # commands in the documentation. 344 | 345 | GENERATE_TODOLIST = YES 346 | 347 | # The GENERATE_TESTLIST tag can be used to enable (YES) or 348 | # disable (NO) the test list. This list is created by putting \test 349 | # commands in the documentation. 350 | 351 | GENERATE_TESTLIST = YES 352 | 353 | # The GENERATE_BUGLIST tag can be used to enable (YES) or 354 | # disable (NO) the bug list. This list is created by putting \bug 355 | # commands in the documentation. 356 | 357 | GENERATE_BUGLIST = YES 358 | 359 | # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 360 | # disable (NO) the deprecated list. This list is created by putting 361 | # \deprecated commands in the documentation. 362 | 363 | GENERATE_DEPRECATEDLIST= YES 364 | 365 | # The ENABLED_SECTIONS tag can be used to enable conditional 366 | # documentation sections, marked by \if sectionname ... \endif. 367 | 368 | ENABLED_SECTIONS = 369 | 370 | # The MAX_INITIALIZER_LINES tag determines the maximum number of lines 371 | # the initial value of a variable or define consists of for it to appear in 372 | # the documentation. If the initializer consists of more lines than specified 373 | # here it will be hidden. Use a value of 0 to hide initializers completely. 374 | # The appearance of the initializer of individual variables and defines in the 375 | # documentation can be controlled using \showinitializer or \hideinitializer 376 | # command in the documentation regardless of this setting. 377 | 378 | MAX_INITIALIZER_LINES = 30 379 | 380 | # Set the SHOW_USED_FILES tag to NO to disable the list of files generated 381 | # at the bottom of the documentation of classes and structs. If set to YES the 382 | # list will mention the files that were used to generate the documentation. 383 | 384 | SHOW_USED_FILES = YES 385 | 386 | # If the sources in your project are distributed over multiple directories 387 | # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 388 | # in the documentation. The default is NO. 389 | 390 | SHOW_DIRECTORIES = NO 391 | 392 | # The FILE_VERSION_FILTER tag can be used to specify a program or script that 393 | # doxygen should invoke to get the current version for each file (typically from the 394 | # version control system). Doxygen will invoke the program by executing (via 395 | # popen()) the command , where is the value of 396 | # the FILE_VERSION_FILTER tag, and is the name of an input file 397 | # provided by doxygen. Whatever the program writes to standard output 398 | # is used as the file version. See the manual for examples. 399 | 400 | FILE_VERSION_FILTER = 401 | 402 | #--------------------------------------------------------------------------- 403 | # configuration options related to warning and progress messages 404 | #--------------------------------------------------------------------------- 405 | 406 | # The QUIET tag can be used to turn on/off the messages that are generated 407 | # by doxygen. Possible values are YES and NO. If left blank NO is used. 408 | 409 | QUIET = NO 410 | 411 | # The WARNINGS tag can be used to turn on/off the warning messages that are 412 | # generated by doxygen. Possible values are YES and NO. If left blank 413 | # NO is used. 414 | 415 | WARNINGS = YES 416 | 417 | # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 418 | # for undocumented members. If EXTRACT_ALL is set to YES then this flag will 419 | # automatically be disabled. 420 | 421 | WARN_IF_UNDOCUMENTED = NO 422 | 423 | # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 424 | # potential errors in the documentation, such as not documenting some 425 | # parameters in a documented function, or documenting parameters that 426 | # don't exist or using markup commands wrongly. 427 | 428 | WARN_IF_DOC_ERROR = YES 429 | 430 | # This WARN_NO_PARAMDOC option can be abled to get warnings for 431 | # functions that are documented, but have no documentation for their parameters 432 | # or return value. If set to NO (the default) doxygen will only warn about 433 | # wrong or incomplete parameter documentation, but not about the absence of 434 | # documentation. 435 | 436 | WARN_NO_PARAMDOC = NO 437 | 438 | # The WARN_FORMAT tag determines the format of the warning messages that 439 | # doxygen can produce. The string should contain the $file, $line, and $text 440 | # tags, which will be replaced by the file and line number from which the 441 | # warning originated and the warning text. Optionally the format may contain 442 | # $version, which will be replaced by the version of the file (if it could 443 | # be obtained via FILE_VERSION_FILTER) 444 | 445 | WARN_FORMAT = "$file:$line: $text" 446 | 447 | # The WARN_LOGFILE tag can be used to specify a file to which warning 448 | # and error messages should be written. If left blank the output is written 449 | # to stderr. 450 | 451 | WARN_LOGFILE = 452 | 453 | #--------------------------------------------------------------------------- 454 | # configuration options related to the input files 455 | #--------------------------------------------------------------------------- 456 | 457 | # The INPUT tag can be used to specify the files and/or directories that contain 458 | # documented source files. You may enter file names like "myfile.cpp" or 459 | # directories like "/usr/src/myproject". Separate the files or directories 460 | # with spaces. 461 | 462 | INPUT = src/ include/ 463 | 464 | # If the value of the INPUT tag contains directories, you can use the 465 | # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 466 | # and *.h) to filter out the source-files in the directories. If left 467 | # blank the following patterns are tested: 468 | # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 469 | # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py 470 | 471 | FILE_PATTERNS = 472 | 473 | # The RECURSIVE tag can be used to turn specify whether or not subdirectories 474 | # should be searched for input files as well. Possible values are YES and NO. 475 | # If left blank NO is used. 476 | 477 | RECURSIVE = YES 478 | 479 | # The EXCLUDE tag can be used to specify files and/or directories that should 480 | # excluded from the INPUT source files. This way you can easily exclude a 481 | # subdirectory from a directory tree whose root is specified with the INPUT tag. 482 | 483 | EXCLUDE = include/config.h 484 | 485 | # The EXCLUDE_SYMLINKS tag can be used select whether or not files or 486 | # directories that are symbolic links (a Unix filesystem feature) are excluded 487 | # from the input. 488 | 489 | EXCLUDE_SYMLINKS = NO 490 | 491 | # If the value of the INPUT tag contains directories, you can use the 492 | # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 493 | # certain files from those directories. Note that the wildcards are matched 494 | # against the file with absolute path, so to exclude all test directories 495 | # for example use the pattern */test/* 496 | 497 | EXCLUDE_PATTERNS = 498 | 499 | # The EXAMPLE_PATH tag can be used to specify one or more files or 500 | # directories that contain example code fragments that are included (see 501 | # the \include command). 502 | 503 | EXAMPLE_PATH = 504 | 505 | # If the value of the EXAMPLE_PATH tag contains directories, you can use the 506 | # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 507 | # and *.h) to filter out the source-files in the directories. If left 508 | # blank all files are included. 509 | 510 | EXAMPLE_PATTERNS = 511 | 512 | # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 513 | # searched for input files to be used with the \include or \dontinclude 514 | # commands irrespective of the value of the RECURSIVE tag. 515 | # Possible values are YES and NO. If left blank NO is used. 516 | 517 | EXAMPLE_RECURSIVE = NO 518 | 519 | # The IMAGE_PATH tag can be used to specify one or more files or 520 | # directories that contain image that are included in the documentation (see 521 | # the \image command). 522 | 523 | IMAGE_PATH = 524 | 525 | # The INPUT_FILTER tag can be used to specify a program that doxygen should 526 | # invoke to filter for each input file. Doxygen will invoke the filter program 527 | # by executing (via popen()) the command , where 528 | # is the value of the INPUT_FILTER tag, and is the name of an 529 | # input file. Doxygen will then use the output that the filter program writes 530 | # to standard output. If FILTER_PATTERNS is specified, this tag will be 531 | # ignored. 532 | 533 | INPUT_FILTER = "sed -e 's#//[^A-Za-z]*\(FIXME\|TODO\|todo\|fixme\)[:?]*#\n/// \\todo#g'" 534 | 535 | # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 536 | # basis. Doxygen will compare the file name with each pattern and apply the 537 | # filter if there is a match. The filters are a list of the form: 538 | # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 539 | # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 540 | # is applied to all files. 541 | 542 | FILTER_PATTERNS = 543 | 544 | # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 545 | # INPUT_FILTER) will be used to filter the input files when producing source 546 | # files to browse (i.e. when SOURCE_BROWSER is set to YES). 547 | 548 | FILTER_SOURCE_FILES = NO 549 | 550 | #--------------------------------------------------------------------------- 551 | # configuration options related to source browsing 552 | #--------------------------------------------------------------------------- 553 | 554 | # If the SOURCE_BROWSER tag is set to YES then a list of source files will 555 | # be generated. Documented entities will be cross-referenced with these sources. 556 | # Note: To get rid of all source code in the generated output, make sure also 557 | # VERBATIM_HEADERS is set to NO. 558 | 559 | SOURCE_BROWSER = NO 560 | 561 | # Setting the INLINE_SOURCES tag to YES will include the body 562 | # of functions and classes directly in the documentation. 563 | 564 | INLINE_SOURCES = NO 565 | 566 | # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 567 | # doxygen to hide any special comment blocks from generated source code 568 | # fragments. Normal C and C++ comments will always remain visible. 569 | 570 | STRIP_CODE_COMMENTS = YES 571 | 572 | # If the REFERENCED_BY_RELATION tag is set to YES (the default) 573 | # then for each documented function all documented 574 | # functions referencing it will be listed. 575 | 576 | REFERENCED_BY_RELATION = YES 577 | 578 | # If the REFERENCES_RELATION tag is set to YES (the default) 579 | # then for each documented function all documented entities 580 | # called/used by that function will be listed. 581 | 582 | REFERENCES_RELATION = YES 583 | 584 | # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 585 | # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 586 | # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 587 | # link to the source code. Otherwise they will link to the documentstion. 588 | 589 | REFERENCES_LINK_SOURCE = YES 590 | 591 | # If the USE_HTAGS tag is set to YES then the references to source code 592 | # will point to the HTML generated by the htags(1) tool instead of doxygen 593 | # built-in source browser. The htags tool is part of GNU's global source 594 | # tagging system (see http://www.gnu.org/software/global/global.html). You 595 | # will need version 4.8.6 or higher. 596 | 597 | USE_HTAGS = NO 598 | 599 | # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 600 | # will generate a verbatim copy of the header file for each class for 601 | # which an include is specified. Set to NO to disable this. 602 | 603 | VERBATIM_HEADERS = YES 604 | 605 | #--------------------------------------------------------------------------- 606 | # configuration options related to the alphabetical class index 607 | #--------------------------------------------------------------------------- 608 | 609 | # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 610 | # of all compounds will be generated. Enable this if the project 611 | # contains a lot of classes, structs, unions or interfaces. 612 | 613 | ALPHABETICAL_INDEX = NO 614 | 615 | # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 616 | # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 617 | # in which this list will be split (can be a number in the range [1..20]) 618 | 619 | COLS_IN_ALPHA_INDEX = 5 620 | 621 | # In case all classes in a project start with a common prefix, all 622 | # classes will be put under the same header in the alphabetical index. 623 | # The IGNORE_PREFIX tag can be used to specify one or more prefixes that 624 | # should be ignored while generating the index headers. 625 | 626 | IGNORE_PREFIX = 627 | 628 | #--------------------------------------------------------------------------- 629 | # configuration options related to the HTML output 630 | #--------------------------------------------------------------------------- 631 | 632 | # If the GENERATE_HTML tag is set to YES (the default) Doxygen will 633 | # generate HTML output. 634 | 635 | GENERATE_HTML = YES 636 | 637 | # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 638 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 639 | # put in front of it. If left blank `html' will be used as the default path. 640 | 641 | HTML_OUTPUT = html 642 | 643 | # The HTML_FILE_EXTENSION tag can be used to specify the file extension for 644 | # each generated HTML page (for example: .htm,.php,.asp). If it is left blank 645 | # doxygen will generate files with .html extension. 646 | 647 | HTML_FILE_EXTENSION = .html 648 | 649 | # The HTML_HEADER tag can be used to specify a personal HTML header for 650 | # each generated HTML page. If it is left blank doxygen will generate a 651 | # standard header. 652 | 653 | HTML_HEADER = 654 | 655 | # The HTML_FOOTER tag can be used to specify a personal HTML footer for 656 | # each generated HTML page. If it is left blank doxygen will generate a 657 | # standard footer. 658 | 659 | HTML_FOOTER = 660 | 661 | # The HTML_STYLESHEET tag can be used to specify a user-defined cascading 662 | # style sheet that is used by each HTML page. It can be used to 663 | # fine-tune the look of the HTML output. If the tag is left blank doxygen 664 | # will generate a default style sheet. Note that doxygen will try to copy 665 | # the style sheet file to the HTML output directory, so don't put your own 666 | # stylesheet in the HTML output directory as well, or it will be erased! 667 | 668 | HTML_STYLESHEET = 669 | 670 | # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 671 | # files or namespaces will be aligned in HTML using tables. If set to 672 | # NO a bullet list will be used. 673 | 674 | HTML_ALIGN_MEMBERS = YES 675 | 676 | # If the GENERATE_HTMLHELP tag is set to YES, additional index files 677 | # will be generated that can be used as input for tools like the 678 | # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 679 | # of the generated HTML documentation. 680 | 681 | GENERATE_HTMLHELP = NO 682 | 683 | # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 684 | # be used to specify the file name of the resulting .chm file. You 685 | # can add a path in front of the file if the result should not be 686 | # written to the html output directory. 687 | 688 | CHM_FILE = 689 | 690 | # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 691 | # be used to specify the location (absolute path including file name) of 692 | # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 693 | # the HTML help compiler on the generated index.hhp. 694 | 695 | HHC_LOCATION = 696 | 697 | # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 698 | # controls if a separate .chi index file is generated (YES) or that 699 | # it should be included in the master .chm file (NO). 700 | 701 | GENERATE_CHI = NO 702 | 703 | # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 704 | # controls whether a binary table of contents is generated (YES) or a 705 | # normal table of contents (NO) in the .chm file. 706 | 707 | BINARY_TOC = NO 708 | 709 | # The TOC_EXPAND flag can be set to YES to add extra items for group members 710 | # to the contents of the HTML help documentation and to the tree view. 711 | 712 | TOC_EXPAND = NO 713 | 714 | # The DISABLE_INDEX tag can be used to turn on/off the condensed index at 715 | # top of each HTML page. The value NO (the default) enables the index and 716 | # the value YES disables it. 717 | 718 | DISABLE_INDEX = NO 719 | 720 | # This tag can be used to set the number of enum values (range [1..20]) 721 | # that doxygen will group on one line in the generated HTML documentation. 722 | 723 | ENUM_VALUES_PER_LINE = 4 724 | 725 | # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be 726 | # generated containing a tree-like index structure (just like the one that 727 | # is generated for HTML Help). For this to work a browser that supports 728 | # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 729 | # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 730 | # probably better off using the HTML help feature. 731 | 732 | GENERATE_TREEVIEW = NO 733 | 734 | # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 735 | # used to set the initial width (in pixels) of the frame in which the tree 736 | # is shown. 737 | 738 | TREEVIEW_WIDTH = 250 739 | 740 | #--------------------------------------------------------------------------- 741 | # configuration options related to the LaTeX output 742 | #--------------------------------------------------------------------------- 743 | 744 | # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 745 | # generate Latex output. 746 | 747 | GENERATE_LATEX = YES 748 | 749 | # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 750 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 751 | # put in front of it. If left blank `latex' will be used as the default path. 752 | 753 | LATEX_OUTPUT = latex 754 | 755 | # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 756 | # invoked. If left blank `latex' will be used as the default command name. 757 | 758 | LATEX_CMD_NAME = latex 759 | 760 | # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 761 | # generate index for LaTeX. If left blank `makeindex' will be used as the 762 | # default command name. 763 | 764 | MAKEINDEX_CMD_NAME = makeindex 765 | 766 | # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 767 | # LaTeX documents. This may be useful for small projects and may help to 768 | # save some trees in general. 769 | 770 | COMPACT_LATEX = NO 771 | 772 | # The PAPER_TYPE tag can be used to set the paper type that is used 773 | # by the printer. Possible values are: a4, a4wide, letter, legal and 774 | # executive. If left blank a4wide will be used. 775 | 776 | PAPER_TYPE = a4wide 777 | 778 | # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 779 | # packages that should be included in the LaTeX output. 780 | 781 | EXTRA_PACKAGES = 782 | 783 | # The LATEX_HEADER tag can be used to specify a personal LaTeX header for 784 | # the generated latex document. The header should contain everything until 785 | # the first chapter. If it is left blank doxygen will generate a 786 | # standard header. Notice: only use this tag if you know what you are doing! 787 | 788 | LATEX_HEADER = 789 | 790 | # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 791 | # is prepared for conversion to pdf (using ps2pdf). The pdf file will 792 | # contain links (just like the HTML output) instead of page references 793 | # This makes the output suitable for online browsing using a pdf viewer. 794 | 795 | PDF_HYPERLINKS = NO 796 | 797 | # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 798 | # plain latex in the generated Makefile. Set this option to YES to get a 799 | # higher quality PDF documentation. 800 | 801 | USE_PDFLATEX = NO 802 | 803 | # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 804 | # command to the generated LaTeX files. This will instruct LaTeX to keep 805 | # running if errors occur, instead of asking the user for help. 806 | # This option is also used when generating formulas in HTML. 807 | 808 | LATEX_BATCHMODE = NO 809 | 810 | # If LATEX_HIDE_INDICES is set to YES then doxygen will not 811 | # include the index chapters (such as File Index, Compound Index, etc.) 812 | # in the output. 813 | 814 | LATEX_HIDE_INDICES = NO 815 | 816 | #--------------------------------------------------------------------------- 817 | # configuration options related to the RTF output 818 | #--------------------------------------------------------------------------- 819 | 820 | # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 821 | # The RTF output is optimized for Word 97 and may not look very pretty with 822 | # other RTF readers or editors. 823 | 824 | GENERATE_RTF = NO 825 | 826 | # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 827 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 828 | # put in front of it. If left blank `rtf' will be used as the default path. 829 | 830 | RTF_OUTPUT = rtf 831 | 832 | # If the COMPACT_RTF tag is set to YES Doxygen generates more compact 833 | # RTF documents. This may be useful for small projects and may help to 834 | # save some trees in general. 835 | 836 | COMPACT_RTF = NO 837 | 838 | # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 839 | # will contain hyperlink fields. The RTF file will 840 | # contain links (just like the HTML output) instead of page references. 841 | # This makes the output suitable for online browsing using WORD or other 842 | # programs which support those fields. 843 | # Note: wordpad (write) and others do not support links. 844 | 845 | RTF_HYPERLINKS = NO 846 | 847 | # Load stylesheet definitions from file. Syntax is similar to doxygen's 848 | # config file, i.e. a series of assignments. You only have to provide 849 | # replacements, missing definitions are set to their default value. 850 | 851 | RTF_STYLESHEET_FILE = 852 | 853 | # Set optional variables used in the generation of an rtf document. 854 | # Syntax is similar to doxygen's config file. 855 | 856 | RTF_EXTENSIONS_FILE = 857 | 858 | #--------------------------------------------------------------------------- 859 | # configuration options related to the man page output 860 | #--------------------------------------------------------------------------- 861 | 862 | # If the GENERATE_MAN tag is set to YES (the default) Doxygen will 863 | # generate man pages 864 | 865 | GENERATE_MAN = NO 866 | 867 | # The MAN_OUTPUT tag is used to specify where the man pages will be put. 868 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 869 | # put in front of it. If left blank `man' will be used as the default path. 870 | 871 | MAN_OUTPUT = man 872 | 873 | # The MAN_EXTENSION tag determines the extension that is added to 874 | # the generated man pages (default is the subroutine's section .3) 875 | 876 | MAN_EXTENSION = .3 877 | 878 | # If the MAN_LINKS tag is set to YES and Doxygen generates man output, 879 | # then it will generate one additional man file for each entity 880 | # documented in the real man page(s). These additional files 881 | # only source the real man page, but without them the man command 882 | # would be unable to find the correct page. The default is NO. 883 | 884 | MAN_LINKS = NO 885 | 886 | #--------------------------------------------------------------------------- 887 | # configuration options related to the XML output 888 | #--------------------------------------------------------------------------- 889 | 890 | # If the GENERATE_XML tag is set to YES Doxygen will 891 | # generate an XML file that captures the structure of 892 | # the code including all documentation. 893 | 894 | GENERATE_XML = NO 895 | 896 | # The XML_OUTPUT tag is used to specify where the XML pages will be put. 897 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 898 | # put in front of it. If left blank `xml' will be used as the default path. 899 | 900 | XML_OUTPUT = xml 901 | 902 | # The XML_SCHEMA tag can be used to specify an XML schema, 903 | # which can be used by a validating XML parser to check the 904 | # syntax of the XML files. 905 | 906 | XML_SCHEMA = 907 | 908 | # The XML_DTD tag can be used to specify an XML DTD, 909 | # which can be used by a validating XML parser to check the 910 | # syntax of the XML files. 911 | 912 | XML_DTD = 913 | 914 | # If the XML_PROGRAMLISTING tag is set to YES Doxygen will 915 | # dump the program listings (including syntax highlighting 916 | # and cross-referencing information) to the XML output. Note that 917 | # enabling this will significantly increase the size of the XML output. 918 | 919 | XML_PROGRAMLISTING = YES 920 | 921 | #--------------------------------------------------------------------------- 922 | # configuration options for the AutoGen Definitions output 923 | #--------------------------------------------------------------------------- 924 | 925 | # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 926 | # generate an AutoGen Definitions (see autogen.sf.net) file 927 | # that captures the structure of the code including all 928 | # documentation. Note that this feature is still experimental 929 | # and incomplete at the moment. 930 | 931 | GENERATE_AUTOGEN_DEF = NO 932 | 933 | #--------------------------------------------------------------------------- 934 | # configuration options related to the Perl module output 935 | #--------------------------------------------------------------------------- 936 | 937 | # If the GENERATE_PERLMOD tag is set to YES Doxygen will 938 | # generate a Perl module file that captures the structure of 939 | # the code including all documentation. Note that this 940 | # feature is still experimental and incomplete at the 941 | # moment. 942 | 943 | GENERATE_PERLMOD = NO 944 | 945 | # If the PERLMOD_LATEX tag is set to YES Doxygen will generate 946 | # the necessary Makefile rules, Perl scripts and LaTeX code to be able 947 | # to generate PDF and DVI output from the Perl module output. 948 | 949 | PERLMOD_LATEX = NO 950 | 951 | # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 952 | # nicely formatted so it can be parsed by a human reader. This is useful 953 | # if you want to understand what is going on. On the other hand, if this 954 | # tag is set to NO the size of the Perl module output will be much smaller 955 | # and Perl will parse it just the same. 956 | 957 | PERLMOD_PRETTY = YES 958 | 959 | # The names of the make variables in the generated doxyrules.make file 960 | # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 961 | # This is useful so different doxyrules.make files included by the same 962 | # Makefile don't overwrite each other's variables. 963 | 964 | PERLMOD_MAKEVAR_PREFIX = 965 | 966 | #--------------------------------------------------------------------------- 967 | # Configuration options related to the preprocessor 968 | #--------------------------------------------------------------------------- 969 | 970 | # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 971 | # evaluate all C-preprocessor directives found in the sources and include 972 | # files. 973 | 974 | ENABLE_PREPROCESSING = YES 975 | 976 | # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 977 | # names in the source code. If set to NO (the default) only conditional 978 | # compilation will be performed. Macro expansion can be done in a controlled 979 | # way by setting EXPAND_ONLY_PREDEF to YES. 980 | 981 | MACRO_EXPANSION = NO 982 | 983 | # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 984 | # then the macro expansion is limited to the macros specified with the 985 | # PREDEFINED and EXPAND_AS_DEFINED tags. 986 | 987 | EXPAND_ONLY_PREDEF = NO 988 | 989 | # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 990 | # in the INCLUDE_PATH (see below) will be search if a #include is found. 991 | 992 | SEARCH_INCLUDES = YES 993 | 994 | # The INCLUDE_PATH tag can be used to specify one or more directories that 995 | # contain include files that are not input files but should be processed by 996 | # the preprocessor. 997 | 998 | INCLUDE_PATH = 999 | 1000 | # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 1001 | # patterns (like *.h and *.hpp) to filter out the header-files in the 1002 | # directories. If left blank, the patterns specified with FILE_PATTERNS will 1003 | # be used. 1004 | 1005 | INCLUDE_FILE_PATTERNS = 1006 | 1007 | # The PREDEFINED tag can be used to specify one or more macro names that 1008 | # are defined before the preprocessor is started (similar to the -D option of 1009 | # gcc). The argument of the tag is a list of macros of the form: name 1010 | # or name=definition (no spaces). If the definition and the = are 1011 | # omitted =1 is assumed. To prevent a macro definition from being 1012 | # undefined via #undef or recursively expanded use the := operator 1013 | # instead of the = operator. 1014 | 1015 | PREDEFINED = 1016 | 1017 | # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 1018 | # this tag can be used to specify a list of macro names that should be expanded. 1019 | # The macro definition that is found in the sources will be used. 1020 | # Use the PREDEFINED tag if you want to use a different macro definition. 1021 | 1022 | EXPAND_AS_DEFINED = 1023 | 1024 | # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 1025 | # doxygen's preprocessor will remove all function-like macros that are alone 1026 | # on a line, have an all uppercase name, and do not end with a semicolon. Such 1027 | # function macros are typically used for boiler-plate code, and will confuse 1028 | # the parser if not removed. 1029 | 1030 | SKIP_FUNCTION_MACROS = YES 1031 | 1032 | #--------------------------------------------------------------------------- 1033 | # Configuration::additions related to external references 1034 | #--------------------------------------------------------------------------- 1035 | 1036 | # The TAGFILES option can be used to specify one or more tagfiles. 1037 | # Optionally an initial location of the external documentation 1038 | # can be added for each tagfile. The format of a tag file without 1039 | # this location is as follows: 1040 | # TAGFILES = file1 file2 ... 1041 | # Adding location for the tag files is done as follows: 1042 | # TAGFILES = file1=loc1 "file2 = loc2" ... 1043 | # where "loc1" and "loc2" can be relative or absolute paths or 1044 | # URLs. If a location is present for each tag, the installdox tool 1045 | # does not have to be run to correct the links. 1046 | # Note that each tag file must have a unique name 1047 | # (where the name does NOT include the path) 1048 | # If a tag file is not located in the directory in which doxygen 1049 | # is run, you must also specify the path to the tagfile here. 1050 | 1051 | TAGFILES = 1052 | 1053 | # When a file name is specified after GENERATE_TAGFILE, doxygen will create 1054 | # a tag file that is based on the input files it reads. 1055 | 1056 | GENERATE_TAGFILE = 1057 | 1058 | # If the ALLEXTERNALS tag is set to YES all external classes will be listed 1059 | # in the class index. If set to NO only the inherited external classes 1060 | # will be listed. 1061 | 1062 | ALLEXTERNALS = NO 1063 | 1064 | # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 1065 | # in the modules index. If set to NO, only the current project's groups will 1066 | # be listed. 1067 | 1068 | EXTERNAL_GROUPS = YES 1069 | 1070 | # The PERL_PATH should be the absolute path and name of the perl script 1071 | # interpreter (i.e. the result of `which perl'). 1072 | 1073 | PERL_PATH = /usr/bin/perl 1074 | 1075 | #--------------------------------------------------------------------------- 1076 | # Configuration options related to the dot tool 1077 | #--------------------------------------------------------------------------- 1078 | 1079 | # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 1080 | # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 1081 | # or super classes. Setting the tag to NO turns the diagrams off. Note that 1082 | # this option is superseded by the HAVE_DOT option below. This is only a 1083 | # fallback. It is recommended to install and use dot, since it yields more 1084 | # powerful graphs. 1085 | 1086 | CLASS_DIAGRAMS = NO 1087 | 1088 | # If set to YES, the inheritance and collaboration graphs will hide 1089 | # inheritance and usage relations if the target is undocumented 1090 | # or is not a class. 1091 | 1092 | HIDE_UNDOC_RELATIONS = YES 1093 | 1094 | # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 1095 | # available from the path. This tool is part of Graphviz, a graph visualization 1096 | # toolkit from AT&T and Lucent Bell Labs. The other options in this section 1097 | # have no effect if this option is set to NO (the default) 1098 | 1099 | HAVE_DOT = NO 1100 | 1101 | # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 1102 | # will generate a graph for each documented class showing the direct and 1103 | # indirect inheritance relations. Setting this tag to YES will force the 1104 | # the CLASS_DIAGRAMS tag to NO. 1105 | 1106 | CLASS_GRAPH = NO 1107 | 1108 | # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 1109 | # will generate a graph for each documented class showing the direct and 1110 | # indirect implementation dependencies (inheritance, containment, and 1111 | # class references variables) of the class with other documented classes. 1112 | 1113 | COLLABORATION_GRAPH = YES 1114 | 1115 | # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 1116 | # will generate a graph for groups, showing the direct groups dependencies 1117 | 1118 | GROUP_GRAPHS = YES 1119 | 1120 | # If the UML_LOOK tag is set to YES doxygen will generate inheritance and 1121 | # collaboration diagrams in a style similar to the OMG's Unified Modeling 1122 | # Language. 1123 | 1124 | UML_LOOK = NO 1125 | 1126 | # If set to YES, the inheritance and collaboration graphs will show the 1127 | # relations between templates and their instances. 1128 | 1129 | TEMPLATE_RELATIONS = NO 1130 | 1131 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 1132 | # tags are set to YES then doxygen will generate a graph for each documented 1133 | # file showing the direct and indirect include dependencies of the file with 1134 | # other documented files. 1135 | 1136 | INCLUDE_GRAPH = YES 1137 | 1138 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 1139 | # HAVE_DOT tags are set to YES then doxygen will generate a graph for each 1140 | # documented header file showing the documented files that directly or 1141 | # indirectly include this file. 1142 | 1143 | INCLUDED_BY_GRAPH = YES 1144 | 1145 | # If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 1146 | # generate a call dependency graph for every global function or class method. 1147 | # Note that enabling this option will significantly increase the time of a run. 1148 | # So in most cases it will be better to enable call graphs for selected 1149 | # functions only using the \callgraph command. 1150 | 1151 | CALL_GRAPH = NO 1152 | 1153 | # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will 1154 | # generate a caller dependency graph for every global function or class method. 1155 | # Note that enabling this option will significantly increase the time of a run. 1156 | # So in most cases it will be better to enable caller graphs for selected 1157 | # functions only using the \callergraph command. 1158 | 1159 | CALLER_GRAPH = NO 1160 | 1161 | # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 1162 | # will graphical hierarchy of all classes instead of a textual one. 1163 | 1164 | GRAPHICAL_HIERARCHY = YES 1165 | 1166 | # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 1167 | # then doxygen will show the dependencies a directory has on other directories 1168 | # in a graphical way. The dependency relations are determined by the #include 1169 | # relations between the files in the directories. 1170 | 1171 | DIRECTORY_GRAPH = YES 1172 | 1173 | # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 1174 | # generated by dot. Possible values are png, jpg, or gif 1175 | # If left blank png will be used. 1176 | 1177 | DOT_IMAGE_FORMAT = png 1178 | 1179 | # The tag DOT_PATH can be used to specify the path where the dot tool can be 1180 | # found. If left blank, it is assumed the dot tool can be found in the path. 1181 | 1182 | DOT_PATH = 1183 | 1184 | # The DOTFILE_DIRS tag can be used to specify one or more directories that 1185 | # contain dot files that are included in the documentation (see the 1186 | # \dotfile command). 1187 | 1188 | DOTFILE_DIRS = 1189 | 1190 | # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 1191 | # (in pixels) of the graphs generated by dot. If a graph becomes larger than 1192 | # this value, doxygen will try to truncate the graph, so that it fits within 1193 | # the specified constraint. Beware that most browsers cannot cope with very 1194 | # large images. 1195 | 1196 | MAX_DOT_GRAPH_WIDTH = 1024 1197 | 1198 | # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 1199 | # (in pixels) of the graphs generated by dot. If a graph becomes larger than 1200 | # this value, doxygen will try to truncate the graph, so that it fits within 1201 | # the specified constraint. Beware that most browsers cannot cope with very 1202 | # large images. 1203 | 1204 | MAX_DOT_GRAPH_HEIGHT = 1024 1205 | 1206 | # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 1207 | # graphs generated by dot. A depth value of 3 means that only nodes reachable 1208 | # from the root by following a path via at most 3 edges will be shown. Nodes 1209 | # that lay further from the root node will be omitted. Note that setting this 1210 | # option to 1 or 2 may greatly reduce the computation time needed for large 1211 | # code bases. Also note that a graph may be further truncated if the graph's 1212 | # image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH 1213 | # and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), 1214 | # the graph is not depth-constrained. 1215 | 1216 | MAX_DOT_GRAPH_DEPTH = 0 1217 | 1218 | # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 1219 | # background. This is disabled by default, which results in a white background. 1220 | # Warning: Depending on the platform used, enabling this option may lead to 1221 | # badly anti-aliased labels on the edges of a graph (i.e. they become hard to 1222 | # read). 1223 | 1224 | DOT_TRANSPARENT = NO 1225 | 1226 | # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 1227 | # files in one run (i.e. multiple -o and -T options on the command line). This 1228 | # makes dot run faster, but since only newer versions of dot (>1.8.10) 1229 | # support this, this feature is disabled by default. 1230 | 1231 | DOT_MULTI_TARGETS = NO 1232 | 1233 | # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 1234 | # generate a legend page explaining the meaning of the various boxes and 1235 | # arrows in the dot generated graphs. 1236 | 1237 | GENERATE_LEGEND = YES 1238 | 1239 | # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 1240 | # remove the intermediate dot files that are used to generate 1241 | # the various graphs. 1242 | 1243 | DOT_CLEANUP = YES 1244 | 1245 | #--------------------------------------------------------------------------- 1246 | # Configuration::additions related to the search engine 1247 | #--------------------------------------------------------------------------- 1248 | 1249 | # The SEARCHENGINE tag specifies whether or not a search engine should be 1250 | # used. If set to NO the values of all tags below this one will be ignored. 1251 | 1252 | SEARCHENGINE = NO 1253 | -------------------------------------------------------------------------------- /doc/man/rcat.1.txt: -------------------------------------------------------------------------------- 1 | RCAT(1) 2 | ======= 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | rcat - RDMA cat (netcat-alike using rdmacm/ibverb-compliant networking) 8 | 9 | SYNOPSIS 10 | -------- 11 | *rcat* {-s|-S addr|-c addr} [-p port] [-m] [-v] [-q] [-b blocksize] [-D statsprefix] [-d] 12 | 13 | DESCRIPTION 14 | ----------- 15 | rcat is a basic tool using RDMA send/recv with a very simple protocol. It probably cannot interface to another RDMA server/client directly, but it can at least be used to check that the connection establishes properly, check latency or naive bandwidth (see EXAMPLES below). 16 | 17 | OPTIONS 18 | ------- 19 | There is only one mandatory option, is rcat used as a client (and if so what's the server) or as a server? It can be one of the following: 20 | 21 | *-c, --client* 'server':: 22 | We're a client, connect to 'server'. 'server' can be an IP address or hostname pointing to an IP. 23 | 24 | *-s, --server*:: 25 | We're a server, bind to all possible interfaces and IPs. 26 | 27 | *-S 'IP'*:: 28 | We're a server, but bind to a specific 'IP' (and only on the interface associated to it). 29 | 30 | There are multiple optional options: 31 | 32 | *-p, --port* 'port':: 33 | Port to use to bind or to connect to. 'port' can be either a numeric port or a service name, e.g. ``ssh'', resolvable by getaddrinfo. Defaults to 1235. 34 | 35 | *-m, --multi*:: 36 | Tell the server to accept more than one connection. A new thread will be spawned to handle each incoming connection. 37 | 38 | *-v, --verbose*:: 39 | Increase verbosity everytime it appears, e.g. -vvv will be more verbose than -v. Actually switches one more MSK_DEBUG_* flag on everytime. 40 | 41 | *-q, --quiet*:: 42 | Opposite operation. 43 | 44 | *-b, --block-size* 'size':: 45 | Sets the block-size to 'size'. This is the maximum size allowed for a single RDMA send operation. 46 | 47 | *-d*:: 48 | Enables internal stats and prints them when the connection closes. 49 | 50 | *-D, --stats* prefix:: 51 | Enables stats and provide an unix socket interface to these, which can be used with `nc -u`. The socket name will be 'prefix' (because a server can have multiple concurrent threads) 52 | 53 | EXAMPLES 54 | -------- 55 | 56 | .Bandwidth 57 | Bandwidth can be checked with one or multiple dd and big chunks. rcat is _synchronous_, when a chunk is sent it will wait for the other side to receive the chunk, send an acknowledgement and will only sent the next chunk when it is received. This means that a single rcat client will never use the maximum bandwidth. 58 | 59 | ==== 60 | server$ rcat -s -m > /dev/null 61 | client$ dd if=/dev/zero bs=1M count=4096 | rcat -c 62 | 4294967296 bytes (4.3 GB) copied, 3.25225 s, 1.3 GB/s 63 | client$ for i in `seq 1 4`; do dd if=/dev/zero bs=1M count=4096 | rcat -c & done; wait 64 | 4294967296 bytes (4.3 GB) copied, 5.17072 s, 831 MB/s 65 | 4294967296 bytes (4.3 GB) copied, 5.17226 s, 830 MB/s 66 | 4294967296 bytes (4.3 GB) copied, 5.17213 s, 830 MB/s 67 | 4294967296 bytes (4.3 GB) copied, 5.17315 s, 830 MB/s 68 | ==== 69 | 70 | .Latency 71 | Lacency can be checked just the same with much smaller chunk. Keeping in mind that each chunk means a round-way trip, latency can be easily deducted as time/count. 72 | 73 | ---- 74 | server$ rcat -s -m -b 10 > /dev/null 75 | client$ dd if=/dev/zero bs=10 count=100000 | rcat -c -b 10 76 | 1000000 bytes (1.0 MB) copied, 3.48947 s, 287 kB/s 77 | -> 3.48947/100k = ~35us round trip 78 | ---- 79 | 80 | SEE ALSO 81 | -------- 82 | mooshika(3), rmitm(1), rreplay(1) 83 | 84 | AUTHORS 85 | ------- 86 | mooshika and its tools have been written by Dominique Martinet 87 | 88 | RESOURCES 89 | --------- 90 | https://github.com/martinetd/mooshika 91 | -------------------------------------------------------------------------------- /doc/man/rmitm.1.txt: -------------------------------------------------------------------------------- 1 | RMITM(1) 2 | ======== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | rmitm - RDMA man in the middle 8 | 9 | SYNOPSIS 10 | -------- 11 | *rmitm* {-s|-S addr} port -c addr port [-f pcap.out] [-t size] [-b bsize] [-r recv-num] [-E proba] [-e proba] [-v] [-q] 12 | 13 | DESCRIPTION 14 | ----------- 15 | rmitm - RDMA man in the middle. Used to dump RDMA send/recv traffic with a voluntary client, connecting to rmitm. 16 | 17 | OPTIONS 18 | ------- 19 | There are two mandatory options, what should rmitm listen on and where should it connect when it receives a connection itself: 20 | 21 | *-c, --client* 'server' 'port':: 22 | Connect to 'server' on 'port'. 'server' can be an IP address or hostname pointing to an IP, 'port' can be numeric or a service name. 23 | 24 | *-s, --server* 'port':: 25 | Bind to all possible interfaces and IPs on 'port'. 26 | 27 | *-S 'IP' 'port'*:: 28 | Bind to a specific 'IP' (and only on the interface associated to it) on 'port'. 29 | 30 | There are multiple optional options: 31 | 32 | *-f, --file* 'file':: 33 | Dump pcap output to 'file'. Can be ``-'' for stdout. 34 | 35 | *-t, --truncate* 'size':: 36 | Truncate packets to given 'size'. Wireshark cannot handle pcap packets bigger than 64k, but if you are going to replay the dump you will want to make it bigger. 37 | Defaults to 4k. 38 | 39 | *-b, --block-size* 'size':: 40 | Maximum size of the packets rmitm will be able to handle (receive and forward). 41 | Defaults to 1M. 42 | 43 | *-r, --recv-num* 'num':: 44 | Number of packets we can receive at once. 45 | Defaults to 10. 46 | 47 | *-E, --rand-byte* 'proba':: 48 | *-e, --flip-bit* 'proba':: 49 | Probability of randomizing completely or flipping a couple of bits in a given byte. 50 | 51 | *-v, --verbose*:: 52 | Increase verbosity everytime it appears, e.g. -vvv will be more verbose than -v. Actually switches one more MSK_DEBUG_* flag on everytime. 53 | 54 | *-q, --quiet*:: 55 | Opposite operation. 56 | 57 | EXAMPLES 58 | -------- 59 | 60 | .Wireshark 61 | You can interface rmitm directly with wireshark, dumping on a different host. (the ``& read; kill $!'' part kills rmitm when the ssh connection closes). 62 | 63 | ---- 64 | station$ ssh server 'rmitm -c remote 5640 -s 5640 -w - & read; kill $!' | wireshark -i - -k 65 | ---- 66 | 67 | .rreplay 68 | You can keep the dump, with error injection, and replay the incrimined dump later. 69 | 70 | ---- 71 | server$ rmitm -c remote 5640 -s 5640 -w pcap.out -t 1M -E 0.02 -e 0.02 72 | server$ rreplay -c remote 5640 -f pcap.out -n 73 | ---- 74 | 75 | SEE ALSO 76 | -------- 77 | mooshika(3), rcat(1), rreplay(1) 78 | 79 | AUTHORS 80 | ------- 81 | mooshika and its tools have been written by Dominique Martinet 82 | 83 | RESOURCES 84 | --------- 85 | https://github.com/martinetd/mooshika 86 | -------------------------------------------------------------------------------- /doc/man/rreplay.1.txt: -------------------------------------------------------------------------------- 1 | RREPLAY(1) 2 | ========== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | rreplay - RDMA replay 8 | 9 | SYNOPSIS 10 | -------- 11 | *rreplay* {{-s|-S addr} port|-c addr port} [-f pcap.out] [-B] [-n] [-b bsize] [-r recv-num] [-v] [-q] 12 | 13 | DESCRIPTION 14 | ----------- 15 | rreplay - RDMA replay. Replay a send/recv dump made with rmitm. 16 | 17 | OPTIONS 18 | ------- 19 | There is only one mandatory option, is rcat used as a client (and if so what's the server) or as a server? It can be one of the following: 20 | 21 | *-c, --client* 'server' 'port':: 22 | We're a client, connect to 'server' on 'port'. 'server' can be an IP address or hostname pointing to an IP, 'port' can be numeric or a service name. 23 | 24 | *-s, --server* 'port':: 25 | We're a server, bind to all possible interfaces and IPs on 'port'. 26 | 27 | *-S 'IP' 'port'*:: 28 | We're a server, bind to a specific 'IP' (and only on the interface associated to it) on 'port'. 29 | 30 | There are multiple optional options: 31 | 32 | *-f, --file* 'file':: 33 | Dump pcap output to 'file'. Can be ``-'' for stdout. 34 | 35 | *-B, --banner*:: 36 | If set, the server is expected to talk first. That's what rreplay uses to decide which side of the connexion is server and client in the dump. 37 | 38 | *-n, --no-check*:: 39 | Do not verify that the received data is what we expected. This may be necessary if the information the server sends will vary on replay, e.g. if there are timestamps in some messages. 40 | 41 | *-b, --block-size* 'size':: 42 | Maximum size of the packets rreplay will be able to handle (receive and forward). 43 | Defaults to 1M. 44 | 45 | *-r, --recv-num* 'num':: 46 | Number of packets we can receive at once. 47 | Defaults to 10. 48 | 49 | *-v, --verbose*:: 50 | Increase verbosity everytime it appears, e.g. -vvv will be more verbose than -v. Actually switches one more MSK_DEBUG_* flag on everytime. 51 | 52 | *-q, --quiet*:: 53 | Opposite operation. 54 | 55 | EXAMPLES 56 | -------- 57 | 58 | .rmitm 59 | You can keep the dump, with error injection, and replay the incrimined dump later. 60 | 61 | ---- 62 | server$ rmitm -c remote 5640 -s 5640 -w pcap.out -t 1M -E 0.02 -e 0.02 63 | server$ rreplay -c remote 5640 -f pcap.out -n 64 | ---- 65 | 66 | SEE ALSO 67 | -------- 68 | mooshika(3), rcat(1), rmitm(1) 69 | 70 | AUTHORS 71 | ------- 72 | mooshika and its tools have been written by Dominique Martinet 73 | 74 | RESOURCES 75 | --------- 76 | https://github.com/martinetd/mooshika 77 | -------------------------------------------------------------------------------- /include/mooshika.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright CEA/DAM/DIF (2012) 4 | * contributor : Dominique Martinet dominique.martinet@cea.fr 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * --------------------------------------- 21 | */ 22 | 23 | /** 24 | * \file mooshika.h 25 | * \brief rdma helper include file 26 | * 27 | * This is (very) loosely based on a mix of diod, rping (librdmacm/examples) 28 | * and kernel's net/9p/trans_rdma.c 29 | * 30 | */ 31 | 32 | #ifndef _MOOSHIKA_H 33 | #define _MOOSHIKA_H 34 | 35 | #include 36 | 37 | #define MOOSHIKA_API_VERSION 5 38 | 39 | typedef struct msk_trans msk_trans_t; 40 | typedef struct msk_trans_attr msk_trans_attr_t; 41 | 42 | /** 43 | * \struct msk_data 44 | * data size and content to send/just received 45 | */ 46 | typedef struct msk_data { 47 | uint32_t max_size; /**< size of the data field */ 48 | uint32_t size; /**< size of the data to actually send/read */ 49 | uint8_t *data; /**< opaque data */ 50 | struct msk_data *next; /**< For recv/sends with multiple elements, used as a linked list */ 51 | struct ibv_mr *mr; 52 | enum ibv_wc_status status; /**< work completion status, set upon reception of work completion */ 53 | } msk_data_t; 54 | 55 | typedef union sockaddr_union { 56 | struct sockaddr sa; 57 | struct sockaddr_in sa_in; 58 | struct sockaddr_in6 sa_int6; 59 | struct sockaddr_storage sa_stor; 60 | } sockaddr_union_t; 61 | 62 | struct msk_stats { 63 | uint64_t rx_bytes; 64 | uint64_t rx_pkt; 65 | uint64_t rx_err; 66 | uint64_t tx_bytes; 67 | uint64_t tx_pkt; 68 | uint64_t tx_err; 69 | /* times only set if debug has MSK_DEBUG_SPEED */ 70 | uint64_t nsec_callback; 71 | uint64_t nsec_compevent; 72 | }; 73 | 74 | struct msk_pd { 75 | struct ibv_context *context; 76 | struct ibv_pd *pd; 77 | struct ibv_srq *srq; 78 | struct msk_ctx *rctx; 79 | void *private; 80 | uint32_t refcnt; 81 | uint32_t used; 82 | }; 83 | #define PD_GUARD ((void*)-1) 84 | 85 | typedef void (*disconnect_callback_t) (msk_trans_t *trans); 86 | 87 | #define MSK_CLIENT 0 88 | #define MSK_SERVER_CHILD -1 89 | 90 | /** 91 | * \struct msk_trans 92 | * RDMA transport instance 93 | */ 94 | struct msk_trans { 95 | enum msk_state { 96 | MSK_INIT, 97 | MSK_LISTENING, 98 | MSK_ADDR_RESOLVED, 99 | MSK_ROUTE_RESOLVED, 100 | MSK_CONNECT_REQUEST, 101 | MSK_CONNECTED, 102 | MSK_CLOSING, 103 | MSK_CLOSED, 104 | MSK_ERROR 105 | } state; /**< tracks the transport state machine for connection setup and tear down */ 106 | struct rdma_cm_id *cm_id; /**< The RDMA CM ID */ 107 | struct rdma_event_channel *event_channel; 108 | struct ibv_comp_channel *comp_channel; 109 | struct msk_pd *pd; /**< Protection Domain pointer list */ 110 | struct ibv_qp *qp; /**< Queue Pair pointer */ 111 | struct ibv_srq *srq; /**< Shared Receive Queue pointer */ 112 | struct ibv_cq *cq; /**< Completion Queue pointer */ 113 | disconnect_callback_t disconnect_callback; 114 | void *private_data; 115 | long timeout; /**< Number of mSecs to wait for connection management events */ 116 | int sq_depth; /**< The depth of the Send Queue */ 117 | int max_send_sge; /**< Maximum number of s/g elements per send */ 118 | int rq_depth; /**< The depth of the Receive Queue. */ 119 | int max_recv_sge; /**< Maximum number of s/g elements per recv */ 120 | char *node; /**< The remote peer's hostname */ 121 | char *port; /**< The service port (or name) */ 122 | int conn_type; /**< RDMA Port space, probably RDMA_PS_TCP */ 123 | int server; /**< 0 if client, connection backlog on server, -1 (MSK_SERVER_CHILD) if server's accepted connection */ 124 | int destroy_on_disconnect; /**< set to 1 if mooshika should perform cleanup */ 125 | int privport; /**< set to 1 if mooshika should use a reserved port for client side */ 126 | uint32_t debug; 127 | struct rdma_cm_id **conn_requests; /**< temporary child cm_id, only used for server */ 128 | struct msk_ctx *wctx; /**< pointer to actual context data */ 129 | struct msk_ctx *rctx; /**< pointer to actual context data */ 130 | pthread_mutex_t cm_lock; /**< lock for connection events */ 131 | pthread_cond_t cm_cond; /**< cond for connection events */ 132 | struct ibv_recv_wr *bad_recv_wr; 133 | struct ibv_send_wr *bad_send_wr; 134 | struct msk_stats stats; 135 | char *stats_prefix; 136 | int stats_sock; 137 | }; 138 | 139 | struct msk_trans_attr { 140 | disconnect_callback_t disconnect_callback; 141 | int debug; /**< verbose output to stderr if set */ 142 | int server; /**< 0 if client, connection backlog on server */ 143 | int destroy_on_disconnect; /**< set to 1 if mooshika should perform cleanup */ 144 | int privport; /**< set to 1 if mooshika should use a reserved port for client side */ 145 | long timeout; /**< Number of mSecs to wait for connection management events */ 146 | int sq_depth; /**< The depth of the Send Queue */ 147 | int max_send_sge; /**< Maximum number of s/g elements per send */ 148 | int use_srq; /**< Does the server use srq? */ 149 | int rq_depth; /**< The depth of the Receive Queue. */ 150 | int max_recv_sge; /**< Maximum number of s/g elements per recv */ 151 | int worker_count; /**< Number of worker threads - works only for the first init */ 152 | int worker_queue_size; /**< Size of the worker data queue - works only for the first init */ 153 | enum rdma_port_space conn_type; /**< RDMA Port space, probably RDMA_PS_TCP */ 154 | char *node; /**< The remote peer's hostname */ 155 | char *port; /**< The service port (or name) */ 156 | struct msk_pd *pd; /**< Protection Domain pointer */ 157 | char *stats_prefix; 158 | }; 159 | 160 | #define MSK_DEBUG_EVENT 0x0001 161 | #define MSK_DEBUG_SETUP 0x0002 162 | #define MSK_DEBUG_SEND 0x0004 163 | #define MSK_DEBUG_RECV 0x0008 164 | #define MSK_DEBUG_WORKERS (MSK_DEBUG_SEND | MSK_DEBUG_RECV) 165 | #define MSK_DEBUG_CM_LOCKS 0x0010 166 | #define MSK_DEBUG_CTX 0x0020 167 | #define MSK_DEBUG_SPEED 0x8000 168 | 169 | 170 | typedef void (*ctx_callback_t)(msk_trans_t *trans, msk_data_t *data, void *arg); 171 | 172 | 173 | /** 174 | * \struct msk_rloc 175 | * stores one remote address to write/read at 176 | */ 177 | typedef struct msk_rloc { 178 | uint64_t raddr; /**< remote memory address */ 179 | uint32_t rkey; /**< remote key */ 180 | uint32_t size; /**< size of the region we can write/read */ 181 | } msk_rloc_t; 182 | 183 | 184 | 185 | int msk_post_n_recv(msk_trans_t *trans, msk_data_t *data, int num_sge, ctx_callback_t callback, ctx_callback_t err_callback, void *callback_arg); 186 | int msk_post_n_send(msk_trans_t *trans, msk_data_t *data, int num_sge, ctx_callback_t callback, ctx_callback_t err_callback, void *callback_arg); 187 | int msk_wait_n_recv(msk_trans_t *trans, msk_data_t *data, int num_sge); 188 | int msk_wait_n_send(msk_trans_t *trans, msk_data_t *data, int num_sge); 189 | int msk_post_n_read(msk_trans_t *trans, msk_data_t *data, int num_sge, msk_rloc_t *rloc, ctx_callback_t callback, ctx_callback_t err_callback, void* callback_arg); 190 | int msk_post_n_write(msk_trans_t *trans, msk_data_t *data, int num_sge, msk_rloc_t *rloc, ctx_callback_t callback, ctx_callback_t err_callback, void* callback_arg); 191 | int msk_wait_n_read(msk_trans_t *trans, msk_data_t *data, int num_sge, msk_rloc_t *rloc); 192 | int msk_wait_n_write(msk_trans_t *trans, msk_data_t *data, int num_sge, msk_rloc_t *rloc); 193 | 194 | static inline int msk_post_recv(msk_trans_t *trans, msk_data_t *data, ctx_callback_t callback, ctx_callback_t err_callback, void *callback_arg) { 195 | return msk_post_n_recv(trans, data, 1, callback, err_callback, callback_arg); 196 | } 197 | static inline int msk_post_send(msk_trans_t *trans, msk_data_t *data, ctx_callback_t callback, ctx_callback_t err_callback, void *callback_arg) { 198 | return msk_post_n_send(trans, data, 1, callback, err_callback, callback_arg); 199 | } 200 | 201 | static inline int msk_wait_recv(msk_trans_t *trans, msk_data_t *data) { 202 | return msk_wait_n_recv(trans, data, 1); 203 | } 204 | 205 | static inline int msk_wait_send(msk_trans_t *trans, msk_data_t *data) { 206 | return msk_wait_n_send(trans, data, 1); 207 | } 208 | 209 | static inline int msk_post_read(msk_trans_t *trans, msk_data_t *data, msk_rloc_t *rloc, ctx_callback_t callback, ctx_callback_t err_callback, void* callback_arg) { 210 | return msk_post_n_read(trans, data, 1, rloc, callback, err_callback, callback_arg); 211 | } 212 | 213 | static inline int msk_post_write(msk_trans_t *trans, msk_data_t *data, msk_rloc_t *rloc, ctx_callback_t callback, ctx_callback_t err_callback, void* callback_arg) { 214 | return msk_post_n_write(trans, data, 1, rloc, callback, err_callback, callback_arg); 215 | } 216 | 217 | static inline int msk_wait_read(msk_trans_t *trans, msk_data_t *data, msk_rloc_t *rloc) { 218 | return msk_wait_n_read(trans, data, 1, rloc); 219 | } 220 | 221 | static inline int msk_wait_write(msk_trans_t *trans, msk_data_t *data, msk_rloc_t *rloc) { 222 | return msk_wait_n_write(trans, data, 1, rloc); 223 | } 224 | 225 | 226 | 227 | int msk_init(msk_trans_t **ptrans, msk_trans_attr_t *attr); 228 | 229 | // server specific: 230 | int msk_bind_server(msk_trans_t *trans); 231 | msk_trans_t *msk_accept_one_wait(msk_trans_t *trans, int msleep); 232 | msk_trans_t *msk_accept_one_timedwait(msk_trans_t *trans, struct timespec *abstime); 233 | static inline msk_trans_t *msk_accept_one(msk_trans_t *trans) { 234 | return msk_accept_one_timedwait(trans, NULL); 235 | } 236 | int msk_finalize_accept(msk_trans_t *trans); 237 | void msk_destroy_trans(msk_trans_t **ptrans); 238 | 239 | int msk_connect(msk_trans_t *trans); 240 | int msk_finalize_connect(msk_trans_t *trans); 241 | 242 | 243 | /* utility functions */ 244 | 245 | struct ibv_mr *msk_reg_mr(msk_trans_t *trans, void *memaddr, size_t size, int access); 246 | int msk_dereg_mr(struct ibv_mr *mr); 247 | 248 | msk_rloc_t *msk_make_rloc(struct ibv_mr *mr, uint64_t addr, uint32_t size); 249 | 250 | void msk_print_devinfo(msk_trans_t *trans); 251 | 252 | struct sockaddr *msk_get_dst_addr(msk_trans_t *trans); 253 | struct sockaddr *msk_get_src_addr(msk_trans_t *trans); 254 | uint16_t msk_get_src_port(msk_trans_t *trans); 255 | uint16_t msk_get_dst_port(msk_trans_t *trans); 256 | 257 | struct msk_pd *msk_getpd(msk_trans_t *trans); 258 | 259 | const char *msk_wc_status_str(enum ibv_wc_status status); 260 | 261 | #endif /* _MOOSHIKA_H */ 262 | -------------------------------------------------------------------------------- /libmooshika.spec.in: -------------------------------------------------------------------------------- 1 | Summary: The mooshika library (libmooshika) 2 | Name: libmooshika 3 | Version: @VERSION@ 4 | Release: @_GIT_HEAD_DESCRIBE@%{?dist} 5 | License: LGPLv3 6 | Source: %{name}-%{version}.tar.gz 7 | BuildRequires: librdmacm-devel, libibverbs-devel, automake, libtool 8 | 9 | %package devel 10 | Summary: Development files for libmooshika 11 | Requires: librdmacm-devel, libibverbs-devel 12 | Requires: %{name}%{?_isa} = %{version}-%{release} 13 | 14 | %package rmitm 15 | Summary: RDMA man in the middle and replay tool (rreplay) 16 | BuildRequires: libpcap-devel 17 | 18 | %package rcat 19 | Summary: RDMA equivalent of netcat 20 | 21 | %description 22 | This package contains the libmooshika library 23 | It is an helper for rdma connection handling 24 | 25 | %description devel 26 | Development files for libmooshika 27 | 28 | %description rmitm 29 | RDMA man in the middle and replay tool 30 | 31 | %description rcat 32 | RDMA equivalent of netcat 33 | 34 | %prep 35 | %setup -q -n %{name}-%{version} 36 | 37 | %build 38 | %configure @ac_configure_args@ --enable-rmitm 39 | make %{?_smp_mflags} 40 | 41 | %install 42 | %makeinstall 43 | # remove unpackaged files from the buildroot 44 | rm -f %{buildroot}%{_libdir}/*.la 45 | 46 | %post -p /sbin/ldconfig 47 | %postun -p /sbin/ldconfig 48 | 49 | %files 50 | %{_libdir}/*.so.* 51 | 52 | %files rmitm 53 | %{_bindir}/rmitm 54 | %{_bindir}/rreplay 55 | 56 | %files rcat 57 | %{_bindir}/rcat 58 | 59 | %files devel 60 | %{_includedir}/* 61 | %{_libdir}/*.so 62 | %{_libdir}/pkgconfig/*.pc 63 | 64 | %changelog 65 | 66 | * Tue Jan 13 2015 Dominique Martinet - @VERSION@-@_GIT_HEAD_DESCRIBE@%{?dist} 67 | - Development package, see revision for git head. 68 | -------------------------------------------------------------------------------- /m4/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinetd/mooshika/938e1518a0da681ba00a16c159f00c8465f91c64/m4/.gitignore -------------------------------------------------------------------------------- /rpm/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | rcat 2 | rmitm 3 | rreplay 4 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = . tests tools #build . before tests 2 | 3 | AM_CFLAGS = -g -D_REENTRANT $(WARNINGS_CFLAGS) -I$(srcdir)/../include 4 | 5 | lib_LTLIBRARIES = libmooshika.la 6 | libmooshika_la_SOURCES = trans_rdma.c 7 | libmooshika_la_LDFLAGS = -version-info 6:0:0 8 | libmooshika_la_LIBADD = -lrdmacm -libverbs -lpthread -lrt 9 | 10 | bin_PROGRAMS = rcat 11 | if ENABLE_RMITM 12 | bin_PROGRAMS += rmitm rreplay 13 | endif 14 | 15 | rcat_SOURCES = rcat.c 16 | rcat_LDADD = -lpthread 17 | rcat_LDADD += libmooshika.la 18 | 19 | rmitm_SOURCES = rmitm.c 20 | rmitm_LDADD = -lpthread -lpcap 21 | rmitm_LDADD += libmooshika.la 22 | 23 | rreplay_SOURCES = rreplay.c 24 | rreplay_LDADD = -lpthread -lpcap 25 | rreplay_LDADD += libmooshika.la 26 | 27 | pkgconfigdir=$(libdir)/pkgconfig 28 | pkgconfig_DATA = libmooshika.pc 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/atomics.h: -------------------------------------------------------------------------------- 1 | #undef GCC_SYNC_FUNCTIONS 2 | #undef GCC_ATOMIC_FUNCTIONS 3 | 4 | #ifndef __GNUC__ 5 | #error Please edit abstract_atomic.h and implement support for \ 6 | non-GNU compilers. 7 | #else /* __GNUC__ */ 8 | #define ATOMIC_GCC_VERSION (__GNUC__ * 10000 \ 9 | + __GNUC_MINOR__ * 100 \ 10 | + __GNUC_PATCHLEVEL__) 11 | 12 | #if ((ATOMIC_GCC_VERSION) >= 40700) 13 | #define GCC_ATOMIC_FUNCTIONS 1 14 | #elif ((ATOMIC_GCC_VERSION) >= 40100) 15 | #define GCC_SYNC_FUNCTIONS 1 16 | #else 17 | #error This verison of GCC does not support atomics. 18 | #endif /* Version check */ 19 | #endif /* __GNUC__ */ 20 | 21 | 22 | #define atomic_postinc(x) __sync_fetch_and_add(&x, 1) 23 | #define atomic_postdec(x) __sync_fetch_and_sub(&x, 1) 24 | #define atomic_postadd(x,i) __sync_fetch_and_add(&x, i) 25 | #define atomic_postsub(x,i) __sync_fetch_and_sub(&x, i) 26 | #define atomic_postmask(x,i) __sync_fetch_and_and(&x, i) 27 | #define atomic_inc(x) __sync_add_and_fetch(&x, 1) 28 | #define atomic_dec(x) __sync_sub_and_fetch(&x, 1) 29 | #define atomic_add(x,i) __sync_add_and_fetch(&x, i) 30 | #define atomic_sub(x,i) __sync_sub_and_fetch(&x, i) 31 | #define atomic_mask(x,i) __sync_and_and_fetch(&x, i) 32 | #define atomic_bool_compare_and_swap __sync_bool_compare_and_swap 33 | 34 | #ifdef GCC_ATOMIC_FUNCTIONS 35 | #define atomic_store(x, n) __atomic_store_n(x, n, __ATOMIC_RELEASE) 36 | #elif defined(GCC_SYNC_FUNCTIONS) 37 | #define atomic_store(x, n) do { *(x) = n; __sync_synchronize(); } while (0) 38 | #endif 39 | -------------------------------------------------------------------------------- /src/libmooshika.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: libmooshika 7 | Description: libmooshika - rdma helper library 8 | Requires: 9 | Version: @VERSION@ 10 | Libs: -L${libdir} -lmooshika -lpthread 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /src/rcat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright CEA/DAM/DIF (2012) 4 | * contributor : Dominique Martinet dominique.martinet@cea.fr 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * --------------------------------------- 21 | */ 22 | 23 | /** 24 | * \file rcat.c 25 | * \brief Example of usage/most basic test program for mooshika 26 | * 27 | * Example of usage/most basic test program for mooshika 28 | * 29 | */ 30 | #ifdef HAVE_CONFIG_H 31 | #include "config.h" 32 | #endif 33 | 34 | #include 35 | #include 36 | #include //printf 37 | #include //malloc 38 | #include //memcpy 39 | #include //read 40 | #include //gethostbyname 41 | #include 42 | #include 43 | #include 44 | #include // PRIu64 45 | 46 | 47 | #include "utils.h" 48 | #include "mooshika.h" 49 | 50 | #define DEFAULT_BLOCK_SIZE 1024*1024 51 | #define DEFAULT_RECV_NUM 4 52 | 53 | struct priv_data { 54 | msk_data_t *ackdata; 55 | pthread_mutex_t *lock; 56 | pthread_cond_t *cond; 57 | int ack; 58 | }; 59 | 60 | struct thread_arg { 61 | int mt_server; 62 | int stats; 63 | int recv_num; 64 | size_t block_size; 65 | msk_data_t *rdata; 66 | }; 67 | 68 | void callback_send(msk_trans_t *trans, msk_data_t *pdata, void *arg) { 69 | } 70 | 71 | void callback_disconnect(msk_trans_t *trans) { 72 | if (!trans->private_data) 73 | return; 74 | 75 | struct priv_data *priv_data = trans->private_data; 76 | pthread_mutex_lock(priv_data->lock); 77 | pthread_cond_signal(priv_data->cond); 78 | pthread_mutex_unlock(priv_data->lock); 79 | } 80 | 81 | 82 | void callback_error(msk_trans_t *trans, msk_data_t *pdata, void *arg) { 83 | INFO_LOG(trans->state != MSK_CLOSING && trans->state != MSK_CLOSED 84 | && trans->debug, "error callback on buffer %p", pdata); 85 | } 86 | 87 | void callback_recv(msk_trans_t *trans, msk_data_t *pdata, void *arg) { 88 | struct priv_data *priv_data = trans->private_data; 89 | int n; 90 | 91 | if (!priv_data) { 92 | ERROR_LOG("no callback_arg?"); 93 | return; 94 | } 95 | 96 | if (pdata->size != 1 || pdata->data[0] != '\0') { 97 | // either we get real data and write it to stdout/send ack 98 | n = write(1, (char *)pdata->data, pdata->size); 99 | fflush(stdout); 100 | 101 | if (n != pdata->size) 102 | ERROR_LOG("Wrote less than what was actually received"); 103 | 104 | if (msk_post_recv(trans, pdata, callback_recv, callback_error, priv_data)) 105 | ERROR_LOG("post_recv failed"); 106 | if (msk_post_send(trans, priv_data->ackdata, NULL, NULL, NULL)) 107 | ERROR_LOG("post_send failed"); 108 | } else { 109 | // or we get an ack and just send a signal to handle_trans thread 110 | if(msk_post_recv(trans, pdata, callback_recv, callback_error, priv_data)) 111 | ERROR_LOG("post_recv failed"); 112 | 113 | pthread_mutex_lock(priv_data->lock); 114 | priv_data->ack = 0; 115 | pthread_cond_signal(priv_data->cond); 116 | pthread_mutex_unlock(priv_data->lock); 117 | } 118 | } 119 | 120 | void print_help(char **argv) { 121 | printf("Usage: %s {-s|-c addr} [-p port] [-m] [-v] [-b blocksize] [-r recvnum]\n", argv[0]); 122 | printf("Mandatory argument, either of:\n" 123 | " -c, --client addr: client to connect to\n" 124 | " -s, --server: server mode\n" 125 | " -S addr: server mode, bind to address\n" 126 | "Optional arguments:\n" 127 | " -p, --port port: port to use\n" 128 | " -m, --multi: server only, multithread/accept multiple connections\n" 129 | " -v, --verbose: enable verbose output (more v for more verbosity)\n" 130 | " -q, --quiet: don't display connection messages\n" 131 | " -D, --stats : create a socket where to look stats up at given path\n" 132 | " -d: display stats summary on close\n" 133 | " -b, --block-size size: size of packets to send (default: %u)\n" 134 | " -r, --recv-num n: size of receive queue for server (default: %u)\n", 135 | DEFAULT_BLOCK_SIZE, DEFAULT_RECV_NUM); 136 | } 137 | 138 | void* handle_trans(void *arg) { 139 | msk_trans_t *trans = arg; 140 | struct thread_arg *thread_arg = trans->private_data; 141 | struct ibv_mr *mr; 142 | msk_data_t *ackdata; 143 | msk_data_t *wdatas; 144 | int cur_data = 0; 145 | 146 | pthread_mutex_t lock; 147 | pthread_cond_t cond; 148 | 149 | struct priv_data *priv_data; 150 | int i; 151 | 152 | struct pollfd pollfd_stdin; 153 | 154 | 155 | // malloc mooshika's data structs (i.e. max_size+size+pointer to actual data), for ack buffer 156 | TEST_NZ(ackdata = malloc(sizeof(msk_data_t)+1)); 157 | TEST_NZ(mr = msk_reg_mr(trans, (uint8_t*)(ackdata + 1), 1, IBV_ACCESS_LOCAL_WRITE)); 158 | ackdata->data = (uint8_t*)(ackdata + 1); 159 | ackdata->max_size = 1; 160 | ackdata->size = 1; 161 | ackdata->data[0] = 0; 162 | ackdata->mr = mr; 163 | 164 | pthread_mutex_init(&lock, NULL); 165 | pthread_cond_init(&cond, NULL); 166 | 167 | TEST_NZ(priv_data = malloc(sizeof(struct priv_data))); 168 | 169 | priv_data->ackdata = ackdata; 170 | priv_data->lock = &lock; 171 | priv_data->cond = &cond; 172 | priv_data->ack = 0; 173 | 174 | trans->private_data = priv_data; 175 | 176 | // receive buffers are posted, we can finalize the connection 177 | if (trans->server) { 178 | TEST_Z(msk_finalize_accept(trans)); 179 | } else { 180 | TEST_Z(msk_finalize_connect(trans)); 181 | } 182 | 183 | 184 | // malloc write (send) structs to post data read from stdin 185 | TEST_NZ(wdatas = malloc(2*(sizeof(msk_data_t)+thread_arg->block_size))); 186 | TEST_NZ(mr = msk_reg_mr(trans, (uint8_t*)(wdatas+2), 2*thread_arg->block_size, IBV_ACCESS_LOCAL_WRITE)); 187 | wdatas[0].data = (uint8_t*)(wdatas+2); 188 | wdatas[1].data = wdatas[0].data + thread_arg->block_size; 189 | wdatas[0].max_size = thread_arg->block_size; 190 | wdatas[1].max_size = thread_arg->block_size; 191 | wdatas[0].mr = mr; 192 | wdatas[1].mr = mr; 193 | 194 | 195 | pollfd_stdin.fd = 0; // stdin 196 | pollfd_stdin.events = POLLIN | POLLPRI; 197 | pollfd_stdin.revents = 0; 198 | 199 | while (trans->state == MSK_CONNECTED) { 200 | 201 | i = poll(&pollfd_stdin, 1, 100); 202 | 203 | if (i == -1) 204 | break; 205 | 206 | if (i == 0) 207 | continue; 208 | 209 | wdatas[cur_data].size = read(0, (char*)wdatas[cur_data].data, wdatas[cur_data].max_size); 210 | if (wdatas[cur_data].size == 0) 211 | break; 212 | 213 | 214 | // Make sure we're done waiting and declare we're waiting for next 215 | pthread_mutex_lock(&lock); 216 | while (trans->state == MSK_CONNECTED && priv_data->ack) { 217 | pthread_cond_wait(&cond, &lock); 218 | } 219 | priv_data->ack = 1; 220 | pthread_mutex_unlock(&lock); 221 | 222 | // can fail if e.g. other side already has hung up 223 | // (can explain error callbacks too, e.g. post_send ok, hang up, actual send fails) 224 | if (msk_post_send(trans, wdatas + cur_data, NULL, NULL, NULL)) 225 | break; 226 | 227 | cur_data = (cur_data + 1) % 2; 228 | } 229 | 230 | pthread_mutex_lock(&lock); 231 | if (trans->state == MSK_CONNECTED && priv_data->ack) { 232 | // We're the ones closing, least we can do is wrap it up 233 | pthread_cond_wait(&cond, &lock); 234 | } 235 | pthread_mutex_unlock(&lock); 236 | 237 | if (thread_arg->stats) 238 | fprintf(stderr, 239 | "stats:\n" 240 | " tx_bytes\ttx_pkt\ttx_err\n" 241 | " %10"PRIu64"\t%"PRIu64"\t%"PRIu64"\n" 242 | " rx_bytes\trx_pkt\trx_err\n" 243 | " %10"PRIu64"\t%"PRIu64"\t%"PRIu64"\n" 244 | " callback time: %"PRIu64".%09"PRIu64" s\n" 245 | " completion time: %"PRIu64".%09"PRIu64" s\n", 246 | trans->stats.tx_bytes, trans->stats.tx_pkt, 247 | trans->stats.tx_err, trans->stats.rx_bytes, 248 | trans->stats.rx_pkt, trans->stats.rx_err, 249 | trans->stats.nsec_callback / NSEC_IN_SEC, trans->stats.nsec_callback % NSEC_IN_SEC, 250 | trans->stats.nsec_compevent / NSEC_IN_SEC, trans->stats.nsec_compevent % NSEC_IN_SEC); 251 | 252 | 253 | TEST_Z(msk_dereg_mr(wdatas->mr)); 254 | TEST_Z(msk_dereg_mr(ackdata->mr)); 255 | 256 | msk_destroy_trans(&trans); 257 | 258 | // free stuff 259 | free(wdatas); 260 | free(priv_data); 261 | free(ackdata); 262 | 263 | if (thread_arg->mt_server) 264 | pthread_exit(NULL); 265 | else 266 | return NULL; 267 | } 268 | 269 | 270 | void post_recvs(msk_trans_t *trans, struct thread_arg *thread_arg) { 271 | uint8_t *rdmabuf; 272 | struct ibv_mr *mr; 273 | int i; 274 | 275 | // malloc memory zone that will contain all buffer data (for mr), and register it for our trans 276 | #define RDMABUF_SIZE (thread_arg->recv_num+2)*thread_arg->block_size 277 | TEST_NZ(rdmabuf = malloc(RDMABUF_SIZE)); 278 | memset(rdmabuf, 0, RDMABUF_SIZE); 279 | TEST_NZ(mr = msk_reg_mr(trans, rdmabuf, RDMABUF_SIZE, IBV_ACCESS_LOCAL_WRITE)); 280 | // malloc receive structs as well as a custom callback argument, and post it for future receive 281 | TEST_NZ(thread_arg->rdata = malloc(thread_arg->recv_num*sizeof(msk_data_t))); 282 | for (i=0; i < thread_arg->recv_num; i++) { 283 | thread_arg->rdata[i].data=rdmabuf+i*thread_arg->block_size; 284 | thread_arg->rdata[i].max_size=thread_arg->block_size; 285 | thread_arg->rdata[i].mr = mr; 286 | TEST_Z(msk_post_recv(trans, &thread_arg->rdata[i], callback_recv, callback_error, NULL)); 287 | } 288 | } 289 | 290 | int setup_recv(msk_trans_t *trans, struct thread_arg *thread_arg) { 291 | struct msk_pd *pd; 292 | 293 | 294 | if (trans->srq) { 295 | TEST_NZ(pd = msk_getpd(trans)); 296 | if (!pd->private) { 297 | post_recvs(trans, thread_arg); 298 | pd->private = (void*)1; 299 | } 300 | } else { 301 | post_recvs(trans, thread_arg); 302 | } 303 | 304 | return 0; 305 | } 306 | 307 | int main(int argc, char **argv) { 308 | 309 | msk_trans_t *trans; 310 | msk_trans_t *child_trans; 311 | 312 | msk_trans_attr_t attr; 313 | 314 | struct thread_arg thread_arg; 315 | 316 | // argument handling 317 | static struct option long_options[] = { 318 | { "client", required_argument, 0, 'c' }, 319 | { "server", no_argument, 0, 's' }, 320 | { "port", required_argument, 0, 'p' }, 321 | { "help", no_argument, 0, 'h' }, 322 | { "multi", no_argument, 0, 'm' }, 323 | { "verbose", no_argument, 0, 'v' }, 324 | { "quiet", no_argument, 0, 'q' }, 325 | { "stats", required_argument, 0, 'D' }, 326 | { "recv-num", required_argument, 0, 'r' }, 327 | { "block-size", required_argument, 0, 'b' }, 328 | { "srq", no_argument, 0, 'x' }, 329 | { 0, 0, 0, 0 } 330 | }; 331 | 332 | int option_index = 0; 333 | int op; 334 | char *tmp_s; 335 | 336 | memset(&attr, 0, sizeof(msk_trans_attr_t)); 337 | memset(&thread_arg, 0, sizeof(struct thread_arg)); 338 | 339 | attr.server = -1; // put an incorrect value to check if we're either client or server 340 | // sane values for optional or non-configurable elements 341 | attr.debug = 1; 342 | attr.use_srq = 0; 343 | attr.disconnect_callback = callback_disconnect; 344 | attr.port = "1235"; /* default port */ 345 | 346 | while ((op = getopt_long(argc, argv, "@hvqmsb:S:c:p:dD:r:x", long_options, &option_index)) != -1) { 347 | switch(op) { 348 | case '@': 349 | printf("%s compiled on %s at %s\n", argv[0], __DATE__, __TIME__); 350 | printf("Release = %s\n", VERSION); 351 | printf("Release comment = %s\n", VERSION_COMMENT); 352 | printf("Git HEAD = %s\n", _GIT_HEAD_COMMIT ) ; 353 | printf("Git Describe = %s\n", _GIT_DESCRIBE ) ; 354 | exit(0); 355 | case 'b': 356 | thread_arg.block_size = strtoul(optarg, &tmp_s, 0); 357 | if (errno || thread_arg.block_size == 0) { 358 | thread_arg.block_size = 0; 359 | ERROR_LOG("Invalid block size, assuming default (%u)", DEFAULT_BLOCK_SIZE); 360 | break; 361 | } 362 | if (tmp_s[0] != 0) { 363 | set_size(thread_arg.block_size, tmp_s); 364 | } 365 | INFO_LOG(attr.debug > 1, "block size: %zu", thread_arg.block_size); 366 | break; 367 | case 'r': 368 | thread_arg.recv_num = strtoul(optarg, &tmp_s, 0); 369 | if (errno || thread_arg.recv_num == 0) { 370 | thread_arg.recv_num = 0; 371 | ERROR_LOG("Invalid block size, assuming default (%u)", DEFAULT_RECV_NUM); 372 | } 373 | break; 374 | case 'h': 375 | print_help(argv); 376 | exit(0); 377 | case 'v': 378 | attr.debug = attr.debug * 2 + 1; 379 | break; 380 | case 'd': 381 | thread_arg.stats = 1; 382 | break; 383 | case 'D': 384 | attr.stats_prefix = optarg; 385 | thread_arg.stats = 1; 386 | break; 387 | case 'q': 388 | attr.debug = 0; 389 | break; 390 | case 'c': 391 | attr.server = 0; 392 | attr.node = optarg; 393 | break; 394 | case 's': 395 | attr.server = 64; 396 | attr.node = "::"; 397 | break; 398 | case 'S': 399 | attr.server = 64; 400 | attr.node = optarg; 401 | break; 402 | case 'p': 403 | attr.port = optarg; 404 | break; 405 | case 'm': 406 | thread_arg.mt_server = 1; 407 | break; 408 | case 'x': 409 | attr.use_srq = 1; 410 | break; 411 | default: 412 | ERROR_LOG("Failed to parse arguments"); 413 | print_help(argv); 414 | exit(EINVAL); 415 | } 416 | } 417 | 418 | if (attr.server == -1) { 419 | ERROR_LOG("must be either a client or a server!"); 420 | print_help(argv); 421 | exit(EINVAL); 422 | } 423 | 424 | if (thread_arg.block_size == 0) 425 | thread_arg.block_size = DEFAULT_BLOCK_SIZE; 426 | if (thread_arg.stats) 427 | attr.debug |= MSK_DEBUG_SPEED; 428 | if (thread_arg.recv_num == 0) 429 | thread_arg.recv_num = attr.use_srq ? DEFAULT_RECV_NUM : 2; 430 | 431 | attr.rq_depth = thread_arg.recv_num+2; 432 | 433 | // writing to stdout is the limiting factor anyway 434 | attr.worker_count = -1; 435 | 436 | TEST_Z(msk_init(&trans, &attr)); 437 | 438 | if (!trans) 439 | exit(-1); 440 | 441 | 442 | trans->private_data = &thread_arg; 443 | 444 | if (trans->server) { 445 | pthread_t id; 446 | pthread_attr_t attr_thr; 447 | 448 | TEST_Z(msk_bind_server(trans)); 449 | 450 | /* Init for thread parameter (mostly for scheduling) */ 451 | if(pthread_attr_init(&attr_thr) != 0) 452 | ERROR_LOG("can't init pthread's attributes"); 453 | 454 | if(pthread_attr_setscope(&attr_thr, PTHREAD_SCOPE_SYSTEM) != 0) 455 | ERROR_LOG("can't set pthread's scope"); 456 | 457 | if(pthread_attr_setdetachstate(&attr_thr, PTHREAD_CREATE_DETACHED) != 0) 458 | ERROR_LOG("can't set pthread's join state"); 459 | 460 | if (thread_arg.mt_server) { 461 | while (1) { 462 | child_trans = msk_accept_one(trans); 463 | if (!child_trans) { 464 | ERROR_LOG("accept_one failed!"); 465 | break; 466 | } 467 | TEST_Z(setup_recv(child_trans, &thread_arg)); 468 | TEST_Z(pthread_create(&id, &attr_thr, handle_trans, child_trans)); 469 | } 470 | } else { 471 | TEST_NZ(child_trans = msk_accept_one(trans)); 472 | TEST_Z(setup_recv(child_trans, &thread_arg)); 473 | handle_trans(child_trans); 474 | } 475 | msk_destroy_trans(&trans); 476 | } else { //client 477 | TEST_Z(msk_connect(trans)); 478 | TEST_Z(setup_recv(trans, &thread_arg)); 479 | handle_trans(trans); 480 | } 481 | 482 | msk_dereg_mr(thread_arg.rdata[0].mr); 483 | free(thread_arg.rdata[0].data); 484 | free(thread_arg.rdata); 485 | 486 | return 0; 487 | } 488 | -------------------------------------------------------------------------------- /src/rmitm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright CEA/DAM/DIF (2012) 4 | * contributor : Dominique Martinet dominique.martinet@cea.fr 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * --------------------------------------- 21 | */ 22 | 23 | /** 24 | * \file rmitm.c 25 | * \brief Example of usage/man in the middle for rdma send 26 | * 27 | * Example of usage/man in the middle for rdma send 28 | * 29 | */ 30 | #ifdef HAVE_CONFIG_H 31 | #include "config.h" 32 | #endif 33 | 34 | #include 35 | #include 36 | #include //printf 37 | #include //malloc 38 | #include //memcpy 39 | #include //read 40 | #include //gethostbyname 41 | #include 42 | #include 43 | #include 44 | #include //open 45 | #include //open 46 | #include //open 47 | #include 48 | #include // PRIu64 49 | 50 | #include 51 | #include 52 | 53 | #include "utils.h" 54 | #include "mooshika.h" 55 | #include "rmitm.h" 56 | 57 | #define DEFAULT_BLOCK_SIZE 1024*1024 // nfs page size 58 | #define DEFAULT_RECV_NUM 16 59 | #define DEFAULT_HARD_TRUNC_LEN (64*1024-1) 60 | #define DEFAULT_TRUNC_LEN 4096 61 | 62 | struct privatedata { 63 | uint32_t seq_nr; 64 | msk_trans_t *o_trans; 65 | struct thread_arg *targ; 66 | }; 67 | 68 | struct thread_arg { 69 | pcap_dumper_t **p_pcap_dumper; 70 | char *pcap_filename; 71 | pcap_t *pcap; 72 | uint32_t block_size; 73 | uint32_t recv_num; 74 | int rand_thresh; 75 | int flip_thresh; 76 | uint32_t trunc; 77 | uint32_t hard_trunc; 78 | uint64_t file_rotate; 79 | pthread_mutex_t *plock; 80 | pthread_cond_t *pcond; 81 | }; 82 | 83 | static int run_threads = 1; 84 | 85 | static void sigHandler(int s) { 86 | run_threads = 0; 87 | } 88 | 89 | static int init_rand() { 90 | int fd, rc; 91 | unsigned int seed; 92 | 93 | fd = open("/dev/urandom", O_RDONLY); 94 | if (fd < 0) { 95 | return errno; 96 | } 97 | rc = read(fd, &seed, 4); 98 | close(fd); 99 | if (rc != 4) { 100 | return EAGAIN; 101 | } 102 | srand(seed); 103 | return 0; 104 | } 105 | 106 | static void callback_recv(msk_trans_t *, msk_data_t *, void*); 107 | 108 | static void callback_error(msk_trans_t *trans, msk_data_t *pdata, void* arg) { 109 | INFO_LOG(trans->state != MSK_CLOSING && trans->state != MSK_CLOSED 110 | && trans->debug, "error callback on buffer %p", pdata); 111 | } 112 | 113 | static void callback_send(msk_trans_t *trans, msk_data_t *pdata, void *arg) { 114 | struct privatedata *priv = trans->private_data; 115 | 116 | if (!priv) { 117 | ERROR_LOG("no callback_arg?"); 118 | return; 119 | } 120 | 121 | if (msk_post_recv(priv->o_trans, pdata, callback_recv, callback_error, arg)) 122 | ERROR_LOG("post_recv failed in send callback!"); 123 | } 124 | 125 | static void callback_disconnect(msk_trans_t *trans) { 126 | struct privatedata *priv = trans->private_data; 127 | 128 | if (!priv) 129 | return; 130 | 131 | pthread_mutex_lock(priv->targ->plock); 132 | pthread_cond_broadcast(priv->targ->pcond); 133 | pthread_mutex_unlock(priv->targ->plock); 134 | } 135 | 136 | static void callback_recv(msk_trans_t *trans, msk_data_t *pdata, void *arg) { 137 | struct pcap_pkthdr pcaphdr; 138 | struct pkt_hdr *packet; 139 | struct privatedata *priv = trans->private_data; 140 | uint32_t next_seq; 141 | 142 | if (!priv) { 143 | ERROR_LOG("no callback_arg?"); 144 | return; 145 | } 146 | 147 | /* error injection */ 148 | if (priv->targ->flip_thresh) { 149 | uint8_t *cur; 150 | int r; 151 | for (cur = pdata->data; cur < pdata->data + pdata->size; cur++) { 152 | r = rand(); 153 | if (r < priv->targ->rand_thresh) { 154 | *cur = (uint8_t)r; 155 | } else if (r < priv->targ->flip_thresh) { 156 | *cur ^= r % 8; 157 | } 158 | } 159 | } 160 | 161 | TEST_NZ(msk_post_send(priv->o_trans, pdata, callback_send, callback_error, arg)); 162 | 163 | packet = (struct pkt_hdr*)(pdata->data - PACKET_HDR_LEN); 164 | 165 | gettimeofday(&pcaphdr.ts, NULL); 166 | if (pdata->size + PACKET_HDR_LEN > priv->targ->hard_trunc) { 167 | pcaphdr.len = priv->targ->hard_trunc; 168 | /* ipv6 payload is tcp header + payload */ 169 | packet->ipv6.ip_len = htons(priv->targ->hard_trunc - sizeof(struct ipv6_hdr)); 170 | /* sequence is incremented by payload only */ 171 | next_seq = htonl(ntohl(priv->seq_nr) + priv->targ->hard_trunc - PACKET_HDR_LEN); 172 | } else { 173 | pcaphdr.len = pdata->size + PACKET_HDR_LEN; 174 | packet->ipv6.ip_len = htons(pdata->size + sizeof(struct tcp_hdr)); 175 | next_seq = htonl(ntohl(priv->seq_nr) + pdata->size); 176 | } 177 | pcaphdr.caplen = min(pcaphdr.len, priv->targ->trunc); 178 | 179 | /* Need the lock for writing into pcap_dumper around file rotations */ 180 | packet->tcp.th_seq_nr = priv->seq_nr; 181 | priv->seq_nr = next_seq; 182 | packet->tcp.th_ack_nr = ((struct privatedata*)priv->o_trans->private_data)->seq_nr; 183 | ipv6_tcp_checksum(packet); 184 | 185 | pthread_mutex_lock(priv->targ->plock); 186 | pcap_dump((u_char*)*priv->targ->p_pcap_dumper, &pcaphdr, (u_char*)packet); 187 | pthread_mutex_unlock(priv->targ->plock); 188 | } 189 | 190 | static void print_help(char **argv) { 191 | printf("Usage: %s -s port -c addr port [-f pcap.out]\n", argv[0]); 192 | printf("Mandatory arguments:\n" 193 | " -c, --client addr port: connect point on incoming connection\n" 194 | " -s, --server port: listen on local addresses at given port\n" 195 | " OR\n" 196 | " -S addr port: listen on given address/port\n" 197 | "Optional arguments:\n" 198 | " -f, --file pcap.out: output file\n" 199 | " -R, --rotate size: rotate output file (to file.1) at size\n" 200 | " -t, --truncate size: size to truncate packets at (with tcp header)\n" 201 | " If viewed in wireshark, max is %u (default: %u)\n" 202 | " -b, --block-size size: size of packets to send (default: %u)\n" 203 | " -r, --recv-num num: number of packets we can recv at once (default: %u)\n" 204 | " -E, --rand-byte : with ratio between 0.0 and 1.0,\n" 205 | " probability for each byte to be changed randomly\n" 206 | " The data is dumped _after_ error injection\n" 207 | " -e, --flip-bit : same, but there's only one bit flip\n" 208 | " -v, --verbose: verbose, more v for more verbosity\n" 209 | " -q, --quiet: quiet output\n", 210 | DEFAULT_HARD_TRUNC_LEN, DEFAULT_TRUNC_LEN, 211 | DEFAULT_BLOCK_SIZE, DEFAULT_RECV_NUM); 212 | 213 | } 214 | 215 | static void* flush_thread(void *arg) { 216 | struct thread_arg *thread_arg = arg; 217 | long pos = strlen(thread_arg->pcap_filename) + 3; 218 | char *backpath = alloca(pos); 219 | snprintf(backpath, pos, "%s.1", thread_arg->pcap_filename); 220 | pcap_dumper_t **p_pcap_dumper = thread_arg->p_pcap_dumper; 221 | /* keep this value for close or race between check in while and dump_flush */ 222 | pcap_dumper_t *pcap_dumper = *p_pcap_dumper; 223 | 224 | while (*p_pcap_dumper) { 225 | pcap_dump_flush(pcap_dumper); 226 | if (thread_arg->file_rotate) { 227 | pos = pcap_dump_ftell(pcap_dumper); 228 | /* on error pos == -1 < file_rotate, just continue */ 229 | if (pos > thread_arg->file_rotate) { 230 | pthread_mutex_lock(thread_arg->plock); 231 | if (!*p_pcap_dumper) { 232 | pthread_mutex_unlock(thread_arg->plock); 233 | break; 234 | } 235 | pcap_dump_flush(pcap_dumper); 236 | pcap_dump_close(pcap_dumper); 237 | if (rename(thread_arg->pcap_filename, backpath)) 238 | ERROR_LOG("renaming pcap file failed: %d", errno); 239 | 240 | TEST_NZ(pcap_dumper = pcap_dump_open(thread_arg->pcap, thread_arg->pcap_filename)); 241 | *p_pcap_dumper = pcap_dumper; 242 | pthread_mutex_unlock(thread_arg->plock); 243 | } 244 | } 245 | 246 | sleep(1); 247 | } 248 | 249 | pcap_dump_close(pcap_dumper); 250 | 251 | pthread_exit(NULL); 252 | } 253 | 254 | static void *setup_thread(void *arg){ 255 | uint8_t *rdmabuf; 256 | struct ibv_mr *mr; 257 | struct thread_arg *thread_arg; 258 | msk_data_t *data; 259 | struct pkt_hdr pkt_hdr; 260 | int i; 261 | struct privatedata *s_priv, *c_priv; 262 | msk_trans_t *child_trans, *c_trans; 263 | 264 | TEST_NZ(child_trans = arg); 265 | TEST_NZ(c_trans = child_trans->private_data); 266 | TEST_NZ(thread_arg = c_trans->private_data); 267 | 268 | const size_t mr_size = 2*(thread_arg->recv_num+1)*(thread_arg->block_size+PACKET_HDR_LEN)*sizeof(char); 269 | 270 | TEST_Z(msk_connect(c_trans)); 271 | 272 | 273 | // set up data_t elements and mr (needs to be common for both as well) 274 | TEST_NZ(rdmabuf = malloc(mr_size)); 275 | memset(rdmabuf, 0, mr_size); 276 | //FIXME that's not possible, can only reg it once -- need to use the same pd for both trans 277 | TEST_NZ(mr = msk_reg_mr(c_trans, rdmabuf, mr_size, IBV_ACCESS_LOCAL_WRITE)); 278 | 279 | 280 | 281 | TEST_NZ(data = malloc(2*thread_arg->recv_num*sizeof(msk_data_t))); 282 | 283 | memset(&pkt_hdr, 0, sizeof(pkt_hdr)); 284 | 285 | pkt_hdr.ipv6.ip_flags[0] = 0x60; /* 6 in the leftmosts 4 bits */ 286 | pkt_hdr.ipv6.ip_nh = IPPROTO_TCP; 287 | pkt_hdr.ipv6.ip_hl = 1; 288 | /** @todo: add options, use one of : 289 | CLIENT 290 | child_trans->cm_id->route.addr.dst_sin 291 | child_trans->cm_id->route.addr.src_sin 292 | RMITM 293 | c_trans->cm_id->route.addr.src_sin 294 | c_trans->cm_id->route.addr.dst_sin 295 | SERVER 296 | */ 297 | pkt_hdr.ipv6.ip_src.s6_addr16[4] = 0xffff; 298 | pkt_hdr.ipv6.ip_src.s6_addr16[5] = 0x0000; 299 | pkt_hdr.ipv6.ip_src.s6_addr32[3] = ((struct sockaddr_in*)msk_get_src_addr(c_trans))->sin_addr.s_addr; 300 | pkt_hdr.tcp.th_sport = msk_get_src_port(c_trans); 301 | 302 | pkt_hdr.ipv6.ip_dst.s6_addr16[4] = 0xffff; 303 | pkt_hdr.ipv6.ip_dst.s6_addr16[5] = 0x0000; 304 | pkt_hdr.ipv6.ip_dst.s6_addr32[3] = ((struct sockaddr_in*)msk_get_dst_addr(c_trans))->sin_addr.s_addr; 305 | pkt_hdr.tcp.th_dport = msk_get_dst_port(c_trans); 306 | 307 | pkt_hdr.tcp.th_data_off = INT8_C(sizeof(struct tcp_hdr) * 4); /* *4 because words of 2 bits? it's odd. */ 308 | pkt_hdr.tcp.th_window = htons(100); 309 | pkt_hdr.tcp.th_flags = THF_ACK; 310 | 311 | for (i=0; i < 2*thread_arg->recv_num; i++) { 312 | if (i == thread_arg->recv_num) { // change packet direction 313 | pkt_hdr.ipv6.ip_src.s6_addr32[3] = ((struct sockaddr_in*)msk_get_dst_addr(c_trans))->sin_addr.s_addr; 314 | pkt_hdr.tcp.th_sport = msk_get_dst_port(c_trans); 315 | pkt_hdr.ipv6.ip_dst.s6_addr32[3] = ((struct sockaddr_in*)msk_get_src_addr(c_trans))->sin_addr.s_addr; 316 | pkt_hdr.tcp.th_dport = msk_get_src_port(c_trans); 317 | } 318 | memcpy(rdmabuf+(i)*(thread_arg->block_size+PACKET_HDR_LEN), &pkt_hdr, PACKET_HDR_LEN); 319 | data[i].data=rdmabuf+(i)*(thread_arg->block_size+PACKET_HDR_LEN)+PACKET_HDR_LEN; 320 | data[i].max_size=thread_arg->block_size; 321 | data[i].mr = mr; 322 | } 323 | 324 | // set up the data needed to communicate 325 | TEST_NZ(child_trans->private_data = malloc(sizeof(struct privatedata))); 326 | TEST_NZ(c_trans->private_data = malloc(sizeof(struct privatedata))); 327 | s_priv = child_trans->private_data; 328 | c_priv = c_trans->private_data; 329 | 330 | s_priv->targ = thread_arg; 331 | c_priv->targ = thread_arg; 332 | 333 | c_priv->seq_nr = pkt_hdr.tcp.th_seq_nr; 334 | s_priv->seq_nr = pkt_hdr.tcp.th_seq_nr; 335 | 336 | s_priv->o_trans = c_trans; 337 | c_priv->o_trans = child_trans; 338 | 339 | for (i=0; irecv_num; i++) { 340 | TEST_Z(msk_post_recv(c_trans, &data[i], callback_recv, callback_error, NULL)); 341 | TEST_Z(msk_post_recv(child_trans, &data[i+thread_arg->recv_num], callback_recv, callback_error, NULL)); 342 | } 343 | 344 | pthread_mutex_lock(thread_arg->plock); 345 | 346 | /* finalize_connect first, finalize_accept second */ 347 | TEST_Z(msk_finalize_connect(c_trans)); 348 | TEST_Z(msk_finalize_accept(child_trans)); 349 | 350 | ERROR_LOG("New connection setup\n"); 351 | 352 | /* Wait till either connection has a disconnect callback */ 353 | while (c_trans->state == MSK_CONNECTED && 354 | child_trans->state == MSK_CONNECTED) { 355 | pthread_cond_wait(thread_arg->pcond, thread_arg->plock); 356 | } 357 | pthread_mutex_unlock(thread_arg->plock); 358 | 359 | msk_destroy_trans(&c_trans); 360 | msk_destroy_trans(&child_trans); 361 | 362 | free(c_priv); 363 | free(s_priv); 364 | free(data); 365 | free(rdmabuf); 366 | 367 | pthread_exit(NULL); 368 | } 369 | 370 | int main(int argc, char **argv) { 371 | msk_trans_t *s_trans; 372 | msk_trans_t *child_trans; 373 | msk_trans_t *c_trans; 374 | msk_trans_attr_t s_attr; 375 | msk_trans_attr_t c_attr; 376 | pthread_mutex_t lock; 377 | pthread_cond_t cond; 378 | 379 | pthread_t thrid, flushthrid; 380 | 381 | pcap_t *pcap; 382 | pcap_dumper_t *pcap_dumper; 383 | double double_proba; 384 | 385 | // argument handling 386 | struct thread_arg thread_arg; 387 | int option_index = 0; 388 | int op, last_op; 389 | char *tmp_s; 390 | static struct option long_options[] = { 391 | { "client", required_argument, 0, 'c' }, 392 | { "server", required_argument, 0, 's' }, 393 | { "help", no_argument, 0, 'h' }, 394 | { "verbose", no_argument, 0, 'v' }, 395 | { "quiet", no_argument, 0, 'q' }, 396 | { "block-size", required_argument, 0, 'b' }, 397 | { "recv-num", required_argument, 0, 'r' }, 398 | { "rotate", required_argument, 0, 'R' }, 399 | { "file", required_argument, 0, 'f' }, 400 | { "truncate", required_argument, 0, 't' }, 401 | { "rand-byte", required_argument, 0, 'E' }, 402 | { "flip-bit", required_argument, 0, 'e' }, 403 | { 0, 0, 0, 0 } 404 | }; 405 | 406 | 407 | memset(&s_attr, 0, sizeof(msk_trans_attr_t)); 408 | memset(&c_attr, 0, sizeof(msk_trans_attr_t)); 409 | memset(&thread_arg, 0, sizeof(struct thread_arg)); 410 | 411 | s_attr.server = -1; // put an incorrect value to check if we're either client or server 412 | c_attr.server = -1; 413 | // sane values for optional or non-configurable elements 414 | s_attr.debug = 1; 415 | c_attr.debug = 1; 416 | s_attr.max_recv_sge = 1; 417 | s_attr.disconnect_callback = callback_disconnect; 418 | c_attr.max_recv_sge = 1; 419 | c_attr.disconnect_callback = callback_disconnect; 420 | s_attr.worker_count = -1; 421 | c_attr.worker_count = -1; 422 | thread_arg.pcap_filename = "pcap.out"; 423 | 424 | last_op = 0; 425 | while ((op = getopt_long(argc, argv, "-@hvqE:e:s:S:c:w:b:f:r:t:R:", long_options, &option_index)) != -1) { 426 | switch(op) { 427 | case 1: // this means double argument 428 | if (last_op == 'c') { 429 | c_attr.port = optarg; 430 | } else if (last_op == 'S') { 431 | s_attr.port = optarg; 432 | } else { 433 | ERROR_LOG("Failed to parse arguments"); 434 | print_help(argv); 435 | exit(EINVAL); 436 | } 437 | break; 438 | case '@': 439 | printf("%s compiled on %s at %s\n", argv[0], __DATE__, __TIME__); 440 | printf("Release = %s\n", VERSION); 441 | printf("Release comment = %s\n", VERSION_COMMENT); 442 | printf("Git HEAD = %s\n", _GIT_HEAD_COMMIT ) ; 443 | printf("Git Describe = %s\n", _GIT_DESCRIBE ) ; 444 | exit(0); 445 | case 'h': 446 | print_help(argv); 447 | exit(0); 448 | case 'v': 449 | c_attr.debug = c_attr.debug * 2 + 1; 450 | s_attr.debug = c_attr.debug; 451 | break; 452 | case 'q': 453 | c_attr.debug = 0; 454 | s_attr.debug = 0; 455 | break; 456 | case 'c': 457 | c_attr.server = 0; 458 | c_attr.node = optarg; 459 | break; 460 | case 's': 461 | s_attr.server = 10; 462 | s_attr.node = "::"; 463 | s_attr.port = optarg; 464 | break; 465 | case 'S': 466 | s_attr.server = 10; 467 | s_attr.node = optarg; 468 | break; 469 | case 'w': 470 | ERROR_LOG("-w has become deprecated, use -f or --file now. Proceeding anyway"); 471 | /* fallthrough */ 472 | case 'f': 473 | thread_arg.pcap_filename = optarg; 474 | break; 475 | case 'R': 476 | thread_arg.file_rotate = strtoull(optarg, &tmp_s, 0); 477 | if (errno || thread_arg.file_rotate == 0) { 478 | ERROR_LOG("Invalid rotate length, assuming no truncate"); 479 | break; 480 | } 481 | if (tmp_s[0] != 0) { 482 | set_size(thread_arg.file_rotate, tmp_s); 483 | } 484 | INFO_LOG(c_attr.debug >1, "rotate length: %"PRIu64, thread_arg.file_rotate); 485 | 486 | break; 487 | case 't': 488 | thread_arg.trunc = strtoul(optarg, &tmp_s, 0); 489 | if (errno || thread_arg.trunc == 0) { 490 | ERROR_LOG("Invalid truncate length, assuming default (%u)", DEFAULT_TRUNC_LEN); 491 | break; 492 | } 493 | if (tmp_s[0] != 0) { 494 | set_size(thread_arg.trunc, tmp_s); 495 | } 496 | INFO_LOG(c_attr.debug > 1, "truncate length: %u", thread_arg.trunc); 497 | break; 498 | case 'b': 499 | thread_arg.block_size = strtoul(optarg, &tmp_s, 0); 500 | if (errno || thread_arg.block_size == 0) { 501 | ERROR_LOG("Invalid block size, assuming default (%u)", DEFAULT_BLOCK_SIZE); 502 | break; 503 | } 504 | if (tmp_s[0] != 0) { 505 | set_size(thread_arg.block_size, tmp_s); 506 | } 507 | INFO_LOG(c_attr.debug > 1, "block size: %u", thread_arg.block_size); 508 | break; 509 | case 'r': 510 | thread_arg.recv_num = strtoul(optarg, NULL, 0); 511 | 512 | if (errno || thread_arg.recv_num == 0) 513 | ERROR_LOG("Invalid recv_num, assuming default (%u)", DEFAULT_RECV_NUM); 514 | break; 515 | case 'E': 516 | double_proba = strtod(optarg, &tmp_s); 517 | if (tmp_s == optarg || double_proba < 0.0 || double_proba > 1.0) { 518 | ERROR_LOG("probability \"%s\" must be between 0.0 and 1.0\n", 519 | optarg); 520 | exit(EINVAL); 521 | } 522 | 523 | thread_arg.rand_thresh = RAND_MAX*double_proba; 524 | break; 525 | case 'e': 526 | double_proba = strtod(optarg, &tmp_s); 527 | if (tmp_s == optarg || double_proba < 0.0 || double_proba > 1.0) { 528 | ERROR_LOG("probability \"%s\" must be between 0.0 and 1.0\n", 529 | optarg); 530 | exit(EINVAL); 531 | } 532 | 533 | thread_arg.flip_thresh = RAND_MAX*double_proba; 534 | break; 535 | default: 536 | ERROR_LOG("Failed to parse arguments"); 537 | print_help(argv); 538 | exit(EINVAL); 539 | } 540 | last_op = op; 541 | } 542 | 543 | if (c_attr.server == -1 || s_attr.server == -1) { 544 | ERROR_LOG("must have both client and server!"); 545 | print_help(argv); 546 | exit(EINVAL); 547 | } 548 | 549 | if (thread_arg.file_rotate && strncmp(thread_arg.pcap_filename, "-", 2) == 0) { 550 | ERROR_LOG("Can't rotate stdout!"); 551 | print_help(argv); 552 | exit(EINVAL); 553 | } 554 | 555 | if (thread_arg.block_size == 0) 556 | thread_arg.block_size = DEFAULT_BLOCK_SIZE; 557 | if (thread_arg.recv_num == 0) 558 | thread_arg.recv_num = DEFAULT_RECV_NUM; 559 | if (thread_arg.trunc == 0) 560 | thread_arg.trunc = DEFAULT_TRUNC_LEN; 561 | thread_arg.hard_trunc = max(thread_arg.trunc, DEFAULT_HARD_TRUNC_LEN); 562 | 563 | if (thread_arg.flip_thresh > RAND_MAX - thread_arg.rand_thresh) { 564 | ERROR_LOG("flip and random probabilities are additive, can't be more than 1!"); 565 | exit(EINVAL); 566 | } 567 | thread_arg.flip_thresh += thread_arg.rand_thresh; 568 | 569 | s_attr.rq_depth = thread_arg.recv_num+1; 570 | s_attr.sq_depth = thread_arg.recv_num+1; 571 | c_attr.rq_depth = thread_arg.recv_num+1; 572 | c_attr.sq_depth = thread_arg.recv_num+1; 573 | 574 | TEST_Z(init_rand()); 575 | 576 | // server init 577 | TEST_Z(msk_init(&s_trans, &s_attr)); 578 | 579 | if (!s_trans) 580 | exit(-1); 581 | 582 | TEST_Z(msk_bind_server(s_trans)); 583 | 584 | pcap = pcap_open_dead(DLT_RAW, thread_arg.hard_trunc); 585 | TEST_NZ(pcap_dumper = pcap_dump_open(pcap, thread_arg.pcap_filename)); 586 | TEST_Z(pthread_create(&flushthrid, NULL, flush_thread, &thread_arg)); 587 | 588 | thread_arg.pcap = pcap; 589 | thread_arg.p_pcap_dumper = &pcap_dumper; 590 | 591 | memset(&lock, 0, sizeof(pthread_mutex_t)); 592 | memset(&cond, 0, sizeof(pthread_cond_t)); 593 | pthread_mutex_init(&lock, NULL); 594 | pthread_cond_init(&cond, NULL); 595 | thread_arg.plock = &lock; 596 | thread_arg.pcond = &cond; 597 | 598 | signal(SIGINT, sigHandler); 599 | signal(SIGHUP, sigHandler); 600 | 601 | while (run_threads) { 602 | child_trans = msk_accept_one_wait(s_trans, 1000); 603 | 604 | if (child_trans == NULL) 605 | continue; 606 | 607 | // got a client, start our own client before we finalize the server's connection 608 | c_attr.pd = child_trans->pd; 609 | TEST_Z(msk_init(&c_trans, &c_attr)); 610 | if (!c_trans) { 611 | ERROR_LOG("Couldn't connect to remote endpoint, aborting"); 612 | break; 613 | } 614 | 615 | // ugly hack to have all the arguments we need given... 616 | child_trans->private_data = c_trans; 617 | c_trans->private_data = &thread_arg; 618 | TEST_Z(pthread_create(&thrid, NULL, setup_thread, child_trans)); 619 | } 620 | pthread_mutex_lock(&lock); 621 | pcap_dumper = NULL; 622 | pthread_mutex_unlock(&lock); 623 | pthread_join(flushthrid, NULL); 624 | 625 | pcap_close(pcap); 626 | 627 | pthread_cond_destroy(&cond); 628 | pthread_mutex_destroy(&lock); 629 | 630 | msk_destroy_trans(&s_trans); 631 | 632 | return 0; 633 | } 634 | 635 | -------------------------------------------------------------------------------- /src/rmitm.h: -------------------------------------------------------------------------------- 1 | /* Based on tcpreplay's tcpr.h, itself based from libnet's libnet-headers.h. Thanks. */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifndef _RMITM_H_ 8 | #define _RMITM_H_ 9 | 10 | #if 0 11 | struct in6_addr 12 | { 13 | union 14 | { 15 | u_int8_t __u6_addr8[16]; 16 | u_int16_t __u6_addr16[8]; 17 | u_int32_t __u6_addr32[4]; 18 | } __u6_addr; /* 128-bit IP6 address */ 19 | }; 20 | #define s6_addr __u6_addr.__u6_addr8 21 | #define s6_addr8 __u6_addr.__u6_addr8 22 | #define s6_addr16 __u6_addr.__u6_addr16 23 | #define s6_addr32 __u6_addr.__u6_addr32 24 | #endif 25 | 26 | /* 27 | * IPv6 header 28 | * Internet Protocol, version 6 29 | * Static header size: 40 bytes 30 | */ 31 | struct ipv6_hdr 32 | { 33 | u_int8_t ip_flags[4]; /* version, traffic class, flow label */ 34 | u_int16_t ip_len; /* total length */ 35 | u_int8_t ip_nh; /* next header */ 36 | u_int8_t ip_hl; /* hop limit */ 37 | struct in6_addr ip_src, ip_dst; /* source and dest address */ 38 | }; 39 | 40 | /* 41 | * tcp header structure. This is the minimal header (20 bytes) 42 | */ 43 | 44 | typedef struct tcp_hdr { 45 | u_int16_t th_sport; 46 | u_int16_t th_dport; 47 | u_int32_t th_seq_nr; 48 | u_int32_t th_ack_nr; 49 | u_int8_t th_data_off; 50 | u_int8_t th_flags; 51 | u_int16_t th_window; 52 | u_int16_t th_sum; 53 | u_int16_t th_urgptr; 54 | } tcp_hdr_t; 55 | 56 | #define TH_DO_MASK 0xf0 57 | 58 | #define TH_FLAGS_MASK 0x3f 59 | #define THF_FIN 0x1 60 | #define THF_SYN 0x2 61 | #define THF_RST 0x4 62 | #define THF_PSH 0x8 63 | #define THF_ACK 0x10 64 | #define THF_URG 0x20 65 | 66 | struct pkt_hdr { 67 | struct ipv6_hdr ipv6; 68 | struct tcp_hdr tcp; 69 | uint8_t data[0]; 70 | }; 71 | 72 | #define PACKET_HDR_LEN sizeof(struct pkt_hdr) 73 | 74 | #define CHECKSUM_CARRY(x) do { \ 75 | x = ((x & 0xffff) + (x >> 16)); \ 76 | if (x > 0xffff) \ 77 | x -= 0xffff; \ 78 | } while (0) 79 | 80 | static inline uint16_t checksum(u_int16_t *data, int len) { 81 | uint32_t sum = 0; 82 | union { 83 | int16_t s; 84 | u_int8_t b[2]; 85 | } pad; 86 | 87 | while (len > 1) { 88 | sum += *data++; 89 | len -= 2; 90 | if (sum >= 0x10000) 91 | sum -= 0xffff; 92 | } 93 | 94 | if (len == 1) { 95 | pad.b[0] = *(u_int8_t *)data; 96 | pad.b[1] = 0; 97 | sum += pad.s; 98 | } 99 | 100 | CHECKSUM_CARRY(sum); 101 | return sum; 102 | } 103 | 104 | static inline void ipv6_tcp_checksum(struct pkt_hdr *hdr) { 105 | uint32_t sum; 106 | hdr->tcp.th_sum = 0; 107 | 108 | sum = checksum((uint16_t*)&hdr->ipv6.ip_src, 2*sizeof(struct in6_addr)); 109 | sum += htons(IPPROTO_TCP + ntohs(hdr->ipv6.ip_len)); 110 | sum += checksum((uint16_t*)&hdr->tcp, ntohs(hdr->ipv6.ip_len)); 111 | 112 | CHECKSUM_CARRY(sum); 113 | hdr->tcp.th_sum = ((~sum) & 0xffff); 114 | } 115 | 116 | static inline int min(int a, int b) { 117 | return (a < b) ? a: b; 118 | } 119 | static inline int max(int a, int b) { 120 | return (a < b) ? b: a; 121 | } 122 | 123 | #endif /* _RMITM_H_ */ 124 | -------------------------------------------------------------------------------- /src/rreplay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright CEA/DAM/DIF (2012) 4 | * contributor : Dominique Martinet dominique.martinet@cea.fr 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * --------------------------------------- 21 | */ 22 | 23 | /** 24 | * \file rreplay.c 25 | * \brief Replays and checks what came out of a dump file 26 | * 27 | * Replays and checks what came out of a dump file 28 | * 29 | */ 30 | #ifdef HAVE_CONFIG_H 31 | #include "config.h" 32 | #endif 33 | 34 | #include 35 | #include 36 | #include //printf 37 | #include //malloc 38 | #include //memcpy 39 | #include //read 40 | #include //gethostbyname 41 | #include 42 | #include 43 | #include 44 | #include //open 45 | #include //open 46 | #include //open 47 | #include 48 | 49 | #include 50 | #include 51 | 52 | #include "utils.h" 53 | #include "mooshika.h" 54 | #include "rmitm.h" 55 | 56 | #define DEFAULT_BLOCK_SIZE 1024*1024 // nfs page size 57 | #define DEFAULT_RECV_NUM 16 58 | 59 | struct datalock { 60 | msk_data_t *data; 61 | pthread_mutex_t lock; 62 | }; 63 | 64 | struct privatedata { 65 | pthread_mutex_t lock; 66 | pthread_cond_t cond; 67 | struct pcap_pkthdr *pcaphdr; 68 | struct pkt_hdr *packet; 69 | int rc; 70 | int docheck; 71 | }; 72 | 73 | 74 | static void callback_error(msk_trans_t *trans, msk_data_t *pdata, void* arg) { 75 | INFO_LOG(trans->state != MSK_CLOSING && trans->state != MSK_CLOSED 76 | && trans->debug, "error callback on buffer %p", pdata); 77 | } 78 | 79 | static void callback_send(msk_trans_t *trans, msk_data_t *pdata, void *arg) { 80 | INFO_LOG(trans->debug & MSK_DEBUG_SEND,"Sent something"); 81 | } 82 | 83 | static void callback_disconnect(msk_trans_t *trans) { 84 | struct privatedata *priv = trans->private_data; 85 | 86 | if (!priv) 87 | return; 88 | 89 | pthread_mutex_lock(&priv->lock); 90 | priv->rc = ENOTCONN; 91 | pthread_cond_broadcast(&priv->cond); 92 | pthread_mutex_unlock(&priv->lock); 93 | } 94 | 95 | static void callback_recv(msk_trans_t *trans, msk_data_t *pdata, void *arg) { 96 | struct privatedata *priv = trans->private_data; 97 | 98 | if (!priv) { 99 | ERROR_LOG("no private data?"); 100 | return; 101 | } 102 | 103 | INFO_LOG(trans->debug & MSK_DEBUG_RECV, "Received something"); 104 | 105 | pthread_mutex_lock(&priv->lock); 106 | 107 | 108 | /* check we got what expected */ 109 | if (priv->docheck && ( 110 | // priv->pcaphdr->len - PACKET_HDR_LEN != min(pdata->size + PACKET_HDR_LEN, PACKET_HARD_MAX_LEN) || 111 | memcmp(priv->packet->data, pdata->data, priv->pcaphdr->caplen - PACKET_HDR_LEN) != 0)) { 112 | ERROR_LOG("Received packet doesn't match what we expected! Aborting."); 113 | priv->rc = EBADMSG; 114 | /* only repost buffer if we didn't have an error (or didn't check) */ 115 | } else if ((priv->rc = msk_post_recv(trans, pdata, callback_recv, callback_error, NULL))) { 116 | ERROR_LOG("Couldn't repost recv buffer, rc %d (%s)", priv->rc, strerror(priv->rc)); 117 | } 118 | 119 | pthread_cond_signal(&priv->cond); 120 | 121 | pthread_mutex_unlock(&priv->lock); 122 | } 123 | 124 | static void print_help(char **argv) { 125 | printf("Usage: %s (-s port|-c addr port) [-f pcap.out]\n", argv[0]); 126 | printf("Mandatory argument:\n" 127 | " -c, --client addr port: connect point on incoming connection\n" 128 | " OR\n" 129 | " -s, --server port: listen on local addresses at given port\n" 130 | " OR\n" 131 | " -S addr port: listen on given address/port\n" 132 | "Optional arguments:\n" 133 | " -f, --file pcap.out: input file\n" 134 | " -B, --banner: server sends a banner and is expected to talk first\n" 135 | " -n, --no-check: do not verify that received data is what we expected\n" 136 | " -b, --block-size size: size of packets to send (default: %u)\n" 137 | " -r, --recv-num num: number of packets we can recv at once (default: %u)\n" 138 | " -v, --verbose: verbose, more v for more verbosity\n" 139 | " -q, --quiet: quiet output\n", 140 | DEFAULT_BLOCK_SIZE, DEFAULT_RECV_NUM); 141 | 142 | } 143 | 144 | int main(int argc, char **argv) { 145 | msk_trans_t *trans, *listen_trans; 146 | msk_trans_attr_t trans_attr; 147 | 148 | char errbuf[PCAP_ERRBUF_SIZE]; 149 | char *pcap_file; 150 | pcap_t *pcap; 151 | 152 | size_t block_size = 0; 153 | uint32_t recv_num = 0; 154 | int banner = 0; 155 | 156 | int i, rc; 157 | uint8_t *rdmabuf; 158 | struct ibv_mr *mr; 159 | msk_data_t *data, *wdata; 160 | struct privatedata priv; 161 | 162 | 163 | // argument handling 164 | int option_index = 0; 165 | int op, last_op; 166 | char *tmp_s; 167 | static struct option long_options[] = { 168 | { "client", required_argument, 0, 'c' }, 169 | { "server", required_argument, 0, 's' }, 170 | { "banner", no_argument, 0, 'B' }, 171 | { "help", no_argument, 0, 'h' }, 172 | { "verbose", no_argument, 0, 'v' }, 173 | { "quiet", no_argument, 0, 'q' }, 174 | { "block-size", required_argument, 0, 'b' }, 175 | { "file", required_argument, 0, 'f' }, 176 | { "recv-num", required_argument, 0, 'r' }, 177 | { "no-check", no_argument, 0, 'n' }, 178 | { 0, 0, 0, 0 } 179 | }; 180 | 181 | 182 | memset(&trans_attr, 0, sizeof(msk_trans_attr_t)); 183 | memset(&priv, 0, sizeof(struct privatedata)); 184 | priv.docheck = 1; 185 | 186 | trans_attr.server = -1; // put an incorrect value to check if we're either client or server 187 | // sane values for optional or non-configurable elements 188 | trans_attr.debug = 1; 189 | trans_attr.max_recv_sge = 1; 190 | trans_attr.disconnect_callback = callback_disconnect; 191 | trans_attr.worker_count = -1; 192 | pcap_file = "pcap.out"; 193 | 194 | last_op = 0; 195 | while ((op = getopt_long(argc, argv, "-@hvqc:s:S:r:b:r:t:f:Bn", long_options, &option_index)) != -1) { 196 | switch(op) { 197 | case 1: // this means double argument 198 | if (last_op == 'c') { 199 | trans_attr.port = optarg; 200 | } else if (last_op == 'S') { 201 | trans_attr.port = optarg; 202 | } else { 203 | ERROR_LOG("Failed to parse arguments"); 204 | print_help(argv); 205 | exit(EINVAL); 206 | } 207 | break; 208 | case '@': 209 | printf("%s compiled on %s at %s\n", argv[0], __DATE__, __TIME__); 210 | printf("Release = %s\n", VERSION); 211 | printf("Release comment = %s\n", VERSION_COMMENT); 212 | printf("Git HEAD = %s\n", _GIT_HEAD_COMMIT ) ; 213 | printf("Git Describe = %s\n", _GIT_DESCRIBE ) ; 214 | exit(0); 215 | case 'h': 216 | print_help(argv); 217 | exit(0); 218 | case 'v': 219 | trans_attr.debug = trans_attr.debug * 2 + 1; 220 | break; 221 | case 'c': 222 | trans_attr.server = 0; 223 | trans_attr.node = optarg; 224 | break; 225 | case 's': 226 | trans_attr.server = 10; 227 | trans_attr.node = "::"; 228 | trans_attr.port = optarg; 229 | break; 230 | case 'S': 231 | trans_attr.server = 10; 232 | trans_attr.node = optarg; 233 | break; 234 | case 'q': 235 | trans_attr.debug = 0; 236 | break; 237 | case 'f': 238 | pcap_file = optarg; 239 | break; 240 | case 'B': 241 | banner = 1; 242 | break; 243 | case 'n': 244 | priv.docheck = 0; 245 | break; 246 | case 'b': 247 | block_size = strtoul(optarg, &tmp_s, 0); 248 | if (errno || block_size == 0) { 249 | ERROR_LOG("Invalid block size, assuming default (%u)", DEFAULT_BLOCK_SIZE); 250 | break; 251 | } 252 | if (tmp_s[0] != 0) { 253 | set_size(block_size, tmp_s); 254 | } 255 | INFO_LOG(trans_attr.debug > 1, "block size: %zu", block_size); 256 | break; 257 | case 'r': 258 | recv_num = strtoul(optarg, NULL, 0); 259 | 260 | if (errno || recv_num == 0) 261 | ERROR_LOG("Invalid recv_num, assuming default (%u)", DEFAULT_RECV_NUM); 262 | break; 263 | default: 264 | ERROR_LOG("Failed to parse arguments"); 265 | print_help(argv); 266 | exit(EINVAL); 267 | } 268 | last_op = op; 269 | } 270 | 271 | if (trans_attr.server == -1) { 272 | ERROR_LOG("Must be either client or server!"); 273 | print_help(argv); 274 | exit(EINVAL); 275 | } 276 | 277 | if (block_size == 0) 278 | block_size = DEFAULT_BLOCK_SIZE; 279 | if (recv_num == 0) 280 | recv_num = DEFAULT_RECV_NUM; 281 | 282 | trans_attr.rq_depth = recv_num+1; 283 | trans_attr.sq_depth = recv_num+1; 284 | 285 | /* open pcap file */ 286 | pcap = pcap_open_offline( pcap_file, errbuf ); 287 | 288 | if (pcap == NULL) { 289 | ERROR_LOG("Couldn't open pcap file: %s", errbuf); 290 | return EINVAL; 291 | } 292 | 293 | /* msk init */ 294 | TEST_Z(msk_init(&trans, &trans_attr)); 295 | 296 | if (!trans) { 297 | ERROR_LOG("msk_init failed! panic!"); 298 | exit(-1); 299 | } 300 | 301 | /* finish msk init */ 302 | const size_t mr_size = (recv_num+1)*block_size; 303 | 304 | if (trans_attr.server == 0) 305 | TEST_Z(msk_connect(trans)); 306 | else { 307 | listen_trans = trans; 308 | TEST_Z(msk_bind_server(listen_trans)); 309 | TEST_NZ(trans = msk_accept_one(listen_trans)); 310 | } 311 | 312 | TEST_NZ(rdmabuf = malloc(mr_size)); 313 | memset(rdmabuf, 0, mr_size); 314 | 315 | TEST_NZ(mr = msk_reg_mr(trans, rdmabuf, mr_size, IBV_ACCESS_LOCAL_WRITE)); 316 | 317 | TEST_NZ(data = malloc((recv_num+1)*sizeof(msk_data_t))); 318 | 319 | for (i=0; i < recv_num + 1; i++) { 320 | data[i].data = rdmabuf+i*block_size; 321 | data[i].max_size = block_size; 322 | data[i].mr = mr; 323 | } 324 | wdata = &data[recv_num]; 325 | 326 | trans->private_data = &priv; 327 | 328 | pthread_mutex_init(&priv.lock, NULL); 329 | pthread_cond_init(&priv.cond, NULL); 330 | 331 | for (i=0; iserver == 0) 338 | TEST_Z(msk_finalize_connect(trans)); 339 | else 340 | TEST_Z(msk_finalize_accept(trans)); 341 | 342 | /* set on first packet */ 343 | uint32_t send_ip = 0; 344 | uint32_t recv_ip = 0; 345 | uint16_t send_port = 0; 346 | uint16_t recv_port = 0; 347 | 348 | i=0; 349 | while ((rc = pcap_next_ex(pcap, &priv.pcaphdr, (const u_char**)&priv.packet)) >= 0) { 350 | INFO_LOG(trans->debug & (MSK_DEBUG_SEND|MSK_DEBUG_RECV), "Iteration %d", i++); 351 | 352 | /* first packet: */ 353 | if (send_ip == 0) { 354 | /* who talks first? */ 355 | if ((trans->server == 0 && banner == 0) || (trans->server && banner == 1)) { 356 | send_ip = priv.packet->ipv6.ip_src.s6_addr32[3]; 357 | send_port = priv.packet->tcp.th_sport; 358 | recv_ip = priv.packet->ipv6.ip_dst.s6_addr32[3]; 359 | recv_port = priv.packet->tcp.th_dport; 360 | } else { 361 | send_ip = priv.packet->ipv6.ip_dst.s6_addr32[3]; 362 | send_port = priv.packet->tcp.th_dport; 363 | recv_ip = priv.packet->ipv6.ip_src.s6_addr32[3]; 364 | recv_port = priv.packet->tcp.th_sport; 365 | } 366 | } 367 | 368 | /* all packets: decide if we send it or if we wait till we receive another */ 369 | if (priv.packet->ipv6.ip_src.s6_addr32[3] == send_ip && 370 | priv.packet->tcp.th_sport == send_port) { 371 | if (priv.pcaphdr->len != priv.pcaphdr->caplen) { 372 | ERROR_LOG("Can't send truncated data! make sure you've stored all the capture (-t in rmitm)"); 373 | rc = EINVAL; 374 | break; 375 | } 376 | memcpy(wdata->data, priv.packet->data, priv.pcaphdr->len); 377 | wdata->size = priv.pcaphdr->len; 378 | rc = msk_post_send(trans, wdata, callback_send, callback_error, NULL); 379 | if (rc) { 380 | ERROR_LOG("msk_post_send failed with rc %d (%s)", rc, strerror(rc)); 381 | break; 382 | } 383 | } else if (priv.packet->ipv6.ip_src.s6_addr32[3] == recv_ip && 384 | priv.packet->tcp.th_sport == recv_port) { 385 | INFO_LOG(trans->debug & (MSK_DEBUG_SEND|MSK_DEBUG_RECV), "Waiting"); 386 | pthread_cond_wait(&priv.cond, &priv.lock); 387 | if (priv.rc != 0) { 388 | /* got an error in recv thread */ 389 | ERROR_LOG("Stopping loop"); 390 | rc = priv.rc; 391 | break; 392 | } 393 | } else { 394 | ERROR_LOG("Multiple streams in pcap file? Stopping loop."); 395 | break; 396 | } 397 | } 398 | pthread_mutex_unlock(&priv.lock); 399 | /* mooshika doesn't use negative return values, so hopefully -1 can only mean pcap error */ 400 | if (rc == -1) { 401 | ERROR_LOG("Pcap error: %s", pcap_geterr(pcap)); 402 | } 403 | 404 | pcap_close(pcap); 405 | msk_destroy_trans(&trans); 406 | 407 | /* -2 is pcap way of saying end of file */ 408 | if (rc == -2) { 409 | rc = 0; 410 | printf("Replay ended succesfully!\n"); 411 | } 412 | 413 | return rc; 414 | } 415 | 416 | -------------------------------------------------------------------------------- /src/tests/.gitignore: -------------------------------------------------------------------------------- 1 | read_write 2 | bench_rdma 3 | nfsv4_client 4 | multiple_sge 5 | -------------------------------------------------------------------------------- /src/tests/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = -g @WARNINGS_CFLAGS@ -I$(srcdir)/../../include -I$(srcdir)/.. 2 | 3 | noinst_PROGRAMS = read_write bench_rdma nfsv4_client multiple_sge 4 | read_write_SOURCES = read_write.c 5 | read_write_LDADD = -lrdmacm -libverbs -lpthread 6 | read_write_LDADD += ../libmooshika.la 7 | 8 | bench_rdma_SOURCES = bench_rdma.c 9 | bench_rdma_LDADD = -lrdmacm -libverbs -lpthread 10 | bench_rdma_LDADD += ../libmooshika.la 11 | 12 | nfsv4_client_SOURCES = nfsv4_client.c 13 | nfsv4_client_LDADD = -lrdmacm -libverbs -lpthread 14 | nfsv4_client_LDADD += ../libmooshika.la 15 | 16 | multiple_sge_SOURCES = multiple_sge.c 17 | multiple_sge_LDADD = -lrdmacm -libverbs -lpthread 18 | multiple_sge_LDADD += ../libmooshika.la 19 | -------------------------------------------------------------------------------- /src/tests/bench_rdma.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright CEA/DAM/DIF (2012) 4 | * contributor : Dominique Martinet dominique.martinet@cea.fr 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * --------------------------------------- 21 | */ 22 | 23 | /** 24 | * \file bench_rdma.c 25 | * \brief spams rdma read or write to check speed. 26 | * 27 | * spams rdma read or write to check speed. 28 | * 29 | */ 30 | #ifdef HAVE_CONFIG_H 31 | #include "config.h" 32 | #endif 33 | 34 | #include 35 | #include 36 | #include //printf 37 | #include //malloc 38 | #include //memcpy 39 | #include //read 40 | #include 41 | #include 42 | #include 43 | #include // PRIu64 44 | 45 | #include "utils.h" 46 | #include "mooshika.h" 47 | 48 | #define CHUNK_SIZE 1024*1024 49 | #define SEND_COUNT 10000 50 | #define RECV_NUM 2 51 | #define msk_post_RW msk_post_read 52 | 53 | struct datalock { 54 | msk_rloc_t *rloc; 55 | int *count; 56 | pthread_mutex_t *lock; 57 | pthread_cond_t *cond; 58 | }; 59 | 60 | void callback_send(msk_trans_t *trans, msk_data_t *data, void *arg) { 61 | 62 | } 63 | 64 | void callback_disconnect(msk_trans_t *trans) { 65 | } 66 | 67 | void callback_recv(msk_trans_t *trans, msk_data_t *data, void *arg) { 68 | struct datalock *datalock = arg; 69 | 70 | pthread_mutex_lock(datalock->lock); 71 | pthread_cond_signal(datalock->cond); 72 | pthread_mutex_unlock(datalock->lock); 73 | } 74 | 75 | void callback_read(msk_trans_t *trans, msk_data_t *data, void *arg) { 76 | struct datalock *datalock = arg; 77 | 78 | if (trans->state == MSK_CONNECTED && *datalock->count < SEND_COUNT) 79 | TEST_Z(msk_post_RW(trans, data, datalock->rloc, callback_read, NULL, datalock)); 80 | 81 | *datalock->count += 1; 82 | pthread_cond_signal(datalock->cond); 83 | } 84 | 85 | void print_help(char **argv) { 86 | printf("Usage: %s {-s|-c addr}\n", argv[0]); 87 | } 88 | 89 | int main(int argc, char **argv) { 90 | 91 | 92 | msk_trans_t *trans; 93 | uint8_t *rdmabuf; 94 | struct ibv_mr *mr; 95 | 96 | msk_data_t *wdata; 97 | msk_data_t *ackdata; 98 | msk_data_t *rdata; 99 | 100 | pthread_mutex_t lock; 101 | pthread_cond_t cond; 102 | struct datalock *datalock; 103 | 104 | msk_rloc_t *rloc; 105 | 106 | int i, count; 107 | 108 | msk_trans_attr_t attr; 109 | 110 | memset(&attr, 0, sizeof(msk_trans_attr_t)); 111 | 112 | attr.server = -1; // put an incorrect value to check if we're either client or server 113 | // sane values for optional or non-configurable elements 114 | attr.rq_depth = 1; 115 | attr.sq_depth = RECV_NUM+2; // RECV_NUM for read requets, one for the final wait_send, one to have a free one (post in a callback) 116 | attr.port = "1235"; 117 | // attr.disconnect_callback = callback_disconnect; 118 | 119 | // argument handling 120 | static struct option long_options[] = { 121 | { "client", required_argument, 0, 'c' }, 122 | { "server", required_argument, 0, 's' }, 123 | { "port", required_argument, 0, 'p' }, 124 | { "help", no_argument, 0, 'h' }, 125 | { 0, 0, 0, 0 } 126 | }; 127 | 128 | int option_index = 0; 129 | int op; 130 | while ((op = getopt_long(argc, argv, "@hvsS:c:p:", long_options, &option_index)) != -1) { 131 | switch(op) { 132 | case '@': 133 | printf("%s compiled on %s at %s\n", argv[0], __DATE__, __TIME__); 134 | printf("Release = %s\n", VERSION); 135 | printf("Release comment = %s\n", VERSION_COMMENT); 136 | printf("Git HEAD = %s\n", _GIT_HEAD_COMMIT ) ; 137 | printf("Git Describe = %s\n", _GIT_DESCRIBE ) ; 138 | exit(0); 139 | case 'h': 140 | print_help(argv); 141 | exit(0); 142 | case 'v': 143 | attr.debug = attr.debug * 2 + 1; 144 | break; 145 | case 'c': 146 | attr.server = 0; 147 | attr.node = optarg; 148 | break; 149 | case 's': 150 | attr.server = 10; 151 | attr.node = "::"; 152 | break; 153 | case 'S': 154 | attr.server = 10; 155 | attr.node = optarg; 156 | break; 157 | case 'p': 158 | attr.port = optarg; 159 | break; 160 | default: 161 | ERROR_LOG("Failed to parse arguments"); 162 | print_help(argv); 163 | exit(EINVAL); 164 | } 165 | } 166 | 167 | if (attr.server == -1) { 168 | ERROR_LOG("must be either a client or a server!"); 169 | print_help(argv); 170 | exit(EINVAL); 171 | } 172 | 173 | TEST_Z(msk_init(&trans, &attr)); 174 | 175 | if (!trans) 176 | exit(-1); 177 | 178 | 179 | if (trans->server) { 180 | TEST_Z(msk_bind_server(trans)); 181 | TEST_NZ(trans = msk_accept_one(trans)); 182 | } else { //client 183 | TEST_Z(msk_connect(trans)); 184 | } 185 | 186 | TEST_NZ(rdmabuf = malloc((RECV_NUM+2)*CHUNK_SIZE*sizeof(char))); 187 | memset(rdmabuf, 0, (RECV_NUM+2)*CHUNK_SIZE*sizeof(char)); 188 | TEST_NZ(mr = msk_reg_mr(trans, rdmabuf, (RECV_NUM+2)*CHUNK_SIZE*sizeof(char), IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ)); 189 | 190 | 191 | 192 | TEST_NZ(ackdata = malloc(sizeof(msk_data_t))); 193 | ackdata->data = rdmabuf+(RECV_NUM+1)*CHUNK_SIZE*sizeof(char); 194 | ackdata->max_size = CHUNK_SIZE*sizeof(char); 195 | ackdata->size = 1; 196 | ackdata->data[0] = 0; 197 | 198 | 199 | pthread_mutex_init(&lock, NULL); 200 | pthread_cond_init(&cond, NULL); 201 | 202 | 203 | TEST_NZ(rdata = malloc(RECV_NUM*sizeof(msk_data_t))); 204 | TEST_NZ(datalock = malloc(RECV_NUM*sizeof(struct datalock))); 205 | 206 | for (i=0; i < RECV_NUM; i++) { 207 | rdata[i].data=rdmabuf+i*CHUNK_SIZE*sizeof(char); 208 | rdata[i].max_size=CHUNK_SIZE*sizeof(char); 209 | rdata[i].mr = mr; 210 | datalock[i].lock = &lock; 211 | datalock[i].cond = &cond; 212 | } 213 | 214 | pthread_mutex_lock(&lock); 215 | TEST_Z(msk_post_recv(trans, rdata, callback_recv, NULL, &(datalock[0]))); // post only one, others will be used for reads 216 | 217 | if (trans->server) { 218 | TEST_Z(msk_finalize_accept(trans)); 219 | } else { 220 | TEST_Z(msk_finalize_connect(trans)); 221 | } 222 | 223 | TEST_NZ(wdata = malloc(sizeof(msk_data_t))); 224 | wdata->data = rdmabuf+RECV_NUM*CHUNK_SIZE*sizeof(char); 225 | wdata->max_size = CHUNK_SIZE*sizeof(char); 226 | wdata->mr = mr; 227 | 228 | if (trans->server) { 229 | printf("wait for rloc\n"); 230 | TEST_Z(pthread_cond_wait(&cond, &lock)); // receive rloc 231 | 232 | TEST_NZ(rloc = malloc(sizeof(msk_rloc_t))); 233 | memcpy(rloc, rdata[0].data, sizeof(msk_rloc_t)); 234 | printf("got rloc! key: %u, addr: %"PRIu64", size: %d\n", 235 | rloc->rkey, rloc->raddr, rloc->size); 236 | 237 | count = 0; 238 | for (i=0; i < RECV_NUM; i++) { 239 | rdata[i].size=CHUNK_SIZE*sizeof(char); 240 | datalock[i].rloc = rloc; 241 | datalock[i].count = &count; 242 | TEST_Z(msk_post_RW(trans, rdata + i, rloc, callback_read, NULL, &(datalock[i]))); 243 | } 244 | 245 | while (count < SEND_COUNT) { 246 | pthread_cond_wait(&cond, &lock); 247 | if (count%100 == 0) 248 | printf("count: %d\n", count); 249 | } 250 | 251 | wdata->size = 1; 252 | TEST_Z(msk_post_send(trans, wdata, NULL, NULL, NULL)); // ack - other can quit 253 | usleep(10000); //FIXME: wait till last work request is done. cannot use wait_send because the other will get the send before we get our ack, so they might disconnect and our threads might fail before we get our WC that would unstuck us. 254 | 255 | free(rloc); 256 | } else { 257 | TEST_NZ(rloc = msk_make_rloc(mr, (uint64_t)(uintptr_t)ackdata->data, ackdata->max_size)); 258 | 259 | memcpy(wdata->data, rloc, sizeof(msk_rloc_t)); 260 | wdata->size = sizeof(msk_rloc_t); 261 | TEST_Z(msk_post_send(trans, wdata, NULL, NULL, NULL)); 262 | 263 | printf("sent rloc, waiting for server to say they're done\n"); 264 | TEST_Z(pthread_cond_wait(&cond, &lock)); // receive server ack (they wrote stuff) 265 | 266 | free(rloc); 267 | } 268 | pthread_mutex_unlock(&lock); 269 | 270 | msk_dereg_mr(mr); 271 | 272 | msk_destroy_trans(&trans); 273 | 274 | free(ackdata); 275 | free(rdata); 276 | free(wdata); 277 | free(datalock); 278 | free(rdmabuf); 279 | 280 | return 0; 281 | } 282 | -------------------------------------------------------------------------------- /src/tests/mooshika.sigmund: -------------------------------------------------------------------------------- 1 | SPEED[bench_rdma]=medium 2 | TEST_TYPE[bench_rdma]=dev,admin,prod 3 | NEEDS_ROOT[bench_rdma]=yes 4 | bench_rdma() { 5 | "$MSK_TEST_DIR/bench_rdma" -S localhost & 6 | SERVPID="$!" 7 | trap "kill $SERVPID" ERR 8 | sleep 0.2 9 | 10 | "$MSK_TEST_DIR/bench_rdma" -c localhost 11 | 12 | # get server exit status 13 | wait $SERVPID 14 | trap ERR 15 | } 16 | 17 | SPEED[multiple_sge]=medium 18 | TEST_TYPE[multiple_sge]=dev,admin,prod 19 | NEEDS_ROOT[multiple_sge]=yes 20 | multiple_sge() { 21 | "$MSK_TEST_DIR/multiple_sge" -S localhost & 22 | SERVPID="$!" 23 | trap "kill $SERVPID" ERR 24 | sleep 0.2 25 | 26 | "$MSK_TEST_DIR/multiple_sge" -c localhost 27 | 28 | # get server exit status 29 | wait $SERVPID 30 | trap ERR 31 | } 32 | 33 | SPEED[read_write]=medium 34 | TEST_TYPE[read_write]=dev,admin,prod 35 | NEEDS_ROOT[read_write]=yes 36 | read_write() { 37 | "$MSK_TEST_DIR/read_write" -S localhost & 38 | SERVPID="$!" 39 | trap "kill $SERVPID" ERR 40 | sleep 0.2 41 | 42 | "$MSK_TEST_DIR/read_write" -c localhost 43 | 44 | # get server exit status 45 | wait $SERVPID 46 | trap ERR 47 | } 48 | 49 | SPEED[rcat]=medium 50 | TEST_TYPE[rcat]=dev,admin,prod 51 | NEEDS_ROOT[rcat]=yes 52 | rcat() { 53 | 54 | # sleep needed because we want rcat to wait to get the other side's message 55 | # and not quit as soon as our own is sent. 56 | 57 | ( (echo foo; sleep 1) | "$MSK_TEST_DIR/../rcat" -S localhost > bar) & 58 | SERVPID="$!" 59 | trap "kill $SERVPID" ERR 60 | sleep 0.2 61 | 62 | FOO=$( (echo bar; sleep 1) | "$MSK_TEST_DIR/../rcat" -c localhost) 63 | 64 | # get server exit status 65 | wait $SERVPID 66 | trap ERR 67 | 68 | [[ "$FOO" = "foo" ]] || error "client didn't receive foo?" 69 | [[ $(cat bar) = "bar" ]] || error "server didn't receive bar?" 70 | } 71 | 72 | run_mooshika() { 73 | export MSK_TEST_DIR=$(dirname "$(readlink -m "$BASH_SOURCE")") 74 | run_test bench_rdma "bench rdma read or writes" 75 | run_test multiple_sge "test send with multiple sge" 76 | run_test read_write "test small rdma read and writes" 77 | run_test rcat "test bidirectional send/recv with rcat" 78 | } 79 | -------------------------------------------------------------------------------- /src/tests/multiple_sge.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright CEA/DAM/DIF (2012) 4 | * contributor : Dominique Martinet dominique.martinet@cea.fr 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * --------------------------------------- 21 | */ 22 | 23 | /** 24 | * \file multiple_sge.c 25 | * \brief tests operations with multiple send/gather elements 26 | * 27 | * tests operations with multiple send/gather elements 28 | * 29 | */ 30 | #ifdef HAVE_CONFIG_H 31 | #include "config.h" 32 | #endif 33 | 34 | #include 35 | #include 36 | #include //printf 37 | #include //malloc 38 | #include //memcpy 39 | #include //read 40 | #include 41 | #include 42 | #include 43 | 44 | #include "utils.h" 45 | #include "mooshika.h" 46 | 47 | #define CHUNK_SIZE 10 48 | #define RECV_NUM 1 49 | #define NUM_SGE 2 50 | 51 | struct datalock { 52 | pthread_mutex_t *lock; 53 | pthread_cond_t *cond; 54 | }; 55 | 56 | void callback_send(msk_trans_t *trans, msk_data_t *data, void *arg) { 57 | 58 | } 59 | 60 | void callback_disconnect(msk_trans_t *trans) { 61 | } 62 | 63 | void callback_recv(msk_trans_t *trans, msk_data_t *data, void *arg) { 64 | struct datalock *datalock = arg; 65 | 66 | pthread_mutex_lock(datalock->lock); 67 | pthread_cond_signal(datalock->cond); 68 | pthread_mutex_unlock(datalock->lock); 69 | } 70 | 71 | 72 | void print_help(char **argv) { 73 | printf("Usage: %s {-s|-c addr}\n", argv[0]); 74 | } 75 | 76 | int main(int argc, char **argv) { 77 | 78 | 79 | msk_trans_t *trans; 80 | uint8_t *mrbuf; 81 | struct ibv_mr *mr; 82 | 83 | msk_data_t *wdata; 84 | 85 | msk_trans_attr_t attr; 86 | 87 | memset(&attr, 0, sizeof(msk_trans_attr_t)); 88 | 89 | attr.server = -1; // put an incorrect value to check if we're either client or server 90 | // sane values for optional or non-configurable elements 91 | attr.rq_depth = RECV_NUM+2; 92 | attr.sq_depth = RECV_NUM+2; 93 | attr.max_recv_sge = NUM_SGE; 94 | attr.max_send_sge = NUM_SGE; 95 | attr.port = "1235"; 96 | // attr.disconnect_callback = callback_disconnect; 97 | 98 | // argument handling 99 | static struct option long_options[] = { 100 | { "client", required_argument, 0, 'c' }, 101 | { "port", required_argument, 0, 'p' }, 102 | { "server", no_argument, 0, 's' }, 103 | { "help", no_argument, 0, 'h' }, 104 | { 0, 0, 0, 0 } 105 | }; 106 | 107 | int option_index = 0; 108 | int op; 109 | while ((op = getopt_long(argc, argv, "@hvsS:c:p:", long_options, &option_index)) != -1) { /* */ 110 | switch(op) { 111 | case '@': 112 | printf("%s compiled on %s at %s\n", argv[0], __DATE__, __TIME__); 113 | printf("Release = %s\n", VERSION); 114 | printf("Release comment = %s\n", VERSION_COMMENT); 115 | printf("Git HEAD = %s\n", _GIT_HEAD_COMMIT ) ; 116 | printf("Git Describe = %s\n", _GIT_DESCRIBE ) ; 117 | exit(0); 118 | case 'h': 119 | print_help(argv); 120 | exit(0); 121 | case 'v': 122 | attr.debug = attr.debug * 2 + 1; 123 | break; 124 | case 'c': 125 | attr.server = 0; 126 | attr.node = optarg; 127 | break; 128 | case 's': 129 | attr.server = 10; 130 | attr.node = "::"; 131 | break; 132 | case 'S': 133 | attr.server = 10; 134 | attr.node = optarg; 135 | break; 136 | case 'p': 137 | attr.port = optarg; 138 | break; 139 | default: 140 | ERROR_LOG("Failed to parse arguments"); 141 | print_help(argv); 142 | exit(EINVAL); 143 | } 144 | } 145 | 146 | if (attr.server == -1) { 147 | ERROR_LOG("must be either a client or a server!"); 148 | print_help(argv); 149 | exit(EINVAL); 150 | } 151 | 152 | TEST_Z(msk_init(&trans, &attr)); 153 | 154 | if (!trans) 155 | exit(-1); 156 | 157 | 158 | 159 | if (trans->server) { 160 | TEST_Z(msk_bind_server(trans)); 161 | TEST_NZ(trans = msk_accept_one(trans)); 162 | 163 | } else { //client 164 | TEST_Z(msk_connect(trans)); 165 | TEST_NZ(trans); 166 | } 167 | 168 | 169 | TEST_NZ(mrbuf = malloc((RECV_NUM*NUM_SGE+1)*CHUNK_SIZE)); 170 | memset(mrbuf, 0, (RECV_NUM*NUM_SGE+1)*CHUNK_SIZE); 171 | TEST_NZ(mr = msk_reg_mr(trans, mrbuf, (RECV_NUM*NUM_SGE+1)*CHUNK_SIZE, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ)); 172 | 173 | 174 | 175 | pthread_mutex_t lock; 176 | pthread_cond_t cond; 177 | 178 | pthread_mutex_init(&lock, NULL); 179 | pthread_cond_init(&cond, NULL); 180 | 181 | msk_data_t *rdata; 182 | struct datalock datalock; 183 | 184 | TEST_NZ(rdata = malloc(RECV_NUM*NUM_SGE*sizeof(msk_data_t))); 185 | int i; 186 | for (i=0; idata = mrbuf+RECV_NUM*NUM_SGE*CHUNK_SIZE; 201 | wdata->mr = mr; 202 | wdata->max_size = CHUNK_SIZE; 203 | 204 | 205 | pthread_mutex_lock(&lock); 206 | if (trans->server) // server receives, client sends 207 | TEST_Z(msk_post_n_recv(trans, rdata, NUM_SGE, callback_recv, NULL, &datalock)); 208 | 209 | 210 | if (trans->server) { 211 | TEST_Z(msk_finalize_accept(trans)); 212 | } else { 213 | TEST_Z(msk_finalize_connect(trans)); 214 | } 215 | 216 | 217 | 218 | if (trans->server) { 219 | TEST_Z(pthread_cond_wait(&cond, &lock)); 220 | 221 | printf("Got something:\n %s (%d), %s (%d)\n", rdata[0].data, rdata[0].size, rdata[1].data, rdata[1].size); 222 | 223 | } else { 224 | 225 | memcpy(rdata[0].data, "012345678", 10); 226 | rdata[0].size = 10; 227 | memcpy(rdata[1].data, "0123456", 8); 228 | rdata[1].size = 8; 229 | 230 | TEST_Z(msk_post_n_send(trans, rdata, NUM_SGE, callback_recv, NULL, &datalock)); 231 | 232 | TEST_Z(pthread_cond_wait(&cond, &lock)); 233 | 234 | printf("Done with send\n"); 235 | } 236 | 237 | pthread_mutex_unlock(&lock); 238 | 239 | msk_dereg_mr(mr); 240 | 241 | msk_destroy_trans(&trans); 242 | 243 | free(rdata); 244 | free(wdata); 245 | free(mrbuf); 246 | 247 | return 0; 248 | } 249 | -------------------------------------------------------------------------------- /src/tests/multithread.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Usage: ./$0 on server, ./$0 serverip on client 4 | 5 | if [[ -z "$1" ]]; then 6 | ./rcat -s -m | pv -W -r > /dev/null 7 | else 8 | for i in `seq 1 5`; do 9 | (for j in `seq 1 20`; do 10 | dd if=/dev/zero bs=1M count=10 | ./rcat -c "$1" & 11 | done)& 12 | done 13 | fi 14 | -------------------------------------------------------------------------------- /src/tests/nfsv4_client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright CEA/DAM/DIF (2012) 4 | * contributor : Dominique Martinet dominique.martinet@cea.fr 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * --------------------------------------- 21 | */ 22 | 23 | /** 24 | * \file nfsv4_client.c 25 | * \brief to be removed 26 | * 27 | * to be removed 28 | * 29 | */ 30 | #ifdef HAVE_CONFIG_H 31 | #include "config.h" 32 | #endif 33 | 34 | #include 35 | #include 36 | #include //printf 37 | #include //malloc 38 | #include //memcpy 39 | #include //read 40 | #include 41 | #include 42 | #include 43 | #include // PRIu64 44 | 45 | #include "utils.h" 46 | #include "mooshika.h" 47 | 48 | #define CHUNK_SIZE 1024 49 | #define RECV_NUM 3 50 | #define NUM_SGE 4 51 | 52 | struct datalock { 53 | msk_data_t *ackdata; 54 | pthread_mutex_t *lock; 55 | pthread_cond_t *cond; 56 | }; 57 | 58 | void callback_send(msk_trans_t *trans, msk_data_t *data, void *arg) { 59 | 60 | } 61 | 62 | void callback_disconnect(msk_trans_t *trans) { 63 | } 64 | 65 | void callback_recv(msk_trans_t *trans, msk_data_t *data, void *arg) { 66 | struct datalock *datalock = arg; 67 | 68 | pthread_mutex_lock(datalock->lock); 69 | pthread_cond_signal(datalock->cond); 70 | pthread_mutex_unlock(datalock->lock); 71 | } 72 | 73 | void print_help(char **argv) { 74 | printf("Usage: %s {-s|-c addr}\n", argv[0]); 75 | } 76 | 77 | int main(int argc, char **argv) { 78 | 79 | 80 | msk_trans_t *trans; 81 | uint8_t *rdmabuf; 82 | struct ibv_mr *mr; 83 | 84 | msk_data_t *wdata; 85 | 86 | msk_trans_attr_t attr; 87 | 88 | memset(&attr, 0, sizeof(msk_trans_attr_t)); 89 | 90 | attr.server = -1; // put an incorrect value to check if we're either client or server 91 | // sane values for optional or non-configurable elements 92 | attr.rq_depth = RECV_NUM+2; 93 | attr.port = "1235"; 94 | // attr.disconnect_callback = callback_disconnect; 95 | 96 | // argument handling 97 | static struct option long_options[] = { 98 | { "client", required_argument, 0, 'c' }, 99 | { "server", required_argument, 0, 's' }, 100 | { "port", required_argument, 0, 'p' }, 101 | { "help", no_argument, 0, 'h' }, 102 | { 0, 0, 0, 0 } 103 | }; 104 | 105 | int option_index = 0; 106 | int op; 107 | while ((op = getopt_long(argc, argv, "@hvsS:c:p:", long_options, &option_index)) != -1) { 108 | switch(op) { 109 | case '@': 110 | printf("%s compiled on %s at %s\n", argv[0], __DATE__, __TIME__); 111 | printf("Release = %s\n", VERSION); 112 | printf("Release comment = %s\n", VERSION_COMMENT); 113 | printf("Git HEAD = %s\n", _GIT_HEAD_COMMIT ) ; 114 | printf("Git Describe = %s\n", _GIT_DESCRIBE ) ; 115 | exit(0); 116 | case 'h': 117 | print_help(argv); 118 | exit(0); 119 | case 'v': 120 | attr.debug = attr.debug * 2 + 1; 121 | break; 122 | case 'c': 123 | attr.server = 0; 124 | attr.node = optarg; 125 | break; 126 | case 's': 127 | attr.server = 10; 128 | attr.node = "::"; 129 | break; 130 | case 'S': 131 | attr.server = 10; 132 | attr.node = optarg; 133 | break; 134 | case 'p': 135 | attr.port = optarg; 136 | break; 137 | default: 138 | ERROR_LOG("Failed to parse arguments"); 139 | print_help(argv); 140 | exit(EINVAL); 141 | } 142 | } 143 | 144 | if (attr.server == -1) { 145 | ERROR_LOG("must be either a client or a server!"); 146 | print_help(argv); 147 | exit(EINVAL); 148 | } 149 | 150 | TEST_Z(msk_init(&trans, &attr)); 151 | 152 | if (!trans) 153 | exit(-1); 154 | 155 | 156 | if (trans->server) { 157 | TEST_Z(msk_bind_server(trans)); 158 | TEST_NZ(trans = msk_accept_one(trans)); 159 | 160 | } else { //client 161 | TEST_Z(msk_connect(trans)); 162 | TEST_NZ(trans); 163 | } 164 | 165 | TEST_NZ(rdmabuf = malloc((RECV_NUM+2)*CHUNK_SIZE*sizeof(char))); 166 | memset(rdmabuf, 0, (RECV_NUM+2)*CHUNK_SIZE*sizeof(char)); 167 | TEST_NZ(mr = msk_reg_mr(trans, rdmabuf, (RECV_NUM+2)*CHUNK_SIZE*sizeof(char), IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ)); 168 | 169 | 170 | 171 | msk_data_t *ackdata; 172 | TEST_NZ(ackdata = malloc(sizeof(msk_data_t))); 173 | ackdata->data = rdmabuf+(RECV_NUM+1)*CHUNK_SIZE*sizeof(char); 174 | ackdata->max_size = CHUNK_SIZE*sizeof(char); 175 | ackdata->size = 1; 176 | ackdata->data[0] = 0; 177 | 178 | pthread_mutex_t lock; 179 | pthread_cond_t cond; 180 | 181 | pthread_mutex_init(&lock, NULL); 182 | pthread_cond_init(&cond, NULL); 183 | 184 | msk_data_t *rdata; 185 | struct datalock datalock; 186 | 187 | TEST_NZ(rdata = malloc(sizeof(msk_data_t))); 188 | rdata->data=rdmabuf; //+i*CHUNK_SIZE*sizeof(char); 189 | rdata->max_size=CHUNK_SIZE*sizeof(char); 190 | rdata->mr = mr; 191 | datalock.ackdata = ackdata; 192 | datalock.lock = &lock; 193 | datalock.cond = &cond; 194 | 195 | pthread_mutex_lock(&lock); 196 | TEST_Z(msk_post_recv(trans, rdata, callback_recv, NULL, &datalock)); 197 | 198 | if (trans->server) { 199 | TEST_Z(msk_finalize_accept(trans)); 200 | } else { 201 | TEST_Z(msk_finalize_connect(trans)); 202 | } 203 | 204 | TEST_NZ(wdata = malloc(sizeof(msk_data_t))); 205 | wdata->data = rdmabuf+RECV_NUM*CHUNK_SIZE*sizeof(char); 206 | wdata->mr = mr; 207 | wdata->max_size = CHUNK_SIZE*sizeof(char); 208 | 209 | msk_rloc_t *rloc; 210 | 211 | if (trans->server) { 212 | printf("wait for rloc\n"); 213 | TEST_Z(pthread_cond_wait(&cond, &lock)); // receive rloc 214 | 215 | TEST_NZ(rloc = malloc(sizeof(msk_rloc_t))); 216 | memcpy(rloc, rdata->data, sizeof(msk_rloc_t)); 217 | printf("got rloc! key: %u, addr: %"PRIu64", size: %d\n", 218 | rloc->rkey, rloc->raddr, rloc->size); 219 | 220 | memcpy(wdata->data, "roses are red", 14); 221 | wdata->size = 14; 222 | 223 | TEST_Z(msk_post_write(trans, wdata, rloc, callback_recv, NULL, &datalock)); 224 | 225 | printf("waiting for write to finish\n"); 226 | TEST_Z(pthread_cond_wait(&cond, &lock)); // write done 227 | 228 | TEST_Z(msk_post_recv(trans, rdata, callback_recv, NULL, &datalock)); 229 | TEST_Z(msk_post_send(trans, wdata, NULL, NULL, NULL)); // ack to say we're done 230 | 231 | printf("waiting for something to be ready to read\n"); 232 | TEST_Z(pthread_cond_wait(&cond, &lock)); 233 | 234 | wdata->size=17; 235 | TEST_Z(msk_post_read(trans, wdata, rloc, callback_recv, NULL, &datalock)); 236 | 237 | printf("wait for read to finish\n"); 238 | TEST_Z(pthread_cond_wait(&cond, &lock)); 239 | 240 | printf("%s\n", wdata->data); 241 | 242 | TEST_Z(msk_wait_send(trans, wdata)); // ack - other can quit 243 | 244 | 245 | } else { 246 | TEST_NZ(rloc = msk_make_rloc(mr, (uint64_t)(uintptr_t)ackdata->data, ackdata->max_size)); 247 | 248 | memcpy(wdata->data, rloc, sizeof(msk_rloc_t)); 249 | wdata->size = sizeof(msk_rloc_t); 250 | TEST_Z(msk_post_send(trans, wdata, NULL, NULL, NULL)); 251 | 252 | printf("sent rloc, waiting for server to say they're done\n"); 253 | TEST_Z(pthread_cond_wait(&cond, &lock)); // receive server ack (they wrote stuff) 254 | 255 | printf("%s\n", ackdata->data); 256 | 257 | TEST_Z(msk_post_recv(trans, rdata, callback_recv, NULL, &datalock)); 258 | 259 | memcpy(ackdata->data, "violets are blue", 17); 260 | TEST_Z(msk_post_send(trans, wdata, NULL, NULL, NULL)); // say we've got something to read 261 | 262 | printf("waiting for server to be done\n"); 263 | TEST_Z(pthread_cond_wait(&cond, &lock)); 264 | 265 | } 266 | pthread_mutex_unlock(&lock); 267 | 268 | msk_dereg_mr(mr); 269 | 270 | msk_destroy_trans(&trans); 271 | msk_destroy_trans(&trans); // check that double_destroy works 272 | 273 | free(rloc); 274 | free(ackdata); 275 | free(rdata); 276 | free(wdata); 277 | free(rdmabuf); 278 | 279 | return 0; 280 | } 281 | -------------------------------------------------------------------------------- /src/tests/read_write.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright CEA/DAM/DIF (2012) 4 | * contributor : Dominique Martinet dominique.martinet@cea.fr 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * --------------------------------------- 21 | */ 22 | 23 | /** 24 | * \file read_write.c 25 | * \brief basic test to check that both read and write work. 26 | * 27 | * basic test to check that both read and write work. 28 | * 29 | */ 30 | #ifdef HAVE_CONFIG_H 31 | #include "config.h" 32 | #endif 33 | 34 | #include 35 | #include 36 | #include //printf 37 | #include //malloc 38 | #include //memcpy 39 | #include //read 40 | #include 41 | #include 42 | #include 43 | #include // PRIu64 44 | 45 | #include "utils.h" 46 | #include "mooshika.h" 47 | 48 | #define CHUNK_SIZE 1024 49 | #define RECV_NUM 3 50 | 51 | struct datalock { 52 | msk_data_t *ackdata; 53 | pthread_mutex_t *lock; 54 | pthread_cond_t *cond; 55 | }; 56 | 57 | void callback_send(msk_trans_t *trans, msk_data_t *data, void *arg) { 58 | 59 | } 60 | 61 | void callback_disconnect(msk_trans_t *trans) { 62 | } 63 | 64 | void callback_recv(msk_trans_t *trans, msk_data_t *data, void *arg) { 65 | struct datalock *datalock = arg; 66 | 67 | pthread_mutex_lock(datalock->lock); 68 | pthread_cond_signal(datalock->cond); 69 | pthread_mutex_unlock(datalock->lock); 70 | } 71 | 72 | void print_help(char **argv) { 73 | printf("Usage: %s {-s|-c addr}\n", argv[0]); 74 | } 75 | 76 | int main(int argc, char **argv) { 77 | 78 | 79 | msk_trans_t *trans; 80 | uint8_t *rdmabuf; 81 | struct ibv_mr *mr; 82 | 83 | msk_data_t *wdata; 84 | 85 | msk_trans_attr_t attr; 86 | 87 | memset(&attr, 0, sizeof(msk_trans_attr_t)); 88 | 89 | attr.server = -1; // put an incorrect value to check if we're either client or server 90 | // sane values for optional or non-configurable elements 91 | attr.rq_depth = RECV_NUM+2; 92 | attr.port = "1235"; 93 | // attr.disconnect_callback = callback_disconnect; 94 | 95 | // argument handling 96 | static struct option long_options[] = { 97 | { "client", required_argument, 0, 'c' }, 98 | { "server", required_argument, 0, 's' }, 99 | { "port", required_argument, 0, 'p' }, 100 | { "help", no_argument, 0, 'h' }, 101 | { 0, 0, 0, 0 } 102 | }; 103 | 104 | int option_index = 0; 105 | int op; 106 | while ((op = getopt_long(argc, argv, "@hvsS:c:p:", long_options, &option_index)) != -1) { 107 | switch(op) { 108 | case '@': 109 | printf("%s compiled on %s at %s\n", argv[0], __DATE__, __TIME__); 110 | printf("Release = %s\n", VERSION); 111 | printf("Release comment = %s\n", VERSION_COMMENT); 112 | printf("Git HEAD = %s\n", _GIT_HEAD_COMMIT ) ; 113 | printf("Git Describe = %s\n", _GIT_DESCRIBE ) ; 114 | exit(0); 115 | case 'h': 116 | print_help(argv); 117 | exit(0); 118 | case 'v': 119 | attr.debug = attr.debug * 2 + 1; 120 | break; 121 | case 'c': 122 | attr.server = 0; 123 | attr.node = optarg; 124 | break; 125 | case 's': 126 | attr.server = 10; 127 | attr.node = "::"; 128 | break; 129 | case 'S': 130 | attr.server = 10; 131 | attr.node = optarg; 132 | break; 133 | case 'p': 134 | attr.port = optarg; 135 | break; 136 | default: 137 | ERROR_LOG("Failed to parse arguments"); 138 | print_help(argv); 139 | exit(EINVAL); 140 | } 141 | } 142 | 143 | if (attr.server == -1) { 144 | ERROR_LOG("must be either a client or a server!"); 145 | print_help(argv); 146 | exit(EINVAL); 147 | } 148 | 149 | TEST_Z(msk_init(&trans, &attr)); 150 | 151 | if (!trans) 152 | exit(-1); 153 | 154 | 155 | if (trans->server) { 156 | TEST_Z(msk_bind_server(trans)); 157 | TEST_NZ(trans = msk_accept_one(trans)); 158 | 159 | } else { //client 160 | TEST_Z(msk_connect(trans)); 161 | TEST_NZ(trans); 162 | } 163 | 164 | TEST_NZ(rdmabuf = malloc((RECV_NUM+2)*CHUNK_SIZE*sizeof(char))); 165 | memset(rdmabuf, 0, (RECV_NUM+2)*CHUNK_SIZE*sizeof(char)); 166 | TEST_NZ(mr = msk_reg_mr(trans, rdmabuf, (RECV_NUM+2)*CHUNK_SIZE*sizeof(char), IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ)); 167 | 168 | 169 | 170 | msk_data_t *ackdata; 171 | TEST_NZ(ackdata = malloc(sizeof(msk_data_t))); 172 | ackdata->data = rdmabuf+(RECV_NUM+1)*CHUNK_SIZE*sizeof(char); 173 | ackdata->max_size = CHUNK_SIZE*sizeof(char); 174 | ackdata->size = 1; 175 | ackdata->mr = mr; 176 | ackdata->data[0] = 0; 177 | 178 | pthread_mutex_t lock; 179 | pthread_cond_t cond; 180 | 181 | pthread_mutex_init(&lock, NULL); 182 | pthread_cond_init(&cond, NULL); 183 | 184 | msk_data_t *rdata; 185 | struct datalock datalock; 186 | 187 | TEST_NZ(rdata = malloc(sizeof(msk_data_t))); 188 | rdata->data=rdmabuf; //+i*CHUNK_SIZE*sizeof(char); 189 | rdata->max_size=CHUNK_SIZE*sizeof(char); 190 | rdata->mr = mr; 191 | datalock.ackdata = ackdata; 192 | datalock.lock = &lock; 193 | datalock.cond = &cond; 194 | 195 | pthread_mutex_lock(&lock); 196 | TEST_Z(msk_post_recv(trans, rdata, callback_recv, NULL, &datalock)); 197 | 198 | if (trans->server) { 199 | TEST_Z(msk_finalize_accept(trans)); 200 | } else { 201 | TEST_Z(msk_finalize_connect(trans)); 202 | } 203 | 204 | TEST_NZ(wdata = malloc(sizeof(msk_data_t))); 205 | wdata->data = rdmabuf+RECV_NUM*CHUNK_SIZE*sizeof(char); 206 | wdata->mr = mr; 207 | wdata->max_size = CHUNK_SIZE*sizeof(char); 208 | 209 | msk_rloc_t *rloc; 210 | 211 | if (trans->server) { 212 | printf("wait for rloc\n"); 213 | TEST_Z(pthread_cond_wait(&cond, &lock)); // receive rloc 214 | 215 | TEST_NZ(rloc = malloc(sizeof(msk_rloc_t))); 216 | memcpy(rloc, rdata->data, sizeof(msk_rloc_t)); 217 | printf("got rloc! key: %u, addr: %"PRIu64", size: %d\n", 218 | rloc->rkey, rloc->raddr, rloc->size); 219 | 220 | memcpy(wdata->data, "roses are red", 14); 221 | wdata->size = 14; 222 | 223 | TEST_Z(msk_post_write(trans, wdata, rloc, callback_recv, NULL, &datalock)); 224 | 225 | printf("waiting for write to finish\n"); 226 | TEST_Z(pthread_cond_wait(&cond, &lock)); // write done 227 | 228 | TEST_Z(msk_post_recv(trans, rdata, callback_recv, NULL, &datalock)); 229 | TEST_Z(msk_post_send(trans, wdata, NULL, NULL, NULL)); // ack to say we're done 230 | 231 | printf("waiting for something to be ready to read\n"); 232 | TEST_Z(pthread_cond_wait(&cond, &lock)); 233 | 234 | wdata->size=17; 235 | TEST_Z(msk_post_read(trans, wdata, rloc, callback_recv, NULL, &datalock)); 236 | 237 | printf("wait for read to finish\n"); 238 | TEST_Z(pthread_cond_wait(&cond, &lock)); 239 | 240 | printf("%s\n", wdata->data); 241 | 242 | TEST_Z(msk_wait_send(trans, wdata)); // ack - other can quit 243 | 244 | } else { 245 | TEST_NZ(rloc = msk_make_rloc(mr, (uint64_t)(uintptr_t)ackdata->data, ackdata->max_size)); 246 | 247 | memcpy(wdata->data, rloc, sizeof(msk_rloc_t)); 248 | wdata->size = sizeof(msk_rloc_t); 249 | TEST_Z(msk_post_send(trans, wdata, NULL, NULL, NULL)); 250 | 251 | printf("sent rloc, waiting for server to say they're done\n"); 252 | TEST_Z(pthread_cond_wait(&cond, &lock)); // receive server ack (they wrote stuff) 253 | 254 | printf("%s\n", ackdata->data); 255 | 256 | TEST_Z(msk_post_recv(trans, rdata, callback_recv, NULL, &datalock)); 257 | 258 | memcpy(ackdata->data, "violets are blue", 17); 259 | TEST_Z(msk_post_send(trans, wdata, NULL, NULL, NULL)); // say we've got something to read 260 | 261 | printf("waiting for server to be done\n"); 262 | TEST_Z(pthread_cond_wait(&cond, &lock)); 263 | 264 | } 265 | pthread_mutex_unlock(&lock); 266 | 267 | msk_dereg_mr(mr); 268 | 269 | msk_destroy_trans(&trans); 270 | msk_destroy_trans(&trans); // check that double_destroy works 271 | 272 | free(rloc); 273 | free(ackdata); 274 | free(rdata); 275 | free(wdata); 276 | free(rdmabuf); 277 | 278 | return 0; 279 | } 280 | -------------------------------------------------------------------------------- /src/tests/run_tests.sh: -------------------------------------------------------------------------------- 1 | 2 | # $1 = display string, $2-rest = program 3 | runit() { 4 | local STR=$1 5 | shift 6 | eval $@ >& /dev/null 7 | printf "%s status: %d\n" "$STR" $? 8 | } 9 | 10 | IP="$1" 11 | 12 | for prog in ./bench_rdma ./multiple_sge ./read_write; do 13 | echo "Starting server $prog -S \"$IP\"" 14 | runit "Server" $prog -S "$IP" & 15 | sleep 0.2 16 | 17 | echo "Starting client: $prog -c \"$IP\"" 18 | runit "Client" $prog -c "$IP" 19 | done 20 | -------------------------------------------------------------------------------- /src/tests/urandom-md5.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Usage: ./$0 on server, ./$0 serverip on client 4 | 5 | if [[ -z "$1" ]]; then 6 | ./rcat -s | md5sum - 7 | else 8 | dd if=/dev/urandom bs=10M count=1 > /tmp/urandom 9 | (for i in `seq 1 10`; do cat /tmp/urandom; done)| tee >(md5sum - 1>&2) | ./rcat -c "$1" 10 | fi 11 | 12 | -------------------------------------------------------------------------------- /src/tools/.gitignore: -------------------------------------------------------------------------------- 1 | pktdump 2 | -------------------------------------------------------------------------------- /src/tools/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = -g -D_REENTRANT @WARNINGS_CFLAGS@ -I$(srcdir)/../../include 2 | 3 | noinst_PROGRAMS = 4 | if ENABLE_RMITM 5 | noinst_PROGRAMS += pktdump 6 | endif 7 | 8 | pktdump_SOURCES = pktdump.c 9 | pktdump_LDADD = -lpcap 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/tools/pktdump.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright CEA/DAM/DIF (2012) 4 | * contributor : Dominique Martinet dominique.martinet@cea.fr 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * --------------------------------------- 21 | */ 22 | 23 | /** 24 | * \file rmitm.c 25 | * \brief Example of usage/man in the middle for rdma send 26 | * 27 | * Example of usage/man in the middle for rdma send 28 | * 29 | */ 30 | #ifdef HAVE_CONFIG_H 31 | #include "config.h" 32 | #endif 33 | 34 | #include 35 | #include 36 | #include //printf 37 | #include //malloc 38 | #include //memcpy 39 | #include //read 40 | #include //gethostbyname 41 | #include 42 | #include 43 | #include 44 | #include //open 45 | #include //open 46 | #include //open 47 | #include 48 | #include // PRIu64 49 | 50 | #include 51 | #include 52 | 53 | #include "mooshika.h" 54 | #include "../utils.h" 55 | #include "../rmitm.h" 56 | 57 | #define PACKET_SIZE (64*1024-1) 58 | 59 | struct thread_arg { 60 | char *pcap_filename; 61 | }; 62 | 63 | static void print_help(char **argv) { 64 | printf("Usage: %s -s port -c addr port [-f pcap.out]\n", argv[0]); 65 | printf("Mandatory arguments (these two don't work atm - assumes localhost:2049):\n" 66 | " -c, --client addr port: \n" 67 | " -s, --server addr port: \n" 68 | "Optional arguments:\n" 69 | " -f, --file pcap.out: output file (defaults to stdout)\n" 70 | " -r, --raw: assume input is binary, not hex\n" 71 | " -v, --verbose: verbose, more v for more verbosity\n" 72 | " -q, --quiet: quiet output\n" 73 | " --not-an-option-yet: disable appending the tcp rpc header...\n"); 74 | 75 | } 76 | 77 | int main(int argc, char **argv) { 78 | msk_trans_attr_t s_attr; 79 | msk_trans_attr_t c_attr; 80 | int hex; 81 | 82 | pcap_t *pcap; 83 | pcap_dumper_t *pcap_dumper; 84 | struct pcap_pkthdr pcaphdr; 85 | char *data; 86 | TEST_NZ(data = malloc(PACKET_SIZE)); 87 | struct pkt_hdr *pkt_hdr = (struct pkt_hdr *)data; 88 | uint32_t *rpchdr = (uint32_t*)(data + PACKET_HDR_LEN); 89 | char *buffer = data + PACKET_HDR_LEN + 4; 90 | int nread; 91 | 92 | // argument handling 93 | struct thread_arg thread_arg; 94 | int option_index = 0; 95 | int op, last_op; 96 | static struct option long_options[] = { 97 | { "client", required_argument, 0, 'c' }, 98 | { "server", required_argument, 0, 's' }, 99 | { "help", no_argument, 0, 'h' }, 100 | { "verbose", no_argument, 0, 'v' }, 101 | { "quiet", no_argument, 0, 'q' }, 102 | { "file", required_argument, 0, 'f' }, 103 | { "raw", no_argument, 0, 'r' }, 104 | { 0, 0, 0, 0 } 105 | }; 106 | 107 | 108 | memset(&s_attr, 0, sizeof(msk_trans_attr_t)); 109 | memset(&c_attr, 0, sizeof(msk_trans_attr_t)); 110 | memset(&thread_arg, 0, sizeof(thread_arg)); 111 | 112 | s_attr.node = NULL; 113 | s_attr.port = NULL; 114 | c_attr.node = NULL; 115 | c_attr.port = NULL; 116 | c_attr.debug = 1; 117 | hex = 1; 118 | thread_arg.pcap_filename = "-"; 119 | 120 | last_op = 0; 121 | while ((op = getopt_long(argc, argv, "-@hvqs:c:w:f:r", long_options, &option_index)) != -1) { 122 | switch(op) { 123 | case 1: // this means double argument 124 | if (last_op == 'c') { 125 | c_attr.port = optarg; 126 | } else if (last_op == 's') { 127 | s_attr.port = optarg; 128 | } else { 129 | ERROR_LOG("Failed to parse arguments"); 130 | print_help(argv); 131 | exit(EINVAL); 132 | } 133 | break; 134 | case '@': 135 | printf("%s compiled on %s at %s\n", argv[0], __DATE__, __TIME__); 136 | printf("Release = %s\n", VERSION); 137 | printf("Release comment = %s\n", VERSION_COMMENT); 138 | printf("Git HEAD = %s\n", _GIT_HEAD_COMMIT ) ; 139 | printf("Git Describe = %s\n", _GIT_DESCRIBE ) ; 140 | exit(0); 141 | case 'h': 142 | print_help(argv); 143 | exit(0); 144 | case 'v': 145 | c_attr.debug = c_attr.debug * 2 + 1; 146 | s_attr.debug = c_attr.debug; 147 | break; 148 | case 'q': 149 | c_attr.debug = 0; 150 | s_attr.debug = 0; 151 | break; 152 | case 'c': 153 | c_attr.node = optarg; 154 | break; 155 | case 's': 156 | s_attr.node = "::"; 157 | s_attr.port = optarg; 158 | break; 159 | case 'S': 160 | s_attr.node = optarg; 161 | break; 162 | case 'w': 163 | ERROR_LOG("-w has become deprecated, use -f or --file now. Proceeding anyway"); 164 | /* fallthrough */ 165 | case 'f': 166 | thread_arg.pcap_filename = optarg; 167 | break; 168 | case 'r': 169 | hex = 0; 170 | break; 171 | default: 172 | ERROR_LOG("Failed to parse arguments"); 173 | print_help(argv); 174 | exit(EINVAL); 175 | } 176 | last_op = op; 177 | } 178 | 179 | if (c_attr.server == -1 || s_attr.server == -1) { 180 | ERROR_LOG("must have both client and server!"); 181 | print_help(argv); 182 | exit(EINVAL); 183 | } 184 | 185 | 186 | pcap = pcap_open_dead(DLT_RAW, PACKET_SIZE); 187 | TEST_NZ(pcap_dumper = pcap_dump_open(pcap, thread_arg.pcap_filename)); 188 | 189 | memset(pkt_hdr, 0, sizeof(*pkt_hdr)); 190 | 191 | pkt_hdr->ipv6.ip_flags[0] = 0x60; /* 6 in the leftmosts 4 bits */ 192 | pkt_hdr->ipv6.ip_nh = IPPROTO_TCP; 193 | pkt_hdr->ipv6.ip_hl = 1; 194 | /** @todo: add options, use one of : 195 | CLIENT 196 | child_trans->cm_id->route.addr.dst_sin 197 | child_trans->cm_id->route.addr.src_sin 198 | RMITM 199 | c_trans->cm_id->route.addr.src_sin 200 | c_trans->cm_id->route.addr.dst_sin 201 | SERVER 202 | */ 203 | pkt_hdr->ipv6.ip_src.s6_addr16[4] = 0; /*0xffff; */ 204 | pkt_hdr->ipv6.ip_src.s6_addr32[3] = ntohl(1); /* get ipv4 here */ 205 | pkt_hdr->tcp.th_sport = ntohs(2049); 206 | 207 | pkt_hdr->ipv6.ip_dst.s6_addr16[4] = 0; /* 0xffff; */ 208 | pkt_hdr->ipv6.ip_dst.s6_addr32[3] = ntohl(1); /* get ipv4 here */ 209 | pkt_hdr->tcp.th_dport = ntohs(2049); 210 | 211 | pkt_hdr->tcp.th_data_off = INT8_C(sizeof(struct tcp_hdr) * 4); /* *4 because words of 2 bits? it's odd. */ 212 | pkt_hdr->tcp.th_window = htons(100); 213 | /* assume stream - nothing to ack 214 | * pkt_hdr->tcp.th_flags = THF_ACK; 215 | * pkt_hdr->tcp.th_ack_nr = 0; 216 | */ 217 | 218 | while (!feof(stdin)) { 219 | if (!hex) { 220 | nread = fread(buffer, 1, PACKET_SIZE - PACKET_HDR_LEN - 4, stdin); 221 | if (nread == 0) 222 | break; 223 | } else { 224 | 225 | /* fugly code to parse hexa dump, allowing truncated input. 226 | * valid format are xxd or hexdump (with or without -C) 227 | * should be groups of 1 or 2 bytes */ 228 | char line[80], *cur; 229 | int no_double_space; 230 | 231 | buffer = data + PACKET_HDR_LEN + 4; 232 | /* stop on eof or empty line */ 233 | while (fgets(line, 80, stdin) && strcmp(line, "\n") != 0) { 234 | /* skip first word iff longer than 4 chars */ 235 | cur = strchr(line, ' '); 236 | 237 | if (!cur) 238 | break; /* not a valid line?! */ 239 | 240 | if (cur - line > 4) { 241 | while (cur[0] == ' ') 242 | cur += 1; 243 | } else 244 | cur = line; 245 | 246 | /* check if we should allow double-spaces */ 247 | if (cur[2] == ' ' && cur[5] == ' ') 248 | no_double_space = 0; 249 | else 250 | no_double_space = 1; 251 | 252 | while ((nread = sscanf(cur, "%2hhx%[^|\n]", buffer, cur)) > 0) { 253 | buffer++; 254 | 255 | if (nread != 2) 256 | break; 257 | 258 | if (no_double_space && cur[0] == ' ' && cur[1] == ' ') 259 | break; 260 | } 261 | } 262 | nread = buffer - (data + PACKET_HDR_LEN + 4); 263 | buffer[0] = '\0'; 264 | buffer = data + PACKET_HDR_LEN + 4; 265 | } 266 | 267 | *rpchdr = htonl(0x80000000 + nread); 268 | gettimeofday(&pcaphdr.ts, NULL); 269 | pcaphdr.len = min(nread + PACKET_HDR_LEN + 4, PACKET_SIZE); 270 | pcaphdr.caplen = pcaphdr.len; 271 | 272 | 273 | pkt_hdr->ipv6.ip_len = htons(nread + 4 + sizeof(struct tcp_hdr)); 274 | 275 | ipv6_tcp_checksum(pkt_hdr); 276 | 277 | pcap_dump((u_char*)pcap_dumper, &pcaphdr, (u_char*)pkt_hdr); 278 | 279 | pkt_hdr->tcp.th_seq_nr = htonl(ntohl(pkt_hdr->tcp.th_seq_nr) + ntohs(pkt_hdr->ipv6.ip_len) - sizeof(struct tcp_hdr)); 280 | 281 | pcap_dump_flush(pcap_dumper); 282 | } 283 | 284 | pcap_dump_close(pcap_dumper); 285 | 286 | pcap_close(pcap); 287 | 288 | return 0; 289 | } 290 | 291 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #define ERROR_LOG(fmt, args...) fprintf(stderr, "ERROR: %s (%d), %s: " fmt "\n", __FILE__, __LINE__, __func__, ##args) 2 | //#define ERROR_LOG(fmt, args...) 3 | #define INFO_LOG(debug, fmt, args...) if (debug) fprintf(stderr, "INFO: %s (%d), %s: " fmt "\n", __FILE__, __LINE__, __func__, ##args) 4 | //#define INFO_LOG(fmt, args...) 5 | 6 | #define TEST_Z(x) do { int retval; if ( (retval=x)) { ERROR_LOG("error: " #x " failed (returned %d, errno %d).", retval, errno ); exit(retval); } } while (0) 7 | #define TEST_NZ(x) do { if (!(x)) { ERROR_LOG("error: " #x " failed (returned zero/null. errno=%d).", errno); exit(-1); }} while (0) 8 | 9 | #include "atomics.h" 10 | 11 | #define set_size(val, unit) do { \ 12 | switch(unit[0]) { \ 13 | case 'k': \ 14 | case 'K': \ 15 | val *= 1024; \ 16 | break; \ 17 | case 'm': \ 18 | case 'M': \ 19 | val *= 1024 * 1024; \ 20 | break; \ 21 | case 'g': \ 22 | case 'G': \ 23 | val *= 1024 * 1024 * 1024; \ 24 | break; \ 25 | default: \ 26 | ERROR_LOG("unknown unit '%c'", unit[0]); \ 27 | val = 0; \ 28 | } \ 29 | } while (0) 30 | 31 | 32 | #define NSEC_IN_SEC 1000000000 33 | 34 | static inline void sub_timespec(uint64_t *new, struct timespec *x, struct timespec *y) { 35 | if (y->tv_nsec < x->tv_nsec) { 36 | *new = (y->tv_sec - x->tv_sec - 1) * NSEC_IN_SEC + 37 | y->tv_nsec + NSEC_IN_SEC - x->tv_nsec; 38 | } else { 39 | *new = (y->tv_sec - x->tv_sec) * NSEC_IN_SEC + 40 | y->tv_nsec - x->tv_nsec; 41 | } 42 | } 43 | 44 | #define MSK_MAX_RESVPORT 1023 45 | #define MSK_MIN_RESVPORT 512 46 | --------------------------------------------------------------------------------