├── .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 |
--------------------------------------------------------------------------------