DF-4837 / df4837_harness.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | /* * DF-4837 harness — ida bio path leaves qcb->flags == 0: * ida.c:391 qcb->flags = 0; (never DMA_DATA_*) * ida.c:399 op = qcb->flags & DMA_DATA_IN ? PREREAD : PREWRITE; -> PREWRITE * ida.c:506 if (qcb->flags & DMA_DATA_TRANSFER) -> 0: SKIPPED * ida.c:509 POSTREAD sync SKIPPED, bus_dmamap_unload SKIPPED * * On a >4GB machine the parent tag (ida_pci.c:260-265 lowaddr 32-bit; * busdma clamps child lowaddr, busdma_machdep.c:272) forces bouncing for * buffers above 4GB. DFly busdma then requires: * bus_dmamap_sync(PREWRITE) orig -> bounce (busdma_machdep.c:1044-1053) * bus_dmamap_sync(POSTREAD) bounce -> orig (busdma_machdep.c:1054-1063) * bus_dmamap_unload return bounce pages to the zone (:1038 bpages) * * Skipping POSTREAD => device-read data stays in the bounce page: the * caller's buffer keeps STALE contents (silent data * corruption on every bounced READ). * Skipping unload => the map keeps its bounce pages forever: they are * never returned to the zone free list (leak per I/O). * * This harness models the map/zone bookkeeping and runs the exact flag logic * of ida_construct_qcb()/ida_done() for READ bios, baseline vs fixed. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define IDA_NSEG 32 #define DMA_DATA_IN 0x0001 #define DMA_DATA_OUT 0x0002 #define IDA_COMMAND 0x0004 #define DMA_DATA_TRANSFER (DMA_DATA_IN | DMA_DATA_OUT) enum { PREWRITE = 1, POSTREAD = 2, PREREAD = 4 }; /* --- model of a DFly bounce-capable map/zone (busdma_machdep.c) --- */ static long zone_total_bpages, zone_free_pool; /* allocated / returned */ static long zone_active(void) { return zone_total_bpages - zone_free_pool; } struct map_s { int loaded; long held_bpages; /* pages this map holds until unload */ uint8_t bounce[512]; } qcb_map; static uint8_t disk_buf[512]; /* bp->b_data page (the FS/user buffer) */ static uint8_t disk_hw[512]; /* what the DEVICE actually wrote (in bounce) */ static void map_load(void) { /* each load reserves a bounce page (add_bounce_page appends to * map->bpages; the zone grows on demand when the free list is empty) */ qcb_map.loaded = 1; if (zone_free_pool > 0) zone_free_pool--; else zone_total_bpages++; qcb_map.held_bpages++; } static void dmasync(int op) { /* _bus_dmamap_sync:1034-1067 */ if (!qcb_map.loaded) return; if (op & PREWRITE) { /* orig -> bounce */ memcpy(qcb_map.bounce, disk_buf, sizeof disk_buf); fprintf(stderr, " sync(PREWRITE): orig->bounce copy done\n"); } if (op & POSTREAD) { /* bounce -> orig */ memcpy(disk_buf, qcb_map.bounce, sizeof disk_buf); fprintf(stderr, " sync(POSTREAD): bounce->orig copy done (data delivered)\n"); } } static void map_unload(void) { /* bus_dmamap_unload */ if (qcb_map.loaded) { qcb_map.loaded = 0; zone_free_pool += qcb_map.held_bpages; qcb_map.held_bpages = 0; fprintf(stderr, " dmamap_unload: bounce page(s) returned to zone\n"); } } /* --- verbatim flag logic of ida.c --- */ struct ida_qcb_h { int flags; }; static struct ida_qcb_h qcb; static void ida_construct_qcb(int is_read, int fixed) { /* ida.c:373-417 */ qcb.flags = 0; /* :391 */ if (fixed) qcb.flags = DMA_DATA_TRANSFER | (is_read ? DMA_DATA_IN : DMA_DATA_OUT); map_load(); int op = qcb.flags & DMA_DATA_IN ? PREREAD : PREWRITE; /* :399-401 */ dmasync(op); /* device performs the transfer into/out of the bounce page */ } static void ida_done(int fixed) { /* ida.c:499-538 */ if (qcb.flags & DMA_DATA_TRANSFER) { /* :506 */ int op = qcb.flags & DMA_DATA_IN ? POSTREAD : 0; /* :509-510 */ if (op) dmasync(op); map_unload(); /* :512 */ } else if (!fixed) { fprintf(stderr, " ida_done: flags&DMA_DATA_TRANSFER==0 -> " "POSTREAD sync and dmamap_unload SKIPPED\n"); } } static void run_reads(int fixed, int n) { memset(&qcb_map, 0, sizeof(qcb_map)); zone_total_bpages = zone_free_pool = 0; memset(disk_buf, 0x11, sizeof disk_buf); /* stale FS page */ for (int i = 0; i < n; i++) { /* the device returns sector data 0xAA.. (only visible in bounce) */ memset(disk_hw, 0xAA, sizeof disk_hw); ida_construct_qcb(1, fixed); memcpy(qcb_map.bounce, disk_hw, sizeof disk_hw); /* device DMA (into bounce) */ ida_done(fixed); } int stale = 0; for (size_t k = 0; k < sizeof disk_buf; k++) if (disk_buf[k] != 0xAA) stale = 1; printf(" %s: after %d READ bios: caller buffer %s; bounce pages allocated=%ld " "still held=%ld (leaked=%ld)\n", fixed ? "FIXED " : "BASELINE", n, stale ? "STALE (device data LOST)" : "has device data", zone_total_bpages, zone_active(), zone_active()); } int main(void) { printf("[arm 1] single READ bio\n"); run_reads(0, 1); run_reads(1, 1); printf("\n[arm 2] sustained I/O (bounce-page accounting per qcb map)\n"); run_reads(0, 64); run_reads(1, 64); return 0; } |