|
| 1 | +# Time Sensitive System Service Prototype (detd) |
| 2 | + |
| 3 | +A proof-of-concept for a developer-friendly system service to handle time-sensitive applications. |
| 4 | + |
| 5 | + |
| 6 | +## Overview |
| 7 | + |
| 8 | +### In a nutshell |
| 9 | + |
| 10 | +This is a prototype of a system service to handle time-sensitive applications |
| 11 | +* **Goal** |
| 12 | + * Prototype the interface between time-sensitive applications and the underlying system facilities |
| 13 | +* **Non-goal** |
| 14 | + * Provide a production-grade, standard system service for that functionality |
| 15 | + |
| 16 | + |
| 17 | +### Principles |
| 18 | + |
| 19 | + * Developer friendliness |
| 20 | + * Serve as enabling vehicle for developer ecosystem discussions |
| 21 | + * Hide implementation specific aspects through a high-level abstraction easy to understand for software developers |
| 22 | + * Decouple time-sensitive applications from platform or device specific aspects |
| 23 | + |
| 24 | + * Flexibility for experimentation |
| 25 | + * Implemented in Python. Integration with other languages via protobuf messages |
| 26 | + * Provide a clear separation of concerns and being easily extensible |
| 27 | + * Include a unit test and integration test suite |
| 28 | + * Allow for testing on host (i.e. without actual TSN hardware) and target environments |
| 29 | + |
| 30 | + |
| 31 | +### Current functionality |
| 32 | + |
| 33 | +The following snippet illustrates the add_talker interface: |
| 34 | + |
| 35 | +```python |
| 36 | +interface_name = "eth0" |
| 37 | +interval = 2 * 1000 * 1000 # ns for 2 ms |
| 38 | +size = 1522 # Bytes |
| 39 | + |
| 40 | +txoffset = 250 * 1000 # ns for 250 us |
| 41 | +addr = "7a:b9:ed:d6:d2:12" |
| 42 | +vid = 3 |
| 43 | +pcp = 6 |
| 44 | + |
| 45 | + |
| 46 | +interface = Interface(interface_name) |
| 47 | +traffic = TrafficSpecification(interval, size) |
| 48 | +stream = StreamConfiguration(addr, vid, pcp, txoffset) |
| 49 | + |
| 50 | +config = Configuration(interface, stream, traffic) |
| 51 | + |
| 52 | + |
| 53 | +manager = Manager() |
| 54 | +manager.add_talker(config) |
| 55 | +``` |
| 56 | + |
| 57 | +In a little bit more detail: |
| 58 | + |
| 59 | +1. **Takes stream configuration and traffic specification as an input** |
| 60 | + * Interface to use |
| 61 | + * Stream configuration: txoffset, DMAC, VID,PCP |
| 62 | + * Traffic specification: Interval in nanoseconds, Bytes to transmit |
| 63 | +2. **Integrates all the inputs and generates the gating schedule** |
| 64 | + * It is able to generate very complex taprio configurations with just the stream and traffic information |
| 65 | +3. **Generates the commands implementing that configuration** |
| 66 | + * Intel Elkhart Lake integrated TSN interface (mGBE) supported |
| 67 | + * taprio offload mode supported |
| 68 | +4. **Applies the configuration by running the commands** |
| 69 | + * When one of the commands fail, it is able to roll back the previous one to leave the system in a well-known state. |
| 70 | +5. **Provides the stream handlers to the caller application** |
| 71 | + * The basic implementation provides the VLAN interface name (e.g. eth0.3) and the socket prio to use to the calling application |
| 72 | + * The plan is to return a fully configured socket. The included client-server example is already implementing part of that flow. |
| 73 | + |
| 74 | + |
| 75 | +### Current Limitations |
| 76 | + |
| 77 | +See [Contributing](README.md#contributing) :) |
| 78 | + |
| 79 | +* Current suppport for Intel mGBE only |
| 80 | + * Code conceived to support additional devices |
| 81 | +* Current support for Linux only |
| 82 | +* Current support for talker streams only |
| 83 | +* Current taprio support restricted to offload mode |
| 84 | + * Code ready to support additional qdiscs |
| 85 | +* Launch-Time Control not supported |
| 86 | + * E.g. etf qdisc |
| 87 | +* Current support for AF_PACKET only |
| 88 | +* Basic local schedule calculation |
| 89 | + * E.g. inter-packet gap not considered |
| 90 | + |
| 91 | + |
| 92 | +## Examples |
| 93 | + |
| 94 | +### Setup a talker stream using detd functions from python |
| 95 | + |
| 96 | +This example is not setting up or connecting to the daemon. It is intended to show the functions called by the daemon to set up a talker. |
| 97 | + |
| 98 | +The following code: |
| 99 | +```python |
| 100 | +interface_name = "eth0" |
| 101 | +interval = 20 * 1000 * 1000 # ns for 20 ms |
| 102 | +size = 1522 # Bytes |
| 103 | + |
| 104 | +txoffset = 250 * 1000 # ns for 250 us |
| 105 | +addr = "7a:b9:ed:d6:d2:12" |
| 106 | +vid = 3 |
| 107 | +pcp = 6 |
| 108 | + |
| 109 | + |
| 110 | +interface = Interface(interface_name) |
| 111 | +traffic = TrafficSpecification(interval, size) |
| 112 | +stream = StreamConfiguration(addr, vid, pcp, txoffset) |
| 113 | + |
| 114 | +config = Configuration(interface, stream, traffic) |
| 115 | + |
| 116 | + |
| 117 | +manager = Manager() |
| 118 | +manager.add_talker(config) |
| 119 | +``` |
| 120 | + |
| 121 | +Will first generate all the required commands, like e.g.: |
| 122 | +``` |
| 123 | +tc qdisc replace |
| 124 | + dev eth0 |
| 125 | + parent root |
| 126 | + taprio |
| 127 | + num_tc 2 |
| 128 | + map 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 |
| 129 | + queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 |
| 130 | + base-time <now + 2 cycles> |
| 131 | + sched-entry S 01 250000 |
| 132 | + sched-entry S 02 12176 |
| 133 | + sched-entry S 01 19737824 |
| 134 | + flags 0x2 |
| 135 | +
|
| 136 | +
|
| 137 | +ip link add |
| 138 | + link eth0 |
| 139 | + name eth0.3 |
| 140 | + type vlan |
| 141 | + protocol 802.1Q |
| 142 | + id 3 |
| 143 | + egress 0:0 1:1 2:2 3:3 4:4 5:5 6:7 7:7 |
| 144 | +
|
| 145 | +
|
| 146 | +ethtool --set-eee eth0 eee off |
| 147 | +``` |
| 148 | + |
| 149 | +To finally apply them. |
| 150 | + |
| 151 | + |
| 152 | + |
| 153 | + |
| 154 | + |
| 155 | + |
| 156 | +### Setup a talker stream for an arbitrary command, using a script that calls detd functions |
| 157 | + |
| 158 | +The script [setup_qos.sh](./setup_qos.sh) allows for quick experimentation without modifying an existing application. |
| 159 | + |
| 160 | +It performs the configuration using detd commands, as in the first example. |
| 161 | + |
| 162 | +Then it uses the cgroup net_prio to automatically map the egress traffic from the command to a given socket prio. So all the queuing disciplines and PCP mapping in place works properly. |
| 163 | + |
| 164 | +Example: |
| 165 | +```bash |
| 166 | +# 2ms period, 1522 bytes, TxOffset 250 microseconds, interface eth0 |
| 167 | +# stream DMAC AB:CD:EF:FE:DC:BA, stream VID 3, stream PCP 6 |
| 168 | +# Command: ping 8.8.8.8 for one second (Busybox's ping) |
| 169 | + |
| 170 | +./setup_qos.sh --period 2000000 --bytes 1522 --offset 250000 --interface eth0 \ |
| 171 | + --address AB:CD:EF:FE:DC:BA --vid 3 --pcp 6 \ |
| 172 | + -- ping -w 1 8.8.8.8 |
| 173 | + |
| 174 | +``` |
| 175 | + |
| 176 | + |
| 177 | +### Setup a talker stream through a system-wide service (without pre-configured socket) |
| 178 | + |
| 179 | +Please refer to [tests/test_service.py](tests/test_service.py) for a working test case showcasing this functionality. The relevant snippets are displayed below. |
| 180 | + |
| 181 | +Spawn the server and wait for the Unix-Domain socket to be available: |
| 182 | +```python |
| 183 | +def run_server(): |
| 184 | + with Service() as srv: |
| 185 | + srv.run() |
| 186 | + |
| 187 | +def setup_server(): |
| 188 | + uds_address = '/tmp/uds_detd_server.sock' |
| 189 | + server = multiprocessing.Process(target=run_server) |
| 190 | + server.start() |
| 191 | + while not os.path.exists(uds_address): |
| 192 | + time.sleep(0.2) |
| 193 | + |
| 194 | + return server |
| 195 | +``` |
| 196 | + |
| 197 | +Initialize the interface, stream and traffic objects to be requested. |
| 198 | +```python |
| 199 | +def setup_configuration(): |
| 200 | + |
| 201 | + interface_name = "eth0" |
| 202 | + interval = 20 * 1000 * 1000 # ns |
| 203 | + size = 1522 # Bytes |
| 204 | + |
| 205 | + txoffset = 250 * 1000 # ns |
| 206 | + addr = "03:C0:FF:EE:FF" |
| 207 | + vid = 3 |
| 208 | + pcp = 6 |
| 209 | + stream = StreamConfiguration(addr, vid, pcp, txoffset) |
| 210 | + traffic = TrafficSpecification(interval, size) |
| 211 | + |
| 212 | + return interface_name, stream, traffic |
| 213 | +``` |
| 214 | + |
| 215 | +Create a proxy object and get the configuration: |
| 216 | +```python |
| 217 | +# Both Server() and ServiceProxy() point to the same UDS by default |
| 218 | +proxy = ServiceProxy() |
| 219 | +vlan_interface, soprio = proxy.setup_talker(interface_name, stream, traffic) |
| 220 | +``` |
| 221 | + |
| 222 | +## Regression testing |
| 223 | + |
| 224 | + |
| 225 | +### Test environments |
| 226 | + |
| 227 | +By default, calls to system commands are mocked, so almost every code path can be exercised without TSN target hardware. |
| 228 | + |
| 229 | +The test environment is controlled by the following environment variable. |
| 230 | +```bash |
| 231 | +DETD_TESTENV |
| 232 | +``` |
| 233 | + |
| 234 | +Host testing is configured by default inside the test suite. |
| 235 | + |
| 236 | +To override the default and setup target testing (e.g. calling actual commands): |
| 237 | +```bash |
| 238 | +DETD_TESTENV=TARGET |
| 239 | +``` |
| 240 | + |
| 241 | +For integration testing, the service is spawned as a different process. This prevents regular use of patch. Hence, the Server class accepts a parameter test_mode, that when set to True will patch the required methods in order to maximize coverage. This is automatically performed when running the unittests. |
| 242 | + |
| 243 | + |
| 244 | +### Test the example "Setup a talker stream using detd functions from python" |
| 245 | + |
| 246 | +* With host test environment |
| 247 | + * Intended to be use during development to test most of the code paths on the development host |
| 248 | + * Does not require actual TSN hardware or the availability of the underlying system commands |
| 249 | + ```bash |
| 250 | + python3 -m unittest tests.test_manager |
| 251 | + ``` |
| 252 | + |
| 253 | +* With target test environment |
| 254 | + * Actual commands are issued to the OS |
| 255 | + * Requires real hardware and system environment |
| 256 | + ```bash |
| 257 | + DETD_TESTENV=TARGET python3 -m unittest tests.test_manager |
| 258 | + ``` |
| 259 | + |
| 260 | +### Run the complete test suite |
| 261 | + |
| 262 | +Integration tests include a client-server example in python. |
| 263 | + |
| 264 | +Run all unit and integration tests in the development host test environment: |
| 265 | +```bash |
| 266 | +python3 -m unittest discover |
| 267 | +``` |
| 268 | + |
| 269 | +A convenience script [test.sh](detd/test.sh) is included in the detd directory, that basically runs the above command: |
| 270 | +```bash |
| 271 | +cd detd |
| 272 | +./test.sh |
| 273 | +``` |
| 274 | + |
| 275 | + |
| 276 | +## Getting involved |
| 277 | + |
| 278 | +### Installation |
| 279 | + |
| 280 | +Dependencies: |
| 281 | +* python3 |
| 282 | +* python3-protobuf |
| 283 | + |
| 284 | +The release process and security checks for the current release were conducted over the following specific versions: |
| 285 | +* python 3.8.10 |
| 286 | +* python-protobuf 3.6.1.3 |
| 287 | + |
| 288 | +To install: |
| 289 | +```bash |
| 290 | +git clone https://github.com/Avnu/detd.git |
| 291 | +cd detd |
| 292 | +python3 setup.py bdist_wheel |
| 293 | +# Uninstall in case a previous version is there |
| 294 | +# We install detd system wide |
| 295 | +sudo pip3 uninstall detd |
| 296 | +sudo pip3 install dist/detd-*.whl |
| 297 | +sudo pip3 show detd |
| 298 | +``` |
| 299 | + |
| 300 | +Optionally, you may want to run the test suite on host to make sure everything is in place: |
| 301 | +```bash |
| 302 | +python3 -m unittest discover |
| 303 | +.....................................ss |
| 304 | +---------------------------------------------------------------------- |
| 305 | +Ran 39 tests in 1.458s |
| 306 | +
|
| 307 | +OK (skipped=2) |
| 308 | +``` |
| 309 | + |
| 310 | + |
| 311 | +### Contributing |
| 312 | + |
| 313 | +**Junior tasks** |
| 314 | + |
| 315 | +* Make setup_qos.sh interface more compact |
| 316 | + * Instead of --address AB:CD:EF:FE:DC:BA --vid 3 --pcp 6 |
| 317 | + * Use --stream AB:CD:EF:FE:DC:BA/3/6 and parse inside the string |
| 318 | +* Move the UDS from /tmp to the right location |
| 319 | + * Also involves providing the installation instructions, e.g. to set the right ownerships |
| 320 | +* Split device features into rx_features and tx_features |
| 321 | + * So e.g. only tx_features are applied when setting up the talker |
| 322 | +* Deb and RPM packaging |
| 323 | +* systemd service file |
| 324 | +* Improve pep8 style compliance |
| 325 | + |
| 326 | + |
| 327 | +**Other improvements** |
| 328 | + |
| 329 | +* Add C library implementing protobuf interaction |
| 330 | + * This will make integration with C applications straighforward |
| 331 | + * The python3 implementation can be used as an example |
| 332 | +* Add device backends |
| 333 | + * Currently mGBE EHL supported |
| 334 | +* Add device auto-identification |
| 335 | + * E.g. look for vendor/product ids for the interface provided and instantiate the right class |
| 336 | +* Add more Linux backend targets |
| 337 | + * taprio offload is already implemented |
| 338 | + * More could follow, e.g. taprio software, taprio tx-assist, launch-time support... |
| 339 | +* Add listener stream setup |
| 340 | + * VLAN tag configuration |
| 341 | + * DMAC subscription |
| 342 | + * tc and ethtool configuration |
| 343 | +* Add profiles |
| 344 | + * A layer on top of the basic interface that further elevates the level of abstraction |
| 345 | + * E.g. 60802, 61850... that provide the right mappings abstracting the developers from that |
| 346 | +* Improve platform independence |
| 347 | + * More clear separation of generic and platform specific details (e.g. subclassing per OS or platform) |
| 348 | +* Integrate time-synchronization |
| 349 | + * When a time-aware stream is requested, check if time synchronization is running and otherwise start it and initialize to the right operation values |
| 350 | +* Evaluate using netlink for consideration instead of calling tc |
| 351 | + * E.g. a programmatic interface could improve aspects like error handling, security, etc, and would make code simpler by removing all the portions devoted to templating commands |
0 commit comments