📄 pcibr_rrb.c
字号:
* * One way of doing this would be to allocate two RRBs * for each device on the bus, before any drivers start * asking for extras. This has the weakness that one * driver might not give back an "extra" RRB until after * another driver has already failed to get one that * it wanted. */ s = pcibr_lock(pcibr_soft); vchan_total = NUMBER_VCHANNELS(bridge); /* Save the boot-time RRB configuration for this slot */ if (pcibr_soft->bs_rrb_valid_dflt[pciio_slot][VCHAN0] < 0) { for (vchan = 0; vchan < vchan_total; vchan++) pcibr_soft->bs_rrb_valid_dflt[pciio_slot][vchan] = pcibr_soft->bs_rrb_valid[pciio_slot][vchan]; pcibr_soft->bs_rrb_res_dflt[pciio_slot] = pcibr_soft->bs_rrb_res[pciio_slot]; } /* How many RRBs do we own? */ orig_vchan0 = pcibr_soft->bs_rrb_valid[pciio_slot][VCHAN0]; orig_vchan1 = pcibr_soft->bs_rrb_valid[pciio_slot][VCHAN1]; /* How many RRBs do we want? */ desired_vchan0 = count_vchan0 ? *count_vchan0 : orig_vchan0; desired_vchan1 = count_vchan1 ? *count_vchan1 : orig_vchan1; /* How many RRBs are free? */ avail_rrbs = pcibr_soft->bs_rrb_avail[pciio_slot & 1] + pcibr_soft->bs_rrb_res[pciio_slot]; /* Figure desired deltas */ delta_vchan0 = desired_vchan0 - orig_vchan0; delta_vchan1 = desired_vchan1 - orig_vchan1; /* Trim back deltas to something * that we can actually meet, by * decreasing the ending allocation * for whichever channel wants * more RRBs. If both want the same * number, cut the second channel. * NOTE: do not change the allocation for * a channel that was passed as NULL. */ while ((delta_vchan0 + delta_vchan1) > avail_rrbs) { if (count_vchan0 && (!count_vchan1 || ((orig_vchan0 + delta_vchan0) > (orig_vchan1 + delta_vchan1)))) delta_vchan0--; else delta_vchan1--; } /* Figure final RRB allocations */ final_vchan0 = orig_vchan0 + delta_vchan0; final_vchan1 = orig_vchan1 + delta_vchan1; /* If either channel wants RRBs but our actions * would leave it with none, declare an error, * but DO NOT change any RRB allocations. */ if ((desired_vchan0 && !final_vchan0) || (desired_vchan1 && !final_vchan1)) { error = -1; } else { /* Commit the allocations: free, then alloc. */ if (delta_vchan0 < 0) (void) do_pcibr_rrb_free(bridge, pciio_slot, VCHAN0, -delta_vchan0); if (delta_vchan1 < 0) (void) do_pcibr_rrb_free(bridge, pciio_slot, VCHAN1, -delta_vchan1); if (delta_vchan0 > 0) (void) do_pcibr_rrb_alloc(bridge, pciio_slot, VCHAN0, delta_vchan0); if (delta_vchan1 > 0) (void) do_pcibr_rrb_alloc(bridge, pciio_slot, VCHAN1, delta_vchan1); /* Return final values to caller. */ if (count_vchan0) *count_vchan0 = final_vchan0; if (count_vchan1) *count_vchan1 = final_vchan1; /* prevent automatic changes to this slot's RRBs */ pcibr_soft->bs_rrb_fixed |= 1 << pciio_slot; /* Track the actual allocations, release * any further reservations, and update the * number of available RRBs. */ pcibr_soft->bs_rrb_valid[pciio_slot][VCHAN0] = final_vchan0; pcibr_soft->bs_rrb_valid[pciio_slot][VCHAN1] = final_vchan1; pcibr_soft->bs_rrb_avail[pciio_slot & 1] = pcibr_soft->bs_rrb_avail[pciio_slot & 1] + pcibr_soft->bs_rrb_res[pciio_slot] - delta_vchan0 - delta_vchan1; pcibr_soft->bs_rrb_res[pciio_slot] = 0; /* * Reserve enough RRBs so this slot's RRB configuration can be * reset to its boot-time default following a hot-plug shut-down */ res_rrbs = (pcibr_soft->bs_rrb_res_dflt[pciio_slot] - pcibr_soft->bs_rrb_res[pciio_slot]); for (vchan = 0; vchan < vchan_total; vchan++) { res_rrbs += (pcibr_soft->bs_rrb_valid_dflt[pciio_slot][vchan] - pcibr_soft->bs_rrb_valid[pciio_slot][vchan]); } if (res_rrbs > 0) { pcibr_soft->bs_rrb_res[pciio_slot] = res_rrbs; pcibr_soft->bs_rrb_avail[pciio_slot & 1] = pcibr_soft->bs_rrb_avail[pciio_slot & 1] - res_rrbs; } pcibr_rrb_debug("pcibr_rrb_alloc", pcibr_soft); error = 0; } pcibr_unlock(pcibr_soft, s); return error;}/* * Device driver interface to check the current state * of the RRB allocations. * * pconn_vhdl is your PCI connection point (specifies which * PCI bus and which slot). * * count_vchan0 points to where to return the number of RRBs * assigned to the primary DMA channel, used by all DMA * that does not explicitly ask for the alternate virtual * channel. * * count_vchan1 points to where to return the number of RRBs * assigned to the secondary DMA channel, used when * PCIBR_VCHAN1 and PCIIO_DMA_A64 are specified. * * count_reserved points to where to return the number of RRBs * that have been automatically reserved for your device at * startup, but which have not been assigned to a * channel. RRBs must be assigned to a channel to be used; * this can be done either with an explicit pcibr_rrb_alloc * call, or automatically by the infrastructure when a DMA * translation is constructed. Any call to pcibr_rrb_alloc * will release any unassigned reserved RRBs back to the * free pool. * * count_pool points to where to return the number of RRBs * that are currently unassigned and unreserved. This * number can (and will) change as other drivers make calls * to pcibr_rrb_alloc, or automatically allocate RRBs for * DMA beyond their initial reservation. * * NULL may be passed for any of the return value pointers * the caller is not interested in. * * The return value is "0" if all went well, or "-1" if * there is a problem. Additionally, if the wrong vertex * is passed in, one of the subsidiary support functions * could panic with a "bad pciio fingerprint." */intpcibr_rrb_check(vertex_hdl_t pconn_vhdl, int *count_vchan0, int *count_vchan1, int *count_reserved, int *count_pool){ pciio_info_t pciio_info; pciio_slot_t pciio_slot; pcibr_soft_t pcibr_soft; unsigned long s; int error = -1; if ((pciio_info = pciio_info_get(pconn_vhdl)) && (pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info)) && ((pciio_slot = PCIBR_INFO_SLOT_GET_INT(pciio_info)) < PCIBR_NUM_SLOTS(pcibr_soft))) { s = pcibr_lock(pcibr_soft); if (count_vchan0) *count_vchan0 = pcibr_soft->bs_rrb_valid[pciio_slot][VCHAN0]; if (count_vchan1) *count_vchan1 = pcibr_soft->bs_rrb_valid[pciio_slot][VCHAN1]; if (count_reserved) *count_reserved = pcibr_soft->bs_rrb_res[pciio_slot]; if (count_pool) *count_pool = pcibr_soft->bs_rrb_avail[pciio_slot & 1]; error = 0; pcibr_unlock(pcibr_soft, s); } return error;}/* * pcibr_slot_initial_rrb_alloc * Allocate a default number of rrbs for this slot on * the two channels. This is dictated by the rrb allocation * strategy routine defined per platform. */intpcibr_slot_initial_rrb_alloc(vertex_hdl_t pcibr_vhdl, pciio_slot_t slot){ pcibr_soft_t pcibr_soft; pcibr_info_h pcibr_infoh; pcibr_info_t pcibr_info; bridge_t *bridge; int vchan_total; int vchan; int chan[4]; pcibr_soft = pcibr_soft_get(pcibr_vhdl); if (!pcibr_soft) return(-EINVAL); if (!PCIBR_VALID_SLOT(pcibr_soft, slot)) return(-EINVAL); bridge = pcibr_soft->bs_base; /* How many RRBs are on this slot? */ vchan_total = NUMBER_VCHANNELS(bridge); for (vchan = 0; vchan < vchan_total; vchan++) chan[vchan] = do_pcibr_rrb_count_valid(bridge, slot, vchan); if (IS_PIC_SOFT(pcibr_soft)) { PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RRB, pcibr_vhdl, "pcibr_slot_initial_rrb_alloc: slot %d started with %d+%d+%d+%d\n", PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), chan[VCHAN0], chan[VCHAN1], chan[VCHAN2], chan[VCHAN3])); } else { PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RRB, pcibr_vhdl, "pcibr_slot_initial_rrb_alloc: slot %d started with %d+%d\n", PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), chan[VCHAN0], chan[VCHAN1])); } /* Do we really need any? */ pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos; pcibr_info = pcibr_infoh[0]; if (PCIBR_WAR_ENABLED(PV856866, pcibr_soft) && IS_PIC_SOFT(pcibr_soft) && (slot == 2 || slot == 3) && (pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE) && !pcibr_soft->bs_slot[slot].has_host) { for (vchan = 0; vchan < 2; vchan++) { do_pcibr_rrb_free(bridge, slot, vchan, 8); pcibr_soft->bs_rrb_valid[slot][vchan] = 0; } pcibr_soft->bs_rrb_valid[slot][3] = chan[3]; return(-ENODEV); } /* Give back any assigned to empty slots */ if ((pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE) && !pcibr_soft->bs_slot[slot].has_host) { do_pcibr_rrb_free_all(pcibr_soft, bridge, slot); return(-ENODEV); } for (vchan = 0; vchan < vchan_total; vchan++) pcibr_soft->bs_rrb_valid[slot][vchan] = chan[vchan]; return(0);}voidrrb_reserved_free(pcibr_soft_t pcibr_soft, int slot){ int res = pcibr_soft->bs_rrb_res[slot]; if (res) { pcibr_soft->bs_rrb_avail[slot & 1] += res; pcibr_soft->bs_rrb_res[slot] = 0; }}/* * pcibr_initial_rrb * Assign an equal total number of RRBs to all candidate slots, * where the total is the sum of the number of RRBs assigned to * the normal channel, the number of RRBs assigned to the virtual * channels, and the number of RRBs assigned as reserved. * * A candidate slot is any existing (populated or empty) slot. * Empty SN1 slots need RRBs to support hot-plug operations. */intpcibr_initial_rrb(vertex_hdl_t pcibr_vhdl, pciio_slot_t first, pciio_slot_t last){ pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl); bridge_t *bridge = pcibr_soft->bs_base; pciio_slot_t slot; int rrb_total; int vchan_total; int vchan; int have[2][3]; int res[2]; int eo; have[0][0] = have[0][1] = have[0][2] = 0; have[1][0] = have[1][1] = have[1][2] = 0; res[0] = res[1] = 0; vchan_total = NUMBER_VCHANNELS(bridge); for (slot = pcibr_soft->bs_min_slot; slot < PCIBR_NUM_SLOTS(pcibr_soft); ++slot) { /* Initial RRB management; give back RRBs in all non-existent slots */ (void) pcibr_slot_initial_rrb_alloc(pcibr_vhdl, slot); /* Base calculations only on existing slots */ if ((slot >= first) && (slot <= last)) { rrb_total = 0; for (vchan = 0; vchan < vchan_total; vchan++) rrb_total += pcibr_soft->bs_rrb_valid[slot][vchan]; if (rrb_total < 3) have[slot & 1][rrb_total]++; } } /* Initialize even/odd slot available RRB counts */ pcibr_soft->bs_rrb_avail[0] = do_pcibr_rrb_count_avail(bridge, 0); pcibr_soft->bs_rrb_avail[1] = do_pcibr_rrb_count_avail(bridge, 1); /* * Calculate reserved RRBs for slots based on current RRB usage */ for (eo = 0; eo < 2; eo++) { if ((3 * have[eo][0] + 2 * have[eo][1] + have[eo][2]) <= pcibr_soft->bs_rrb_avail[eo]) res[eo] = 3; else if ((2 * have[eo][0] + have[eo][1]) <= pcibr_soft->bs_rrb_avail[eo]) res[eo] = 2; else if (have[eo][0] <= pcibr_soft->bs_rrb_avail[eo]) res[eo] = 1; else res[eo] = 0; } /* Assign reserved RRBs to existing slots */ for (slot = first; slot <= last; ++slot) { int r; rrb_total = 0; for (vchan = 0; vchan < vchan_total; vchan++) rrb_total += pcibr_soft->bs_rrb_valid[slot][vchan]; r = res[slot & 1] - (rrb_total); if (r > 0) { pcibr_soft->bs_rrb_res[slot] = r; pcibr_soft->bs_rrb_avail[slot & 1] -= r; } } pcibr_rrb_debug("pcibr_initial_rrb", pcibr_soft); return 0;}/* * Dump the pcibr_soft_t RRB state variable */voidpcibr_rrb_debug(char *calling_func, pcibr_soft_t pcibr_soft){ pciio_slot_t slot; char tmp_str[256]; if (pcibr_debug_mask & PCIBR_DEBUG_RRB) { PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RRB, pcibr_soft->bs_vhdl, "%s: rrbs available, even=%d, odd=%d\n", calling_func, pcibr_soft->bs_rrb_avail[0], pcibr_soft->bs_rrb_avail[1])); if (IS_PIC_SOFT(pcibr_soft)) { PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RRB, pcibr_soft->bs_vhdl, "\tslot\tvchan0\tvchan1\tvchan2\tvchan3\treserved\n")); } else { PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RRB, pcibr_soft->bs_vhdl, "\tslot\tvchan0\tvchan1\treserved\n")); } for (slot=0; slot < PCIBR_NUM_SLOTS(pcibr_soft); slot++) { /* * The kernel only allows functions to have so many variable args, * attempting to call PCIBR_DEBUG_ALWAYS() with more than 5 printf * arguments fails so sprintf() it into a temporary string. */ if (IS_PIC_SOFT(pcibr_soft)) { sprintf(tmp_str, "\t %d\t %d\t %d\t %d\t %d\t %d\n", PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), 0xFFF & pcibr_soft->bs_rrb_valid[slot][VCHAN0], 0xFFF & pcibr_soft->bs_rrb_valid[slot][VCHAN1], 0xFFF & pcibr_soft->bs_rrb_valid[slot][VCHAN2], 0xFFF & pcibr_soft->bs_rrb_valid[slot][VCHAN3], pcibr_soft->bs_rrb_res[slot]); } else { sprintf(tmp_str, "\t %d\t %d\t %d\t %d\n", PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), 0xFFF & pcibr_soft->bs_rrb_valid[slot][VCHAN0], 0xFFF & pcibr_soft->bs_rrb_valid[slot][VCHAN1], pcibr_soft->bs_rrb_res[slot]); } PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RRB, pcibr_soft->bs_vhdl, "%s", tmp_str)); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -