mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1109 lines
27 KiB
C
1109 lines
27 KiB
C
/*
|
|
* Copyright 2022 Advanced Micro Devices, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
*/
|
|
#include "amdgpu.h"
|
|
#include "amdgpu_xcp.h"
|
|
#include "amdgpu_drv.h"
|
|
|
|
#include <drm/drm_drv.h>
|
|
#include "../amdxcp/amdgpu_xcp_drv.h"
|
|
|
|
static void amdgpu_xcp_sysfs_entries_init(struct amdgpu_xcp_mgr *xcp_mgr);
|
|
static void amdgpu_xcp_sysfs_entries_update(struct amdgpu_xcp_mgr *xcp_mgr);
|
|
|
|
static int __amdgpu_xcp_run(struct amdgpu_xcp_mgr *xcp_mgr,
|
|
struct amdgpu_xcp_ip *xcp_ip, int xcp_state)
|
|
{
|
|
int (*run_func)(void *handle, uint32_t inst_mask);
|
|
int ret = 0;
|
|
|
|
if (!xcp_ip || !xcp_ip->valid || !xcp_ip->ip_funcs)
|
|
return 0;
|
|
|
|
run_func = NULL;
|
|
|
|
switch (xcp_state) {
|
|
case AMDGPU_XCP_PREPARE_SUSPEND:
|
|
run_func = xcp_ip->ip_funcs->prepare_suspend;
|
|
break;
|
|
case AMDGPU_XCP_SUSPEND:
|
|
run_func = xcp_ip->ip_funcs->suspend;
|
|
break;
|
|
case AMDGPU_XCP_PREPARE_RESUME:
|
|
run_func = xcp_ip->ip_funcs->prepare_resume;
|
|
break;
|
|
case AMDGPU_XCP_RESUME:
|
|
run_func = xcp_ip->ip_funcs->resume;
|
|
break;
|
|
}
|
|
|
|
if (run_func)
|
|
ret = run_func(xcp_mgr->adev, xcp_ip->inst_mask);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int amdgpu_xcp_run_transition(struct amdgpu_xcp_mgr *xcp_mgr, int xcp_id,
|
|
int state)
|
|
{
|
|
struct amdgpu_xcp_ip *xcp_ip;
|
|
struct amdgpu_xcp *xcp;
|
|
int i, ret;
|
|
|
|
if (xcp_id >= MAX_XCP || !xcp_mgr->xcp[xcp_id].valid)
|
|
return -EINVAL;
|
|
|
|
xcp = &xcp_mgr->xcp[xcp_id];
|
|
for (i = 0; i < AMDGPU_XCP_MAX_BLOCKS; ++i) {
|
|
xcp_ip = &xcp->ip[i];
|
|
ret = __amdgpu_xcp_run(xcp_mgr, xcp_ip, state);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int amdgpu_xcp_prepare_suspend(struct amdgpu_xcp_mgr *xcp_mgr, int xcp_id)
|
|
{
|
|
return amdgpu_xcp_run_transition(xcp_mgr, xcp_id,
|
|
AMDGPU_XCP_PREPARE_SUSPEND);
|
|
}
|
|
|
|
int amdgpu_xcp_suspend(struct amdgpu_xcp_mgr *xcp_mgr, int xcp_id)
|
|
{
|
|
return amdgpu_xcp_run_transition(xcp_mgr, xcp_id, AMDGPU_XCP_SUSPEND);
|
|
}
|
|
|
|
int amdgpu_xcp_prepare_resume(struct amdgpu_xcp_mgr *xcp_mgr, int xcp_id)
|
|
{
|
|
return amdgpu_xcp_run_transition(xcp_mgr, xcp_id,
|
|
AMDGPU_XCP_PREPARE_RESUME);
|
|
}
|
|
|
|
int amdgpu_xcp_resume(struct amdgpu_xcp_mgr *xcp_mgr, int xcp_id)
|
|
{
|
|
return amdgpu_xcp_run_transition(xcp_mgr, xcp_id, AMDGPU_XCP_RESUME);
|
|
}
|
|
|
|
static void __amdgpu_xcp_add_block(struct amdgpu_xcp_mgr *xcp_mgr, int xcp_id,
|
|
struct amdgpu_xcp_ip *ip)
|
|
{
|
|
struct amdgpu_xcp *xcp;
|
|
|
|
if (!ip)
|
|
return;
|
|
|
|
xcp = &xcp_mgr->xcp[xcp_id];
|
|
xcp->ip[ip->ip_id] = *ip;
|
|
xcp->ip[ip->ip_id].valid = true;
|
|
|
|
xcp->valid = true;
|
|
}
|
|
|
|
static void __amdgpu_xcp_set_unique_id(struct amdgpu_xcp_mgr *xcp_mgr,
|
|
int xcp_id)
|
|
{
|
|
struct amdgpu_xcp *xcp = &xcp_mgr->xcp[xcp_id];
|
|
struct amdgpu_device *adev = xcp_mgr->adev;
|
|
uint32_t inst_mask;
|
|
uint64_t uid;
|
|
int i;
|
|
|
|
if (!amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX, &inst_mask) &&
|
|
inst_mask) {
|
|
i = GET_INST(GC, (ffs(inst_mask) - 1));
|
|
uid = amdgpu_device_get_uid(xcp_mgr->adev->uid_info,
|
|
AMDGPU_UID_TYPE_XCD, i);
|
|
if (uid)
|
|
xcp->unique_id = uid;
|
|
}
|
|
}
|
|
|
|
int amdgpu_xcp_init(struct amdgpu_xcp_mgr *xcp_mgr, int num_xcps, int mode)
|
|
{
|
|
struct amdgpu_device *adev = xcp_mgr->adev;
|
|
struct amdgpu_xcp_ip ip;
|
|
uint8_t mem_id;
|
|
int i, j, ret;
|
|
|
|
if (!num_xcps || num_xcps > MAX_XCP)
|
|
return -EINVAL;
|
|
|
|
xcp_mgr->mode = mode;
|
|
|
|
for (i = 0; i < MAX_XCP; ++i)
|
|
xcp_mgr->xcp[i].valid = false;
|
|
|
|
/* This is needed for figuring out memory id of xcp */
|
|
xcp_mgr->num_xcp_per_mem_partition = num_xcps / xcp_mgr->adev->gmc.num_mem_partitions;
|
|
|
|
for (i = 0; i < num_xcps; ++i) {
|
|
for (j = AMDGPU_XCP_GFXHUB; j < AMDGPU_XCP_MAX_BLOCKS; ++j) {
|
|
ret = xcp_mgr->funcs->get_ip_details(xcp_mgr, i, j,
|
|
&ip);
|
|
if (ret)
|
|
continue;
|
|
|
|
__amdgpu_xcp_add_block(xcp_mgr, i, &ip);
|
|
}
|
|
|
|
xcp_mgr->xcp[i].id = i;
|
|
|
|
if (xcp_mgr->funcs->get_xcp_mem_id) {
|
|
ret = xcp_mgr->funcs->get_xcp_mem_id(
|
|
xcp_mgr, &xcp_mgr->xcp[i], &mem_id);
|
|
if (ret)
|
|
continue;
|
|
else
|
|
xcp_mgr->xcp[i].mem_id = mem_id;
|
|
}
|
|
__amdgpu_xcp_set_unique_id(xcp_mgr, i);
|
|
}
|
|
|
|
xcp_mgr->num_xcps = num_xcps;
|
|
amdgpu_xcp_update_partition_sched_list(adev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __amdgpu_xcp_switch_partition_mode(struct amdgpu_xcp_mgr *xcp_mgr,
|
|
int mode)
|
|
{
|
|
int ret, curr_mode, num_xcps = 0;
|
|
|
|
if (!xcp_mgr->funcs || !xcp_mgr->funcs->switch_partition_mode)
|
|
return 0;
|
|
|
|
mutex_lock(&xcp_mgr->xcp_lock);
|
|
|
|
curr_mode = xcp_mgr->mode;
|
|
/* State set to transient mode */
|
|
xcp_mgr->mode = AMDGPU_XCP_MODE_TRANS;
|
|
|
|
ret = xcp_mgr->funcs->switch_partition_mode(xcp_mgr, mode, &num_xcps);
|
|
|
|
if (ret) {
|
|
/* Failed, get whatever mode it's at now */
|
|
if (xcp_mgr->funcs->query_partition_mode)
|
|
xcp_mgr->mode = amdgpu_xcp_query_partition_mode(
|
|
xcp_mgr, AMDGPU_XCP_FL_LOCKED);
|
|
else
|
|
xcp_mgr->mode = curr_mode;
|
|
|
|
goto out;
|
|
}
|
|
amdgpu_xcp_sysfs_entries_update(xcp_mgr);
|
|
out:
|
|
mutex_unlock(&xcp_mgr->xcp_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int amdgpu_xcp_switch_partition_mode(struct amdgpu_xcp_mgr *xcp_mgr, int mode)
|
|
{
|
|
if (!xcp_mgr || mode == AMDGPU_XCP_MODE_NONE)
|
|
return -EINVAL;
|
|
|
|
if (xcp_mgr->mode == mode)
|
|
return 0;
|
|
|
|
return __amdgpu_xcp_switch_partition_mode(xcp_mgr, mode);
|
|
}
|
|
|
|
int amdgpu_xcp_restore_partition_mode(struct amdgpu_xcp_mgr *xcp_mgr)
|
|
{
|
|
if (!xcp_mgr || xcp_mgr->mode == AMDGPU_XCP_MODE_NONE)
|
|
return 0;
|
|
|
|
return __amdgpu_xcp_switch_partition_mode(xcp_mgr, xcp_mgr->mode);
|
|
}
|
|
|
|
static bool __amdgpu_xcp_is_cached_mode_valid(struct amdgpu_xcp_mgr *xcp_mgr)
|
|
{
|
|
if (!xcp_mgr->funcs || !xcp_mgr->funcs->query_partition_mode)
|
|
return true;
|
|
|
|
if (!amdgpu_sriov_vf(xcp_mgr->adev) &&
|
|
xcp_mgr->mode == AMDGPU_XCP_MODE_NONE)
|
|
return true;
|
|
|
|
if (xcp_mgr->mode != AMDGPU_XCP_MODE_NONE &&
|
|
xcp_mgr->mode != AMDGPU_XCP_MODE_TRANS)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
int amdgpu_xcp_query_partition_mode(struct amdgpu_xcp_mgr *xcp_mgr, u32 flags)
|
|
{
|
|
int mode;
|
|
|
|
if (__amdgpu_xcp_is_cached_mode_valid(xcp_mgr))
|
|
return xcp_mgr->mode;
|
|
|
|
if (!(flags & AMDGPU_XCP_FL_LOCKED))
|
|
mutex_lock(&xcp_mgr->xcp_lock);
|
|
mode = xcp_mgr->funcs->query_partition_mode(xcp_mgr);
|
|
|
|
/* First time query for VF, set the mode here */
|
|
if (amdgpu_sriov_vf(xcp_mgr->adev) &&
|
|
xcp_mgr->mode == AMDGPU_XCP_MODE_NONE)
|
|
xcp_mgr->mode = mode;
|
|
|
|
if (xcp_mgr->mode != AMDGPU_XCP_MODE_TRANS && mode != xcp_mgr->mode)
|
|
dev_WARN(
|
|
xcp_mgr->adev->dev,
|
|
"Cached partition mode %d not matching with device mode %d",
|
|
xcp_mgr->mode, mode);
|
|
|
|
if (!(flags & AMDGPU_XCP_FL_LOCKED))
|
|
mutex_unlock(&xcp_mgr->xcp_lock);
|
|
|
|
return mode;
|
|
}
|
|
|
|
static int amdgpu_xcp_dev_alloc(struct amdgpu_device *adev)
|
|
{
|
|
struct drm_device *p_ddev;
|
|
struct drm_device *ddev;
|
|
int i, ret;
|
|
|
|
ddev = adev_to_drm(adev);
|
|
|
|
/* xcp #0 shares drm device setting with adev */
|
|
adev->xcp_mgr->xcp->ddev = ddev;
|
|
|
|
for (i = 1; i < MAX_XCP; i++) {
|
|
ret = amdgpu_xcp_drm_dev_alloc(&p_ddev);
|
|
if (ret == -ENOSPC) {
|
|
dev_warn(adev->dev,
|
|
"Skip xcp node #%d when out of drm node resource.", i);
|
|
ret = 0;
|
|
goto out;
|
|
} else if (ret) {
|
|
goto out;
|
|
}
|
|
|
|
/* Redirect all IOCTLs to the primary device */
|
|
adev->xcp_mgr->xcp[i].rdev = p_ddev->render->dev;
|
|
adev->xcp_mgr->xcp[i].pdev = p_ddev->primary->dev;
|
|
adev->xcp_mgr->xcp[i].driver = (struct drm_driver *)p_ddev->driver;
|
|
adev->xcp_mgr->xcp[i].vma_offset_manager = p_ddev->vma_offset_manager;
|
|
p_ddev->render->dev = ddev;
|
|
p_ddev->primary->dev = ddev;
|
|
p_ddev->vma_offset_manager = ddev->vma_offset_manager;
|
|
p_ddev->driver = &amdgpu_partition_driver;
|
|
adev->xcp_mgr->xcp[i].ddev = p_ddev;
|
|
|
|
dev_set_drvdata(p_ddev->dev, &adev->xcp_mgr->xcp[i]);
|
|
}
|
|
ret = 0;
|
|
out:
|
|
amdgpu_xcp_sysfs_entries_init(adev->xcp_mgr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int amdgpu_xcp_mgr_init(struct amdgpu_device *adev, int init_mode,
|
|
int init_num_xcps,
|
|
struct amdgpu_xcp_mgr_funcs *xcp_funcs)
|
|
{
|
|
struct amdgpu_xcp_mgr *xcp_mgr;
|
|
int i;
|
|
|
|
if (!xcp_funcs || !xcp_funcs->get_ip_details)
|
|
return -EINVAL;
|
|
|
|
xcp_mgr = kzalloc_obj(*xcp_mgr);
|
|
|
|
if (!xcp_mgr)
|
|
return -ENOMEM;
|
|
|
|
xcp_mgr->adev = adev;
|
|
xcp_mgr->funcs = xcp_funcs;
|
|
xcp_mgr->mode = init_mode;
|
|
mutex_init(&xcp_mgr->xcp_lock);
|
|
|
|
if (init_mode != AMDGPU_XCP_MODE_NONE)
|
|
amdgpu_xcp_init(xcp_mgr, init_num_xcps, init_mode);
|
|
|
|
adev->xcp_mgr = xcp_mgr;
|
|
for (i = 0; i < MAX_XCP; ++i)
|
|
xcp_mgr->xcp[i].xcp_mgr = xcp_mgr;
|
|
|
|
return amdgpu_xcp_dev_alloc(adev);
|
|
}
|
|
|
|
int amdgpu_xcp_get_partition(struct amdgpu_xcp_mgr *xcp_mgr,
|
|
enum AMDGPU_XCP_IP_BLOCK ip, int instance)
|
|
{
|
|
struct amdgpu_xcp *xcp;
|
|
int i, id_mask = 0;
|
|
|
|
if (ip >= AMDGPU_XCP_MAX_BLOCKS)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < xcp_mgr->num_xcps; ++i) {
|
|
xcp = &xcp_mgr->xcp[i];
|
|
if ((xcp->valid) && (xcp->ip[ip].valid) &&
|
|
(xcp->ip[ip].inst_mask & BIT(instance)))
|
|
id_mask |= BIT(i);
|
|
}
|
|
|
|
if (!id_mask)
|
|
id_mask = -ENXIO;
|
|
|
|
return id_mask;
|
|
}
|
|
|
|
int amdgpu_xcp_get_inst_details(struct amdgpu_xcp *xcp,
|
|
enum AMDGPU_XCP_IP_BLOCK ip,
|
|
uint32_t *inst_mask)
|
|
{
|
|
if (!xcp->valid || !inst_mask || !(xcp->ip[ip].valid))
|
|
return -EINVAL;
|
|
|
|
*inst_mask = xcp->ip[ip].inst_mask;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
|
|
const struct pci_device_id *ent)
|
|
{
|
|
int i, ret;
|
|
|
|
if (!adev->xcp_mgr)
|
|
return 0;
|
|
|
|
for (i = 1; i < MAX_XCP; i++) {
|
|
if (!adev->xcp_mgr->xcp[i].ddev)
|
|
break;
|
|
|
|
ret = drm_dev_register(adev->xcp_mgr->xcp[i].ddev, ent->driver_data);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void amdgpu_xcp_dev_unplug(struct amdgpu_device *adev)
|
|
{
|
|
struct drm_device *p_ddev;
|
|
int i;
|
|
|
|
if (!adev->xcp_mgr)
|
|
return;
|
|
|
|
for (i = 1; i < MAX_XCP; i++) {
|
|
if (!adev->xcp_mgr->xcp[i].ddev)
|
|
break;
|
|
|
|
p_ddev = adev->xcp_mgr->xcp[i].ddev;
|
|
drm_dev_unplug(p_ddev);
|
|
p_ddev->render->dev = adev->xcp_mgr->xcp[i].rdev;
|
|
p_ddev->primary->dev = adev->xcp_mgr->xcp[i].pdev;
|
|
p_ddev->driver = adev->xcp_mgr->xcp[i].driver;
|
|
p_ddev->vma_offset_manager = adev->xcp_mgr->xcp[i].vma_offset_manager;
|
|
amdgpu_xcp_drm_dev_free(p_ddev);
|
|
}
|
|
}
|
|
|
|
int amdgpu_xcp_open_device(struct amdgpu_device *adev,
|
|
struct amdgpu_fpriv *fpriv,
|
|
struct drm_file *file_priv)
|
|
{
|
|
int i;
|
|
|
|
if (!adev->xcp_mgr)
|
|
return 0;
|
|
|
|
fpriv->xcp_id = AMDGPU_XCP_NO_PARTITION;
|
|
for (i = 0; i < MAX_XCP; ++i) {
|
|
if (!adev->xcp_mgr->xcp[i].ddev)
|
|
break;
|
|
|
|
if (file_priv->minor == adev->xcp_mgr->xcp[i].ddev->render) {
|
|
if (adev->xcp_mgr->xcp[i].valid == FALSE) {
|
|
dev_err(adev->dev, "renderD%d partition %d not valid!",
|
|
file_priv->minor->index, i);
|
|
return -ENOENT;
|
|
}
|
|
dev_dbg(adev->dev, "renderD%d partition %d opened!",
|
|
file_priv->minor->index, i);
|
|
fpriv->xcp_id = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
fpriv->vm.mem_id = fpriv->xcp_id == AMDGPU_XCP_NO_PARTITION ? -1 :
|
|
adev->xcp_mgr->xcp[fpriv->xcp_id].mem_id;
|
|
return 0;
|
|
}
|
|
|
|
void amdgpu_xcp_release_sched(struct amdgpu_device *adev,
|
|
struct amdgpu_ctx_entity *entity)
|
|
{
|
|
struct drm_gpu_scheduler *sched;
|
|
struct amdgpu_ring *ring;
|
|
|
|
if (!adev->xcp_mgr)
|
|
return;
|
|
|
|
sched = entity->entity.rq->sched;
|
|
if (drm_sched_wqueue_ready(sched)) {
|
|
ring = to_amdgpu_ring(entity->entity.rq->sched);
|
|
atomic_dec(&adev->xcp_mgr->xcp[ring->xcp_id].ref_cnt);
|
|
}
|
|
}
|
|
|
|
int amdgpu_xcp_select_scheds(struct amdgpu_device *adev,
|
|
u32 hw_ip, u32 hw_prio,
|
|
struct amdgpu_fpriv *fpriv,
|
|
unsigned int *num_scheds,
|
|
struct drm_gpu_scheduler ***scheds)
|
|
{
|
|
u32 sel_xcp_id;
|
|
int i;
|
|
struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
|
|
|
|
if (fpriv->xcp_id == AMDGPU_XCP_NO_PARTITION) {
|
|
u32 least_ref_cnt = ~0;
|
|
|
|
fpriv->xcp_id = 0;
|
|
for (i = 0; i < xcp_mgr->num_xcps; i++) {
|
|
u32 total_ref_cnt;
|
|
|
|
total_ref_cnt = atomic_read(&xcp_mgr->xcp[i].ref_cnt);
|
|
if (total_ref_cnt < least_ref_cnt) {
|
|
fpriv->xcp_id = i;
|
|
least_ref_cnt = total_ref_cnt;
|
|
}
|
|
}
|
|
}
|
|
sel_xcp_id = fpriv->xcp_id;
|
|
|
|
if (xcp_mgr->xcp[sel_xcp_id].gpu_sched[hw_ip][hw_prio].num_scheds) {
|
|
*num_scheds =
|
|
xcp_mgr->xcp[fpriv->xcp_id].gpu_sched[hw_ip][hw_prio].num_scheds;
|
|
*scheds =
|
|
xcp_mgr->xcp[fpriv->xcp_id].gpu_sched[hw_ip][hw_prio].sched;
|
|
atomic_inc(&adev->xcp_mgr->xcp[sel_xcp_id].ref_cnt);
|
|
dev_dbg(adev->dev, "Selected partition #%d", sel_xcp_id);
|
|
} else {
|
|
dev_err(adev->dev, "Failed to schedule partition #%d.", sel_xcp_id);
|
|
return -ENOENT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void amdgpu_set_xcp_id(struct amdgpu_device *adev,
|
|
uint32_t inst_idx,
|
|
struct amdgpu_ring *ring)
|
|
{
|
|
int xcp_id;
|
|
enum AMDGPU_XCP_IP_BLOCK ip_blk;
|
|
uint32_t inst_mask;
|
|
|
|
ring->xcp_id = AMDGPU_XCP_NO_PARTITION;
|
|
if (ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE)
|
|
adev->gfx.enforce_isolation[0].xcp_id = ring->xcp_id;
|
|
if ((adev->xcp_mgr->mode == AMDGPU_XCP_MODE_NONE) ||
|
|
(ring->funcs->type == AMDGPU_RING_TYPE_CPER))
|
|
return;
|
|
|
|
inst_mask = 1 << inst_idx;
|
|
|
|
switch (ring->funcs->type) {
|
|
case AMDGPU_HW_IP_GFX:
|
|
case AMDGPU_RING_TYPE_COMPUTE:
|
|
case AMDGPU_RING_TYPE_KIQ:
|
|
case AMDGPU_RING_TYPE_MES:
|
|
ip_blk = AMDGPU_XCP_GFX;
|
|
break;
|
|
case AMDGPU_RING_TYPE_SDMA:
|
|
ip_blk = AMDGPU_XCP_SDMA;
|
|
break;
|
|
case AMDGPU_RING_TYPE_VCN_ENC:
|
|
case AMDGPU_RING_TYPE_VCN_JPEG:
|
|
ip_blk = AMDGPU_XCP_VCN;
|
|
break;
|
|
default:
|
|
dev_err(adev->dev, "Not support ring type %d!", ring->funcs->type);
|
|
return;
|
|
}
|
|
|
|
for (xcp_id = 0; xcp_id < adev->xcp_mgr->num_xcps; xcp_id++) {
|
|
if (adev->xcp_mgr->xcp[xcp_id].ip[ip_blk].inst_mask & inst_mask) {
|
|
ring->xcp_id = xcp_id;
|
|
dev_dbg(adev->dev, "ring:%s xcp_id :%u", ring->name,
|
|
ring->xcp_id);
|
|
if (ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE)
|
|
adev->gfx.enforce_isolation[xcp_id].xcp_id = xcp_id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void amdgpu_xcp_gpu_sched_update(struct amdgpu_device *adev,
|
|
struct amdgpu_ring *ring,
|
|
unsigned int sel_xcp_id)
|
|
{
|
|
unsigned int *num_gpu_sched;
|
|
|
|
num_gpu_sched = &adev->xcp_mgr->xcp[sel_xcp_id]
|
|
.gpu_sched[ring->funcs->type][ring->hw_prio].num_scheds;
|
|
adev->xcp_mgr->xcp[sel_xcp_id].gpu_sched[ring->funcs->type][ring->hw_prio]
|
|
.sched[(*num_gpu_sched)++] = &ring->sched;
|
|
dev_dbg(adev->dev, "%s :[%d] gpu_sched[%d][%d] = %d",
|
|
ring->name, sel_xcp_id, ring->funcs->type,
|
|
ring->hw_prio, *num_gpu_sched);
|
|
}
|
|
|
|
static int amdgpu_xcp_sched_list_update(struct amdgpu_device *adev)
|
|
{
|
|
struct amdgpu_ring *ring;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_XCP; i++) {
|
|
atomic_set(&adev->xcp_mgr->xcp[i].ref_cnt, 0);
|
|
memset(adev->xcp_mgr->xcp[i].gpu_sched, 0, sizeof(adev->xcp_mgr->xcp->gpu_sched));
|
|
}
|
|
|
|
if (adev->xcp_mgr->mode == AMDGPU_XCP_MODE_NONE)
|
|
return 0;
|
|
|
|
for (i = 0; i < AMDGPU_MAX_RINGS; i++) {
|
|
ring = adev->rings[i];
|
|
if (!ring || !ring->sched.ready || ring->no_scheduler)
|
|
continue;
|
|
|
|
amdgpu_xcp_gpu_sched_update(adev, ring, ring->xcp_id);
|
|
|
|
/* VCN may be shared by two partitions under CPX MODE in certain
|
|
* configs.
|
|
*/
|
|
if ((ring->funcs->type == AMDGPU_RING_TYPE_VCN_ENC ||
|
|
ring->funcs->type == AMDGPU_RING_TYPE_VCN_JPEG) &&
|
|
(adev->xcp_mgr->num_xcps > adev->vcn.num_vcn_inst))
|
|
amdgpu_xcp_gpu_sched_update(adev, ring, ring->xcp_id + 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int amdgpu_xcp_update_partition_sched_list(struct amdgpu_device *adev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < adev->num_rings; i++) {
|
|
struct amdgpu_ring *ring = adev->rings[i];
|
|
|
|
if (ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE ||
|
|
ring->funcs->type == AMDGPU_RING_TYPE_KIQ)
|
|
amdgpu_set_xcp_id(adev, ring->xcc_id, ring);
|
|
else
|
|
amdgpu_set_xcp_id(adev, ring->me, ring);
|
|
}
|
|
|
|
return amdgpu_xcp_sched_list_update(adev);
|
|
}
|
|
|
|
void amdgpu_xcp_update_supported_modes(struct amdgpu_xcp_mgr *xcp_mgr)
|
|
{
|
|
struct amdgpu_device *adev = xcp_mgr->adev;
|
|
|
|
xcp_mgr->supp_xcp_modes = 0;
|
|
|
|
switch (NUM_XCC(adev->gfx.xcc_mask)) {
|
|
case 8:
|
|
xcp_mgr->supp_xcp_modes = BIT(AMDGPU_SPX_PARTITION_MODE) |
|
|
BIT(AMDGPU_DPX_PARTITION_MODE) |
|
|
BIT(AMDGPU_QPX_PARTITION_MODE) |
|
|
BIT(AMDGPU_CPX_PARTITION_MODE);
|
|
break;
|
|
case 6:
|
|
xcp_mgr->supp_xcp_modes = BIT(AMDGPU_SPX_PARTITION_MODE) |
|
|
BIT(AMDGPU_TPX_PARTITION_MODE) |
|
|
BIT(AMDGPU_CPX_PARTITION_MODE);
|
|
break;
|
|
case 4:
|
|
xcp_mgr->supp_xcp_modes = BIT(AMDGPU_SPX_PARTITION_MODE) |
|
|
BIT(AMDGPU_DPX_PARTITION_MODE) |
|
|
BIT(AMDGPU_CPX_PARTITION_MODE);
|
|
break;
|
|
case 2:
|
|
xcp_mgr->supp_xcp_modes = BIT(AMDGPU_SPX_PARTITION_MODE) |
|
|
BIT(AMDGPU_CPX_PARTITION_MODE);
|
|
break;
|
|
case 1:
|
|
xcp_mgr->supp_xcp_modes = BIT(AMDGPU_SPX_PARTITION_MODE) |
|
|
BIT(AMDGPU_CPX_PARTITION_MODE);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int amdgpu_xcp_pre_partition_switch(struct amdgpu_xcp_mgr *xcp_mgr, u32 flags)
|
|
{
|
|
/* TODO:
|
|
* Stop user queues and threads, and make sure GPU is empty of work.
|
|
*/
|
|
|
|
if (flags & AMDGPU_XCP_OPS_KFD)
|
|
amdgpu_amdkfd_device_fini_sw(xcp_mgr->adev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int amdgpu_xcp_post_partition_switch(struct amdgpu_xcp_mgr *xcp_mgr, u32 flags)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (flags & AMDGPU_XCP_OPS_KFD) {
|
|
amdgpu_amdkfd_device_probe(xcp_mgr->adev);
|
|
amdgpu_amdkfd_device_init(xcp_mgr->adev);
|
|
/* If KFD init failed, return failure */
|
|
if (!xcp_mgr->adev->kfd.init_complete)
|
|
ret = -EIO;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*====================== xcp sysfs - configuration ======================*/
|
|
#define XCP_CFG_SYSFS_RES_ATTR_SHOW(_name) \
|
|
static ssize_t amdgpu_xcp_res_sysfs_##_name##_show( \
|
|
struct amdgpu_xcp_res_details *xcp_res, char *buf) \
|
|
{ \
|
|
return sysfs_emit(buf, "%d\n", xcp_res->_name); \
|
|
}
|
|
|
|
struct amdgpu_xcp_res_sysfs_attribute {
|
|
struct attribute attr;
|
|
ssize_t (*show)(struct amdgpu_xcp_res_details *xcp_res, char *buf);
|
|
};
|
|
|
|
#define XCP_CFG_SYSFS_RES_ATTR(_name) \
|
|
struct amdgpu_xcp_res_sysfs_attribute xcp_res_sysfs_attr_##_name = { \
|
|
.attr = { .name = __stringify(_name), .mode = 0400 }, \
|
|
.show = amdgpu_xcp_res_sysfs_##_name##_show, \
|
|
}
|
|
|
|
XCP_CFG_SYSFS_RES_ATTR_SHOW(num_inst)
|
|
XCP_CFG_SYSFS_RES_ATTR(num_inst);
|
|
XCP_CFG_SYSFS_RES_ATTR_SHOW(num_shared)
|
|
XCP_CFG_SYSFS_RES_ATTR(num_shared);
|
|
|
|
#define XCP_CFG_SYSFS_RES_ATTR_PTR(_name) xcp_res_sysfs_attr_##_name.attr
|
|
|
|
static struct attribute *xcp_cfg_res_sysfs_attrs[] = {
|
|
&XCP_CFG_SYSFS_RES_ATTR_PTR(num_inst),
|
|
&XCP_CFG_SYSFS_RES_ATTR_PTR(num_shared), NULL
|
|
};
|
|
|
|
static const char *xcp_desc[] = {
|
|
[AMDGPU_SPX_PARTITION_MODE] = "SPX",
|
|
[AMDGPU_DPX_PARTITION_MODE] = "DPX",
|
|
[AMDGPU_TPX_PARTITION_MODE] = "TPX",
|
|
[AMDGPU_QPX_PARTITION_MODE] = "QPX",
|
|
[AMDGPU_CPX_PARTITION_MODE] = "CPX",
|
|
};
|
|
|
|
static const char *nps_desc[] = {
|
|
[UNKNOWN_MEMORY_PARTITION_MODE] = "UNKNOWN",
|
|
[AMDGPU_NPS1_PARTITION_MODE] = "NPS1",
|
|
[AMDGPU_NPS2_PARTITION_MODE] = "NPS2",
|
|
[AMDGPU_NPS3_PARTITION_MODE] = "NPS3",
|
|
[AMDGPU_NPS4_PARTITION_MODE] = "NPS4",
|
|
[AMDGPU_NPS6_PARTITION_MODE] = "NPS6",
|
|
[AMDGPU_NPS8_PARTITION_MODE] = "NPS8",
|
|
};
|
|
|
|
ATTRIBUTE_GROUPS(xcp_cfg_res_sysfs);
|
|
|
|
#define to_xcp_attr(x) \
|
|
container_of(x, struct amdgpu_xcp_res_sysfs_attribute, attr)
|
|
#define to_xcp_res(x) container_of(x, struct amdgpu_xcp_res_details, kobj)
|
|
|
|
static ssize_t xcp_cfg_res_sysfs_attr_show(struct kobject *kobj,
|
|
struct attribute *attr, char *buf)
|
|
{
|
|
struct amdgpu_xcp_res_sysfs_attribute *attribute;
|
|
struct amdgpu_xcp_res_details *xcp_res;
|
|
|
|
attribute = to_xcp_attr(attr);
|
|
xcp_res = to_xcp_res(kobj);
|
|
|
|
if (!attribute->show)
|
|
return -EIO;
|
|
|
|
return attribute->show(xcp_res, buf);
|
|
}
|
|
|
|
static const struct sysfs_ops xcp_cfg_res_sysfs_ops = {
|
|
.show = xcp_cfg_res_sysfs_attr_show,
|
|
};
|
|
|
|
static const struct kobj_type xcp_cfg_res_sysfs_ktype = {
|
|
.sysfs_ops = &xcp_cfg_res_sysfs_ops,
|
|
.default_groups = xcp_cfg_res_sysfs_groups,
|
|
};
|
|
|
|
const char *xcp_res_names[] = {
|
|
[AMDGPU_XCP_RES_XCC] = "xcc",
|
|
[AMDGPU_XCP_RES_DMA] = "dma",
|
|
[AMDGPU_XCP_RES_DEC] = "dec",
|
|
[AMDGPU_XCP_RES_JPEG] = "jpeg",
|
|
};
|
|
|
|
static int amdgpu_xcp_get_res_info(struct amdgpu_xcp_mgr *xcp_mgr,
|
|
int mode,
|
|
struct amdgpu_xcp_cfg *xcp_cfg)
|
|
{
|
|
if (xcp_mgr->funcs && xcp_mgr->funcs->get_xcp_res_info)
|
|
return xcp_mgr->funcs->get_xcp_res_info(xcp_mgr, mode, xcp_cfg);
|
|
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
#define to_xcp_cfg(x) container_of(x, struct amdgpu_xcp_cfg, kobj)
|
|
static ssize_t supported_xcp_configs_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct amdgpu_xcp_cfg *xcp_cfg = to_xcp_cfg(kobj);
|
|
struct amdgpu_xcp_mgr *xcp_mgr = xcp_cfg->xcp_mgr;
|
|
int size = 0, mode;
|
|
char *sep = "";
|
|
|
|
if (!xcp_mgr || !xcp_mgr->supp_xcp_modes)
|
|
return sysfs_emit(buf, "Not supported\n");
|
|
|
|
for_each_inst(mode, xcp_mgr->supp_xcp_modes) {
|
|
size += sysfs_emit_at(buf, size, "%s%s", sep, xcp_desc[mode]);
|
|
sep = ", ";
|
|
}
|
|
|
|
size += sysfs_emit_at(buf, size, "\n");
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t supported_nps_configs_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct amdgpu_xcp_cfg *xcp_cfg = to_xcp_cfg(kobj);
|
|
int size = 0, mode;
|
|
char *sep = "";
|
|
|
|
if (!xcp_cfg || !xcp_cfg->compatible_nps_modes)
|
|
return sysfs_emit(buf, "Not supported\n");
|
|
|
|
for_each_inst(mode, xcp_cfg->compatible_nps_modes) {
|
|
size += sysfs_emit_at(buf, size, "%s%s", sep, nps_desc[mode]);
|
|
sep = ", ";
|
|
}
|
|
|
|
size += sysfs_emit_at(buf, size, "\n");
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t xcp_config_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct amdgpu_xcp_cfg *xcp_cfg = to_xcp_cfg(kobj);
|
|
|
|
return sysfs_emit(buf, "%s\n",
|
|
amdgpu_gfx_compute_mode_desc(xcp_cfg->mode));
|
|
}
|
|
|
|
static ssize_t xcp_config_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct amdgpu_xcp_cfg *xcp_cfg = to_xcp_cfg(kobj);
|
|
int mode, r;
|
|
|
|
if (!strncasecmp("SPX", buf, strlen("SPX")))
|
|
mode = AMDGPU_SPX_PARTITION_MODE;
|
|
else if (!strncasecmp("DPX", buf, strlen("DPX")))
|
|
mode = AMDGPU_DPX_PARTITION_MODE;
|
|
else if (!strncasecmp("TPX", buf, strlen("TPX")))
|
|
mode = AMDGPU_TPX_PARTITION_MODE;
|
|
else if (!strncasecmp("QPX", buf, strlen("QPX")))
|
|
mode = AMDGPU_QPX_PARTITION_MODE;
|
|
else if (!strncasecmp("CPX", buf, strlen("CPX")))
|
|
mode = AMDGPU_CPX_PARTITION_MODE;
|
|
else
|
|
return -EINVAL;
|
|
|
|
r = amdgpu_xcp_get_res_info(xcp_cfg->xcp_mgr, mode, xcp_cfg);
|
|
|
|
if (r)
|
|
return r;
|
|
|
|
xcp_cfg->mode = mode;
|
|
return size;
|
|
}
|
|
|
|
static struct kobj_attribute xcp_cfg_sysfs_mode =
|
|
__ATTR_RW_MODE(xcp_config, 0644);
|
|
|
|
static void xcp_cfg_sysfs_release(struct kobject *kobj)
|
|
{
|
|
struct amdgpu_xcp_cfg *xcp_cfg = to_xcp_cfg(kobj);
|
|
|
|
kfree(xcp_cfg);
|
|
}
|
|
|
|
static const struct kobj_type xcp_cfg_sysfs_ktype = {
|
|
.release = xcp_cfg_sysfs_release,
|
|
.sysfs_ops = &kobj_sysfs_ops,
|
|
};
|
|
|
|
static struct kobj_attribute supp_part_sysfs_mode =
|
|
__ATTR_RO(supported_xcp_configs);
|
|
|
|
static struct kobj_attribute supp_nps_sysfs_mode =
|
|
__ATTR_RO(supported_nps_configs);
|
|
|
|
static const struct attribute *xcp_attrs[] = {
|
|
&supp_part_sysfs_mode.attr,
|
|
&xcp_cfg_sysfs_mode.attr,
|
|
NULL,
|
|
};
|
|
|
|
static void amdgpu_xcp_cfg_sysfs_init(struct amdgpu_device *adev)
|
|
{
|
|
struct amdgpu_xcp_res_details *xcp_res;
|
|
struct amdgpu_xcp_cfg *xcp_cfg;
|
|
int i, r, j, rid, mode;
|
|
|
|
if (!adev->xcp_mgr)
|
|
return;
|
|
|
|
xcp_cfg = kzalloc_obj(*xcp_cfg);
|
|
if (!xcp_cfg)
|
|
return;
|
|
xcp_cfg->xcp_mgr = adev->xcp_mgr;
|
|
|
|
r = kobject_init_and_add(&xcp_cfg->kobj, &xcp_cfg_sysfs_ktype,
|
|
&adev->dev->kobj, "compute_partition_config");
|
|
if (r)
|
|
goto err1;
|
|
|
|
r = sysfs_create_files(&xcp_cfg->kobj, xcp_attrs);
|
|
if (r)
|
|
goto err1;
|
|
|
|
if (adev->gmc.supported_nps_modes != 0) {
|
|
r = sysfs_create_file(&xcp_cfg->kobj, &supp_nps_sysfs_mode.attr);
|
|
if (r) {
|
|
sysfs_remove_files(&xcp_cfg->kobj, xcp_attrs);
|
|
goto err1;
|
|
}
|
|
}
|
|
|
|
mode = (xcp_cfg->xcp_mgr->mode ==
|
|
AMDGPU_UNKNOWN_COMPUTE_PARTITION_MODE) ?
|
|
AMDGPU_SPX_PARTITION_MODE :
|
|
xcp_cfg->xcp_mgr->mode;
|
|
r = amdgpu_xcp_get_res_info(xcp_cfg->xcp_mgr, mode, xcp_cfg);
|
|
if (r) {
|
|
sysfs_remove_file(&xcp_cfg->kobj, &supp_nps_sysfs_mode.attr);
|
|
sysfs_remove_files(&xcp_cfg->kobj, xcp_attrs);
|
|
goto err1;
|
|
}
|
|
|
|
xcp_cfg->mode = mode;
|
|
for (i = 0; i < xcp_cfg->num_res; i++) {
|
|
xcp_res = &xcp_cfg->xcp_res[i];
|
|
rid = xcp_res->id;
|
|
r = kobject_init_and_add(&xcp_res->kobj,
|
|
&xcp_cfg_res_sysfs_ktype,
|
|
&xcp_cfg->kobj, "%s",
|
|
xcp_res_names[rid]);
|
|
if (r)
|
|
goto err;
|
|
}
|
|
|
|
adev->xcp_mgr->xcp_cfg = xcp_cfg;
|
|
return;
|
|
err:
|
|
for (j = 0; j < i; j++) {
|
|
xcp_res = &xcp_cfg->xcp_res[i];
|
|
kobject_put(&xcp_res->kobj);
|
|
}
|
|
|
|
sysfs_remove_file(&xcp_cfg->kobj, &supp_nps_sysfs_mode.attr);
|
|
sysfs_remove_files(&xcp_cfg->kobj, xcp_attrs);
|
|
err1:
|
|
kobject_put(&xcp_cfg->kobj);
|
|
}
|
|
|
|
static void amdgpu_xcp_cfg_sysfs_fini(struct amdgpu_device *adev)
|
|
{
|
|
struct amdgpu_xcp_res_details *xcp_res;
|
|
struct amdgpu_xcp_cfg *xcp_cfg;
|
|
int i;
|
|
|
|
if (!adev->xcp_mgr || !adev->xcp_mgr->xcp_cfg)
|
|
return;
|
|
|
|
xcp_cfg = adev->xcp_mgr->xcp_cfg;
|
|
for (i = 0; i < xcp_cfg->num_res; i++) {
|
|
xcp_res = &xcp_cfg->xcp_res[i];
|
|
kobject_put(&xcp_res->kobj);
|
|
}
|
|
|
|
sysfs_remove_file(&xcp_cfg->kobj, &supp_nps_sysfs_mode.attr);
|
|
sysfs_remove_files(&xcp_cfg->kobj, xcp_attrs);
|
|
kobject_put(&xcp_cfg->kobj);
|
|
}
|
|
|
|
/*====================== xcp sysfs - data entries ======================*/
|
|
|
|
#define to_xcp(x) container_of(x, struct amdgpu_xcp, kobj)
|
|
|
|
static ssize_t xcp_metrics_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct amdgpu_xcp *xcp = to_xcp(kobj);
|
|
struct amdgpu_xcp_mgr *xcp_mgr;
|
|
ssize_t size;
|
|
|
|
xcp_mgr = xcp->xcp_mgr;
|
|
size = amdgpu_dpm_get_xcp_metrics(xcp_mgr->adev, xcp->id, NULL);
|
|
if (size <= 0)
|
|
return size;
|
|
|
|
if (size > PAGE_SIZE)
|
|
return -ENOSPC;
|
|
|
|
return amdgpu_dpm_get_xcp_metrics(xcp_mgr->adev, xcp->id, buf);
|
|
}
|
|
|
|
static umode_t amdgpu_xcp_attrs_is_visible(struct kobject *kobj,
|
|
struct attribute *attr, int n)
|
|
{
|
|
struct amdgpu_xcp *xcp = to_xcp(kobj);
|
|
|
|
if (!xcp || !xcp->valid)
|
|
return 0;
|
|
|
|
return attr->mode;
|
|
}
|
|
|
|
static struct kobj_attribute xcp_sysfs_metrics = __ATTR_RO(xcp_metrics);
|
|
|
|
static struct attribute *amdgpu_xcp_attrs[] = {
|
|
&xcp_sysfs_metrics.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group amdgpu_xcp_attrs_group = {
|
|
.attrs = amdgpu_xcp_attrs,
|
|
.is_visible = amdgpu_xcp_attrs_is_visible
|
|
};
|
|
|
|
static const struct kobj_type xcp_sysfs_ktype = {
|
|
.sysfs_ops = &kobj_sysfs_ops,
|
|
};
|
|
|
|
static void amdgpu_xcp_sysfs_entries_fini(struct amdgpu_xcp_mgr *xcp_mgr, int n)
|
|
{
|
|
struct amdgpu_xcp *xcp;
|
|
|
|
for (n--; n >= 0; n--) {
|
|
xcp = &xcp_mgr->xcp[n];
|
|
if (!xcp->ddev || !xcp->valid)
|
|
continue;
|
|
sysfs_remove_group(&xcp->kobj, &amdgpu_xcp_attrs_group);
|
|
kobject_put(&xcp->kobj);
|
|
}
|
|
}
|
|
|
|
static void amdgpu_xcp_sysfs_entries_init(struct amdgpu_xcp_mgr *xcp_mgr)
|
|
{
|
|
struct amdgpu_xcp *xcp;
|
|
int i, r;
|
|
|
|
for (i = 0; i < MAX_XCP; i++) {
|
|
/* Redirect all IOCTLs to the primary device */
|
|
xcp = &xcp_mgr->xcp[i];
|
|
if (!xcp->ddev)
|
|
break;
|
|
r = kobject_init_and_add(&xcp->kobj, &xcp_sysfs_ktype,
|
|
&xcp->ddev->dev->kobj, "xcp");
|
|
if (r)
|
|
goto out;
|
|
|
|
r = sysfs_create_group(&xcp->kobj, &amdgpu_xcp_attrs_group);
|
|
if (r)
|
|
goto out;
|
|
}
|
|
|
|
return;
|
|
out:
|
|
kobject_put(&xcp->kobj);
|
|
}
|
|
|
|
static void amdgpu_xcp_sysfs_entries_update(struct amdgpu_xcp_mgr *xcp_mgr)
|
|
{
|
|
struct amdgpu_xcp *xcp;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_XCP; i++) {
|
|
/* Redirect all IOCTLs to the primary device */
|
|
xcp = &xcp_mgr->xcp[i];
|
|
if (!xcp->ddev)
|
|
continue;
|
|
sysfs_update_group(&xcp->kobj, &amdgpu_xcp_attrs_group);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void amdgpu_xcp_sysfs_init(struct amdgpu_device *adev)
|
|
{
|
|
if (!adev->xcp_mgr)
|
|
return;
|
|
|
|
amdgpu_xcp_cfg_sysfs_init(adev);
|
|
|
|
return;
|
|
}
|
|
|
|
void amdgpu_xcp_sysfs_fini(struct amdgpu_device *adev)
|
|
{
|
|
if (!adev->xcp_mgr)
|
|
return;
|
|
amdgpu_xcp_sysfs_entries_fini(adev->xcp_mgr, MAX_XCP);
|
|
amdgpu_xcp_cfg_sysfs_fini(adev);
|
|
}
|