Import of kernel-5.14.0-687.41.1.el9_8
This commit is contained in:
parent
8b119a97cd
commit
3cbb97e6f5
@ -12,7 +12,7 @@ RHEL_MINOR = 8
|
||||
#
|
||||
# Use this spot to avoid future merge conflicts.
|
||||
# Do not trim this comment.
|
||||
RHEL_RELEASE = 687.39.1
|
||||
RHEL_RELEASE = 687.41.1
|
||||
|
||||
#
|
||||
# ZSTREAM
|
||||
|
||||
@ -19,6 +19,15 @@ EXPORT_SYMBOL_GPL(fips_enabled);
|
||||
ATOMIC_NOTIFIER_HEAD(fips_fail_notif_chain);
|
||||
EXPORT_SYMBOL_GPL(fips_fail_notif_chain);
|
||||
|
||||
static unsigned long rh_fips_exception;
|
||||
|
||||
int fips_allows(unsigned long feature)
|
||||
{
|
||||
return !fips_enabled ||
|
||||
(rh_fips_exception & feature);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fips_allows);
|
||||
|
||||
/* Process kernel command-line parameter at boot time. fips=0 or fips=1 */
|
||||
static int fips_enable(char *str)
|
||||
{
|
||||
@ -30,6 +39,17 @@ static int fips_enable(char *str)
|
||||
|
||||
__setup("fips=", fips_enable);
|
||||
|
||||
static int fips_exception(char *str)
|
||||
{
|
||||
if (kstrtoul(str, 0, &rh_fips_exception))
|
||||
return 0;
|
||||
|
||||
printk(KERN_INFO "fips exceptions: 0x%lx\n", rh_fips_exception);
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("rh_fips_exception=", fips_exception);
|
||||
|
||||
static char fips_name[] = FIPS_MODULE_NAME;
|
||||
static char fips_version[] = FIPS_MODULE_VERSION;
|
||||
|
||||
@ -41,6 +61,13 @@ static struct ctl_table crypto_sysctl_table[] = {
|
||||
.mode = 0444,
|
||||
.proc_handler = proc_dointvec
|
||||
},
|
||||
{
|
||||
.procname = "rh_fips_exception",
|
||||
.data = &rh_fips_exception,
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0444,
|
||||
.proc_handler = proc_doulongvec_minmax
|
||||
},
|
||||
{
|
||||
.procname = "fips_name",
|
||||
.data = &fips_name,
|
||||
|
||||
@ -262,12 +262,19 @@ void amdgpu_gart_table_ram_free(struct amdgpu_device *adev)
|
||||
*/
|
||||
int amdgpu_gart_table_vram_alloc(struct amdgpu_device *adev)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (adev->gart.bo != NULL)
|
||||
return 0;
|
||||
|
||||
return amdgpu_bo_create_kernel(adev, adev->gart.table_size, PAGE_SIZE,
|
||||
AMDGPU_GEM_DOMAIN_VRAM, &adev->gart.bo,
|
||||
NULL, (void *)&adev->gart.ptr);
|
||||
r = amdgpu_bo_create_kernel(adev, adev->gart.table_size, PAGE_SIZE,
|
||||
AMDGPU_GEM_DOMAIN_VRAM, &adev->gart.bo,
|
||||
NULL, (void *)&adev->gart.ptr);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
memset_io(adev->gart.ptr, adev->gart.gart_pte_flags, adev->gart.table_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -112,47 +112,6 @@ amdgpu_gem_update_timeline_node(struct drm_file *filp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
amdgpu_gem_update_bo_mapping(struct drm_file *filp,
|
||||
struct amdgpu_bo_va *bo_va,
|
||||
uint32_t operation,
|
||||
uint64_t point,
|
||||
struct dma_fence *fence,
|
||||
struct drm_syncobj *syncobj,
|
||||
struct dma_fence_chain *chain)
|
||||
{
|
||||
struct amdgpu_bo *bo = bo_va ? bo_va->base.bo : NULL;
|
||||
struct amdgpu_fpriv *fpriv = filp->driver_priv;
|
||||
struct amdgpu_vm *vm = &fpriv->vm;
|
||||
struct dma_fence *last_update;
|
||||
|
||||
if (!syncobj)
|
||||
return;
|
||||
|
||||
/* Find the last update fence */
|
||||
switch (operation) {
|
||||
case AMDGPU_VA_OP_MAP:
|
||||
case AMDGPU_VA_OP_REPLACE:
|
||||
if (bo && (bo->tbo.base.resv == vm->root.bo->tbo.base.resv))
|
||||
last_update = vm->last_update;
|
||||
else
|
||||
last_update = bo_va->last_pt_update;
|
||||
break;
|
||||
case AMDGPU_VA_OP_UNMAP:
|
||||
case AMDGPU_VA_OP_CLEAR:
|
||||
last_update = fence;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add fence to timeline */
|
||||
if (!point)
|
||||
drm_syncobj_replace_fence(syncobj, last_update);
|
||||
else
|
||||
drm_syncobj_add_point(syncobj, chain, last_update, point);
|
||||
}
|
||||
|
||||
static vm_fault_t amdgpu_gem_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct ttm_buffer_object *bo = vmf->vma->vm_private_data;
|
||||
@ -761,16 +720,27 @@ amdgpu_gem_va_update_vm(struct amdgpu_device *adev,
|
||||
struct amdgpu_bo_va *bo_va,
|
||||
uint32_t operation)
|
||||
{
|
||||
struct dma_fence *fence = dma_fence_get_stub();
|
||||
int r;
|
||||
struct dma_fence *fence;
|
||||
int r = 0;
|
||||
|
||||
/* Always start from the VM's existing last update fence. */
|
||||
fence = dma_fence_get(vm->last_update);
|
||||
|
||||
if (!amdgpu_vm_ready(vm))
|
||||
return fence;
|
||||
|
||||
/*
|
||||
* First clean up any freed mappings in the VM.
|
||||
*
|
||||
* amdgpu_vm_clear_freed() may replace @fence with a new fence if it
|
||||
* schedules GPU work. If nothing needs clearing, @fence can remain as
|
||||
* the original vm->last_update.
|
||||
*/
|
||||
r = amdgpu_vm_clear_freed(adev, vm, &fence);
|
||||
if (r)
|
||||
goto error;
|
||||
|
||||
/* For MAP/REPLACE we also need to update the BO mappings. */
|
||||
if (operation == AMDGPU_VA_OP_MAP ||
|
||||
operation == AMDGPU_VA_OP_REPLACE) {
|
||||
r = amdgpu_vm_bo_update(adev, bo_va, false);
|
||||
@ -778,7 +748,46 @@ amdgpu_gem_va_update_vm(struct amdgpu_device *adev,
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Always update PDEs after we touched the mappings. */
|
||||
r = amdgpu_vm_update_pdes(adev, vm, false);
|
||||
if (r)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* Decide which fence best represents the last update:
|
||||
*
|
||||
* MAP/REPLACE:
|
||||
* - For always-valid mappings, use vm->last_update.
|
||||
* - Otherwise, export bo_va->last_pt_update.
|
||||
*
|
||||
* UNMAP/CLEAR:
|
||||
* Keep the fence returned by amdgpu_vm_clear_freed(). If no work was
|
||||
* needed, it can remain as vm->last_pt_update.
|
||||
*
|
||||
* The VM and BO update fences are always initialized to a valid value.
|
||||
* vm->last_update and bo_va->last_pt_update always start as valid fences.
|
||||
* and are never expected to be NULL.
|
||||
*/
|
||||
switch (operation) {
|
||||
case AMDGPU_VA_OP_MAP:
|
||||
case AMDGPU_VA_OP_REPLACE:
|
||||
/*
|
||||
* For MAP/REPLACE, return the page table update fence for the
|
||||
* mapping we just modified. bo_va is expected to be valid here.
|
||||
*/
|
||||
dma_fence_put(fence);
|
||||
|
||||
if (amdgpu_vm_is_bo_always_valid(vm, bo_va->base.bo))
|
||||
fence = dma_fence_get(vm->last_update);
|
||||
else
|
||||
fence = dma_fence_get(bo_va->last_pt_update);
|
||||
break;
|
||||
case AMDGPU_VA_OP_UNMAP:
|
||||
case AMDGPU_VA_OP_CLEAR:
|
||||
default:
|
||||
/* keep @fence as returned by amdgpu_vm_clear_freed() */
|
||||
break;
|
||||
}
|
||||
|
||||
error:
|
||||
if (r && r != -ERESTARTSYS)
|
||||
@ -810,6 +819,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
|
||||
uint64_t vm_size;
|
||||
int r = 0;
|
||||
|
||||
/* Validate virtual address range against reserved regions. */
|
||||
if (args->va_address < AMDGPU_VA_RESERVED_BOTTOM) {
|
||||
dev_dbg(dev->dev,
|
||||
"va_address 0x%llx is in reserved area 0x%llx\n",
|
||||
@ -843,6 +853,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Validate operation type. */
|
||||
switch (args->operation) {
|
||||
case AMDGPU_VA_OP_MAP:
|
||||
case AMDGPU_VA_OP_UNMAP:
|
||||
@ -866,6 +877,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
|
||||
abo = NULL;
|
||||
}
|
||||
|
||||
/* Add input syncobj fences (if any) for synchronization. */
|
||||
r = amdgpu_gem_add_input_fence(filp,
|
||||
args->input_fence_syncobj_handles,
|
||||
args->num_syncobj_handles);
|
||||
@ -888,6 +900,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Resolve the BO-VA mapping for this VM/BO combination. */
|
||||
if (abo) {
|
||||
bo_va = amdgpu_vm_bo_find(&fpriv->vm, abo);
|
||||
if (!bo_va) {
|
||||
@ -900,6 +913,11 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
|
||||
bo_va = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare the timeline syncobj node if the user requested a VM
|
||||
* timeline update. This only allocates/looks up the syncobj and
|
||||
* chain node; the actual fence is attached later.
|
||||
*/
|
||||
r = amdgpu_gem_update_timeline_node(filp,
|
||||
args->vm_timeline_syncobj_out,
|
||||
args->vm_timeline_point,
|
||||
@ -931,18 +949,30 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Once the VA operation is done, update the VM and obtain the fence
|
||||
* that represents the last relevant update for this mapping. This
|
||||
* fence can then be exported to the user-visible VM timeline.
|
||||
*/
|
||||
if (!r && !(args->flags & AMDGPU_VM_DELAY_UPDATE) && !adev->debug_vm) {
|
||||
fence = amdgpu_gem_va_update_vm(adev, &fpriv->vm, bo_va,
|
||||
args->operation);
|
||||
|
||||
if (timeline_syncobj)
|
||||
amdgpu_gem_update_bo_mapping(filp, bo_va,
|
||||
args->operation,
|
||||
args->vm_timeline_point,
|
||||
fence, timeline_syncobj,
|
||||
timeline_chain);
|
||||
else
|
||||
dma_fence_put(fence);
|
||||
if (timeline_syncobj && fence) {
|
||||
if (!args->vm_timeline_point) {
|
||||
/* Replace the existing fence when no point is given. */
|
||||
drm_syncobj_replace_fence(timeline_syncobj,
|
||||
fence);
|
||||
} else {
|
||||
/* Attach the last-update fence at a specific point. */
|
||||
drm_syncobj_add_point(timeline_syncobj,
|
||||
timeline_chain,
|
||||
fence,
|
||||
args->vm_timeline_point);
|
||||
}
|
||||
}
|
||||
dma_fence_put(fence);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -51,8 +51,6 @@
|
||||
#include "amdgpu_amdkfd.h"
|
||||
#include "amdgpu_hmm.h"
|
||||
|
||||
#define MAX_WALK_BYTE (2UL << 30)
|
||||
|
||||
/**
|
||||
* amdgpu_hmm_invalidate_gfx - callback to notify about mm change
|
||||
*
|
||||
@ -170,11 +168,13 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
|
||||
void *owner,
|
||||
struct hmm_range **phmm_range)
|
||||
{
|
||||
const u64 max_bytes = SZ_2G;
|
||||
|
||||
struct hmm_range *hmm_range;
|
||||
unsigned long end;
|
||||
unsigned long timeout;
|
||||
unsigned long *pfns;
|
||||
int r = 0;
|
||||
unsigned long end;
|
||||
int r;
|
||||
|
||||
hmm_range = kzalloc(sizeof(*hmm_range), GFP_KERNEL);
|
||||
if (unlikely(!hmm_range))
|
||||
@ -195,8 +195,9 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
|
||||
end = start + npages * PAGE_SIZE;
|
||||
hmm_range->dev_private_owner = owner;
|
||||
|
||||
hmm_range->notifier_seq = mmu_interval_read_begin(notifier);
|
||||
do {
|
||||
hmm_range->end = min(hmm_range->start + MAX_WALK_BYTE, end);
|
||||
hmm_range->end = min(hmm_range->start + max_bytes, end);
|
||||
|
||||
pr_debug("hmm range: start = 0x%lx, end = 0x%lx",
|
||||
hmm_range->start, hmm_range->end);
|
||||
@ -204,7 +205,6 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
|
||||
timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
|
||||
|
||||
retry:
|
||||
hmm_range->notifier_seq = mmu_interval_read_begin(notifier);
|
||||
r = hmm_range_fault(hmm_range);
|
||||
if (unlikely(r)) {
|
||||
if (r == -EBUSY && !time_after(jiffies, timeout))
|
||||
@ -214,7 +214,7 @@ retry:
|
||||
|
||||
if (hmm_range->end == end)
|
||||
break;
|
||||
hmm_range->hmm_pfns += MAX_WALK_BYTE >> PAGE_SHIFT;
|
||||
hmm_range->hmm_pfns += max_bytes >> PAGE_SHIFT;
|
||||
hmm_range->start = hmm_range->end;
|
||||
} while (hmm_range->end < end);
|
||||
|
||||
|
||||
@ -401,27 +401,25 @@ static int kfd_dbg_get_dev_watch_id(struct kfd_process_device *pdd, int *watch_i
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void kfd_dbg_clear_dev_watch_id(struct kfd_process_device *pdd, int watch_id)
|
||||
static void kfd_dbg_clear_dev_watch_id(struct kfd_process_device *pdd, u32 watch_id)
|
||||
{
|
||||
spin_lock(&pdd->dev->watch_points_lock);
|
||||
|
||||
/* process owns device watch point so safe to clear */
|
||||
if ((pdd->alloc_watch_ids >> watch_id) & 0x1) {
|
||||
pdd->alloc_watch_ids &= ~(0x1 << watch_id);
|
||||
pdd->dev->alloc_watch_ids &= ~(0x1 << watch_id);
|
||||
if (pdd->alloc_watch_ids & BIT(watch_id)) {
|
||||
pdd->alloc_watch_ids &= ~BIT(watch_id);
|
||||
pdd->dev->alloc_watch_ids &= ~BIT(watch_id);
|
||||
}
|
||||
|
||||
spin_unlock(&pdd->dev->watch_points_lock);
|
||||
}
|
||||
|
||||
static bool kfd_dbg_owns_dev_watch_id(struct kfd_process_device *pdd, int watch_id)
|
||||
static bool kfd_dbg_owns_dev_watch_id(struct kfd_process_device *pdd, u32 watch_id)
|
||||
{
|
||||
bool owns_watch_id = false;
|
||||
|
||||
spin_lock(&pdd->dev->watch_points_lock);
|
||||
owns_watch_id = watch_id < MAX_WATCH_ADDRESSES &&
|
||||
((pdd->alloc_watch_ids >> watch_id) & 0x1);
|
||||
|
||||
owns_watch_id = pdd->alloc_watch_ids & BIT(watch_id);
|
||||
spin_unlock(&pdd->dev->watch_points_lock);
|
||||
|
||||
return owns_watch_id;
|
||||
@ -432,6 +430,9 @@ int kfd_dbg_trap_clear_dev_address_watch(struct kfd_process_device *pdd,
|
||||
{
|
||||
int r;
|
||||
|
||||
if (watch_id >= MAX_WATCH_ADDRESSES)
|
||||
return -EINVAL;
|
||||
|
||||
if (!kfd_dbg_owns_dev_watch_id(pdd, watch_id))
|
||||
return -EINVAL;
|
||||
|
||||
@ -469,6 +470,9 @@ int kfd_dbg_trap_set_dev_address_watch(struct kfd_process_device *pdd,
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (*watch_id >= MAX_WATCH_ADDRESSES)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pdd->dev->kfd->shared_resources.enable_mes) {
|
||||
r = debug_lock_and_unmap(pdd->dev->dqm);
|
||||
if (r) {
|
||||
|
||||
@ -331,6 +331,12 @@ static int kfd_event_page_set(struct kfd_process *p, void *kernel_address,
|
||||
if (p->signal_page)
|
||||
return -EBUSY;
|
||||
|
||||
if (size < KFD_SIGNAL_EVENT_LIMIT * 8) {
|
||||
pr_err("Event page size %llu is too small, need at least %lu bytes\n",
|
||||
size, (unsigned long)(KFD_SIGNAL_EVENT_LIMIT * 8));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
page = kzalloc(sizeof(*page), GFP_KERNEL);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -334,8 +334,7 @@ static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, voi
|
||||
|
||||
static void restore_mqd(struct mqd_manager *mm, void **mqd,
|
||||
struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr,
|
||||
struct queue_properties *qp,
|
||||
const void *mqd_src,
|
||||
struct queue_properties *qp, const void *mqd_src,
|
||||
const void *ctl_stack_src, const u32 ctl_stack_size)
|
||||
{
|
||||
uint64_t addr;
|
||||
@ -351,14 +350,48 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
|
||||
*gart_addr = addr;
|
||||
|
||||
m->cp_hqd_pq_doorbell_control =
|
||||
qp->doorbell_off <<
|
||||
CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT;
|
||||
pr_debug("cp_hqd_pq_doorbell_control 0x%x\n",
|
||||
m->cp_hqd_pq_doorbell_control);
|
||||
qp->doorbell_off << CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT;
|
||||
pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", m->cp_hqd_pq_doorbell_control);
|
||||
|
||||
qp->is_active = 0;
|
||||
}
|
||||
|
||||
static void checkpoint_mqd_sdma(struct mqd_manager *mm,
|
||||
void *mqd,
|
||||
void *mqd_dst,
|
||||
void *ctl_stack_dst)
|
||||
{
|
||||
struct v11_sdma_mqd *m;
|
||||
|
||||
m = get_sdma_mqd(mqd);
|
||||
|
||||
memcpy(mqd_dst, m, sizeof(struct v11_sdma_mqd));
|
||||
}
|
||||
|
||||
static void restore_mqd_sdma(struct mqd_manager *mm, void **mqd,
|
||||
struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr,
|
||||
struct queue_properties *qp,
|
||||
const void *mqd_src,
|
||||
const void *ctl_stack_src,
|
||||
const u32 ctl_stack_size)
|
||||
{
|
||||
uint64_t addr;
|
||||
struct v11_sdma_mqd *m;
|
||||
|
||||
m = (struct v11_sdma_mqd *) mqd_mem_obj->cpu_ptr;
|
||||
addr = mqd_mem_obj->gpu_addr;
|
||||
|
||||
memcpy(m, mqd_src, sizeof(*m));
|
||||
|
||||
m->sdmax_rlcx_doorbell_offset =
|
||||
qp->doorbell_off << SDMA0_QUEUE0_DOORBELL_OFFSET__OFFSET__SHIFT;
|
||||
|
||||
*mqd = m;
|
||||
if (gart_addr)
|
||||
*gart_addr = addr;
|
||||
|
||||
qp->is_active = 0;
|
||||
}
|
||||
|
||||
static void init_mqd_hiq(struct mqd_manager *mm, void **mqd,
|
||||
struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr,
|
||||
@ -543,8 +576,8 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type,
|
||||
mqd->update_mqd = update_mqd_sdma;
|
||||
mqd->destroy_mqd = kfd_destroy_mqd_sdma;
|
||||
mqd->is_occupied = kfd_is_occupied_sdma;
|
||||
mqd->checkpoint_mqd = checkpoint_mqd;
|
||||
mqd->restore_mqd = restore_mqd;
|
||||
mqd->checkpoint_mqd = checkpoint_mqd_sdma;
|
||||
mqd->restore_mqd = restore_mqd_sdma;
|
||||
mqd->mqd_size = sizeof(struct v11_sdma_mqd);
|
||||
mqd->mqd_stride = kfd_mqd_stride;
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
|
||||
@ -288,8 +288,8 @@ bool dal_vector_reserve(struct vector *vector, uint32_t capacity)
|
||||
if (capacity <= vector->capacity)
|
||||
return true;
|
||||
|
||||
new_container = krealloc(vector->container,
|
||||
capacity * vector->struct_size, GFP_KERNEL);
|
||||
new_container = krealloc_array(vector->container,
|
||||
capacity, vector->struct_size, GFP_KERNEL);
|
||||
|
||||
if (new_container) {
|
||||
vector->container = new_container;
|
||||
|
||||
@ -2584,14 +2584,16 @@ static enum bp_result get_integrated_info_v11(
|
||||
info_v11->extdispconninfo.checksum;
|
||||
|
||||
info->dp0_ext_hdmi_slv_addr = info_v11->dp0_retimer_set.HdmiSlvAddr;
|
||||
info->dp0_ext_hdmi_reg_num = info_v11->dp0_retimer_set.HdmiRegNum;
|
||||
info->dp0_ext_hdmi_reg_num = min_t(u8, info_v11->dp0_retimer_set.HdmiRegNum,
|
||||
ARRAY_SIZE(info->dp0_ext_hdmi_reg_settings));
|
||||
for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) {
|
||||
info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index =
|
||||
info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex;
|
||||
info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val =
|
||||
info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp0_ext_hdmi_6g_reg_num = info_v11->dp0_retimer_set.Hdmi6GRegNum;
|
||||
info->dp0_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp0_retimer_set.Hdmi6GRegNum,
|
||||
ARRAY_SIZE(info->dp0_ext_hdmi_6g_reg_settings));
|
||||
for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) {
|
||||
info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index =
|
||||
info_v11->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex;
|
||||
@ -2600,14 +2602,16 @@ static enum bp_result get_integrated_info_v11(
|
||||
}
|
||||
|
||||
info->dp1_ext_hdmi_slv_addr = info_v11->dp1_retimer_set.HdmiSlvAddr;
|
||||
info->dp1_ext_hdmi_reg_num = info_v11->dp1_retimer_set.HdmiRegNum;
|
||||
info->dp1_ext_hdmi_reg_num = min_t(u8, info_v11->dp1_retimer_set.HdmiRegNum,
|
||||
ARRAY_SIZE(info->dp1_ext_hdmi_reg_settings));
|
||||
for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) {
|
||||
info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index =
|
||||
info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex;
|
||||
info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val =
|
||||
info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp1_ext_hdmi_6g_reg_num = info_v11->dp1_retimer_set.Hdmi6GRegNum;
|
||||
info->dp1_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp1_retimer_set.Hdmi6GRegNum,
|
||||
ARRAY_SIZE(info->dp1_ext_hdmi_6g_reg_settings));
|
||||
for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) {
|
||||
info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index =
|
||||
info_v11->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex;
|
||||
@ -2616,14 +2620,16 @@ static enum bp_result get_integrated_info_v11(
|
||||
}
|
||||
|
||||
info->dp2_ext_hdmi_slv_addr = info_v11->dp2_retimer_set.HdmiSlvAddr;
|
||||
info->dp2_ext_hdmi_reg_num = info_v11->dp2_retimer_set.HdmiRegNum;
|
||||
info->dp2_ext_hdmi_reg_num = min_t(u8, info_v11->dp2_retimer_set.HdmiRegNum,
|
||||
ARRAY_SIZE(info->dp2_ext_hdmi_reg_settings));
|
||||
for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) {
|
||||
info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index =
|
||||
info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex;
|
||||
info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val =
|
||||
info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp2_ext_hdmi_6g_reg_num = info_v11->dp2_retimer_set.Hdmi6GRegNum;
|
||||
info->dp2_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp2_retimer_set.Hdmi6GRegNum,
|
||||
ARRAY_SIZE(info->dp2_ext_hdmi_6g_reg_settings));
|
||||
for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) {
|
||||
info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index =
|
||||
info_v11->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex;
|
||||
@ -2632,14 +2638,16 @@ static enum bp_result get_integrated_info_v11(
|
||||
}
|
||||
|
||||
info->dp3_ext_hdmi_slv_addr = info_v11->dp3_retimer_set.HdmiSlvAddr;
|
||||
info->dp3_ext_hdmi_reg_num = info_v11->dp3_retimer_set.HdmiRegNum;
|
||||
info->dp3_ext_hdmi_reg_num = min_t(u8, info_v11->dp3_retimer_set.HdmiRegNum,
|
||||
ARRAY_SIZE(info->dp3_ext_hdmi_reg_settings));
|
||||
for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) {
|
||||
info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index =
|
||||
info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex;
|
||||
info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val =
|
||||
info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp3_ext_hdmi_6g_reg_num = info_v11->dp3_retimer_set.Hdmi6GRegNum;
|
||||
info->dp3_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp3_retimer_set.Hdmi6GRegNum,
|
||||
ARRAY_SIZE(info->dp3_ext_hdmi_6g_reg_settings));
|
||||
for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) {
|
||||
info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index =
|
||||
info_v11->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex;
|
||||
@ -2789,14 +2797,16 @@ static enum bp_result get_integrated_info_v2_1(
|
||||
info->ext_disp_conn_info.checksum =
|
||||
info_v2_1->extdispconninfo.checksum;
|
||||
info->dp0_ext_hdmi_slv_addr = info_v2_1->dp0_retimer_set.HdmiSlvAddr;
|
||||
info->dp0_ext_hdmi_reg_num = info_v2_1->dp0_retimer_set.HdmiRegNum;
|
||||
info->dp0_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp0_retimer_set.HdmiRegNum,
|
||||
ARRAY_SIZE(info->dp0_ext_hdmi_reg_settings));
|
||||
for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) {
|
||||
info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index =
|
||||
info_v2_1->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex;
|
||||
info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val =
|
||||
info_v2_1->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp0_ext_hdmi_6g_reg_num = info_v2_1->dp0_retimer_set.Hdmi6GRegNum;
|
||||
info->dp0_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp0_retimer_set.Hdmi6GRegNum,
|
||||
ARRAY_SIZE(info->dp0_ext_hdmi_6g_reg_settings));
|
||||
for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) {
|
||||
info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index =
|
||||
info_v2_1->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex;
|
||||
@ -2804,14 +2814,16 @@ static enum bp_result get_integrated_info_v2_1(
|
||||
info_v2_1->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp1_ext_hdmi_slv_addr = info_v2_1->dp1_retimer_set.HdmiSlvAddr;
|
||||
info->dp1_ext_hdmi_reg_num = info_v2_1->dp1_retimer_set.HdmiRegNum;
|
||||
info->dp1_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp1_retimer_set.HdmiRegNum,
|
||||
ARRAY_SIZE(info->dp1_ext_hdmi_reg_settings));
|
||||
for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) {
|
||||
info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index =
|
||||
info_v2_1->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex;
|
||||
info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val =
|
||||
info_v2_1->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp1_ext_hdmi_6g_reg_num = info_v2_1->dp1_retimer_set.Hdmi6GRegNum;
|
||||
info->dp1_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp1_retimer_set.Hdmi6GRegNum,
|
||||
ARRAY_SIZE(info->dp1_ext_hdmi_6g_reg_settings));
|
||||
for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) {
|
||||
info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index =
|
||||
info_v2_1->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex;
|
||||
@ -2819,14 +2831,16 @@ static enum bp_result get_integrated_info_v2_1(
|
||||
info_v2_1->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp2_ext_hdmi_slv_addr = info_v2_1->dp2_retimer_set.HdmiSlvAddr;
|
||||
info->dp2_ext_hdmi_reg_num = info_v2_1->dp2_retimer_set.HdmiRegNum;
|
||||
info->dp2_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp2_retimer_set.HdmiRegNum,
|
||||
ARRAY_SIZE(info->dp2_ext_hdmi_reg_settings));
|
||||
for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) {
|
||||
info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index =
|
||||
info_v2_1->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex;
|
||||
info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val =
|
||||
info_v2_1->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp2_ext_hdmi_6g_reg_num = info_v2_1->dp2_retimer_set.Hdmi6GRegNum;
|
||||
info->dp2_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp2_retimer_set.Hdmi6GRegNum,
|
||||
ARRAY_SIZE(info->dp2_ext_hdmi_6g_reg_settings));
|
||||
for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) {
|
||||
info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index =
|
||||
info_v2_1->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex;
|
||||
@ -2834,14 +2848,16 @@ static enum bp_result get_integrated_info_v2_1(
|
||||
info_v2_1->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp3_ext_hdmi_slv_addr = info_v2_1->dp3_retimer_set.HdmiSlvAddr;
|
||||
info->dp3_ext_hdmi_reg_num = info_v2_1->dp3_retimer_set.HdmiRegNum;
|
||||
info->dp3_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp3_retimer_set.HdmiRegNum,
|
||||
ARRAY_SIZE(info->dp3_ext_hdmi_reg_settings));
|
||||
for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) {
|
||||
info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index =
|
||||
info_v2_1->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex;
|
||||
info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val =
|
||||
info_v2_1->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal;
|
||||
}
|
||||
info->dp3_ext_hdmi_6g_reg_num = info_v2_1->dp3_retimer_set.Hdmi6GRegNum;
|
||||
info->dp3_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp3_retimer_set.Hdmi6GRegNum,
|
||||
ARRAY_SIZE(info->dp3_ext_hdmi_6g_reg_settings));
|
||||
for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) {
|
||||
info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index =
|
||||
info_v2_1->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex;
|
||||
|
||||
@ -5884,7 +5884,11 @@ bool dc_process_dmub_aux_transfer_async(struct dc *dc,
|
||||
uint8_t action;
|
||||
union dmub_rb_cmd cmd = {0};
|
||||
|
||||
ASSERT(payload->length <= 16);
|
||||
if (link_index >= dc->link_count || !dc->links[link_index])
|
||||
return false;
|
||||
|
||||
if (payload->length > sizeof(cmd.dp_aux_access.aux_control.dpaux.data))
|
||||
return false;
|
||||
|
||||
cmd.dp_aux_access.header.type = DMUB_CMD__DP_AUX_ACCESS;
|
||||
cmd.dp_aux_access.header.payload_bytes = 0;
|
||||
|
||||
@ -17,6 +17,17 @@
|
||||
#include "i915_gem_tiling.h"
|
||||
#include "i915_scatterlist.h"
|
||||
|
||||
/* Abuse scatterlist to store pointer instead of struct page. */
|
||||
static inline void __set_phys_vaddr(struct scatterlist *sg, void *vaddr)
|
||||
{
|
||||
sg_assign_page(sg, (struct page *)vaddr);
|
||||
}
|
||||
|
||||
static inline void *__get_phys_vaddr(struct scatterlist *sg)
|
||||
{
|
||||
return (void *)sg_page(sg);
|
||||
}
|
||||
|
||||
static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct address_space *mapping = obj->base.filp->f_mapping;
|
||||
@ -57,7 +68,7 @@ static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj)
|
||||
sg->offset = 0;
|
||||
sg->length = obj->base.size;
|
||||
|
||||
sg_assign_page(sg, (struct page *)vaddr);
|
||||
__set_phys_vaddr(sg, vaddr);
|
||||
sg_dma_address(sg) = dma;
|
||||
sg_dma_len(sg) = obj->base.size;
|
||||
|
||||
@ -98,7 +109,7 @@ i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj,
|
||||
struct sg_table *pages)
|
||||
{
|
||||
dma_addr_t dma = sg_dma_address(pages->sgl);
|
||||
void *vaddr = sg_page(pages->sgl);
|
||||
void *vaddr = __get_phys_vaddr(pages->sgl);
|
||||
|
||||
__i915_gem_object_release_shmem(obj, pages, false);
|
||||
|
||||
@ -138,7 +149,7 @@ i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj,
|
||||
int i915_gem_object_pwrite_phys(struct drm_i915_gem_object *obj,
|
||||
const struct drm_i915_gem_pwrite *args)
|
||||
{
|
||||
void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset;
|
||||
void *vaddr = __get_phys_vaddr(obj->mm.pages->sgl) + args->offset;
|
||||
char __user *user_data = u64_to_user_ptr(args->data_ptr);
|
||||
struct drm_i915_private *i915 = to_i915(obj->base.dev);
|
||||
int err;
|
||||
@ -169,7 +180,7 @@ int i915_gem_object_pwrite_phys(struct drm_i915_gem_object *obj,
|
||||
int i915_gem_object_pread_phys(struct drm_i915_gem_object *obj,
|
||||
const struct drm_i915_gem_pread *args)
|
||||
{
|
||||
void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset;
|
||||
void *vaddr = __get_phys_vaddr(obj->mm.pages->sgl) + args->offset;
|
||||
char __user *user_data = u64_to_user_ptr(args->data_ptr);
|
||||
int err;
|
||||
|
||||
|
||||
@ -416,8 +416,6 @@ void i915_ttm_free_cached_io_rsgt(struct drm_i915_gem_object *obj)
|
||||
int i915_ttm_purge(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct ttm_buffer_object *bo = i915_gem_to_ttm(obj);
|
||||
struct i915_ttm_tt *i915_tt =
|
||||
container_of(bo->ttm, typeof(*i915_tt), ttm);
|
||||
struct ttm_operation_ctx ctx = {
|
||||
.interruptible = true,
|
||||
.no_wait_gpu = false,
|
||||
@ -432,16 +430,22 @@ int i915_ttm_purge(struct drm_i915_gem_object *obj)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (bo->ttm && i915_tt->filp) {
|
||||
/*
|
||||
* The below fput(which eventually calls shmem_truncate) might
|
||||
* be delayed by worker, so when directly called to purge the
|
||||
* pages(like by the shrinker) we should try to be more
|
||||
* aggressive and release the pages immediately.
|
||||
*/
|
||||
shmem_truncate_range(file_inode(i915_tt->filp),
|
||||
0, (loff_t)-1);
|
||||
fput(fetch_and_zero(&i915_tt->filp));
|
||||
if (bo->ttm) {
|
||||
struct i915_ttm_tt *i915_tt =
|
||||
container_of(bo->ttm, typeof(*i915_tt), ttm);
|
||||
|
||||
if (i915_tt->filp) {
|
||||
/*
|
||||
* The below fput(which eventually calls shmem_truncate)
|
||||
* might be delayed by worker, so when directly called
|
||||
* to purge the pages(like by the shrinker) we should
|
||||
* try to be more aggressive and release the pages
|
||||
* immediately.
|
||||
*/
|
||||
shmem_truncate_range(file_inode(i915_tt->filp),
|
||||
0, (loff_t)-1);
|
||||
fput(fetch_and_zero(&i915_tt->filp));
|
||||
}
|
||||
}
|
||||
|
||||
obj->write_domain = 0;
|
||||
|
||||
@ -52,13 +52,13 @@
|
||||
|
||||
#define GVE_DEFAULT_RX_BUFFER_OFFSET 2048
|
||||
|
||||
#define GVE_DEFAULT_MIN_TX_RING_SIZE 256
|
||||
#define GVE_DEFAULT_MIN_RX_RING_SIZE 512
|
||||
|
||||
#define GVE_XDP_ACTIONS 5
|
||||
|
||||
#define GVE_GQ_TX_MIN_PKT_DESC_BYTES 182
|
||||
|
||||
#define DQO_QPL_DEFAULT_TX_PAGES 512
|
||||
#define DQO_QPL_DEFAULT_RX_PAGES 2048
|
||||
|
||||
/* Maximum TSO size supported on DQO */
|
||||
#define GVE_DQO_TX_MAX 0x3FFFF
|
||||
|
||||
@ -650,8 +650,14 @@ struct gve_priv {
|
||||
u16 num_event_counters;
|
||||
u16 tx_desc_cnt; /* num desc per ring */
|
||||
u16 rx_desc_cnt; /* num desc per ring */
|
||||
u16 tx_pages_per_qpl; /* Suggested number of pages per qpl for TX queues by NIC */
|
||||
u16 rx_pages_per_qpl; /* Suggested number of pages per qpl for RX queues by NIC */
|
||||
u16 max_tx_desc_cnt;
|
||||
u16 max_rx_desc_cnt;
|
||||
u16 min_tx_desc_cnt;
|
||||
u16 min_rx_desc_cnt;
|
||||
bool modify_ring_size_enabled;
|
||||
bool default_min_ring_size;
|
||||
u16 tx_pages_per_qpl; /* Number of pages per qpl for TX queues */
|
||||
u16 rx_pages_per_qpl; /* Number of pages per qpl for RX queues */
|
||||
u16 rx_data_slot_cnt; /* rx buffer length */
|
||||
u64 max_registered_pages;
|
||||
u64 num_registered_pages; /* num pages registered with NIC */
|
||||
@ -1068,6 +1074,9 @@ int gve_reset(struct gve_priv *priv, bool attempt_teardown);
|
||||
int gve_adjust_queues(struct gve_priv *priv,
|
||||
struct gve_queue_config new_rx_config,
|
||||
struct gve_queue_config new_tx_config);
|
||||
int gve_adjust_ring_sizes(struct gve_priv *priv,
|
||||
u16 new_tx_desc_cnt,
|
||||
u16 new_rx_desc_cnt);
|
||||
/* report stats handling */
|
||||
void gve_handle_report_stats(struct gve_priv *priv);
|
||||
/* exported by ethtool.c */
|
||||
|
||||
@ -20,6 +20,8 @@
|
||||
|
||||
#define GVE_DEVICE_OPTION_TOO_BIG_FMT "Length of %s option larger than expected. Possible older version of guest driver.\n"
|
||||
|
||||
#define GVE_DEVICE_OPTION_NO_MIN_RING_SIZE 8
|
||||
|
||||
static
|
||||
struct gve_device_option *gve_get_next_option(struct gve_device_descriptor *descriptor,
|
||||
struct gve_device_option *option)
|
||||
@ -40,7 +42,8 @@ void gve_parse_device_option(struct gve_priv *priv,
|
||||
struct gve_device_option_gqi_qpl **dev_op_gqi_qpl,
|
||||
struct gve_device_option_dqo_rda **dev_op_dqo_rda,
|
||||
struct gve_device_option_jumbo_frames **dev_op_jumbo_frames,
|
||||
struct gve_device_option_dqo_qpl **dev_op_dqo_qpl)
|
||||
struct gve_device_option_dqo_qpl **dev_op_dqo_qpl,
|
||||
struct gve_device_option_modify_ring **dev_op_modify_ring)
|
||||
{
|
||||
u32 req_feat_mask = be32_to_cpu(option->required_features_mask);
|
||||
u16 option_length = be16_to_cpu(option->option_length);
|
||||
@ -129,6 +132,27 @@ void gve_parse_device_option(struct gve_priv *priv,
|
||||
}
|
||||
*dev_op_dqo_qpl = (void *)(option + 1);
|
||||
break;
|
||||
case GVE_DEV_OPT_ID_MODIFY_RING:
|
||||
if (option_length < GVE_DEVICE_OPTION_NO_MIN_RING_SIZE ||
|
||||
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_MODIFY_RING) {
|
||||
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
||||
"Modify Ring", (int)sizeof(**dev_op_modify_ring),
|
||||
GVE_DEV_OPT_REQ_FEAT_MASK_MODIFY_RING,
|
||||
option_length, req_feat_mask);
|
||||
break;
|
||||
}
|
||||
|
||||
if (option_length > sizeof(**dev_op_modify_ring)) {
|
||||
dev_warn(&priv->pdev->dev,
|
||||
GVE_DEVICE_OPTION_TOO_BIG_FMT, "Modify Ring");
|
||||
}
|
||||
|
||||
*dev_op_modify_ring = (void *)(option + 1);
|
||||
|
||||
/* device has not provided min ring size */
|
||||
if (option_length == GVE_DEVICE_OPTION_NO_MIN_RING_SIZE)
|
||||
priv->default_min_ring_size = true;
|
||||
break;
|
||||
case GVE_DEV_OPT_ID_JUMBO_FRAMES:
|
||||
if (option_length < sizeof(**dev_op_jumbo_frames) ||
|
||||
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_JUMBO_FRAMES) {
|
||||
@ -164,7 +188,8 @@ gve_process_device_options(struct gve_priv *priv,
|
||||
struct gve_device_option_gqi_qpl **dev_op_gqi_qpl,
|
||||
struct gve_device_option_dqo_rda **dev_op_dqo_rda,
|
||||
struct gve_device_option_jumbo_frames **dev_op_jumbo_frames,
|
||||
struct gve_device_option_dqo_qpl **dev_op_dqo_qpl)
|
||||
struct gve_device_option_dqo_qpl **dev_op_dqo_qpl,
|
||||
struct gve_device_option_modify_ring **dev_op_modify_ring)
|
||||
{
|
||||
const int num_options = be16_to_cpu(descriptor->num_device_options);
|
||||
struct gve_device_option *dev_opt;
|
||||
@ -185,7 +210,7 @@ gve_process_device_options(struct gve_priv *priv,
|
||||
gve_parse_device_option(priv, descriptor, dev_opt,
|
||||
dev_op_gqi_rda, dev_op_gqi_qpl,
|
||||
dev_op_dqo_rda, dev_op_jumbo_frames,
|
||||
dev_op_dqo_qpl);
|
||||
dev_op_dqo_qpl, dev_op_modify_ring);
|
||||
dev_opt = next_opt;
|
||||
}
|
||||
|
||||
@ -728,6 +753,12 @@ static int gve_set_desc_cnt(struct gve_priv *priv,
|
||||
{
|
||||
priv->tx_desc_cnt = be16_to_cpu(descriptor->tx_queue_entries);
|
||||
priv->rx_desc_cnt = be16_to_cpu(descriptor->rx_queue_entries);
|
||||
|
||||
/* set default ranges */
|
||||
priv->max_tx_desc_cnt = priv->tx_desc_cnt;
|
||||
priv->max_rx_desc_cnt = priv->rx_desc_cnt;
|
||||
priv->min_tx_desc_cnt = priv->tx_desc_cnt;
|
||||
priv->min_rx_desc_cnt = priv->rx_desc_cnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -739,6 +770,12 @@ gve_set_desc_cnt_dqo(struct gve_priv *priv,
|
||||
priv->tx_desc_cnt = be16_to_cpu(descriptor->tx_queue_entries);
|
||||
priv->rx_desc_cnt = be16_to_cpu(descriptor->rx_queue_entries);
|
||||
|
||||
/* set default ranges */
|
||||
priv->max_tx_desc_cnt = priv->tx_desc_cnt;
|
||||
priv->max_rx_desc_cnt = priv->rx_desc_cnt;
|
||||
priv->min_tx_desc_cnt = priv->tx_desc_cnt;
|
||||
priv->min_rx_desc_cnt = priv->rx_desc_cnt;
|
||||
|
||||
if (priv->queue_format == GVE_DQO_QPL_FORMAT)
|
||||
return 0;
|
||||
|
||||
@ -755,7 +792,9 @@ static void gve_enable_supported_features(struct gve_priv *priv,
|
||||
const struct gve_device_option_jumbo_frames
|
||||
*dev_op_jumbo_frames,
|
||||
const struct gve_device_option_dqo_qpl
|
||||
*dev_op_dqo_qpl)
|
||||
*dev_op_dqo_qpl,
|
||||
const struct gve_device_option_modify_ring
|
||||
*dev_op_modify_ring)
|
||||
{
|
||||
/* Before control reaches this point, the page-size-capped max MTU from
|
||||
* the gve_device_descriptor field has already been stored in
|
||||
@ -768,21 +807,59 @@ static void gve_enable_supported_features(struct gve_priv *priv,
|
||||
priv->dev->max_mtu = be16_to_cpu(dev_op_jumbo_frames->max_mtu);
|
||||
}
|
||||
|
||||
/* Override pages for qpl for DQO-QPL */
|
||||
if (dev_op_dqo_qpl) {
|
||||
priv->tx_pages_per_qpl =
|
||||
be16_to_cpu(dev_op_dqo_qpl->tx_pages_per_qpl);
|
||||
priv->rx_pages_per_qpl =
|
||||
be16_to_cpu(dev_op_dqo_qpl->rx_pages_per_qpl);
|
||||
if (priv->tx_pages_per_qpl == 0)
|
||||
priv->tx_pages_per_qpl = DQO_QPL_DEFAULT_TX_PAGES;
|
||||
if (priv->rx_pages_per_qpl == 0)
|
||||
priv->rx_pages_per_qpl = DQO_QPL_DEFAULT_RX_PAGES;
|
||||
/* Read and store ring size ranges given by device.
|
||||
*
|
||||
* Modifiable ring sizes are supported for DQO queue formats only.
|
||||
* In this kernel's gve, the GQI branches of
|
||||
* gve_adminq_create_{tx,rx}_queue do not carry a ring size, so after
|
||||
* a resize the device would keep using its default ring size against
|
||||
* rings the driver re-sized, desynchronizing device and driver views
|
||||
* of the rings. The GQI datapath (rx_data_slot_cnt data ring, TX
|
||||
* FIFO) also assumes the probe-time fixed ring sizes. Keep GQI at
|
||||
* fixed ring sizes.
|
||||
*/
|
||||
if (dev_op_modify_ring && !gve_is_gqi(priv) &&
|
||||
(supported_features_mask & GVE_SUP_MODIFY_RING_MASK)) {
|
||||
priv->modify_ring_size_enabled = true;
|
||||
|
||||
priv->max_rx_desc_cnt = be16_to_cpu(dev_op_modify_ring->max_rx_ring_size);
|
||||
priv->max_tx_desc_cnt = be16_to_cpu(dev_op_modify_ring->max_tx_ring_size);
|
||||
|
||||
if (priv->default_min_ring_size) {
|
||||
/* If device hasn't provided minimums, use default minimums */
|
||||
priv->min_tx_desc_cnt = GVE_DEFAULT_MIN_TX_RING_SIZE;
|
||||
priv->min_rx_desc_cnt = GVE_DEFAULT_MIN_RX_RING_SIZE;
|
||||
} else {
|
||||
priv->min_rx_desc_cnt = be16_to_cpu(dev_op_modify_ring->min_rx_ring_size);
|
||||
priv->min_tx_desc_cnt = be16_to_cpu(dev_op_modify_ring->min_tx_ring_size);
|
||||
}
|
||||
|
||||
/* In this kernel's gve, the DQO-RDA TX completion ring and RX
|
||||
* buffer ring keep the fixed sizes provided in the DQO-RDA
|
||||
* device option; they do not scale with the descriptor rings
|
||||
* (upstream removed that coupling before modifiable ring
|
||||
* sizes were introduced). Clamp the advertised range so a
|
||||
* resize cannot create a descriptor ring the fixed rings
|
||||
* cannot absorb:
|
||||
* - growing the TX ring beyond tx_comp_ring_entries could
|
||||
* overrun the TX completion queue with report-event
|
||||
* completions (or be rejected at queue creation);
|
||||
* - shrinking the RX ring (the RX completion queue) below
|
||||
* rx_buff_ring_entries could overflow the RX completion
|
||||
* queue.
|
||||
*/
|
||||
if (priv->queue_format == GVE_DQO_RDA_FORMAT) {
|
||||
priv->max_tx_desc_cnt = min(priv->max_tx_desc_cnt,
|
||||
priv->options_dqo_rda.tx_comp_ring_entries);
|
||||
priv->min_rx_desc_cnt = max(priv->min_rx_desc_cnt,
|
||||
priv->options_dqo_rda.rx_buff_ring_entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int gve_adminq_describe_device(struct gve_priv *priv)
|
||||
{
|
||||
struct gve_device_option_modify_ring *dev_op_modify_ring = NULL;
|
||||
struct gve_device_option_jumbo_frames *dev_op_jumbo_frames = NULL;
|
||||
struct gve_device_option_gqi_rda *dev_op_gqi_rda = NULL;
|
||||
struct gve_device_option_gqi_qpl *dev_op_gqi_qpl = NULL;
|
||||
@ -816,7 +893,7 @@ int gve_adminq_describe_device(struct gve_priv *priv)
|
||||
err = gve_process_device_options(priv, descriptor, &dev_op_gqi_rda,
|
||||
&dev_op_gqi_qpl, &dev_op_dqo_rda,
|
||||
&dev_op_jumbo_frames,
|
||||
&dev_op_dqo_qpl);
|
||||
&dev_op_dqo_qpl, &dev_op_modify_ring);
|
||||
if (err)
|
||||
goto free_device_descriptor;
|
||||
|
||||
@ -885,7 +962,8 @@ int gve_adminq_describe_device(struct gve_priv *priv)
|
||||
priv->default_num_queues = be16_to_cpu(descriptor->default_num_queues);
|
||||
|
||||
gve_enable_supported_features(priv, supported_features_mask,
|
||||
dev_op_jumbo_frames, dev_op_dqo_qpl);
|
||||
dev_op_jumbo_frames, dev_op_dqo_qpl,
|
||||
dev_op_modify_ring);
|
||||
|
||||
free_device_descriptor:
|
||||
dma_pool_free(priv->adminq_pool, descriptor, descriptor_bus);
|
||||
|
||||
@ -125,6 +125,16 @@ struct gve_device_option_jumbo_frames {
|
||||
|
||||
static_assert(sizeof(struct gve_device_option_jumbo_frames) == 8);
|
||||
|
||||
struct gve_device_option_modify_ring {
|
||||
__be32 supported_featured_mask;
|
||||
__be16 max_rx_ring_size;
|
||||
__be16 max_tx_ring_size;
|
||||
__be16 min_rx_ring_size;
|
||||
__be16 min_tx_ring_size;
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct gve_device_option_modify_ring) == 12);
|
||||
|
||||
/* Terminology:
|
||||
*
|
||||
* RDA - Raw DMA Addressing - Buffers associated with SKBs are directly DMA
|
||||
@ -138,6 +148,7 @@ enum gve_dev_opt_id {
|
||||
GVE_DEV_OPT_ID_GQI_RDA = 0x2,
|
||||
GVE_DEV_OPT_ID_GQI_QPL = 0x3,
|
||||
GVE_DEV_OPT_ID_DQO_RDA = 0x4,
|
||||
GVE_DEV_OPT_ID_MODIFY_RING = 0x6,
|
||||
GVE_DEV_OPT_ID_DQO_QPL = 0x7,
|
||||
GVE_DEV_OPT_ID_JUMBO_FRAMES = 0x8,
|
||||
};
|
||||
@ -149,9 +160,11 @@ enum gve_dev_opt_req_feat_mask {
|
||||
GVE_DEV_OPT_REQ_FEAT_MASK_DQO_RDA = 0x0,
|
||||
GVE_DEV_OPT_REQ_FEAT_MASK_JUMBO_FRAMES = 0x0,
|
||||
GVE_DEV_OPT_REQ_FEAT_MASK_DQO_QPL = 0x0,
|
||||
GVE_DEV_OPT_REQ_FEAT_MASK_MODIFY_RING = 0x0,
|
||||
};
|
||||
|
||||
enum gve_sup_feature_mask {
|
||||
GVE_SUP_MODIFY_RING_MASK = 1 << 0,
|
||||
GVE_SUP_JUMBO_FRAMES_MASK = 1 << 2,
|
||||
};
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/log2.h>
|
||||
#include "gve.h"
|
||||
#include "gve_adminq.h"
|
||||
#include "gve_dqo.h"
|
||||
@ -476,12 +477,56 @@ static void gve_get_ringparam(struct net_device *netdev,
|
||||
{
|
||||
struct gve_priv *priv = netdev_priv(netdev);
|
||||
|
||||
cmd->rx_max_pending = priv->rx_desc_cnt;
|
||||
cmd->tx_max_pending = priv->tx_desc_cnt;
|
||||
cmd->rx_max_pending = priv->max_rx_desc_cnt;
|
||||
cmd->tx_max_pending = priv->max_tx_desc_cnt;
|
||||
cmd->rx_pending = priv->rx_desc_cnt;
|
||||
cmd->tx_pending = priv->tx_desc_cnt;
|
||||
}
|
||||
|
||||
static int gve_validate_req_ring_size(struct gve_priv *priv, u16 new_tx_desc_cnt,
|
||||
u16 new_rx_desc_cnt)
|
||||
{
|
||||
/* check for valid range */
|
||||
if (new_tx_desc_cnt < priv->min_tx_desc_cnt ||
|
||||
new_tx_desc_cnt > priv->max_tx_desc_cnt ||
|
||||
new_rx_desc_cnt < priv->min_rx_desc_cnt ||
|
||||
new_rx_desc_cnt > priv->max_rx_desc_cnt) {
|
||||
dev_err(&priv->pdev->dev, "Requested descriptor count out of range\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(new_tx_desc_cnt) || !is_power_of_2(new_rx_desc_cnt)) {
|
||||
dev_err(&priv->pdev->dev, "Requested descriptor count has to be a power of 2\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gve_set_ringparam(struct net_device *netdev,
|
||||
struct ethtool_ringparam *cmd,
|
||||
struct kernel_ethtool_ringparam *kernel_cmd,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct gve_priv *priv = netdev_priv(netdev);
|
||||
u16 new_tx_cnt, new_rx_cnt;
|
||||
|
||||
if (cmd->tx_pending == priv->tx_desc_cnt && cmd->rx_pending == priv->rx_desc_cnt)
|
||||
return 0;
|
||||
|
||||
if (!priv->modify_ring_size_enabled) {
|
||||
dev_err(&priv->pdev->dev, "Modify ring size is not supported.\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
new_tx_cnt = cmd->tx_pending;
|
||||
new_rx_cnt = cmd->rx_pending;
|
||||
|
||||
if (gve_validate_req_ring_size(priv, new_tx_cnt, new_rx_cnt))
|
||||
return -EINVAL;
|
||||
|
||||
return gve_adjust_ring_sizes(priv, new_tx_cnt, new_rx_cnt);
|
||||
}
|
||||
|
||||
static int gve_user_reset(struct net_device *netdev, u32 *flags)
|
||||
{
|
||||
struct gve_priv *priv = netdev_priv(netdev);
|
||||
@ -667,6 +712,7 @@ const struct ethtool_ops gve_ethtool_ops = {
|
||||
.get_coalesce = gve_get_coalesce,
|
||||
.set_coalesce = gve_set_coalesce,
|
||||
.get_ringparam = gve_get_ringparam,
|
||||
.set_ringparam = gve_set_ringparam,
|
||||
.reset = gve_user_reset,
|
||||
.get_tunable = gve_get_tunable,
|
||||
.set_tunable = gve_set_tunable,
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/sched.h>
|
||||
@ -1106,6 +1107,43 @@ free_qpls:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* For DQO, update the suggested number of pages per qpl to be more flexible
|
||||
* and honor the "max_registered_pages" parameter from the gVNIC device.
|
||||
*
|
||||
* Ignore the "tx_pages_per_qpl"/"rx_pages_per_qpl" parameters indicated in the
|
||||
* DQO-QPL device option and instead allocate up to (tx_queue_length / 2) pages
|
||||
* per TX QPL and up to (rx_queue_length * 2) pages per RX QPL while keeping the
|
||||
* total number of pages under "max_registered_pages".
|
||||
*/
|
||||
static void gve_update_num_qpl_pages(struct gve_priv *priv)
|
||||
{
|
||||
u64 ideal_tx_pages, ideal_rx_pages;
|
||||
u16 tx_num_queues, rx_num_queues;
|
||||
u64 max_pages, tx_pages;
|
||||
|
||||
if (priv->queue_format != GVE_DQO_QPL_FORMAT)
|
||||
return;
|
||||
|
||||
/* We want 2 pages per RX descriptor and half a page per TX descriptor,
|
||||
* which means the fraction ideal_tx_pages / (ideal_tx_pages +
|
||||
* ideal_rx_pages) of the pages we allocate should be for TX. Shrink
|
||||
* proportionally as necessary to avoid allocating more than
|
||||
* max_registered_pages total pages.
|
||||
*/
|
||||
tx_num_queues = priv->tx_cfg.num_queues;
|
||||
rx_num_queues = priv->rx_cfg.num_queues;
|
||||
|
||||
ideal_tx_pages = (u64)priv->tx_desc_cnt * tx_num_queues / 2;
|
||||
ideal_rx_pages = (u64)priv->rx_desc_cnt * rx_num_queues * 2;
|
||||
max_pages = min(priv->max_registered_pages,
|
||||
ideal_tx_pages + ideal_rx_pages);
|
||||
|
||||
tx_pages = div64_u64(max_pages * ideal_tx_pages,
|
||||
ideal_tx_pages + ideal_rx_pages);
|
||||
priv->tx_pages_per_qpl = div_u64(tx_pages, tx_num_queues);
|
||||
priv->rx_pages_per_qpl = div_u64(max_pages - tx_pages, rx_num_queues);
|
||||
}
|
||||
|
||||
static int gve_alloc_qpls(struct gve_priv *priv)
|
||||
{
|
||||
int max_queues = priv->tx_cfg.max_queues + priv->rx_cfg.max_queues;
|
||||
@ -1117,6 +1155,8 @@ static int gve_alloc_qpls(struct gve_priv *priv)
|
||||
if (!gve_is_qpl(priv))
|
||||
return 0;
|
||||
|
||||
gve_update_num_qpl_pages(priv);
|
||||
|
||||
priv->qpls = kvcalloc(max_queues, sizeof(*priv->qpls), GFP_KERNEL);
|
||||
if (!priv->qpls)
|
||||
return -ENOMEM;
|
||||
@ -1740,6 +1780,51 @@ err:
|
||||
return err;
|
||||
}
|
||||
|
||||
int gve_adjust_ring_sizes(struct gve_priv *priv,
|
||||
u16 new_tx_desc_cnt,
|
||||
u16 new_rx_desc_cnt)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Key on the administrative state (netif_running), not the link
|
||||
* state: the rings are allocated whenever the interface is up, even
|
||||
* while the carrier is down. Changing the descriptor counts without
|
||||
* reallocating the rings would desynchronize them from
|
||||
* priv->{tx,rx}_desc_cnt, which the GQI free path uses to size
|
||||
* dma_free_coherent().
|
||||
*/
|
||||
if (netif_running(priv->dev)) {
|
||||
/* To make this process as simple as possible we teardown the
|
||||
* device, set the new ring sizes, and then bring the device
|
||||
* up again.
|
||||
*/
|
||||
err = gve_close(priv->dev);
|
||||
/* we have already tried to reset in close,
|
||||
* just fail at this point
|
||||
*/
|
||||
if (err)
|
||||
return err;
|
||||
priv->tx_desc_cnt = new_tx_desc_cnt;
|
||||
priv->rx_desc_cnt = new_rx_desc_cnt;
|
||||
|
||||
err = gve_open(priv->dev);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* Set the ring sizes for the next up. */
|
||||
priv->tx_desc_cnt = new_tx_desc_cnt;
|
||||
priv->rx_desc_cnt = new_rx_desc_cnt;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
netif_err(priv, drv, priv->dev,
|
||||
"Adjust ring sizes failed! !!! DISABLING ALL QUEUES !!!\n");
|
||||
gve_turndown(priv);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void gve_turndown(struct gve_priv *priv)
|
||||
{
|
||||
int idx;
|
||||
|
||||
@ -484,14 +484,16 @@ static void iwl_init_vht_hw_capab(struct iwl_trans *trans,
|
||||
*/
|
||||
switch (iwlwifi_mod_params.amsdu_size) {
|
||||
case IWL_AMSDU_DEF:
|
||||
if (trans->mac_cfg->mq_rx_supported && !fips_enabled)
|
||||
if (trans->mac_cfg->mq_rx_supported &&
|
||||
fips_allows(FIPS_EXCEPTION_WIFI_MFP))
|
||||
vht_cap->cap |=
|
||||
IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
|
||||
else
|
||||
vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
|
||||
break;
|
||||
case IWL_AMSDU_2K:
|
||||
if (trans->mac_cfg->mq_rx_supported && !fips_enabled)
|
||||
if (trans->mac_cfg->mq_rx_supported &&
|
||||
fips_allows(FIPS_EXCEPTION_WIFI_MFP))
|
||||
vht_cap->cap |=
|
||||
IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
|
||||
else
|
||||
@ -850,7 +852,7 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
|
||||
|
||||
/* EHT needs WPA3/MFP so cannot do it for fips_enabled */
|
||||
if (!data->sku_cap_11be_enable || iwlwifi_mod_params.disable_11be ||
|
||||
fips_enabled)
|
||||
!fips_allows(FIPS_EXCEPTION_WIFI_MFP))
|
||||
iftype_data->eht_cap.has_eht = false;
|
||||
|
||||
/* Advertise an A-MPDU exponent extension based on
|
||||
@ -1140,7 +1142,8 @@ static void iwl_init_sbands(struct iwl_trans *trans,
|
||||
* avoid spending time on scanning those channels and perhaps
|
||||
* even finding APs there that cannot be used.
|
||||
*/
|
||||
if (!fips_enabled && data->sku_cap_11ax_enable &&
|
||||
if (fips_allows(FIPS_EXCEPTION_WIFI_MFP) &&
|
||||
data->sku_cap_11ax_enable &&
|
||||
!iwlwifi_mod_params.disable_11ax)
|
||||
iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains,
|
||||
fw);
|
||||
|
||||
@ -157,7 +157,7 @@ static void iwl_mld_hw_set_security(struct iwl_mld *mld)
|
||||
WLAN_CIPHER_SUITE_BIP_GMAC_256
|
||||
};
|
||||
|
||||
if (fips_enabled)
|
||||
if (!fips_allows(FIPS_EXCEPTION_WIFI_MFP))
|
||||
return;
|
||||
|
||||
hw->wiphy->n_cipher_suites = ARRAY_SIZE(mld_ciphers);
|
||||
@ -166,6 +166,9 @@ static void iwl_mld_hw_set_security(struct iwl_mld *mld)
|
||||
ieee80211_hw_set(hw, MFP_CAPABLE);
|
||||
wiphy_ext_feature_set(hw->wiphy,
|
||||
NL80211_EXT_FEATURE_BEACON_PROTECTION);
|
||||
|
||||
if (fips_enabled)
|
||||
IWL_WARN(mld, "FIPS: MFP enabled with known firmware limitation\n");
|
||||
}
|
||||
|
||||
static void iwl_mld_hw_set_antennas(struct iwl_mld *mld)
|
||||
@ -295,7 +298,7 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld)
|
||||
if (mld->nvm_data->sku_cap_11be_enable &&
|
||||
!iwlwifi_mod_params.disable_11ax &&
|
||||
!iwlwifi_mod_params.disable_11be &&
|
||||
!fips_enabled)
|
||||
fips_allows(FIPS_EXCEPTION_WIFI_MFP))
|
||||
wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
|
||||
|
||||
/* the firmware uses u8 for num of iterations, but 0xff is saved for
|
||||
|
||||
@ -462,8 +462,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
|
||||
IWL_ERR(mvm,
|
||||
"iwlmvm doesn't allow to disable BT Coex, check bt_coex_active module parameter\n");
|
||||
|
||||
if (!fips_enabled)
|
||||
if (fips_allows(FIPS_EXCEPTION_WIFI_MFP)) {
|
||||
ieee80211_hw_set(hw, MFP_CAPABLE);
|
||||
if (fips_enabled)
|
||||
IWL_WARN(mvm, "FIPS: MFP enabled with known firmware limitation\n");
|
||||
}
|
||||
|
||||
mvm->ciphers[hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_AES_CMAC;
|
||||
hw->wiphy->n_cipher_suites++;
|
||||
@ -492,12 +495,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
|
||||
* beacon protection must be handled by firmware,
|
||||
* so cannot be done with fips_enabled
|
||||
*/
|
||||
if (!fips_enabled && sec_key_ver &&
|
||||
if (fips_allows(FIPS_EXCEPTION_WIFI_MFP) && sec_key_ver &&
|
||||
fw_has_capa(&mvm->fw->ucode_capa,
|
||||
IWL_UCODE_TLV_CAPA_BIGTK_TX_SUPPORT))
|
||||
wiphy_ext_feature_set(hw->wiphy,
|
||||
NL80211_EXT_FEATURE_BEACON_PROTECTION);
|
||||
else if (!fips_enabled &&
|
||||
else if (fips_allows(FIPS_EXCEPTION_WIFI_MFP) &&
|
||||
fw_has_capa(&mvm->fw->ucode_capa,
|
||||
IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT))
|
||||
wiphy_ext_feature_set(hw->wiphy,
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
*/
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/fips.h>
|
||||
#include "iwl-trans.h"
|
||||
#include "mvm.h"
|
||||
#include "fw-api.h"
|
||||
@ -494,6 +495,14 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
|
||||
return 0;
|
||||
case RX_MPDU_RES_STATUS_SEC_CMAC_GMAC_ENC:
|
||||
break;
|
||||
case RX_MPDU_RES_STATUS_SEC_ENC_ERR:
|
||||
if (fips_enabled) {
|
||||
IWL_DEBUG_RX(mvm,
|
||||
"FIPS mode: firmware cannot decrypt, status: 0x%x\n",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
default:
|
||||
/*
|
||||
* Sometimes we can get frames that were not decrypted
|
||||
|
||||
@ -2329,8 +2329,8 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
|
||||
|
||||
if (conn->conn_ops->DataDigest) {
|
||||
iscsit_do_crypto_hash_buf(conn->conn_rx_hash,
|
||||
text_in, rx_size, 0, NULL,
|
||||
&data_crc);
|
||||
text_in, ALIGN(payload_length, 4),
|
||||
0, NULL, &data_crc);
|
||||
|
||||
if (checksum != data_crc) {
|
||||
pr_err("Text data CRC32C DataDigest"
|
||||
@ -2350,6 +2350,7 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
|
||||
" Command CmdSN: 0x%08x due to"
|
||||
" DataCRC error.\n", hdr->cmdsn);
|
||||
kfree(text_in);
|
||||
cmd->text_in_ptr = NULL;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -1642,6 +1642,14 @@ static long vhost_vring_set_num_addr(struct vhost_dev *d,
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* The metadata cache holds the IOTLB mapping that backed the previous
|
||||
* desc/avail/used addresses and vring size, both of which are being
|
||||
* replaced here. iotlb_access_ok() takes a cache hit as proof that the
|
||||
* region was validated, so the stale entries have to go.
|
||||
*/
|
||||
__vhost_vq_meta_reset(vq);
|
||||
|
||||
mutex_unlock(&vq->mutex);
|
||||
|
||||
return r;
|
||||
|
||||
@ -889,7 +889,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
*/
|
||||
fattr->cf_mode &= ~07777;
|
||||
fattr->cf_mode |=
|
||||
le32_to_cpu(ppace[i]->sid.sub_auth[2]);
|
||||
le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777;
|
||||
break;
|
||||
} else {
|
||||
if (compare_sids(&(ppace[i]->sid), pownersid) == 0) {
|
||||
|
||||
@ -254,9 +254,23 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
||||
goto out;
|
||||
|
||||
if (out_buf) {
|
||||
*pbytes_returned = resp_iov.iov_len;
|
||||
if (resp_iov.iov_len)
|
||||
memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len);
|
||||
/* Use smbCalcSize() for both single- and multi-part T2 responses,
|
||||
* both here and in coalesce_t2().
|
||||
*/
|
||||
unsigned int copy_len;
|
||||
if (WARN_ON_ONCE(!resp_iov.iov_base)) {
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
copy_len = smbCalcSize(resp_iov.iov_base);
|
||||
if (copy_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
|
||||
cifs_dbg(VFS, "response size %u exceeds buffer\n",
|
||||
copy_len);
|
||||
rc = -ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
*pbytes_returned = copy_len;
|
||||
memcpy(out_buf, resp_iov.iov_base, copy_len);
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
@ -935,6 +935,8 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
||||
int i, rc = 0;
|
||||
char *data_end;
|
||||
struct dfs_referral_level_3 *ref;
|
||||
unsigned int path_consumed;
|
||||
size_t search_name_len;
|
||||
|
||||
if (rsp_size < sizeof(*rsp)) {
|
||||
cifs_dbg(VFS | ONCE,
|
||||
@ -982,6 +984,7 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
||||
rc = -ENOMEM;
|
||||
goto parse_DFS_referrals_exit;
|
||||
}
|
||||
search_name_len = strlen(searchName);
|
||||
|
||||
/* collect necessary data from referrals */
|
||||
for (i = 0; i < *num_of_nodes; i++) {
|
||||
@ -990,21 +993,34 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
||||
struct dfs_info3_param *node = (*target_nodes)+i;
|
||||
|
||||
node->flags = le32_to_cpu(rsp->DFSFlags);
|
||||
path_consumed = le16_to_cpu(rsp->PathConsumed);
|
||||
if (is_unicode) {
|
||||
__le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
|
||||
GFP_KERNEL);
|
||||
if (tmp == NULL) {
|
||||
size_t search_name_utf16_len = search_name_len * 2 + 2;
|
||||
__le16 *tmp;
|
||||
|
||||
if (path_consumed > search_name_utf16_len) {
|
||||
rc = -EINVAL;
|
||||
goto parse_DFS_referrals_exit;
|
||||
}
|
||||
|
||||
tmp = kmalloc(search_name_utf16_len, GFP_KERNEL);
|
||||
if (!tmp) {
|
||||
rc = -ENOMEM;
|
||||
goto parse_DFS_referrals_exit;
|
||||
}
|
||||
cifsConvertToUTF16((__le16 *) tmp, searchName,
|
||||
cifsConvertToUTF16((__le16 *)tmp, searchName,
|
||||
PATH_MAX, nls_codepage, remap);
|
||||
node->path_consumed = cifs_utf16_bytes(tmp,
|
||||
le16_to_cpu(rsp->PathConsumed),
|
||||
nls_codepage);
|
||||
node->path_consumed = cifs_utf16_bytes(tmp, path_consumed,
|
||||
nls_codepage);
|
||||
kfree(tmp);
|
||||
} else
|
||||
node->path_consumed = le16_to_cpu(rsp->PathConsumed);
|
||||
} else {
|
||||
if (path_consumed > search_name_len) {
|
||||
rc = -EINVAL;
|
||||
goto parse_DFS_referrals_exit;
|
||||
}
|
||||
|
||||
node->path_consumed = path_consumed;
|
||||
}
|
||||
|
||||
node->server_type = le16_to_cpu(ref->ServerType);
|
||||
node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
|
||||
|
||||
@ -399,11 +399,13 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len)
|
||||
}
|
||||
put_bcc(byte_count, target_hdr);
|
||||
|
||||
byte_count = *pdu_len;
|
||||
byte_count += total_in_src;
|
||||
/* use smbCalcSize() rather than *pdu_len: the demux loop resets
|
||||
* *pdu_len to each secondary's pdu_length, making it unreliable.
|
||||
*/
|
||||
byte_count = smbCalcSize(target_hdr);
|
||||
/* don't allow buffer to overflow */
|
||||
if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
|
||||
cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n",
|
||||
cifs_dbg(FYI, "coalesced size exceeds buffer size (%u)\n",
|
||||
byte_count);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
@ -3498,6 +3498,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
|
||||
struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
|
||||
u32 out_data_len;
|
||||
char *buf = NULL;
|
||||
u64 range_start, range_len, range_end;
|
||||
loff_t l;
|
||||
int rc;
|
||||
|
||||
@ -3534,13 +3535,21 @@ static int smb3_simple_fallocate_range(unsigned int xid,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (off < le64_to_cpu(tmp_data->file_offset)) {
|
||||
range_start = le64_to_cpu(tmp_data->file_offset);
|
||||
range_len = le64_to_cpu(tmp_data->length);
|
||||
if (check_add_overflow(range_start, range_len, &range_end) ||
|
||||
range_end > S64_MAX) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (off < range_start) {
|
||||
/*
|
||||
* We are at a hole. Write until the end of the region
|
||||
* or until the next allocated data,
|
||||
* whichever comes next.
|
||||
*/
|
||||
l = le64_to_cpu(tmp_data->file_offset) - off;
|
||||
l = range_start - off;
|
||||
if (len < l)
|
||||
l = len;
|
||||
rc = smb3_simple_fallocate_write_range(xid, tcon,
|
||||
@ -3557,11 +3566,13 @@ static int smb3_simple_fallocate_range(unsigned int xid,
|
||||
* until the end of the data or the end of the region
|
||||
* we are supposed to fallocate, whichever comes first.
|
||||
*/
|
||||
l = le64_to_cpu(tmp_data->length);
|
||||
if (len < l)
|
||||
l = len;
|
||||
off += l;
|
||||
len -= l;
|
||||
if (off < range_end) {
|
||||
l = range_end - off;
|
||||
if (len < l)
|
||||
l = len;
|
||||
off += l;
|
||||
len -= l;
|
||||
}
|
||||
|
||||
tmp_data = &tmp_data[1];
|
||||
out_data_len -= sizeof(struct file_allocated_range_buffer);
|
||||
|
||||
@ -3231,6 +3231,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
||||
|
||||
replay_again:
|
||||
/* reinitialize for possible replay */
|
||||
resp_buftype = CIFS_NO_BUFFER;
|
||||
memset(&rsp_iov, 0, sizeof(rsp_iov));
|
||||
flags = 0;
|
||||
server = cifs_pick_channel(ses);
|
||||
oparms->replay = !!(retries);
|
||||
@ -3847,6 +3849,8 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
replay_again:
|
||||
/* reinitialize for possible replay */
|
||||
resp_buftype = CIFS_NO_BUFFER;
|
||||
memset(&rsp_iov, 0, sizeof(rsp_iov));
|
||||
flags = 0;
|
||||
allocated = false;
|
||||
server = cifs_pick_channel(ses);
|
||||
|
||||
@ -1617,8 +1617,9 @@ static struct udf_vds_record *handle_partition_descriptor(
|
||||
return &(data->part_descs_loc[i].rec);
|
||||
if (data->num_part_descs >= data->size_part_descs) {
|
||||
struct part_desc_seq_scan_data *new_loc;
|
||||
unsigned int new_size = ALIGN(partnum, PART_DESC_ALLOC_STEP);
|
||||
unsigned int new_size;
|
||||
|
||||
new_size = data->num_part_descs + PART_DESC_ALLOC_STEP;
|
||||
new_loc = kcalloc(new_size, sizeof(*new_loc), GFP_KERNEL);
|
||||
if (!new_loc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -1628,6 +1629,7 @@ static struct udf_vds_record *handle_partition_descriptor(
|
||||
data->part_descs_loc = new_loc;
|
||||
data->size_part_descs = new_size;
|
||||
}
|
||||
data->part_descs_loc[data->num_part_descs].partnum = partnum;
|
||||
return &(data->part_descs_loc[data->num_part_descs++].rec);
|
||||
}
|
||||
|
||||
|
||||
@ -16,12 +16,24 @@ extern struct atomic_notifier_head fips_fail_notif_chain;
|
||||
#endif
|
||||
|
||||
void fips_fail_notify(void);
|
||||
/*
|
||||
* fips_allows - check if a not fully FIPS-compliant feature is allowed
|
||||
* via an explicit boot-time exception (rh_fips_exception=).
|
||||
* Not for fully FIPS-compliant features.
|
||||
*/
|
||||
int fips_allows(unsigned long feature);
|
||||
|
||||
#else
|
||||
#define fips_enabled 0
|
||||
|
||||
static inline void fips_fail_notify(void) {}
|
||||
static inline int fips_allows(unsigned long feature)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define FIPS_EXCEPTION_WIFI_MFP BIT(0)
|
||||
|
||||
#endif
|
||||
|
||||
@ -24,7 +24,9 @@ void psi_memstall_leave(unsigned long *flags);
|
||||
int psi_show(struct seq_file *s, struct psi_group *group, enum psi_res res);
|
||||
struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
|
||||
enum psi_res res, struct file *file,
|
||||
struct kernfs_open_file *of);
|
||||
struct kernfs_open_file *of,
|
||||
bool *need_rtpoll_worker);
|
||||
int psi_trigger_create_rtpoll_worker(struct psi_group *group);
|
||||
void psi_trigger_destroy(struct psi_trigger *t);
|
||||
|
||||
__poll_t psi_trigger_poll(void **trigger_ptr, struct file *file,
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
|
||||
kernel.rhel,1,Red Hat,kernel-core,5.14.0-687.39.1.el9.x86_64,mailto:secalert@redhat.com
|
||||
kernel.almalinux,1,AlmaLinux,kernel-core,5.14.0-687.39.1.el9.x86_64,mailto:security@almalinux.org
|
||||
kernel.rhel,1,Red Hat,kernel-core,5.14.0-687.41.1.el9.x86_64,mailto:secalert@redhat.com
|
||||
kernel.almalinux,1,AlmaLinux,kernel-core,5.14.0-687.41.1.el9.x86_64,mailto:security@almalinux.org
|
||||
|
||||
@ -3859,33 +3859,62 @@ static int cgroup_cpu_pressure_show(struct seq_file *seq, void *v)
|
||||
static ssize_t pressure_write(struct kernfs_open_file *of, char *buf,
|
||||
size_t nbytes, enum psi_res res)
|
||||
{
|
||||
struct cgroup_file_ctx *ctx = of->priv;
|
||||
struct cgroup_file_ctx *ctx;
|
||||
struct psi_trigger *new;
|
||||
struct cgroup *cgrp;
|
||||
struct psi_group *psi;
|
||||
bool need_rtpoll_worker;
|
||||
ssize_t ret = 0;
|
||||
|
||||
cgrp = cgroup_kn_lock_live(of->kn, false);
|
||||
if (!cgrp)
|
||||
return -ENODEV;
|
||||
|
||||
cgroup_get(cgrp);
|
||||
cgroup_kn_unlock(of->kn);
|
||||
ctx = of->priv;
|
||||
if (!ctx) {
|
||||
ret = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* Allow only one trigger per file descriptor */
|
||||
if (ctx->psi.trigger) {
|
||||
cgroup_put(cgrp);
|
||||
return -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
psi = cgroup_psi(cgrp);
|
||||
new = psi_trigger_create(psi, buf, res, of->file, of);
|
||||
new = psi_trigger_create(psi, buf, res, of->file, of,
|
||||
&need_rtpoll_worker);
|
||||
if (IS_ERR(new)) {
|
||||
cgroup_put(cgrp);
|
||||
return PTR_ERR(new);
|
||||
ret = PTR_ERR(new);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* The worker fork must run with neither cgroup_mutex nor the file's
|
||||
* kernfs active reference held. The latter is broken since
|
||||
* cgroup_kn_lock_live(). @of->priv may be released while unlocked, so
|
||||
* recheck before publishing @new.
|
||||
*/
|
||||
if (need_rtpoll_worker) {
|
||||
cgroup_unlock();
|
||||
ret = psi_trigger_create_rtpoll_worker(psi);
|
||||
cgroup_lock();
|
||||
|
||||
if (!ret && !of->priv)
|
||||
ret = -ENODEV;
|
||||
if (ret) {
|
||||
psi_trigger_destroy(new);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
smp_store_release(&ctx->psi.trigger, new);
|
||||
cgroup_put(cgrp);
|
||||
|
||||
out_unlock:
|
||||
cgroup_kn_unlock(of->kn);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
@ -4149,6 +4178,7 @@ static void cgroup_file_release(struct kernfs_open_file *of)
|
||||
cft->release(of);
|
||||
put_cgroup_ns(ctx->ns);
|
||||
kfree(ctx);
|
||||
of->priv = NULL;
|
||||
}
|
||||
|
||||
static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf,
|
||||
|
||||
@ -184,7 +184,13 @@ static void __exit_signal(struct task_struct *tsk)
|
||||
* doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals.
|
||||
*/
|
||||
flush_sigqueue(&tsk->pending);
|
||||
tsk->sighand = NULL;
|
||||
|
||||
/*
|
||||
* Ensure that all preceeding state is visible. Pairs with
|
||||
* the smp_acquire__after_ctrl_dep() in the sighand == NULL
|
||||
* path of lock_task_sighand().
|
||||
*/
|
||||
smp_store_release(&tsk->sighand, NULL);
|
||||
spin_unlock(&sighand->siglock);
|
||||
|
||||
__cleanup_sighand(sighand);
|
||||
|
||||
@ -1312,9 +1312,44 @@ static int psi_cpu_open(struct inode *inode, struct file *file)
|
||||
return single_open(file, psi_cpu_show, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create @group's rtpoll worker after psi_trigger_create() reported the need
|
||||
* for one. kthread creation depends on the whole fork path and we don't want
|
||||
* all of that nested inside cgroup_mutex, so the caller must drop it and any
|
||||
* other lock that forks can wait behind. If two callers race, the loser stops
|
||||
* its never-woken kthread.
|
||||
*/
|
||||
int psi_trigger_create_rtpoll_worker(struct psi_group *group)
|
||||
{
|
||||
struct task_struct *task;
|
||||
|
||||
task = kthread_create(psi_rtpoll_worker, group, "psimon");
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
|
||||
scoped_guard(mutex, &group->rtpoll_trigger_lock) {
|
||||
if (!rcu_access_pointer(group->rtpoll_task)) {
|
||||
atomic_set(&group->rtpoll_wakeup, 0);
|
||||
wake_up_process(task);
|
||||
rcu_assign_pointer(group->rtpoll_task, task);
|
||||
|
||||
/*
|
||||
* Poll once to catch up on scheduling attempts dropped
|
||||
* while there was no rtpoll worker.
|
||||
*/
|
||||
psi_schedule_rtpoll_work(group, 1, true);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
kthread_stop(task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
|
||||
enum psi_res res, struct file *file,
|
||||
struct kernfs_open_file *of)
|
||||
struct kernfs_open_file *of,
|
||||
bool *need_rtpoll_worker)
|
||||
{
|
||||
struct psi_trigger *t;
|
||||
enum psi_states state;
|
||||
@ -1322,6 +1357,8 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
|
||||
bool privileged;
|
||||
u32 window_us;
|
||||
|
||||
*need_rtpoll_worker = false;
|
||||
|
||||
if (static_branch_likely(&psi_disabled))
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
@ -1381,26 +1418,14 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
|
||||
if (privileged) {
|
||||
mutex_lock(&group->rtpoll_trigger_lock);
|
||||
|
||||
if (!rcu_access_pointer(group->rtpoll_task)) {
|
||||
struct task_struct *task;
|
||||
|
||||
task = kthread_create(psi_rtpoll_worker, group, "psimon");
|
||||
if (IS_ERR(task)) {
|
||||
kfree(t);
|
||||
mutex_unlock(&group->rtpoll_trigger_lock);
|
||||
return ERR_CAST(task);
|
||||
}
|
||||
atomic_set(&group->rtpoll_wakeup, 0);
|
||||
wake_up_process(task);
|
||||
rcu_assign_pointer(group->rtpoll_task, task);
|
||||
}
|
||||
|
||||
list_add(&t->node, &group->rtpoll_triggers);
|
||||
group->rtpoll_min_period = min(group->rtpoll_min_period,
|
||||
div_u64(t->win.size, UPDATES_PER_WINDOW));
|
||||
group->rtpoll_nr_triggers[t->state]++;
|
||||
group->rtpoll_states |= (1 << t->state);
|
||||
|
||||
*need_rtpoll_worker = !rcu_access_pointer(group->rtpoll_task);
|
||||
|
||||
mutex_unlock(&group->rtpoll_trigger_lock);
|
||||
} else {
|
||||
mutex_lock(&group->avgs_lock);
|
||||
@ -1524,6 +1549,8 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf,
|
||||
size_t buf_size;
|
||||
struct seq_file *seq;
|
||||
struct psi_trigger *new;
|
||||
bool need_rtpoll_worker;
|
||||
int ret;
|
||||
|
||||
if (static_branch_likely(&psi_disabled))
|
||||
return -EOPNOTSUPP;
|
||||
@ -1548,12 +1575,22 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf,
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
new = psi_trigger_create(&psi_system, buf, res, file, NULL);
|
||||
new = psi_trigger_create(&psi_system, buf, res, file, NULL,
|
||||
&need_rtpoll_worker);
|
||||
if (IS_ERR(new)) {
|
||||
mutex_unlock(&seq->lock);
|
||||
return PTR_ERR(new);
|
||||
}
|
||||
|
||||
if (need_rtpoll_worker) {
|
||||
ret = psi_trigger_create_rtpoll_worker(&psi_system);
|
||||
if (ret) {
|
||||
psi_trigger_destroy(new);
|
||||
mutex_unlock(&seq->lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
smp_store_release(&seq->private, new);
|
||||
mutex_unlock(&seq->lock);
|
||||
|
||||
|
||||
@ -1395,8 +1395,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
|
||||
rcu_read_lock();
|
||||
for (;;) {
|
||||
sighand = rcu_dereference(tsk->sighand);
|
||||
if (unlikely(sighand == NULL))
|
||||
if (unlikely(sighand == NULL)) {
|
||||
/*
|
||||
* Pairs with the smp_store_release() in
|
||||
* __exit_signal(). It ensures that all state
|
||||
* modifications to the task preceeding the store are
|
||||
* visible to the callers of lock_task_sighand().
|
||||
*/
|
||||
smp_acquire__after_ctrl_dep();
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* This sighand can be already freed and even reused, but
|
||||
|
||||
@ -462,6 +462,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
|
||||
trigger_base_recalc_expires(timer, p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand.
|
||||
*
|
||||
* This can race with the reaping of the task:
|
||||
*
|
||||
* CPU0 CPU1
|
||||
*
|
||||
* // Finds task
|
||||
* p = pid_task(pid, pid_type); __exit_signal(p)
|
||||
* lock(p, sighand);
|
||||
* posix_cpu_timers*_exit();
|
||||
* sighand = lock_task_sighand(p); unhash_task(p);
|
||||
* p->sighand = NULL;
|
||||
* unlock(sighand);
|
||||
*
|
||||
* In this case sighand is NULL, which means the task and the associated timer
|
||||
* queue cannot be longer accessed safely.
|
||||
*
|
||||
* __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is
|
||||
* dead it also invokes posix_cpu_timers_group_exit(). These functions delete
|
||||
* all pending timers from the related timer queues. The POSIX timers (k_itimer)
|
||||
* themself are still accessible, but not longer connected to the task.
|
||||
*
|
||||
* exec() works slightly differently. The task which exec()'s terminates all
|
||||
* other threads in the thread group and runs __exit_signal() on them. As the
|
||||
* thread group is not dead they only clean up the per task timers via
|
||||
* posix_cpu_timers_exit().
|
||||
*
|
||||
* As the TGID on exec() stays the same per process timers stay queued, if they
|
||||
* are armed. This works without a problem when exec() is done by the thread
|
||||
* group leader. If a non-leader thread exec()'s this can end up in the
|
||||
* following scenario:
|
||||
*
|
||||
* CPU0 CPU1
|
||||
* // Returns old leader
|
||||
* p = pid_task(pid, pid_type); de_thread()
|
||||
* switch_leader()
|
||||
* release_task(old leader)
|
||||
* __exit_signal()
|
||||
* old_leader->sighand = NULL;
|
||||
* // Returns NULL
|
||||
* sighand = lock_task_sighand(p)
|
||||
*
|
||||
* That's problematic for several functions:
|
||||
*
|
||||
* - posix_cpu_timer_del(): If the timer is still enqueued on the task the
|
||||
* underlying k_itimer will be freed which results in a UAF in
|
||||
* run_posix_cpu_timers() or on timerqueue related add/delete operations.
|
||||
* If the timer is not enqueued, the failure is harmless
|
||||
*
|
||||
* - posix_cpu_timer_set(): Independent of the enqueued state that results in a
|
||||
* transient failure which is user space visible (-ESRCH) for regular posix
|
||||
* timers. But for the use case in do_cpu_nanosleep() it's the same UAF
|
||||
* problem just that the timer is allocated on the stack.
|
||||
*
|
||||
* - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this
|
||||
* silently ignores the rearm request, which is a functional problem as the
|
||||
* timer wont expire anymore.
|
||||
*/
|
||||
static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags)
|
||||
{
|
||||
enum pid_type type = clock_pid_type(timer->it_clock);
|
||||
struct cpu_timer *ctmr = &timer->it.cpu;
|
||||
|
||||
guard(rcu)();
|
||||
|
||||
for (;;) {
|
||||
struct task_struct *t = pid_task(timer->it.cpu.pid, type);
|
||||
|
||||
/* Fail if the task cannot be found. */
|
||||
if (!t)
|
||||
break;
|
||||
|
||||
/* Try to lock the task's sighand */
|
||||
if (lock_task_sighand(t, flags))
|
||||
return t;
|
||||
|
||||
/*
|
||||
* The next PID lookup might either fail or return the new
|
||||
* leader. This is correct for both exit() and exec().
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* If the timer is still enqueued, warn. There is nothing safe to do
|
||||
* here as there might be two timers in there which are removed in
|
||||
* parallel and that will cause more damage than good. This should never
|
||||
* happen!
|
||||
*
|
||||
* Ensure that the stores to the timer and timerqueue are visible:
|
||||
*
|
||||
* __exit_signal()
|
||||
* posix_cpu_timers*_exit()
|
||||
* write_seqlock(seqlock)
|
||||
* smp_wmb(); <-------
|
||||
* __unhash_process() | !pid_task()
|
||||
* ----> smp_rmb();
|
||||
* WARN_ON_ONCE(...)
|
||||
*/
|
||||
smp_rmb();
|
||||
WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up a CPU-clock timer that is about to be destroyed.
|
||||
@ -471,29 +574,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
|
||||
*/
|
||||
static int posix_cpu_timer_del(struct k_itimer *timer)
|
||||
{
|
||||
struct cpu_timer *ctmr = &timer->it.cpu;
|
||||
struct sighand_struct *sighand;
|
||||
struct task_struct *p;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
p = cpu_timer_task_rcu(timer);
|
||||
if (!p)
|
||||
goto out;
|
||||
p = timer_lock_sighand(timer, &flags);
|
||||
|
||||
/*
|
||||
* Protect against sighand release/switch in exit/exec and process/
|
||||
* thread timer list entry concurrent read/writes.
|
||||
*/
|
||||
sighand = lock_task_sighand(p, &flags);
|
||||
if (unlikely(sighand == NULL)) {
|
||||
/*
|
||||
* This raced with the reaping of the task. The exit cleanup
|
||||
* should have removed this timer from the timer queue.
|
||||
*/
|
||||
WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
|
||||
} else {
|
||||
if (likely(p)) {
|
||||
if (timer->it.cpu.firing)
|
||||
ret = TIMER_RETRY;
|
||||
else
|
||||
@ -502,10 +589,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
|
||||
unlock_task_sighand(p, &flags);
|
||||
}
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
if (!ret)
|
||||
put_pid(ctmr->pid);
|
||||
put_pid(timer->it.cpu.pid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -585,12 +670,7 @@ static void cpu_timer_fire(struct k_itimer *timer)
|
||||
{
|
||||
struct cpu_timer *ctmr = &timer->it.cpu;
|
||||
|
||||
if ((timer->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE) {
|
||||
/*
|
||||
* User don't want any signal.
|
||||
*/
|
||||
cpu_timer_setexpires(ctmr, 0);
|
||||
} else if (unlikely(timer->sigq == NULL)) {
|
||||
if (unlikely(timer->sigq == NULL)) {
|
||||
/*
|
||||
* This a special case for clock_nanosleep,
|
||||
* not a normal timer from sys_timer_create.
|
||||
@ -615,6 +695,8 @@ static void cpu_timer_fire(struct k_itimer *timer)
|
||||
}
|
||||
}
|
||||
|
||||
static void __posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp, u64 now);
|
||||
|
||||
/*
|
||||
* Guts of sys_timer_settime for CPU timers.
|
||||
* This is called with the timer locked and interrupts disabled.
|
||||
@ -624,24 +706,21 @@ static void cpu_timer_fire(struct k_itimer *timer)
|
||||
static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
|
||||
struct itimerspec64 *new, struct itimerspec64 *old)
|
||||
{
|
||||
bool sigev_none = timer->it_sigev_notify == SIGEV_NONE;
|
||||
clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
|
||||
u64 old_expires, new_expires, old_incr, val;
|
||||
struct cpu_timer *ctmr = &timer->it.cpu;
|
||||
struct sighand_struct *sighand;
|
||||
u64 old_expires, new_expires, now;
|
||||
struct task_struct *p;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
p = cpu_timer_task_rcu(timer);
|
||||
if (!p) {
|
||||
/*
|
||||
* If p has just been reaped, we can no
|
||||
* longer get any information about it at all.
|
||||
*/
|
||||
rcu_read_unlock();
|
||||
p = timer_lock_sighand(timer, &flags);
|
||||
/*
|
||||
* If p has just been reaped, we can no longer get any information about
|
||||
* it at all.
|
||||
*/
|
||||
if (!p)
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the to_ktime conversion because that clamps the maximum
|
||||
@ -649,24 +728,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
|
||||
*/
|
||||
new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value));
|
||||
|
||||
/*
|
||||
* Protect against sighand release/switch in exit/exec and p->cpu_timers
|
||||
* and p->signal->cpu_timers read/write in arm_timer()
|
||||
*/
|
||||
sighand = lock_task_sighand(p, &flags);
|
||||
/*
|
||||
* If p has just been reaped, we can no
|
||||
* longer get any information about it at all.
|
||||
*/
|
||||
if (unlikely(sighand == NULL)) {
|
||||
rcu_read_unlock();
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disarm any old timer after extracting its expiry time.
|
||||
*/
|
||||
old_incr = timer->it_interval;
|
||||
/* Retrieve the current expiry time before disarming the timer */
|
||||
old_expires = cpu_timer_getexpires(ctmr);
|
||||
|
||||
if (unlikely(timer->it.cpu.firing)) {
|
||||
@ -677,65 +739,46 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to sample the current value to convert the new
|
||||
* value from to relative and absolute, and to convert the
|
||||
* old value from absolute to relative. To set a process
|
||||
* timer, we need a sample to balance the thread expiry
|
||||
* times (in arm_timer). With an absolute time, we must
|
||||
* check if it's already passed. In short, we need a sample.
|
||||
* Sample the current clock for saving the previous setting
|
||||
* and for rearming the timer.
|
||||
*/
|
||||
if (CPUCLOCK_PERTHREAD(timer->it_clock))
|
||||
val = cpu_clock_sample(clkid, p);
|
||||
now = cpu_clock_sample(clkid, p);
|
||||
else
|
||||
val = cpu_clock_sample_group(clkid, p, true);
|
||||
now = cpu_clock_sample_group(clkid, p, !sigev_none);
|
||||
|
||||
/* Retrieve the previous expiry value if requested. */
|
||||
if (old) {
|
||||
if (old_expires == 0) {
|
||||
old->it_value.tv_sec = 0;
|
||||
old->it_value.tv_nsec = 0;
|
||||
} else {
|
||||
/*
|
||||
* Update the timer in case it has overrun already.
|
||||
* If it has, we'll report it as having overrun and
|
||||
* with the next reloaded timer already ticking,
|
||||
* though we are swallowing that pending
|
||||
* notification here to install the new setting.
|
||||
*/
|
||||
u64 exp = bump_cpu_timer(timer, val);
|
||||
|
||||
if (val < exp) {
|
||||
old_expires = exp - val;
|
||||
old->it_value = ns_to_timespec64(old_expires);
|
||||
} else {
|
||||
old->it_value.tv_nsec = 1;
|
||||
old->it_value.tv_sec = 0;
|
||||
}
|
||||
}
|
||||
old->it_value = (struct timespec64){ };
|
||||
if (old_expires)
|
||||
__posix_cpu_timer_get(timer, old, now);
|
||||
}
|
||||
|
||||
/* Retry if the timer expiry is running concurrently */
|
||||
if (unlikely(ret)) {
|
||||
/*
|
||||
* We are colliding with the timer actually firing.
|
||||
* Punt after filling in the timer's old value, and
|
||||
* disable this firing since we are already reporting
|
||||
* it as an overrun (thanks to bump_cpu_timer above).
|
||||
*/
|
||||
unlock_task_sighand(p, &flags);
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (new_expires != 0 && !(timer_flags & TIMER_ABSTIME)) {
|
||||
new_expires += val;
|
||||
}
|
||||
/* Convert relative expiry time to absolute */
|
||||
if (new_expires && !(timer_flags & TIMER_ABSTIME))
|
||||
new_expires += now;
|
||||
|
||||
/* Set the new expiry time (might be 0) */
|
||||
cpu_timer_setexpires(ctmr, new_expires);
|
||||
|
||||
/*
|
||||
* Install the new expiry time (or zero).
|
||||
* For a timer with no notification action, we don't actually
|
||||
* arm the timer (we'll just fake it for timer_gettime).
|
||||
* Arm the timer if it is not disabled, the new expiry value has
|
||||
* not yet expired and the timer requires signal delivery.
|
||||
* SIGEV_NONE timers are never armed. In case the timer is not
|
||||
* armed, enforce the reevaluation of the timer base so that the
|
||||
* process wide cputime counter can be disabled eventually.
|
||||
*/
|
||||
cpu_timer_setexpires(ctmr, new_expires);
|
||||
if (new_expires != 0 && val < new_expires) {
|
||||
arm_timer(timer, p);
|
||||
if (likely(!sigev_none)) {
|
||||
if (new_expires && now < new_expires)
|
||||
arm_timer(timer, p);
|
||||
else
|
||||
trigger_base_recalc_expires(timer, p);
|
||||
}
|
||||
|
||||
unlock_task_sighand(p, &flags);
|
||||
@ -755,76 +798,70 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
|
||||
timer->it_overrun_last = 0;
|
||||
timer->it_overrun = -1;
|
||||
|
||||
if (val >= new_expires) {
|
||||
if (new_expires != 0) {
|
||||
/*
|
||||
* The designated time already passed, so we notify
|
||||
* immediately, even if the thread never runs to
|
||||
* accumulate more time on this clock.
|
||||
*/
|
||||
cpu_timer_fire(timer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure we don't keep around the process wide cputime
|
||||
* counter or the tick dependency if they are not necessary.
|
||||
*/
|
||||
sighand = lock_task_sighand(p, &flags);
|
||||
if (!sighand)
|
||||
goto out;
|
||||
|
||||
if (!cpu_timer_queued(ctmr))
|
||||
trigger_base_recalc_expires(timer, p);
|
||||
|
||||
unlock_task_sighand(p, &flags);
|
||||
}
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
if (old)
|
||||
old->it_interval = ns_to_timespec64(old_incr);
|
||||
|
||||
/*
|
||||
* If the new expiry time was already in the past the timer was not
|
||||
* queued. Fire it immediately even if the thread never runs to
|
||||
* accumulate more time on this clock.
|
||||
*/
|
||||
if (!sigev_none && new_expires && now >= new_expires)
|
||||
cpu_timer_fire(timer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp, u64 now)
|
||||
{
|
||||
bool sigev_none = timer->it_sigev_notify == SIGEV_NONE;
|
||||
u64 expires, iv = timer->it_interval;
|
||||
|
||||
/*
|
||||
* Make sure that interval timers are moved forward for the
|
||||
* following cases:
|
||||
* - SIGEV_NONE timers which are never armed
|
||||
* - Timers which expired, but the signal has not yet been
|
||||
* delivered
|
||||
*/
|
||||
if (iv && ((timer->it_requeue_pending & REQUEUE_PENDING) || sigev_none))
|
||||
expires = bump_cpu_timer(timer, now);
|
||||
else
|
||||
expires = cpu_timer_getexpires(&timer->it.cpu);
|
||||
|
||||
/*
|
||||
* Expired interval timers cannot have a remaining time <= 0.
|
||||
* The kernel has to move them forward so that the next
|
||||
* timer expiry is > @now.
|
||||
*/
|
||||
if (now < expires) {
|
||||
itp->it_value = ns_to_timespec64(expires - now);
|
||||
} else {
|
||||
/*
|
||||
* A single shot SIGEV_NONE timer must return 0, when it is
|
||||
* expired! Timers which have a real signal delivery mode
|
||||
* must return a remaining time greater than 0 because the
|
||||
* signal has not yet been delivered.
|
||||
*/
|
||||
if (!sigev_none)
|
||||
itp->it_value.tv_nsec = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp)
|
||||
{
|
||||
clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
|
||||
struct cpu_timer *ctmr = &timer->it.cpu;
|
||||
u64 now, expires = cpu_timer_getexpires(ctmr);
|
||||
struct task_struct *p;
|
||||
u64 now;
|
||||
|
||||
rcu_read_lock();
|
||||
p = cpu_timer_task_rcu(timer);
|
||||
if (!p)
|
||||
goto out;
|
||||
if (p && cpu_timer_getexpires(&timer->it.cpu)) {
|
||||
itp->it_interval = ktime_to_timespec64(timer->it_interval);
|
||||
|
||||
/*
|
||||
* Easy part: convert the reload time.
|
||||
*/
|
||||
itp->it_interval = ktime_to_timespec64(timer->it_interval);
|
||||
if (CPUCLOCK_PERTHREAD(timer->it_clock))
|
||||
now = cpu_clock_sample(clkid, p);
|
||||
else
|
||||
now = cpu_clock_sample_group(clkid, p, false);
|
||||
|
||||
if (!expires)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Sample the clock to take the difference with the expiry time.
|
||||
*/
|
||||
if (CPUCLOCK_PERTHREAD(timer->it_clock))
|
||||
now = cpu_clock_sample(clkid, p);
|
||||
else
|
||||
now = cpu_clock_sample_group(clkid, p, false);
|
||||
|
||||
if (now < expires) {
|
||||
itp->it_value = ns_to_timespec64(expires - now);
|
||||
} else {
|
||||
/*
|
||||
* The timer should have expired already, but the firing
|
||||
* hasn't taken place yet. Say it's just about to expire.
|
||||
*/
|
||||
itp->it_value.tv_nsec = 1;
|
||||
itp->it_value.tv_sec = 0;
|
||||
__posix_cpu_timer_get(timer, itp, now);
|
||||
}
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
@ -1047,19 +1084,12 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
|
||||
{
|
||||
clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
|
||||
struct task_struct *p;
|
||||
struct sighand_struct *sighand;
|
||||
unsigned long flags;
|
||||
u64 now;
|
||||
|
||||
rcu_read_lock();
|
||||
p = cpu_timer_task_rcu(timer);
|
||||
if (!p)
|
||||
goto out;
|
||||
|
||||
/* Protect timer list r/w in arm_timer() */
|
||||
sighand = lock_task_sighand(p, &flags);
|
||||
if (unlikely(sighand == NULL))
|
||||
goto out;
|
||||
p = timer_lock_sighand(timer, &flags);
|
||||
if (unlikely(!p))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Fetch the current sample and update the timer's expiry time.
|
||||
@ -1076,8 +1106,6 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
|
||||
*/
|
||||
arm_timer(timer, p);
|
||||
unlock_task_sighand(p, &flags);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1477,6 +1505,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
|
||||
spin_lock_irq(&timer.it_lock);
|
||||
error = posix_cpu_timer_set(&timer, flags, &it, NULL);
|
||||
if (error) {
|
||||
posix_cpu_timer_del(&timer);
|
||||
spin_unlock_irq(&timer.it_lock);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -902,7 +902,7 @@ static int do_timer_settime(timer_t timer_id, int tmr_flags,
|
||||
const struct k_clock *kc;
|
||||
struct k_itimer *timr;
|
||||
unsigned long flags;
|
||||
int error = 0;
|
||||
int error;
|
||||
|
||||
if (!timespec64_valid(&new_spec64->it_interval) ||
|
||||
!timespec64_valid(&new_spec64->it_value))
|
||||
@ -916,6 +916,9 @@ retry:
|
||||
if (!timr)
|
||||
return -EINVAL;
|
||||
|
||||
if (old_spec64)
|
||||
old_spec64->it_interval = ktime_to_timespec64(timr->it_interval);
|
||||
|
||||
kc = timr->kclock;
|
||||
if (WARN_ON_ONCE(!kc || !kc->timer_set))
|
||||
error = -EINVAL;
|
||||
|
||||
@ -2145,7 +2145,9 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
|
||||
if (!PageReferenced(page) && pmd_young(old_pmd))
|
||||
SetPageReferenced(page);
|
||||
page_remove_rmap(page, vma, true);
|
||||
add_mm_counter(mm, mm_counter_file(page), -HPAGE_PMD_NR);
|
||||
put_page(page);
|
||||
return;
|
||||
}
|
||||
add_mm_counter(mm, mm_counter_file(page), -HPAGE_PMD_NR);
|
||||
return;
|
||||
|
||||
12
mm/memfd.c
12
mm/memfd.c
@ -257,6 +257,12 @@ static int memfd_add_seals(struct file *file, unsigned int seals)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* SEAL_EXEC implies SEAL_WRITE, making W^X from the start.
|
||||
*/
|
||||
if (seals & F_SEAL_EXEC && inode->i_mode & 0111)
|
||||
seals |= F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_FUTURE_WRITE;
|
||||
|
||||
if ((seals & F_SEAL_WRITE) && !(*file_seals & F_SEAL_WRITE)) {
|
||||
error = mapping_deny_writable(file->f_mapping);
|
||||
if (error)
|
||||
@ -269,12 +275,6 @@ static int memfd_add_seals(struct file *file, unsigned int seals)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SEAL_EXEC implys SEAL_WRITE, making W^X from the start.
|
||||
*/
|
||||
if (seals & F_SEAL_EXEC && inode->i_mode & 0111)
|
||||
seals |= F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_FUTURE_WRITE;
|
||||
|
||||
*file_seals |= seals;
|
||||
error = 0;
|
||||
|
||||
|
||||
@ -2327,10 +2327,11 @@ static int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[],
|
||||
struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct ip6gre_net *ign = net_generic(dev_net(dev), ip6gre_net_id);
|
||||
struct ip6_tnl *t = netdev_priv(dev);
|
||||
struct __ip6_tnl_parm p;
|
||||
struct ip6_tnl *t;
|
||||
struct ip6gre_net *ign;
|
||||
|
||||
ign = net_generic(t->net, ip6gre_net_id);
|
||||
t = ip6gre_changelink_common(dev, tb, data, &p, extack);
|
||||
if (IS_ERR(t))
|
||||
return PTR_ERR(t);
|
||||
|
||||
@ -515,7 +515,7 @@ int drv_set_key(struct ieee80211_local *local,
|
||||
!(sdata->vif.active_links & BIT(key->link_id))))
|
||||
return -ENOLINK;
|
||||
|
||||
if (fips_enabled)
|
||||
if (!fips_allows(FIPS_EXCEPTION_WIFI_MFP))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
trace_drv_set_key(local, cmd, sdata, sta, key);
|
||||
|
||||
@ -903,7 +903,7 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,
|
||||
if (!check_sdata_in_driver(sdata))
|
||||
return;
|
||||
|
||||
if (fips_enabled)
|
||||
if (!fips_allows(FIPS_EXCEPTION_WIFI_MFP))
|
||||
return;
|
||||
|
||||
trace_drv_set_rekey_data(local, sdata, data);
|
||||
|
||||
@ -796,7 +796,7 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f,
|
||||
|
||||
if (ext || (son->attr & OPEN)) {
|
||||
BYTE_ALIGN(bs);
|
||||
if (nf_h323_error_boundary(bs, len, 0))
|
||||
if (nf_h323_error_boundary(bs, 2, 0))
|
||||
return H323_ERROR_BOUND;
|
||||
len = get_len(bs);
|
||||
if (nf_h323_error_boundary(bs, len, 0))
|
||||
|
||||
@ -199,6 +199,8 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff,
|
||||
if (skb_ensure_writable(skb, optend))
|
||||
return 0;
|
||||
|
||||
th = (struct tcphdr *)(skb->data + protoff);
|
||||
|
||||
while (optoff < optend) {
|
||||
unsigned char *op = skb->data + optoff;
|
||||
|
||||
|
||||
@ -1380,7 +1380,8 @@ smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm *aclc,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ini->ism_offered_cnt + 1; i++) {
|
||||
if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) {
|
||||
if (ini->ism_dev[i] &&
|
||||
ini->ism_chid[i] == ntohs(aclc->d1.chid)) {
|
||||
ini->ism_selected = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,3 +1,57 @@
|
||||
* Wed Aug 19 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-687.41.1.el9_8]
|
||||
- smb: client: validate DFS referral PathConsumed (CKI Backport Bot) [RHEL-237668] {CVE-2026-68343}
|
||||
- posix-cpu-timers: Prevent UAF caused by non-leader exec() race (Waiman Long) [RHEL-227844] {CVE-2026-64560}
|
||||
- posix-cpu-timers: Fix pid refcount leak in do_cpu_nanosleep() error path (Waiman Long) [RHEL-227844] {CVE-2026-64370}
|
||||
- posix-timers: Retrieve interval in common timer_settime() code (Waiman Long) [RHEL-227844]
|
||||
- posix-cpu-timers: Simplify posix_cpu_timer_set() (Waiman Long) [RHEL-227844]
|
||||
- posix-cpu-timers: Remove incorrect comment in posix_cpu_timer_set() (Waiman Long) [RHEL-227844]
|
||||
- posix-cpu-timers: Use @now instead of @val for clarity (Waiman Long) [RHEL-227844]
|
||||
- posix-cpu-timers: Do not arm SIGEV_NONE timers (Waiman Long) [RHEL-227844]
|
||||
- posix-cpu-timers: Replace old expiry retrieval in posix_cpu_timer_set() (Waiman Long) [RHEL-227844]
|
||||
- posix-cpu-timers: Handle SIGEV_NONE timers correctly in timer_set() (Waiman Long) [RHEL-227844]
|
||||
- posix-cpu-timers: Handle SIGEV_NONE timers correctly in timer_get() (Waiman Long) [RHEL-227844]
|
||||
- posix-cpu-timers: Handle interval timers correctly in timer_get() (Waiman Long) [RHEL-227844]
|
||||
- posix-cpu-timers: Save interval only for armed timers (Waiman Long) [RHEL-227844]
|
||||
- posix-cpu-timers: Split up posix_cpu_timer_get() (Waiman Long) [RHEL-227844]
|
||||
- smb/client: handle overlapping allocated ranges in fallocate (CKI Backport Bot) [RHEL-236207] {CVE-2026-68388}
|
||||
- smb: client: mask server-provided mode to 07777 in modefromsid (CKI Backport Bot) [RHEL-234528] {CVE-2026-64379}
|
||||
- cgroup/psi: Set of->priv to NULL upon file release (Waiman Long) [RHEL-232554]
|
||||
- sched/psi: Create the psimon kthread outside of cgroup_mutex (Waiman Long) [RHEL-232554]
|
||||
- sched/psi: fix race between file release and pressure write (Waiman Long) [RHEL-232554] {CVE-2026-52991}
|
||||
- smb: client: fix query_info() replay double-free (CKI Backport Bot) [RHEL-234121] {CVE-2026-64386}
|
||||
- smb: client: fix double-free in SMB2_open() replay (CKI Backport Bot) [RHEL-234107] {CVE-2026-64382}
|
||||
- mm/huge_memory: update file PMD counter before folio_put() (Luiz Capitulino) [RHEL-231225] {CVE-2026-53189}
|
||||
- net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot (CKI Backport Bot) [RHEL-230090] {CVE-2026-64048}
|
||||
- ip6_gre: Use cached t->net in ip6erspan_changelink(). (CKI Backport Bot) [RHEL-180144] {CVE-2026-46120}
|
||||
Resolves: RHEL-180144, RHEL-227844, RHEL-230090, RHEL-231225, RHEL-232554, RHEL-234107, RHEL-234121, RHEL-234528, RHEL-236207, RHEL-237668
|
||||
|
||||
* Mon Aug 17 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-687.40.1.el9_8]
|
||||
- scsi: target: iscsi: Fix CRC overread and double-free in iscsit_handle_text_cmd() (Maurizio Lombardi) [RHEL-213222] {CVE-2026-63888}
|
||||
- smb: client: fix SMB1 TRANS2 multi-response truncation in SendReceive() (Paulo Alcantara) [RHEL-235811]
|
||||
- drm/amdgpu: fix amdgpu_hmm_range_get_pages (José Expósito) [RHEL-222614] {CVE-2026-63879}
|
||||
- drm/amd/display: Use krealloc_array() in dal_vector_reserve() (CKI Backport Bot) [RHEL-222668] {CVE-2026-53329}
|
||||
- drm/amd/display: Clamp VBIOS HDMI retimer register count to array size (CKI Backport Bot) [RHEL-222683] {CVE-2026-53136}
|
||||
- drm/amdkfd: Fix buffer overflow in SDMA queue checkpoint/restore on GFX11 (CKI Backport Bot) [RHEL-222703] {CVE-2026-53143}
|
||||
- drm/amdkfd: Fix watch_id bounds checking in debug address watch v2 (CKI Backport Bot) [RHEL-222718] {CVE-2026-45878}
|
||||
- drm/i915: Fix potential UAF in TTM object purge (CKI Backport Bot) [RHEL-222742] {CVE-2026-63884}
|
||||
- drm/i915/gem: Fix phys BO pread/pwrite with offset (CKI Backport Bot) [RHEL-222748] {CVE-2026-53356}
|
||||
- drm/amd/display: Validate payload length and link_index in dc_process_dmub_aux_transfer_async (CKI Backport Bot) [RHEL-222571] {CVE-2026-64219}
|
||||
- drm/amdgpu: zero-initialize GART table on allocation (CKI Backport Bot) [RHEL-222647] {CVE-2026-53374}
|
||||
- drm/amdkfd: Fix out-of-bounds write in kfd_event_page_set() (CKI Backport Bot) [RHEL-221333] {CVE-2026-43206}
|
||||
- drm/amdgpu: Refactor amdgpu_gem_va_ioctl for Handling Last Fence Update and Timeline Management v7 (CKI Backport Bot) [RHEL-221368] {CVE-2026-43237}
|
||||
- drm/amdgpu: Refactor amdgpu_gem_va_ioctl for Handling Last Fence Update and Timeline Management v4 (CKI Backport Bot) [RHEL-221368] {CVE-2026-43237}
|
||||
- wifi: iwlwifi: reduce encryption error message to debug level in FIPS mode (Jose Ignacio Tornos Martinez) [RHEL-181065]
|
||||
- wifi: iwlwifi: restore FIPS-disabled features with fips exception (Jose Ignacio Tornos Martinez) [RHEL-181065]
|
||||
- wifi: mac80211: allow keys to driver with fips exception (Jose Ignacio Tornos Martinez) [RHEL-181065]
|
||||
- crypto: fips: add rh_fips_exception kernel boot parameter and fips_allows() helper (Jose Ignacio Tornos Martinez) [RHEL-181065]
|
||||
- netfilter: nf_conntrack_h323: fix OOB read in decode_choice() (CKI Backport Bot) [RHEL-230625] {CVE-2026-43233}
|
||||
- netfilter: synproxy: refresh tcphdr after skb_ensure_writable (CKI Backport Bot) [RHEL-228909] {CVE-2026-64007}
|
||||
- memfd: deny writeable mappings when implying SEAL_WRITE (Luiz Capitulino) [RHEL-228523] {CVE-2026-63952}
|
||||
- mm/memfd: fix spelling in memfd_add_seals() (Luiz Capitulino) [RHEL-228523]
|
||||
- vhost: reset the vring metadata cache on vring reconfiguration (CKI Backport Bot) [RHEL-224549]
|
||||
- udf: fix partition descriptor append bookkeeping (CKI Backport Bot) [RHEL-179581] {CVE-2026-45991}
|
||||
Resolves: RHEL-179581, RHEL-181065, RHEL-213222, RHEL-221333, RHEL-221368, RHEL-222571, RHEL-222614, RHEL-222647, RHEL-222668, RHEL-222683, RHEL-222703, RHEL-222718, RHEL-222742, RHEL-222748, RHEL-224549, RHEL-228523, RHEL-228909, RHEL-230625, RHEL-235811
|
||||
|
||||
* Wed Aug 12 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-687.39.1.el9_8]
|
||||
- x86/mm: Add missing saved dirty bit to page protection change mask (Luiz Capitulino) [RHEL-220787]
|
||||
- net/sched: act_api: use RCU with deferred freeing for action lifecycle (CKI Backport Bot) [RHEL-218181] {CVE-2026-53264}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user