mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
synced 2026-04-27 03:49:57 -04:00
drm/amdgpu: separate VMID and PASID handling
Move both into the new files amdgpu_ids.[ch]. No functional change. Signed-off-by: Christian König <christian.koenig@amd.com> Reviewed-by: Chunming Zhou <david1.zhou@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
committed by
Alex Deucher
parent
df2869abd9
commit
620f774f46
@@ -33,52 +33,6 @@
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_trace.h"
|
||||
|
||||
/*
|
||||
* PASID manager
|
||||
*
|
||||
* PASIDs are global address space identifiers that can be shared
|
||||
* between the GPU, an IOMMU and the driver. VMs on different devices
|
||||
* may use the same PASID if they share the same address
|
||||
* space. Therefore PASIDs are allocated using a global IDA. VMs are
|
||||
* looked up from the PASID per amdgpu_device.
|
||||
*/
|
||||
static DEFINE_IDA(amdgpu_vm_pasid_ida);
|
||||
|
||||
/**
|
||||
* amdgpu_vm_alloc_pasid - Allocate a PASID
|
||||
* @bits: Maximum width of the PASID in bits, must be at least 1
|
||||
*
|
||||
* Allocates a PASID of the given width while keeping smaller PASIDs
|
||||
* available if possible.
|
||||
*
|
||||
* Returns a positive integer on success. Returns %-EINVAL if bits==0.
|
||||
* Returns %-ENOSPC if no PASID was available. Returns %-ENOMEM on
|
||||
* memory allocation failure.
|
||||
*/
|
||||
int amdgpu_vm_alloc_pasid(unsigned int bits)
|
||||
{
|
||||
int pasid = -EINVAL;
|
||||
|
||||
for (bits = min(bits, 31U); bits > 0; bits--) {
|
||||
pasid = ida_simple_get(&amdgpu_vm_pasid_ida,
|
||||
1U << (bits - 1), 1U << bits,
|
||||
GFP_KERNEL);
|
||||
if (pasid != -ENOSPC)
|
||||
break;
|
||||
}
|
||||
|
||||
return pasid;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_free_pasid - Free a PASID
|
||||
* @pasid: PASID to free
|
||||
*/
|
||||
void amdgpu_vm_free_pasid(unsigned int pasid)
|
||||
{
|
||||
ida_simple_remove(&amdgpu_vm_pasid_ida, pasid);
|
||||
}
|
||||
|
||||
/*
|
||||
* GPUVM
|
||||
* GPUVM is similar to the legacy gart on older asics, however
|
||||
@@ -447,286 +401,6 @@ int amdgpu_vm_alloc_pts(struct amdgpu_device *adev,
|
||||
adev->vm_manager.root_level);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_had_gpu_reset - check if reset occured since last use
|
||||
*
|
||||
* @adev: amdgpu_device pointer
|
||||
* @id: VMID structure
|
||||
*
|
||||
* Check if GPU reset occured since last use of the VMID.
|
||||
*/
|
||||
static bool amdgpu_vm_had_gpu_reset(struct amdgpu_device *adev,
|
||||
struct amdgpu_vm_id *id)
|
||||
{
|
||||
return id->current_gpu_reset_count !=
|
||||
atomic_read(&adev->gpu_reset_counter);
|
||||
}
|
||||
|
||||
static bool amdgpu_vm_reserved_vmid_ready(struct amdgpu_vm *vm, unsigned vmhub)
|
||||
{
|
||||
return !!vm->reserved_vmid[vmhub];
|
||||
}
|
||||
|
||||
/* idr_mgr->lock must be held */
|
||||
static int amdgpu_vm_grab_reserved_vmid_locked(struct amdgpu_vm *vm,
|
||||
struct amdgpu_ring *ring,
|
||||
struct amdgpu_sync *sync,
|
||||
struct dma_fence *fence,
|
||||
struct amdgpu_job *job)
|
||||
{
|
||||
struct amdgpu_device *adev = ring->adev;
|
||||
unsigned vmhub = ring->funcs->vmhub;
|
||||
uint64_t fence_context = adev->fence_context + ring->idx;
|
||||
struct amdgpu_vm_id *id = vm->reserved_vmid[vmhub];
|
||||
struct amdgpu_vm_id_manager *id_mgr = &adev->vm_manager.id_mgr[vmhub];
|
||||
struct dma_fence *updates = sync->last_vm_update;
|
||||
int r = 0;
|
||||
struct dma_fence *flushed, *tmp;
|
||||
bool needs_flush = vm->use_cpu_for_update;
|
||||
|
||||
flushed = id->flushed_updates;
|
||||
if ((amdgpu_vm_had_gpu_reset(adev, id)) ||
|
||||
(atomic64_read(&id->owner) != vm->client_id) ||
|
||||
(job->vm_pd_addr != id->pd_gpu_addr) ||
|
||||
(updates && (!flushed || updates->context != flushed->context ||
|
||||
dma_fence_is_later(updates, flushed))) ||
|
||||
(!id->last_flush || (id->last_flush->context != fence_context &&
|
||||
!dma_fence_is_signaled(id->last_flush)))) {
|
||||
needs_flush = true;
|
||||
/* to prevent one context starved by another context */
|
||||
id->pd_gpu_addr = 0;
|
||||
tmp = amdgpu_sync_peek_fence(&id->active, ring);
|
||||
if (tmp) {
|
||||
r = amdgpu_sync_fence(adev, sync, tmp, false);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Good we can use this VMID. Remember this submission as
|
||||
* user of the VMID.
|
||||
*/
|
||||
r = amdgpu_sync_fence(ring->adev, &id->active, fence, false);
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
if (updates && (!flushed || updates->context != flushed->context ||
|
||||
dma_fence_is_later(updates, flushed))) {
|
||||
dma_fence_put(id->flushed_updates);
|
||||
id->flushed_updates = dma_fence_get(updates);
|
||||
}
|
||||
id->pd_gpu_addr = job->vm_pd_addr;
|
||||
atomic64_set(&id->owner, vm->client_id);
|
||||
job->vm_needs_flush = needs_flush;
|
||||
if (needs_flush) {
|
||||
dma_fence_put(id->last_flush);
|
||||
id->last_flush = NULL;
|
||||
}
|
||||
job->vm_id = id - id_mgr->ids;
|
||||
trace_amdgpu_vm_grab_id(vm, ring, job);
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_grab_id - allocate the next free VMID
|
||||
*
|
||||
* @vm: vm to allocate id for
|
||||
* @ring: ring we want to submit job to
|
||||
* @sync: sync object where we add dependencies
|
||||
* @fence: fence protecting ID from reuse
|
||||
*
|
||||
* Allocate an id for the vm, adding fences to the sync obj as necessary.
|
||||
*/
|
||||
int amdgpu_vm_grab_id(struct amdgpu_vm *vm, struct amdgpu_ring *ring,
|
||||
struct amdgpu_sync *sync, struct dma_fence *fence,
|
||||
struct amdgpu_job *job)
|
||||
{
|
||||
struct amdgpu_device *adev = ring->adev;
|
||||
unsigned vmhub = ring->funcs->vmhub;
|
||||
struct amdgpu_vm_id_manager *id_mgr = &adev->vm_manager.id_mgr[vmhub];
|
||||
uint64_t fence_context = adev->fence_context + ring->idx;
|
||||
struct dma_fence *updates = sync->last_vm_update;
|
||||
struct amdgpu_vm_id *id, *idle;
|
||||
struct dma_fence **fences;
|
||||
unsigned i;
|
||||
int r = 0;
|
||||
|
||||
mutex_lock(&id_mgr->lock);
|
||||
if (amdgpu_vm_reserved_vmid_ready(vm, vmhub)) {
|
||||
r = amdgpu_vm_grab_reserved_vmid_locked(vm, ring, sync, fence, job);
|
||||
mutex_unlock(&id_mgr->lock);
|
||||
return r;
|
||||
}
|
||||
fences = kmalloc_array(sizeof(void *), id_mgr->num_ids, GFP_KERNEL);
|
||||
if (!fences) {
|
||||
mutex_unlock(&id_mgr->lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Check if we have an idle VMID */
|
||||
i = 0;
|
||||
list_for_each_entry(idle, &id_mgr->ids_lru, list) {
|
||||
fences[i] = amdgpu_sync_peek_fence(&idle->active, ring);
|
||||
if (!fences[i])
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
|
||||
/* If we can't find a idle VMID to use, wait till one becomes available */
|
||||
if (&idle->list == &id_mgr->ids_lru) {
|
||||
u64 fence_context = adev->vm_manager.fence_context + ring->idx;
|
||||
unsigned seqno = ++adev->vm_manager.seqno[ring->idx];
|
||||
struct dma_fence_array *array;
|
||||
unsigned j;
|
||||
|
||||
for (j = 0; j < i; ++j)
|
||||
dma_fence_get(fences[j]);
|
||||
|
||||
array = dma_fence_array_create(i, fences, fence_context,
|
||||
seqno, true);
|
||||
if (!array) {
|
||||
for (j = 0; j < i; ++j)
|
||||
dma_fence_put(fences[j]);
|
||||
kfree(fences);
|
||||
r = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
r = amdgpu_sync_fence(ring->adev, sync, &array->base, false);
|
||||
dma_fence_put(&array->base);
|
||||
if (r)
|
||||
goto error;
|
||||
|
||||
mutex_unlock(&id_mgr->lock);
|
||||
return 0;
|
||||
|
||||
}
|
||||
kfree(fences);
|
||||
|
||||
job->vm_needs_flush = vm->use_cpu_for_update;
|
||||
/* Check if we can use a VMID already assigned to this VM */
|
||||
list_for_each_entry_reverse(id, &id_mgr->ids_lru, list) {
|
||||
struct dma_fence *flushed;
|
||||
bool needs_flush = vm->use_cpu_for_update;
|
||||
|
||||
/* Check all the prerequisites to using this VMID */
|
||||
if (amdgpu_vm_had_gpu_reset(adev, id))
|
||||
continue;
|
||||
|
||||
if (atomic64_read(&id->owner) != vm->client_id)
|
||||
continue;
|
||||
|
||||
if (job->vm_pd_addr != id->pd_gpu_addr)
|
||||
continue;
|
||||
|
||||
if (!id->last_flush ||
|
||||
(id->last_flush->context != fence_context &&
|
||||
!dma_fence_is_signaled(id->last_flush)))
|
||||
needs_flush = true;
|
||||
|
||||
flushed = id->flushed_updates;
|
||||
if (updates && (!flushed || dma_fence_is_later(updates, flushed)))
|
||||
needs_flush = true;
|
||||
|
||||
/* Concurrent flushes are only possible starting with Vega10 */
|
||||
if (adev->asic_type < CHIP_VEGA10 && needs_flush)
|
||||
continue;
|
||||
|
||||
/* Good we can use this VMID. Remember this submission as
|
||||
* user of the VMID.
|
||||
*/
|
||||
r = amdgpu_sync_fence(ring->adev, &id->active, fence, false);
|
||||
if (r)
|
||||
goto error;
|
||||
|
||||
if (updates && (!flushed || dma_fence_is_later(updates, flushed))) {
|
||||
dma_fence_put(id->flushed_updates);
|
||||
id->flushed_updates = dma_fence_get(updates);
|
||||
}
|
||||
|
||||
if (needs_flush)
|
||||
goto needs_flush;
|
||||
else
|
||||
goto no_flush_needed;
|
||||
|
||||
};
|
||||
|
||||
/* Still no ID to use? Then use the idle one found earlier */
|
||||
id = idle;
|
||||
|
||||
/* Remember this submission as user of the VMID */
|
||||
r = amdgpu_sync_fence(ring->adev, &id->active, fence, false);
|
||||
if (r)
|
||||
goto error;
|
||||
|
||||
id->pd_gpu_addr = job->vm_pd_addr;
|
||||
dma_fence_put(id->flushed_updates);
|
||||
id->flushed_updates = dma_fence_get(updates);
|
||||
atomic64_set(&id->owner, vm->client_id);
|
||||
|
||||
needs_flush:
|
||||
job->vm_needs_flush = true;
|
||||
dma_fence_put(id->last_flush);
|
||||
id->last_flush = NULL;
|
||||
|
||||
no_flush_needed:
|
||||
list_move_tail(&id->list, &id_mgr->ids_lru);
|
||||
|
||||
job->vm_id = id - id_mgr->ids;
|
||||
trace_amdgpu_vm_grab_id(vm, ring, job);
|
||||
|
||||
error:
|
||||
mutex_unlock(&id_mgr->lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void amdgpu_vm_free_reserved_vmid(struct amdgpu_device *adev,
|
||||
struct amdgpu_vm *vm,
|
||||
unsigned vmhub)
|
||||
{
|
||||
struct amdgpu_vm_id_manager *id_mgr = &adev->vm_manager.id_mgr[vmhub];
|
||||
|
||||
mutex_lock(&id_mgr->lock);
|
||||
if (vm->reserved_vmid[vmhub]) {
|
||||
list_add(&vm->reserved_vmid[vmhub]->list,
|
||||
&id_mgr->ids_lru);
|
||||
vm->reserved_vmid[vmhub] = NULL;
|
||||
atomic_dec(&id_mgr->reserved_vmid_num);
|
||||
}
|
||||
mutex_unlock(&id_mgr->lock);
|
||||
}
|
||||
|
||||
static int amdgpu_vm_alloc_reserved_vmid(struct amdgpu_device *adev,
|
||||
struct amdgpu_vm *vm,
|
||||
unsigned vmhub)
|
||||
{
|
||||
struct amdgpu_vm_id_manager *id_mgr;
|
||||
struct amdgpu_vm_id *idle;
|
||||
int r = 0;
|
||||
|
||||
id_mgr = &adev->vm_manager.id_mgr[vmhub];
|
||||
mutex_lock(&id_mgr->lock);
|
||||
if (vm->reserved_vmid[vmhub])
|
||||
goto unlock;
|
||||
if (atomic_inc_return(&id_mgr->reserved_vmid_num) >
|
||||
AMDGPU_VM_MAX_RESERVED_VMID) {
|
||||
DRM_ERROR("Over limitation of reserved vmid\n");
|
||||
atomic_dec(&id_mgr->reserved_vmid_num);
|
||||
r = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
/* Select the first entry VMID */
|
||||
idle = list_first_entry(&id_mgr->ids_lru, struct amdgpu_vm_id, list);
|
||||
list_del_init(&idle->list);
|
||||
vm->reserved_vmid[vmhub] = idle;
|
||||
mutex_unlock(&id_mgr->lock);
|
||||
|
||||
return 0;
|
||||
unlock:
|
||||
mutex_unlock(&id_mgr->lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_check_compute_bug - check whether asic has compute vm bug
|
||||
*
|
||||
@@ -767,8 +441,8 @@ bool amdgpu_vm_need_pipeline_sync(struct amdgpu_ring *ring,
|
||||
{
|
||||
struct amdgpu_device *adev = ring->adev;
|
||||
unsigned vmhub = ring->funcs->vmhub;
|
||||
struct amdgpu_vm_id_manager *id_mgr = &adev->vm_manager.id_mgr[vmhub];
|
||||
struct amdgpu_vm_id *id;
|
||||
struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
|
||||
struct amdgpu_vmid *id;
|
||||
bool gds_switch_needed;
|
||||
bool vm_flush_needed = job->vm_needs_flush || ring->has_compute_vm_bug;
|
||||
|
||||
@@ -783,7 +457,7 @@ bool amdgpu_vm_need_pipeline_sync(struct amdgpu_ring *ring,
|
||||
id->oa_base != job->oa_base ||
|
||||
id->oa_size != job->oa_size);
|
||||
|
||||
if (amdgpu_vm_had_gpu_reset(adev, id))
|
||||
if (amdgpu_vmid_had_gpu_reset(adev, id))
|
||||
return true;
|
||||
|
||||
return vm_flush_needed || gds_switch_needed;
|
||||
@@ -807,8 +481,8 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_
|
||||
{
|
||||
struct amdgpu_device *adev = ring->adev;
|
||||
unsigned vmhub = ring->funcs->vmhub;
|
||||
struct amdgpu_vm_id_manager *id_mgr = &adev->vm_manager.id_mgr[vmhub];
|
||||
struct amdgpu_vm_id *id = &id_mgr->ids[job->vm_id];
|
||||
struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
|
||||
struct amdgpu_vmid *id = &id_mgr->ids[job->vm_id];
|
||||
bool gds_switch_needed = ring->funcs->emit_gds_switch && (
|
||||
id->gds_base != job->gds_base ||
|
||||
id->gds_size != job->gds_size ||
|
||||
@@ -820,7 +494,7 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_
|
||||
unsigned patch_offset = 0;
|
||||
int r;
|
||||
|
||||
if (amdgpu_vm_had_gpu_reset(adev, id)) {
|
||||
if (amdgpu_vmid_had_gpu_reset(adev, id)) {
|
||||
gds_switch_needed = true;
|
||||
vm_flush_needed = true;
|
||||
}
|
||||
@@ -875,49 +549,6 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_reset_id - reset VMID to zero
|
||||
*
|
||||
* @adev: amdgpu device structure
|
||||
* @vm_id: vmid number to use
|
||||
*
|
||||
* Reset saved GDW, GWS and OA to force switch on next flush.
|
||||
*/
|
||||
void amdgpu_vm_reset_id(struct amdgpu_device *adev, unsigned vmhub,
|
||||
unsigned vmid)
|
||||
{
|
||||
struct amdgpu_vm_id_manager *id_mgr = &adev->vm_manager.id_mgr[vmhub];
|
||||
struct amdgpu_vm_id *id = &id_mgr->ids[vmid];
|
||||
|
||||
atomic64_set(&id->owner, 0);
|
||||
id->gds_base = 0;
|
||||
id->gds_size = 0;
|
||||
id->gws_base = 0;
|
||||
id->gws_size = 0;
|
||||
id->oa_base = 0;
|
||||
id->oa_size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_reset_all_id - reset VMID to zero
|
||||
*
|
||||
* @adev: amdgpu device structure
|
||||
*
|
||||
* Reset VMID to force flush on next use
|
||||
*/
|
||||
void amdgpu_vm_reset_all_ids(struct amdgpu_device *adev)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
for (i = 0; i < AMDGPU_MAX_VMHUBS; ++i) {
|
||||
struct amdgpu_vm_id_manager *id_mgr =
|
||||
&adev->vm_manager.id_mgr[i];
|
||||
|
||||
for (j = 1; j < id_mgr->num_ids; ++j)
|
||||
amdgpu_vm_reset_id(adev, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_bo_find - find the bo_va for a specific vm & bo
|
||||
*
|
||||
@@ -2819,7 +2450,7 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
|
||||
amdgpu_bo_unref(&root);
|
||||
dma_fence_put(vm->last_update);
|
||||
for (i = 0; i < AMDGPU_MAX_VMHUBS; i++)
|
||||
amdgpu_vm_free_reserved_vmid(adev, vm, i);
|
||||
amdgpu_vmid_free_reserved(adev, vm, i);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2861,23 +2492,9 @@ bool amdgpu_vm_pasid_fault_credit(struct amdgpu_device *adev,
|
||||
*/
|
||||
void amdgpu_vm_manager_init(struct amdgpu_device *adev)
|
||||
{
|
||||
unsigned i, j;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < AMDGPU_MAX_VMHUBS; ++i) {
|
||||
struct amdgpu_vm_id_manager *id_mgr =
|
||||
&adev->vm_manager.id_mgr[i];
|
||||
|
||||
mutex_init(&id_mgr->lock);
|
||||
INIT_LIST_HEAD(&id_mgr->ids_lru);
|
||||
atomic_set(&id_mgr->reserved_vmid_num, 0);
|
||||
|
||||
/* skip over VMID 0, since it is the system VM */
|
||||
for (j = 1; j < id_mgr->num_ids; ++j) {
|
||||
amdgpu_vm_reset_id(adev, i, j);
|
||||
amdgpu_sync_create(&id_mgr->ids[i].active);
|
||||
list_add_tail(&id_mgr->ids[j].list, &id_mgr->ids_lru);
|
||||
}
|
||||
}
|
||||
amdgpu_vmid_mgr_init(adev);
|
||||
|
||||
adev->vm_manager.fence_context =
|
||||
dma_fence_context_alloc(AMDGPU_MAX_RINGS);
|
||||
@@ -2918,24 +2535,10 @@ void amdgpu_vm_manager_init(struct amdgpu_device *adev)
|
||||
*/
|
||||
void amdgpu_vm_manager_fini(struct amdgpu_device *adev)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
WARN_ON(!idr_is_empty(&adev->vm_manager.pasid_idr));
|
||||
idr_destroy(&adev->vm_manager.pasid_idr);
|
||||
|
||||
for (i = 0; i < AMDGPU_MAX_VMHUBS; ++i) {
|
||||
struct amdgpu_vm_id_manager *id_mgr =
|
||||
&adev->vm_manager.id_mgr[i];
|
||||
|
||||
mutex_destroy(&id_mgr->lock);
|
||||
for (j = 0; j < AMDGPU_NUM_VM; ++j) {
|
||||
struct amdgpu_vm_id *id = &id_mgr->ids[j];
|
||||
|
||||
amdgpu_sync_free(&id->active);
|
||||
dma_fence_put(id->flushed_updates);
|
||||
dma_fence_put(id->last_flush);
|
||||
}
|
||||
}
|
||||
amdgpu_vmid_mgr_fini(adev);
|
||||
}
|
||||
|
||||
int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
|
||||
@@ -2948,13 +2551,12 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
|
||||
switch (args->in.op) {
|
||||
case AMDGPU_VM_OP_RESERVE_VMID:
|
||||
/* current, we only have requirement to reserve vmid from gfxhub */
|
||||
r = amdgpu_vm_alloc_reserved_vmid(adev, &fpriv->vm,
|
||||
AMDGPU_GFXHUB);
|
||||
r = amdgpu_vmid_alloc_reserved(adev, &fpriv->vm, AMDGPU_GFXHUB);
|
||||
if (r)
|
||||
return r;
|
||||
break;
|
||||
case AMDGPU_VM_OP_UNRESERVE_VMID:
|
||||
amdgpu_vm_free_reserved_vmid(adev, &fpriv->vm, AMDGPU_GFXHUB);
|
||||
amdgpu_vmid_free_reserved(adev, &fpriv->vm, AMDGPU_GFXHUB);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
||||
Reference in New Issue
Block a user