1 /* 2 * Copyright (c) 2017 Politecnico di Torino 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "BPF.h" 18 #include <linux/version.h> 19 20 #include "catch.hpp" 21 22 TEST_CASE("test hash table", "[hash_table]") { 23 const std::string BPF_PROGRAM = R"( 24 BPF_TABLE("hash", int, int, myhash, 1024); 25 BPF_TABLE("array", int, int, myarray, 1024); 26 )"; 27 28 ebpf::BPF bpf; 29 ebpf::StatusTuple res(0); 30 res = bpf.init(BPF_PROGRAM); 31 REQUIRE(res.code() == 0); 32 33 ebpf::BPFHashTable<int, int> t = bpf.get_hash_table<int, int>("myhash"); 34 35 SECTION("bad table type") { 36 // try to get table of wrong type __anon74abbefd0102()37 auto f1 = [&](){ 38 bpf.get_hash_table<int, int>("myarray"); 39 }; 40 41 REQUIRE_THROWS(f1()); 42 } 43 44 SECTION("standard methods") { 45 int k, v1, v2; 46 k = 1; 47 v1 = 42; 48 // create new element 49 res = t.update_value(k, v1); 50 REQUIRE(res.code() == 0); 51 res = t.get_value(k, v2); 52 REQUIRE(res.code() == 0); 53 REQUIRE(v2 == 42); 54 55 // update existing element 56 v1 = 69; 57 res = t.update_value(k, v1); 58 REQUIRE(res.code() == 0); 59 res = t.get_value(k, v2); 60 REQUIRE(res.code() == 0); 61 REQUIRE(v2 == 69); 62 63 // remove existing element 64 res = t.remove_value(k); 65 REQUIRE(res.code() == 0); 66 67 // remove non existing element 68 res = t.remove_value(k); 69 REQUIRE(res.code() != 0); 70 71 // get non existing element 72 res = t.get_value(k, v2); 73 REQUIRE(res.code() != 0); 74 } 75 76 SECTION("walk table") { 77 for (int i = 1; i <= 10; i++) { 78 res = t.update_value(i * 3, i); 79 REQUIRE(res.code() == 0); 80 } 81 auto offline = t.get_table_offline(); 82 REQUIRE(offline.size() == 10); 83 for (const auto &pair : offline) { 84 REQUIRE(pair.first % 3 == 0); 85 REQUIRE(pair.first / 3 == pair.second); 86 } 87 88 // clear table 89 t.clear_table_non_atomic(); 90 REQUIRE(t.get_table_offline().size() == 0); 91 } 92 } 93 94 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0) 95 TEST_CASE("percpu hash table", "[percpu_hash_table]") { 96 const std::string BPF_PROGRAM = R"( 97 BPF_TABLE("percpu_hash", int, u64, myhash, 128); 98 BPF_TABLE("percpu_array", int, u64, myarray, 64); 99 )"; 100 101 ebpf::BPF bpf; 102 ebpf::StatusTuple res(0); 103 res = bpf.init(BPF_PROGRAM); 104 REQUIRE(res.code() == 0); 105 106 ebpf::BPFPercpuHashTable<int, uint64_t> t = 107 bpf.get_percpu_hash_table<int, uint64_t>("myhash"); 108 size_t ncpus = ebpf::BPFTable::get_possible_cpu_count(); 109 110 SECTION("bad table type") { 111 // try to get table of wrong type __anon74abbefd0202()112 auto f1 = [&](){ 113 bpf.get_percpu_hash_table<int, uint64_t>("myarray"); 114 }; 115 116 REQUIRE_THROWS(f1()); 117 } 118 119 SECTION("standard methods") { 120 int k; 121 std::vector<uint64_t> v1(ncpus); 122 std::vector<uint64_t> v2; 123 124 for (size_t j = 0; j < ncpus; j++) { 125 v1[j] = 42 * j; 126 } 127 128 k = 1; 129 130 // create new element 131 res = t.update_value(k, v1); 132 REQUIRE(res.code() == 0); 133 res = t.get_value(k, v2); 134 REQUIRE(res.code() == 0); 135 for (size_t j = 0; j < ncpus; j++) { 136 REQUIRE(v2.at(j) == 42 * j); 137 } 138 139 // update existing element 140 for (size_t j = 0; j < ncpus; j++) { 141 v1[j] = 69 * j; 142 } 143 res = t.update_value(k, v1); 144 REQUIRE(res.code() == 0); 145 res = t.get_value(k, v2); 146 REQUIRE(res.code() == 0); 147 for (size_t j = 0; j < ncpus; j++) { 148 REQUIRE(v2.at(j) == 69 * j); 149 } 150 151 // remove existing element 152 res = t.remove_value(k); 153 REQUIRE(res.code() == 0); 154 155 // remove non existing element 156 res = t.remove_value(k); 157 REQUIRE(res.code() != 0); 158 159 // get non existing element 160 res = t.get_value(k, v2); 161 REQUIRE(res.code() != 0); 162 } 163 164 SECTION("walk table") { 165 std::vector<uint64_t> v(ncpus); 166 167 for (int k = 3; k <= 30; k+=3) { 168 for (size_t cpu = 0; cpu < ncpus; cpu++) { 169 v[cpu] = k * cpu; 170 } 171 res = t.update_value(k, v); 172 REQUIRE(res.code() == 0); 173 } 174 175 // get whole table 176 auto offline = t.get_table_offline(); 177 REQUIRE(offline.size() == 10); 178 for (int i = 0; i < 10; i++) { 179 // check the key 180 REQUIRE(offline.at(i).first % 3 == 0); 181 182 // check value 183 for (size_t cpu = 0; cpu < ncpus; cpu++) { 184 REQUIRE(offline.at(i).second.at(cpu) == cpu * offline.at(i).first); 185 } 186 } 187 188 // clear table 189 t.clear_table_non_atomic(); 190 REQUIRE(t.get_table_offline().size() == 0); 191 } 192 } 193 #endif 194