/* * Check for invalid and overflow values
*/ if (entry == 0xffff || !entry) return 0; if (base > (UINT_MAX / (entry))) return 0;
/* * CDAT fields follow the format of HMAT fields. See table 5 Device * Scoped Latency and Bandwidth Information Structure in Coherent Device * Attribute Table (CDAT) Specification v1.01.
*/
value = entry * base; switch (type) { case ACPI_HMAT_ACCESS_LATENCY: case ACPI_HMAT_READ_LATENCY: case ACPI_HMAT_WRITE_LATENCY:
value = DIV_ROUND_UP(value, 1000); break; default: break;
} return value;
}
/* * No need to reset_dpa_perf() here as find_cxl_root() is guaranteed to * succeed when called in the cxl_endpoint_port_probe() path.
*/ if (!cxl_root) return;
root_port = &cxl_root->port;
/* * Save userspace from needing to check if a qos class has any matches * by hiding qos class info if the memdev is not mapped by a root * decoder, or the partition class does not match any root decoder * class.
*/ if (!device_for_each_child(&root_port->dev,
cxlmd->endpoint->host_bridge,
match_cxlrd_hb)) { for (int i = 0; i < cxlds->nr_partitions; i++) { struct cxl_dpa_perf *perf = &cxlds->part[i].perf;
reset_dpa_perf(perf);
} return;
}
for (int i = 0; i < cxlds->nr_partitions; i++) { struct cxl_dpa_perf *perf = &cxlds->part[i].perf;
if (!cxl_qos_match(root_port, perf))
reset_dpa_perf(perf);
}
}
if (cxled->part < 0) return ERR_PTR(-EINVAL);
perf = &cxlds->part[cxled->part].perf;
if (!perf) return ERR_PTR(-EINVAL);
if (!dpa_perf_contains(perf, cxled->dpa_res)) return ERR_PTR(-EINVAL);
return perf;
}
/* * Transient context for containing the current calculation of bandwidth when * doing walking the port hierarchy to deal with shared upstream link.
*/ struct cxl_perf_ctx { struct access_coordinate coord[ACCESS_COORDINATE_MAX]; struct cxl_port *port;
};
/** * cxl_endpoint_gather_bandwidth - collect all the endpoint bandwidth in an xarray * @cxlr: CXL region for the bandwidth calculation * @cxled: endpoint decoder to start on * @usp_xa: (output) the xarray that collects all the bandwidth coordinates * indexed by the upstream device with data of 'struct cxl_perf_ctx'. * @gp_is_root: (output) bool of whether the grandparent is cxl root. * * Return: 0 for success or -errno * * Collects aggregated endpoint bandwidth and store the bandwidth in * an xarray indexed by the upstream device of the switch or the RP * device. Each endpoint consists the minimum of the bandwidth from DSLBIS * from the endpoint CDAT, the endpoint upstream link bandwidth, and the * bandwidth from the SSLBIS of the switch CDAT for the switch upstream port to * the downstream port that's associated with the endpoint. If the * device is directly connected to a RP, then no SSLBIS is involved.
*/ staticint cxl_endpoint_gather_bandwidth(struct cxl_region *cxlr, struct cxl_endpoint_decoder *cxled, struct xarray *usp_xa, bool *gp_is_root)
{ struct cxl_port *endpoint = to_cxl_port(cxled->cxld.dev.parent); struct cxl_port *parent_port = to_cxl_port(endpoint->dev.parent); struct cxl_port *gp_port = to_cxl_port(parent_port->dev.parent); struct access_coordinate pci_coord[ACCESS_COORDINATE_MAX]; struct access_coordinate sw_coord[ACCESS_COORDINATE_MAX]; struct access_coordinate ep_coord[ACCESS_COORDINATE_MAX]; struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); struct cxl_dev_state *cxlds = cxlmd->cxlds; struct pci_dev *pdev = to_pci_dev(cxlds->dev); struct cxl_perf_ctx *perf_ctx; struct cxl_dpa_perf *perf; unsignedlong index; void *ptr; int rc;
if (!dev_is_pci(cxlds->dev)) return -ENODEV;
if (cxlds->rcd) return -ENODEV;
perf = cxled_get_dpa_perf(cxled); if (IS_ERR(perf)) return PTR_ERR(perf);
*gp_is_root = is_cxl_root(gp_port);
/* * If the grandparent is cxl root, then index is the root port, * otherwise it's the parent switch upstream device.
*/ if (*gp_is_root)
index = (unsignedlong)endpoint->parent_dport->dport_dev; else
index = (unsignedlong)parent_port->uport_dev;
/* Direct upstream link from EP bandwidth */
rc = cxl_pci_get_bandwidth(pdev, pci_coord); if (rc < 0) return rc;
/* * Min of upstream link bandwidth and Endpoint CDAT bandwidth from * DSLBIS.
*/
cxl_coordinates_combine(ep_coord, pci_coord, perf->cdat_coord);
/* * If grandparent port is root, then there's no switch involved and * the endpoint is connected to a root port.
*/ if (!*gp_is_root) { /* * Retrieve the switch SSLBIS for switch downstream port * associated with the endpoint bandwidth.
*/
rc = cxl_port_get_switch_dport_bandwidth(endpoint, sw_coord); if (rc) return rc;
/* * Min of the earlier coordinates with the switch SSLBIS * bandwidth
*/
cxl_coordinates_combine(ep_coord, ep_coord, sw_coord);
}
/* * Aggregate the computed bandwidth with the current aggregated bandwidth * of the endpoints with the same switch upstream device or RP.
*/
cxl_bandwidth_add(perf_ctx->coord, perf_ctx->coord, ep_coord);
/** * cxl_switch_gather_bandwidth - collect all the bandwidth at switch level in an xarray * @cxlr: The region being operated on * @input_xa: xarray indexed by upstream device of a switch with data of 'struct * cxl_perf_ctx' * @gp_is_root: (output) bool of whether the grandparent is cxl root. * * Return: a xarray of resulting cxl_perf_ctx per parent switch or root port * or ERR_PTR(-errno) * * Iterate through the xarray. Take the minimum of the downstream calculated * bandwidth, the upstream link bandwidth, and the SSLBIS of the upstream * switch if exists. Sum the resulting bandwidth under the switch upstream * device or a RP device. The function can be iterated over multiple switches * if the switches are present.
*/ staticstruct xarray *cxl_switch_gather_bandwidth(struct cxl_region *cxlr, struct xarray *input_xa, bool *gp_is_root)
{ struct xarray *res_xa __free(free_perf_xa) =
kzalloc(sizeof(*res_xa), GFP_KERNEL); struct access_coordinate coords[ACCESS_COORDINATE_MAX]; struct cxl_perf_ctx *ctx, *us_ctx; unsignedlong index, us_index; int dev_count = 0; int gp_count = 0; void *ptr; int rc;
if (!res_xa) return ERR_PTR(-ENOMEM);
xa_init(res_xa);
dev_count++; if (is_cxl_root(gp_port)) {
is_root = true;
gp_count++;
}
/* * If the grandparent is cxl root, then index is the root port, * otherwise it's the parent switch upstream device.
*/ if (is_root)
us_index = (unsignedlong)port->parent_dport->dport_dev; else
us_index = (unsignedlong)parent_port->uport_dev;
ptr = xa_store(res_xa, us_index, n, GFP_KERNEL); if (xa_is_err(ptr)) return ERR_PTR(xa_err(ptr));
us_ctx = no_free_ptr(n);
us_ctx->port = parent_port;
}
/* * If the device isn't an upstream PCIe port, there's something * wrong with the topology.
*/ if (!dev_is_pci(dev)) return ERR_PTR(-EINVAL);
/* Retrieve the upstream link bandwidth */
rc = cxl_pci_get_bandwidth(to_pci_dev(dev), coords); if (rc) return ERR_PTR(-ENXIO);
/* * Take the min of downstream bandwidth and the upstream link * bandwidth.
*/
cxl_coordinates_combine(coords, coords, ctx->coord);
/* * Take the min of the calculated bandwdith and the upstream * switch SSLBIS bandwidth if there's a parent switch
*/ if (!is_root)
cxl_coordinates_combine(coords, coords, dport->coord);
/* * Aggregate the calculated bandwidth common to an upstream * switch.
*/
cxl_bandwidth_add(us_ctx->coord, us_ctx->coord, coords);
}
/* Asymmetric topology detected. */ if (gp_count) { if (gp_count != dev_count) {
dev_dbg(&cxlr->dev, "Asymmetric hierarchy detected, bandwidth not updated\n"); return ERR_PTR(-EOPNOTSUPP);
}
*gp_is_root = true;
}
return no_free_ptr(res_xa);
}
/** * cxl_rp_gather_bandwidth - handle the root port level bandwidth collection * @xa: the xarray that holds the cxl_perf_ctx that has the bandwidth calculated * below each root port device. * * Return: xarray that holds cxl_perf_ctx per host bridge or ERR_PTR(-errno)
*/ staticstruct xarray *cxl_rp_gather_bandwidth(struct xarray *xa)
{ struct xarray *hb_xa __free(free_perf_xa) =
kzalloc(sizeof(*hb_xa), GFP_KERNEL); struct cxl_perf_ctx *ctx; unsignedlong index;
if (!hb_xa) return ERR_PTR(-ENOMEM);
xa_init(hb_xa);
for (int i = 0; i < ACCESS_COORDINATE_MAX; i++) {
cxlr->coord[i].read_bandwidth = coord[i].read_bandwidth;
cxlr->coord[i].write_bandwidth = coord[i].write_bandwidth;
}
}
/** * cxl_region_shared_upstream_bandwidth_update - Recalculate the bandwidth for * the region * @cxlr: the cxl region to recalculate * * The function walks the topology from bottom up and calculates the bandwidth. It * starts at the endpoints, processes at the switches if any, processes at the rootport * level, at the host bridge level, and finally aggregates at the region.
*/ void cxl_region_shared_upstream_bandwidth_update(struct cxl_region *cxlr)
{ struct xarray *working_xa; int root_count = 0; bool is_root; int rc;
/* Collect bandwidth data from all the endpoints. */ for (int i = 0; i < cxlr->params.nr_targets; i++) { struct cxl_endpoint_decoder *cxled = cxlr->params.targets[i];
/* Detect asymmetric hierarchy with some direct attached endpoints. */ if (root_count && root_count != cxlr->params.nr_targets) {
dev_dbg(&cxlr->dev, "Asymmetric hierarchy detected, bandwidth not updated\n"); return;
}
/* * Walk up one or more switches to deal with the bandwidth of the * switches if they exist. Endpoints directly attached to RPs skip * over this part.
*/ if (!root_count) { do {
working_xa = cxl_switch_gather_bandwidth(cxlr, usp_xa,
&is_root); if (IS_ERR(working_xa)) return;
free_perf_xa(usp_xa);
usp_xa = working_xa;
} while (!is_root);
}
/* Handle the bandwidth at the root port of the hierarchy */
working_xa = cxl_rp_gather_bandwidth(usp_xa); if (IS_ERR(working_xa)) return;
free_perf_xa(usp_xa);
usp_xa = working_xa;
/* Handle the bandwidth at the host bridge of the hierarchy */
working_xa = cxl_hb_gather_bandwidth(usp_xa); if (IS_ERR(working_xa)) return;
free_perf_xa(usp_xa);
usp_xa = working_xa;
/* * Aggregate all the bandwidth collected per CFMWS (ACPI0017) and * update the region bandwidth with the final calculated values.
*/
cxl_region_update_bandwidth(cxlr, usp_xa);
}
perf = cxled_get_dpa_perf(cxled); if (IS_ERR(perf)) return;
for (int i = 0; i < ACCESS_COORDINATE_MAX; i++) { /* Get total bandwidth and the worst latency for the cxl region */
cxlr->coord[i].read_latency = max_t(unsignedint,
cxlr->coord[i].read_latency,
perf->coord[i].read_latency);
cxlr->coord[i].write_latency = max_t(unsignedint,
cxlr->coord[i].write_latency,
perf->coord[i].write_latency);
cxlr->coord[i].read_bandwidth += perf->coord[i].read_bandwidth;
cxlr->coord[i].write_bandwidth += perf->coord[i].write_bandwidth;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.