qcacmn: Properly abstract the Linux bottom half interfaces
When migrating to Linux Kernel v5.19 the following compilation error was encountered: i_qdf_defer.h:192:19: error: cast from 'qdf_defer_fn_t' (aka 'void (*)(void *)') to '__qdf_bh_fn_t' (aka 'void (*)(unsigned long)') converts to incompatible function type [-Werror,-Wcast-function-type] tasklet_init(bh, (__qdf_bh_fn_t) func, (unsigned long)arg); This revealed the fact that the QDF bottom half abstraction was not cleanly implemented. The current implementation freely typecasts the abstracted signature: void (*func)(void *arg) with the Linux-specific signature: void (*func)(unsigned long arg) This has worked in the past since a void * and and unsigned long are the same size and hence could be freely converted from one to another. However the Linux Kernel now supports Call Flow Integrity which requires that the function signatures must always exactly match. To address this issue rewrite the bottom half abstraction to use an intermediate dispatching function, exactly like is already done for the deferred work abstraction. Change-Id: I56b5a8ab9515033d8237302300fd6b55ea755633 CRs-Fixed: 3305515
This commit is contained in:

committed by
Madan Koyyalamudi

parent
f1da02acc9
commit
4c5b6e76fe
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
|
* Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
|
||||||
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for
|
* Permission to use, copy, modify, and/or distribute this software for
|
||||||
* any purpose with or without fee is hereby granted, provided that the
|
* any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -30,7 +31,6 @@
|
|||||||
#include <qdf_status.h>
|
#include <qdf_status.h>
|
||||||
#include <qdf_trace.h>
|
#include <qdf_trace.h>
|
||||||
|
|
||||||
typedef struct tasklet_struct __qdf_bh_t;
|
|
||||||
typedef struct workqueue_struct __qdf_workqueue_t;
|
typedef struct workqueue_struct __qdf_workqueue_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,9 +45,41 @@ typedef struct {
|
|||||||
void *arg;
|
void *arg;
|
||||||
} __qdf_work_t;
|
} __qdf_work_t;
|
||||||
|
|
||||||
extern void __qdf_defer_func(struct work_struct *work);
|
/**
|
||||||
|
* __qdf_bh_t - wrapper around the real task func
|
||||||
|
* @bh: Instance of the bottom half
|
||||||
|
* @fn: function pointer to the handler
|
||||||
|
* @arg: pointer to argument
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
struct tasklet_struct bh;
|
||||||
|
qdf_defer_fn_t fn;
|
||||||
|
void *arg;
|
||||||
|
} __qdf_bh_t;
|
||||||
|
|
||||||
typedef void (*__qdf_bh_fn_t)(unsigned long arg);
|
/**
|
||||||
|
* __qdf_defer_func() - Linux-specific defer work handler
|
||||||
|
* @work: Pointer to defer work
|
||||||
|
*
|
||||||
|
* This function services all Linux-specific deferred work
|
||||||
|
* and dispatches them to the correct handler using the
|
||||||
|
* abstracted functional interface.
|
||||||
|
*
|
||||||
|
* Return: none
|
||||||
|
*/
|
||||||
|
void __qdf_defer_func(struct work_struct *work);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __qdf_bh_func() - bottom half handler
|
||||||
|
* @arg: Pointer to bottom half abstraction
|
||||||
|
*
|
||||||
|
* This function services all Linux-specific bottom halfs
|
||||||
|
* and dispatches them to the correct handler using the
|
||||||
|
* abstracted functional interface.
|
||||||
|
*
|
||||||
|
* Return: none
|
||||||
|
*/
|
||||||
|
void __qdf_bh_func(unsigned long arg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __qdf_init_work - Initialize a work/task queue, This runs in non-interrupt
|
* __qdf_init_work - Initialize a work/task queue, This runs in non-interrupt
|
||||||
@@ -184,24 +216,25 @@ static inline void __qdf_destroy_workqueue(__qdf_workqueue_t *wqueue)
|
|||||||
* @bh: pointer to bottom
|
* @bh: pointer to bottom
|
||||||
* @func: deferred function to run at bottom half interrupt context.
|
* @func: deferred function to run at bottom half interrupt context.
|
||||||
* @arg: argument for the deferred function
|
* @arg: argument for the deferred function
|
||||||
|
*
|
||||||
* Return: none
|
* Return: none
|
||||||
*/
|
*/
|
||||||
static inline QDF_STATUS
|
static inline void __qdf_init_bh(__qdf_bh_t *bh, qdf_defer_fn_t func, void *arg)
|
||||||
__qdf_init_bh(struct tasklet_struct *bh, qdf_defer_fn_t func, void *arg)
|
|
||||||
{
|
{
|
||||||
tasklet_init(bh, (__qdf_bh_fn_t) func, (unsigned long)arg);
|
bh->fn = func;
|
||||||
return QDF_STATUS_SUCCESS;
|
bh->arg = arg;
|
||||||
|
tasklet_init(&bh->bh, __qdf_bh_func, (unsigned long)bh);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __qdf_sched_bh - schedule a bottom half (DPC)
|
* __qdf_sched_bh - schedule a bottom half (DPC)
|
||||||
* @bh: pointer to bottom
|
* @bh: pointer to bottom
|
||||||
|
*
|
||||||
* Return: none
|
* Return: none
|
||||||
*/
|
*/
|
||||||
static inline QDF_STATUS __qdf_sched_bh(struct tasklet_struct *bh)
|
static inline void __qdf_sched_bh(__qdf_bh_t *bh)
|
||||||
{
|
{
|
||||||
tasklet_schedule(bh);
|
tasklet_schedule(&bh->bh);
|
||||||
return QDF_STATUS_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -220,12 +253,12 @@ static inline QDF_STATUS __qdf_disable_work(__qdf_work_t *work)
|
|||||||
/**
|
/**
|
||||||
* __qdf_disable_bh - destroy the bh (synchronous)
|
* __qdf_disable_bh - destroy the bh (synchronous)
|
||||||
* @bh: pointer to bottom
|
* @bh: pointer to bottom
|
||||||
|
*
|
||||||
* Return: none
|
* Return: none
|
||||||
*/
|
*/
|
||||||
static inline QDF_STATUS __qdf_disable_bh(struct tasklet_struct *bh)
|
static inline void __qdf_disable_bh(__qdf_bh_t *bh)
|
||||||
{
|
{
|
||||||
tasklet_kill(bh);
|
tasklet_kill(&bh->bh);
|
||||||
return QDF_STATUS_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /*_I_QDF_DEFER_H*/
|
#endif /*_I_QDF_DEFER_H*/
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014-2019,2021 The Linux Foundation. All rights reserved.
|
* Copyright (c) 2014-2019,2021 The Linux Foundation. All rights reserved.
|
||||||
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for
|
* Permission to use, copy, modify, and/or distribute this software for
|
||||||
* any purpose with or without fee is hereby granted, provided that the
|
* any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -29,12 +30,6 @@
|
|||||||
#include <qdf_module.h>
|
#include <qdf_module.h>
|
||||||
#include <qdf_defer.h>
|
#include <qdf_defer.h>
|
||||||
|
|
||||||
/**
|
|
||||||
* __qdf_defer_func() - defer work handler
|
|
||||||
* @work: Pointer to defer work
|
|
||||||
*
|
|
||||||
* Return: none
|
|
||||||
*/
|
|
||||||
void __qdf_defer_func(struct work_struct *work)
|
void __qdf_defer_func(struct work_struct *work)
|
||||||
{
|
{
|
||||||
__qdf_work_t *ctx = container_of(work, __qdf_work_t, work);
|
__qdf_work_t *ctx = container_of(work, __qdf_work_t, work);
|
||||||
@@ -48,6 +43,19 @@ void __qdf_defer_func(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
qdf_export_symbol(__qdf_defer_func);
|
qdf_export_symbol(__qdf_defer_func);
|
||||||
|
|
||||||
|
void __qdf_bh_func(unsigned long arg)
|
||||||
|
{
|
||||||
|
__qdf_bh_t *ctx = (__qdf_bh_t *)arg;
|
||||||
|
|
||||||
|
if (!ctx->fn) {
|
||||||
|
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
|
||||||
|
"No callback registered !!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx->fn(ctx->arg);
|
||||||
|
}
|
||||||
|
qdf_export_symbol(__qdf_bh_func);
|
||||||
|
|
||||||
#ifdef ENHANCED_OS_ABSTRACTION
|
#ifdef ENHANCED_OS_ABSTRACTION
|
||||||
void
|
void
|
||||||
qdf_create_bh(qdf_bh_t *bh, qdf_defer_fn_t func, void *arg)
|
qdf_create_bh(qdf_bh_t *bh, qdf_defer_fn_t func, void *arg)
|
||||||
|
Reference in New Issue
Block a user