1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or https://opensource.org/licenses/CDDL-1.0. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21/* 22 * Copyright (c) 2023, 2024, Klara Inc. 23 */ 24 25#include <stdint.h> 26#include <stdio.h> 27#include <stdbool.h> 28#include <sys/param.h> 29#include <stdlib.h> 30 31/* 32 * This tests the vdev_disk page alignment check callback 33 * vdev_disk_check_pages_cb(). For now, this test includes a copy of that 34 * function from module/os/linux/zfs/vdev_disk.c. If you change it here, 35 * remember to change it there too, and add tests data here to validate the 36 * change you're making. 37 */ 38 39struct page; 40 41typedef struct { 42 uint32_t bmask; 43 uint32_t npages; 44 uint32_t end; 45} vdev_disk_check_pages_t; 46 47static int 48vdev_disk_check_pages_cb(struct page *page, size_t off, size_t len, void *priv) 49{ 50 (void) page; 51 vdev_disk_check_pages_t *s = priv; 52 53 /* 54 * If we didn't finish on a block size boundary last time, then there 55 * would be a gap if we tried to use this ABD as-is, so abort. 56 */ 57 if (s->end != 0) 58 return (1); 59 60 /* 61 * Note if we're taking less than a full block, so we can check it 62 * above on the next call. 63 */ 64 s->end = (off+len) & s->bmask; 65 66 /* All blocks after the first must start on a block size boundary. */ 67 if (s->npages != 0 && (off & s->bmask) != 0) 68 return (1); 69 70 s->npages++; 71 return (0); 72} 73 74typedef struct { 75 /* test name */ 76 const char *name; 77 78 /* blocks size mask */ 79 uint32_t mask; 80 81 /* amount of data to take */ 82 size_t size; 83 84 /* [start offset in page, len to end of page or size] */ 85 size_t pages[16][2]; 86} page_test_t; 87 88static const page_test_t valid_tests[] = { 89 /* 512B block tests */ 90 { 91 "512B blocks, 4K single page", 92 0x1ff, 0x1000, { 93 { 0x0, 0x1000 }, 94 }, 95 }, { 96 "512B blocks, 1K at start of page", 97 0x1ff, 0x400, { 98 { 0x0, 0x1000 }, 99 }, 100 }, { 101 "512B blocks, 1K at end of page", 102 0x1ff, 0x400, { 103 { 0x0c00, 0x0400 }, 104 }, 105 }, { 106 "512B blocks, 1K within page, 512B start offset", 107 0x1ff, 0x400, { 108 { 0x0200, 0x0e00 }, 109 }, 110 }, { 111 "512B blocks, 8K across 2x4K pages", 112 0x1ff, 0x2000, { 113 { 0x0, 0x1000 }, 114 { 0x0, 0x1000 }, 115 }, 116 }, { 117 "512B blocks, 4K across two pages, 2K start offset", 118 0x1ff, 0x1000, { 119 { 0x0800, 0x0800 }, 120 { 0x0, 0x0800 }, 121 }, 122 }, { 123 "512B blocks, 16K across 5x4K pages, 512B start offset", 124 0x1ff, 0x4000, { 125 { 0x0200, 0x0e00 }, 126 { 0x0, 0x1000 }, 127 { 0x0, 0x1000 }, 128 { 0x0, 0x1000 }, 129 { 0x0, 0x0200 }, 130 }, 131 }, { 132 "512B blocks, 64K data, 8x8K compound pages", 133 0x1ff, 0x10000, { 134 { 0x0, 0x2000 }, 135 { 0x0, 0x2000 }, 136 { 0x0, 0x2000 }, 137 { 0x0, 0x2000 }, 138 { 0x0, 0x2000 }, 139 { 0x0, 0x2000 }, 140 { 0x0, 0x2000 }, 141 { 0x0, 0x2000 }, 142 }, 143 }, { 144 "512B blocks, 64K data, 9x8K compound pages, 512B start offset", 145 0x1ff, 0x10000, { 146 { 0x0200, 0x1e00 }, 147 { 0x0, 0x2000 }, 148 { 0x0, 0x2000 }, 149 { 0x0, 0x2000 }, 150 { 0x0, 0x2000 }, 151 { 0x0, 0x2000 }, 152 { 0x0, 0x2000 }, 153 { 0x0, 0x2000 }, 154 { 0x0, 0x0200 }, 155 }, 156 }, { 157 "512B blocks, 64K data, 2x16K compound pages, 8x4K pages", 158 0x1ff, 0x10000, { 159 { 0x0, 0x8000 }, 160 { 0x0, 0x8000 }, 161 { 0x0, 0x1000 }, 162 { 0x0, 0x1000 }, 163 { 0x0, 0x1000 }, 164 { 0x0, 0x1000 }, 165 { 0x0, 0x1000 }, 166 { 0x0, 0x1000 }, 167 { 0x0, 0x1000 }, 168 { 0x0, 0x1000 }, 169 }, 170 }, { 171 "512B blocks, 64K data, mixed 4K/8K/16K pages", 172 0x1ff, 0x10000, { 173 { 0x0, 0x1000 }, 174 { 0x0, 0x2000 }, 175 { 0x0, 0x1000 }, 176 { 0x0, 0x8000 }, 177 { 0x0, 0x1000 }, 178 { 0x0, 0x1000 }, 179 { 0x0, 0x2000 }, 180 { 0x0, 0x1000 }, 181 { 0x0, 0x1000 }, 182 { 0x0, 0x2000 }, 183 }, 184 }, { 185 "512B blocks, 64K data, mixed 4K/8K/16K pages, 1K start offset", 186 0x1ff, 0x10000, { 187 { 0x0400, 0x0c00 }, 188 { 0x0, 0x1000 }, 189 { 0x0, 0x1000 }, 190 { 0x0, 0x1000 }, 191 { 0x0, 0x2000 }, 192 { 0x0, 0x2000 }, 193 { 0x0, 0x1000 }, 194 { 0x0, 0x8000 }, 195 { 0x0, 0x1000 }, 196 { 0x0, 0x0400 }, 197 }, 198 }, 199 200 /* 4K block tests */ 201 { 202 "4K blocks, 4K single page", 203 0xfff, 0x1000, { 204 { 0x0, 0x1000 }, 205 }, 206 }, { 207 "4K blocks, 1K at start of page", 208 0xfff, 0x400, { 209 { 0x0, 0x1000 }, 210 }, 211 }, { 212 "4K blocks, 1K at end of page", 213 0xfff, 0x400, { 214 { 0x0c00, 0x0400 }, 215 }, 216 }, { 217 "4K blocks, 1K within page, 512B start offset", 218 0xfff, 0x400, { 219 { 0x0200, 0x0e00 }, 220 }, 221 }, { 222 "4K blocks, 8K across 2x4K pages", 223 0xfff, 0x2000, { 224 { 0x0, 0x1000 }, 225 { 0x0, 0x1000 }, 226 }, 227 }, { 228 "4K blocks, 4K across two pages, 2K start offset", 229 0xfff, 0x1000, { 230 { 0x0800, 0x0800 }, 231 { 0x0, 0x0800 }, 232 }, 233 }, { 234 "4K blocks, 16K across 5x4K pages, 512B start offset", 235 0xfff, 0x4000, { 236 { 0x0200, 0x0e00 }, 237 { 0x0, 0x1000 }, 238 { 0x0, 0x1000 }, 239 { 0x0, 0x1000 }, 240 { 0x0, 0x0200 }, 241 }, 242 }, { 243 "4K blocks, 64K data, 8x8K compound pages", 244 0xfff, 0x10000, { 245 { 0x0, 0x2000 }, 246 { 0x0, 0x2000 }, 247 { 0x0, 0x2000 }, 248 { 0x0, 0x2000 }, 249 { 0x0, 0x2000 }, 250 { 0x0, 0x2000 }, 251 { 0x0, 0x2000 }, 252 { 0x0, 0x2000 }, 253 }, 254 }, { 255 "4K blocks, 64K data, 9x8K compound pages, 512B start offset", 256 0xfff, 0x10000, { 257 { 0x0200, 0x1e00 }, 258 { 0x0, 0x2000 }, 259 { 0x0, 0x2000 }, 260 { 0x0, 0x2000 }, 261 { 0x0, 0x2000 }, 262 { 0x0, 0x2000 }, 263 { 0x0, 0x2000 }, 264 { 0x0, 0x2000 }, 265 { 0x0, 0x0200 }, 266 }, 267 }, { 268 "4K blocks, 64K data, 2x16K compound pages, 8x4K pages", 269 0xfff, 0x10000, { 270 { 0x0, 0x8000 }, 271 { 0x0, 0x8000 }, 272 { 0x0, 0x1000 }, 273 { 0x0, 0x1000 }, 274 { 0x0, 0x1000 }, 275 { 0x0, 0x1000 }, 276 { 0x0, 0x1000 }, 277 { 0x0, 0x1000 }, 278 { 0x0, 0x1000 }, 279 { 0x0, 0x1000 }, 280 }, 281 }, { 282 "4K blocks, 64K data, mixed 4K/8K/16K pages", 283 0xfff, 0x10000, { 284 { 0x0, 0x1000 }, 285 { 0x0, 0x2000 }, 286 { 0x0, 0x1000 }, 287 { 0x0, 0x8000 }, 288 { 0x0, 0x1000 }, 289 { 0x0, 0x1000 }, 290 { 0x0, 0x2000 }, 291 { 0x0, 0x1000 }, 292 { 0x0, 0x1000 }, 293 { 0x0, 0x2000 }, 294 }, 295 }, { 296 "4K blocks, 64K data, mixed 4K/8K/16K pages, 1K start offset", 297 0xfff, 0x10000, { 298 { 0x0400, 0x0c00 }, 299 { 0x0, 0x1000 }, 300 { 0x0, 0x1000 }, 301 { 0x0, 0x1000 }, 302 { 0x0, 0x2000 }, 303 { 0x0, 0x2000 }, 304 { 0x0, 0x1000 }, 305 { 0x0, 0x8000 }, 306 { 0x0, 0x1000 }, 307 { 0x0, 0x0400 }, 308 }, 309 }, 310 311 { 0 }, 312}; 313 314static const page_test_t invalid_tests[] = { 315 { 316 "512B blocks, 16K data, 512 leader (gang block simulation)", 317 0x1ff, 0x8000, { 318 { 0x0, 0x0200 }, 319 { 0x0, 0x1000 }, 320 { 0x0, 0x1000 }, 321 { 0x0, 0x1000 }, 322 { 0x0, 0x0c00 }, 323 }, 324 }, { 325 "4K blocks, 32K data, 2 incompatible spans " 326 "(gang abd simulation)", 327 0xfff, 0x8000, { 328 { 0x0800, 0x0800 }, 329 { 0x0, 0x1000 }, 330 { 0x0, 0x1000 }, 331 { 0x0, 0x1000 }, 332 { 0x0, 0x0800 }, 333 { 0x0800, 0x0800 }, 334 { 0x0, 0x1000 }, 335 { 0x0, 0x1000 }, 336 { 0x0, 0x1000 }, 337 { 0x0, 0x0800 }, 338 }, 339 }, 340 { 0 }, 341}; 342 343static bool 344run_test(const page_test_t *test, bool verbose) 345{ 346 size_t rem = test->size; 347 348 vdev_disk_check_pages_t s = { 349 .bmask = 0xfff, 350 .npages = 0, 351 .end = 0, 352 }; 353 354 for (int i = 0; test->pages[i][1] > 0; i++) { 355 size_t off = test->pages[i][0]; 356 size_t len = test->pages[i][1]; 357 358 size_t take = MIN(rem, len); 359 360 if (verbose) 361 printf(" page %d [off %lx len %lx], " 362 "rem %lx, take %lx\n", 363 i, off, len, rem, take); 364 365 if (vdev_disk_check_pages_cb(NULL, off, take, &s)) { 366 if (verbose) 367 printf(" ABORT: misalignment detected, " 368 "rem %lx\n", rem); 369 return (false); 370 } 371 372 rem -= take; 373 if (rem == 0) 374 break; 375 } 376 377 if (rem > 0) { 378 if (verbose) 379 printf(" ABORT: ran out of pages, rem %lx\n", rem); 380 return (false); 381 } 382 383 return (true); 384} 385 386static void 387run_test_set(const page_test_t *tests, bool want, int *ntests, int *npassed) 388{ 389 for (const page_test_t *test = &tests[0]; test->name; test++) { 390 bool pass = (run_test(test, false) == want); 391 if (pass) { 392 printf("%s: PASS\n", test->name); 393 (*npassed)++; 394 } else { 395 printf("%s: FAIL [expected %s, got %s]\n", test->name, 396 want ? "VALID" : "INVALID", 397 want ? "INVALID" : "VALID"); 398 run_test(test, true); 399 } 400 (*ntests)++; 401 } 402} 403 404int main(void) { 405 int ntests = 0, npassed = 0; 406 407 run_test_set(valid_tests, true, &ntests, &npassed); 408 run_test_set(invalid_tests, false, &ntests, &npassed); 409 410 printf("\n%d/%d tests passed\n", npassed, ntests); 411 412 return (ntests == npassed ? 0 : 1); 413} 414