diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2018-11-15 17:49:41 -0800 |
|---|---|---|
| committer | Linux Build Service Account <lnxbuild@localhost> | 2018-11-15 17:49:41 -0800 |
| commit | 60e9df34e4699833eca19e10d4497dcab37508aa (patch) | |
| tree | 55888043166c90382e4bbe6c81c4a791e10c29e1 | |
| parent | 92e6ee5cc2a1041efa8bba06fb245da8c233c90e (diff) | |
| parent | 3514d2d0a1a2c6fd989e0e91963a9ba2aebab69f (diff) | |
Merge 3514d2d0a1a2c6fd989e0e91963a9ba2aebab69f on remote branch
Change-Id: I3dbd035c5b4cae34c0c8caed989c1848add7afa6
| -rw-r--r-- | hif/inc/hif.h | 2 | ||||
| -rw-r--r-- | hif/src/hif_napi.c | 155 | ||||
| -rw-r--r-- | qdf/inc/qdf_cpuhp.h | 106 | ||||
| -rw-r--r-- | qdf/linux/src/i_qdf_cpuhp.h | 34 | ||||
| -rw-r--r-- | qdf/linux/src/i_qdf_nbuf.h | 10 | ||||
| -rw-r--r-- | qdf/linux/src/qdf_cpuhp.c | 119 | ||||
| -rw-r--r-- | qdf/src/qdf_cpuhp.c | 158 |
7 files changed, 499 insertions, 85 deletions
diff --git a/hif/inc/hif.h b/hif/inc/hif.h index 9d4aae5a8dba..db34ff80438c 100644 --- a/hif/inc/hif.h +++ b/hif/inc/hif.h @@ -200,7 +200,7 @@ struct qca_napi_data { struct qca_napi_cpu napi_cpu[NR_CPUS]; int lilcl_head, bigcl_head; enum qca_napi_tput_state napi_mode; - struct notifier_block hnc_cpu_notifier; + struct qdf_cpuhp_handler *cpuhp_handler; uint8_t flags; }; diff --git a/hif/src/hif_napi.c b/hif/src/hif_napi.c index b42be4ea3037..492028f1e477 100644 --- a/hif/src/hif_napi.c +++ b/hif/src/hif_napi.c @@ -45,6 +45,7 @@ #include <hif_io32.h> #include <ce_api.h> #include <ce_internal.h> +#include "qdf_cpuhp.h" enum napi_decision_vector { HIF_NAPI_NOEVENT = 0, @@ -1155,103 +1156,91 @@ static int hnc_link_clusters(struct qca_napi_data *napid) */ /** - * hnc_cpu_notify_cb() - handles CPU hotplug events + * hnc_cpu_online_cb() - handles CPU hotplug "up" events + * @context: the associated HIF context + * @cpu: the CPU Id of the CPU the event happened on * - * On transitions to online, we onlu handle the ONLINE event, - * and ignore the PREP events, because we dont want to act too - * early. - * On transtion to offline, we act on PREP events, because - * we may need to move the irqs/NAPIs to another CPU before - * it is actually off-lined. - * - * Return: NOTIFY_OK (dont block action) + * Return: None */ -static int hnc_cpu_notify_cb(struct notifier_block *nb, - unsigned long action, - void *hcpu) +static void hnc_cpu_online_cb(void *context, uint32_t cpu) { - int rc = NOTIFY_OK; - unsigned long cpu = (unsigned long)hcpu; - struct hif_opaque_softc *hif; - struct qca_napi_data *napid = NULL; + struct hif_softc *hif = context; + struct qca_napi_data *napid = &hif->napi_data; - NAPI_DEBUG("-->%s(act=%ld, cpu=%ld)", __func__, action, cpu); + if (cpu >= NR_CPUS) + return; - napid = qdf_container_of(nb, struct qca_napi_data, hnc_cpu_notifier); - hif = &qdf_container_of(napid, struct hif_softc, napi_data)->osc; + NAPI_DEBUG("-->%s(act=online, cpu=%u)", __func__, cpu); - switch (action) { - case CPU_ONLINE: - case CPU_ONLINE_FROZEN: - napid->napi_cpu[cpu].state = QCA_NAPI_CPU_UP; - NAPI_DEBUG("%s: CPU %ld marked %d", - __func__, cpu, napid->napi_cpu[cpu].state); - break; - case CPU_DEAD: /* already dead; we have marked it before, but ... */ - case CPU_DEAD_FROZEN: - napid->napi_cpu[cpu].state = QCA_NAPI_CPU_DOWN; - NAPI_DEBUG("%s: CPU %ld marked %d", - __func__, cpu, napid->napi_cpu[cpu].state); - break; - case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: - napid->napi_cpu[cpu].state = QCA_NAPI_CPU_DOWN; - - NAPI_DEBUG("%s: CPU %ld marked %d; updating affinity", - __func__, cpu, napid->napi_cpu[cpu].state); + napid->napi_cpu[cpu].state = QCA_NAPI_CPU_UP; + NAPI_DEBUG("%s: CPU %u marked %d", + __func__, cpu, napid->napi_cpu[cpu].state); - /** - * we need to move any NAPIs on this CPU out. - * if we are in LO throughput mode, then this is valid - * if the CPU is the the low designated CPU. - */ - hif_napi_event(hif, - NAPI_EVT_CPU_STATE, - (void *) - ((cpu << 16) | napid->napi_cpu[cpu].state)); - break; - default: - NAPI_DEBUG("%s: ignored. action: %ld", __func__, action); - break; - } /* switch */ - NAPI_DEBUG("<--%s [%d]", __func__, rc); - return rc; + NAPI_DEBUG("<--%s", __func__); } /** - * hnc_hotplug_hook() - installs a hotplug notifier - * @hif_sc: hif_sc context - * @register: !0 => register , =0 => deregister - * - * Because the callback relies on the data layout of - * struct hif_softc & its napi_data member, this callback - * registration requires that the hif_softc is passed in. + * hnc_cpu_before_offline_cb() - handles CPU hotplug "prepare down" events + * @context: the associated HIF context + * @cpu: the CPU Id of the CPU the event happened on * - * Note that this is different from the cpu notifier used by - * rx_thread (cds_schedule.c). - * We may consider combining these modifiers in the future. + * On transtion to offline, we act on PREP events, because we may need to move + * the irqs/NAPIs to another CPU before it is actually off-lined. * - * Return: 0: success - * <0: error + * Return: None */ -static int hnc_hotplug_hook(struct hif_softc *hif_sc, int install) +static void hnc_cpu_before_offline_cb(void *context, uint32_t cpu) { - int rc = 0; + struct hif_softc *hif = context; + struct qca_napi_data *napid = &hif->napi_data; - NAPI_DEBUG("-->%s(%d)", __func__, install); + if (cpu >= NR_CPUS) + return; - if (install) { - hif_sc->napi_data.hnc_cpu_notifier.notifier_call - = hnc_cpu_notify_cb; - rc = register_hotcpu_notifier( - &hif_sc->napi_data.hnc_cpu_notifier); - } else { - unregister_hotcpu_notifier( - &hif_sc->napi_data.hnc_cpu_notifier); - } + NAPI_DEBUG("-->%s(act=before_offline, cpu=%u)", __func__, cpu); - NAPI_DEBUG("<--%s()[%d]", __func__, rc); - return rc; + napid->napi_cpu[cpu].state = QCA_NAPI_CPU_DOWN; + + NAPI_DEBUG("%s: CPU %u marked %d; updating affinity", + __func__, cpu, napid->napi_cpu[cpu].state); + + /** + * we need to move any NAPIs on this CPU out. + * if we are in LO throughput mode, then this is valid + * if the CPU is the the low designated CPU. + */ + hif_napi_event(GET_HIF_OPAQUE_HDL(hif), + NAPI_EVT_CPU_STATE, + (void *) + ((size_t)cpu << 16 | napid->napi_cpu[cpu].state)); + + NAPI_DEBUG("<--%s", __func__); +} + +static int hnc_hotplug_register(struct hif_softc *hif_sc) +{ + QDF_STATUS status; + + NAPI_DEBUG("-->%s", __func__); + + status = qdf_cpuhp_register(&hif_sc->napi_data.cpuhp_handler, + hif_sc, + hnc_cpu_online_cb, + hnc_cpu_before_offline_cb); + + NAPI_DEBUG("<--%s [%d]", __func__, status); + + return qdf_status_to_os_return(status); +} + +static void hnc_hotplug_unregister(struct hif_softc *hif_sc) +{ + NAPI_DEBUG("-->%s", __func__); + + if (hif_sc->napi_data.cpuhp_handler) + qdf_cpuhp_unregister(&hif_sc->napi_data.cpuhp_handler); + + NAPI_DEBUG("<--%s", __func__); } /** @@ -1332,7 +1321,7 @@ int hif_napi_cpu_init(struct hif_opaque_softc *hif) goto lab_err_topology; /* install hotplug notifier */ - rc = hnc_hotplug_hook(HIF_GET_SOFTC(hif), 1); + rc = hnc_hotplug_register(HIF_GET_SOFTC(hif)); if (0 != rc) goto lab_err_hotplug; @@ -1343,7 +1332,7 @@ int hif_napi_cpu_init(struct hif_opaque_softc *hif) lab_err_hotplug: hnc_tput_hook(0); - hnc_hotplug_hook(HIF_GET_SOFTC(hif), 0); + hnc_hotplug_unregister(HIF_GET_SOFTC(hif)); lab_err_topology: memset(napid->napi_cpu, 0, sizeof(struct qca_napi_cpu) * NR_CPUS); lab_rss_init: @@ -1370,7 +1359,7 @@ int hif_napi_cpu_deinit(struct hif_opaque_softc *hif) rc = hnc_tput_hook(0); /* uninstall hotplug notifier */ - rc = hnc_hotplug_hook(HIF_GET_SOFTC(hif), 0); + hnc_hotplug_unregister(HIF_GET_SOFTC(hif)); /* clear the topology table */ memset(napid->napi_cpu, 0, sizeof(struct qca_napi_cpu) * NR_CPUS); diff --git a/qdf/inc/qdf_cpuhp.h b/qdf/inc/qdf_cpuhp.h new file mode 100644 index 000000000000..586f4a2a831e --- /dev/null +++ b/qdf/inc/qdf_cpuhp.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: qdf_cpuhp (CPU hotplug) + * QCA driver framework (QDF) CPU hotplug APIs + */ + +#ifndef __QDF_CPUHP_H +#define __QDF_CPUHP_H + +#include "qdf_status.h" +#include "qdf_types.h" + +/** + * struct qdf_cpuhp_handler - an opaque hotplug event registration handle + */ +struct qdf_cpuhp_handler; + +typedef void (*qdf_cpuhp_callback)(void *context, uint32_t cpu); + +#ifdef QCA_CONFIG_SMP +/** + * qdf_cpuhp_init() - Initialize the CPU hotplug event infrastructure + * + * To be called once, globally. + * + * Return: None + */ +QDF_STATUS qdf_cpuhp_init(void); + +/** + * qdf_cpuhp_deinit() - De-initialize the CPU hotplug event infrastructure + * + * To be called once, globally. + * + * Return: None + */ +QDF_STATUS qdf_cpuhp_deinit(void); + +/** + * qdf_cpuhp_register() - Register for CPU up/down event notifications + * @handler: a double pointer to the event registration handle to allocate + * @context: an opaque context to pass back to event listeners + * @up_callback: the function pointer to invoke for CPU up events + * @down_callback: the function pointer to invoke for CPU down events + * + * "Up" happens just after the CPU is up. Inversely, "down" happens just before + * the CPU goes down. + * + * @handler will point to a valid memory address on success, or NULL on failure. + * + * Return: QDF_STATUS + */ +QDF_STATUS qdf_cpuhp_register(struct qdf_cpuhp_handler **handler, + void *context, + qdf_cpuhp_callback up_callback, + qdf_cpuhp_callback down_callback); + +/** + * qdf_cpuhp_unregister() - Un-register for CPU up/down event notifications + * @handler: a double pointer to the event registration handle to de-allocate + * + * @handler point to NULL upon completion + * + * Return: None + */ +void qdf_cpuhp_unregister(struct qdf_cpuhp_handler **handler); +#else +static inline QDF_STATUS qdf_cpuhp_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS qdf_cpuhp_deinit(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS qdf_cpuhp_register(struct qdf_cpuhp_handler **handler, + void *context, + qdf_cpuhp_callback up_callback, + qdf_cpuhp_callback down_callback) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void qdf_cpuhp_unregister(struct qdf_cpuhp_handler **handler) {} +#endif /* QCA_CONFIG_SMP */ + +#endif /* __QDF_CPUHP_H */ diff --git a/qdf/linux/src/i_qdf_cpuhp.h b/qdf/linux/src/i_qdf_cpuhp.h new file mode 100644 index 000000000000..982320efcd9f --- /dev/null +++ b/qdf/linux/src/i_qdf_cpuhp.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: i_qdf_cpuhp.h (CPU hotplug) + * Linux-specific definitions for QDF CPU hotplug API's + */ + +#ifndef __I_QDF_CPUHP_H +#define __I_QDF_CPUHP_H + +#include "linux/types.h" + +typedef void (*__qdf_cpuhp_emit)(uint32_t cpu); + +void __qdf_cpuhp_os_init(__qdf_cpuhp_emit on_up, __qdf_cpuhp_emit on_down); +void __qdf_cpuhp_os_deinit(void); + +#endif /* __I_QDF_CPUHP_H */ diff --git a/qdf/linux/src/i_qdf_nbuf.h b/qdf/linux/src/i_qdf_nbuf.h index 4e797fe8629a..ab65c2655853 100644 --- a/qdf/linux/src/i_qdf_nbuf.h +++ b/qdf/linux/src/i_qdf_nbuf.h @@ -117,6 +117,8 @@ typedef union { * @tx.trace.vdev_id : vdev (for protocol trace) * @tx.ipa.owned : packet owned by IPA * @tx.ipa.priv : private data, used by IPA + * @tx.dma_option.bi_map: flag to do special dma map with QDF_DMA_BIDIRECTIONAL + * @tx.dma_option.reserved: reserved bits for future use * @tx.desc_id : tx desc id, used to sync between host and fw */ struct qdf_nbuf_cb { @@ -192,8 +194,12 @@ struct qdf_nbuf_cb { uint32_t owned:1, priv:31; } ipa; /* 4 */ + struct { + uint8_t bi_map:1, + reserved:7; + } dma_option; /* 1 byte */ uint16_t desc_id; /* 2 bytes */ - } mcl;/* 14 bytes*/ + } mcl;/* 15 bytes*/ } dev; } tx; /* 40 bytes */ } u; @@ -298,6 +304,8 @@ struct qdf_nbuf_cb { (((struct qdf_nbuf_cb *)((skb)->cb))->u.tx.dev.mcl.ipa.owned) #define QDF_NBUF_CB_TX_IPA_PRIV(skb) \ (((struct qdf_nbuf_cb *)((skb)->cb))->u.tx.dev.mcl.ipa.priv) +#define QDF_NBUF_CB_TX_DMA_BI_MAP(skb) \ + (((struct qdf_nbuf_cb *)((skb)->cb))->u.tx.dev.mcl.dma_option.bi_map) #define QDF_NBUF_CB_TX_DESC_ID(skb) \ (((struct qdf_nbuf_cb *)((skb)->cb))->u.tx.dev.mcl.desc_id) #define QDF_NBUF_CB_TX_FTYPE(skb) \ diff --git a/qdf/linux/src/qdf_cpuhp.c b/qdf/linux/src/qdf_cpuhp.c new file mode 100644 index 000000000000..199213da32dc --- /dev/null +++ b/qdf/linux/src/qdf_cpuhp.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: qdf_cpuhp + * This file provides OS dependent QDF CPU hotplug APIs + */ + +#include "i_qdf_cpuhp.h" +#include "qdf_trace.h" +#include "linux/cpu.h" +#include "linux/notifier.h" +#include "linux/version.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) +#include "linux/cpuhotplug.h" +#endif + +static __qdf_cpuhp_emit __qdf_cpuhp_on_up; +static __qdf_cpuhp_emit __qdf_cpuhp_on_down; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +static int qdf_cpuhp_legacy_handler(struct notifier_block *block, + unsigned long state, + void *hcpu) +{ + unsigned long cpu = (unsigned long)hcpu; + + switch (state) { + case CPU_ONLINE: + __qdf_cpuhp_on_up(cpu); + break; + + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + __qdf_cpuhp_on_down(cpu); + break; + + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block qdf_cpuhp_notifier_block = { + .notifier_call = qdf_cpuhp_legacy_handler, +}; + +static inline void qdf_cpuhp_register_callbacks(void) +{ + register_hotcpu_notifier(&qdf_cpuhp_notifier_block); +} + +static inline void qdf_cpuhp_unregister_callbacks(void) +{ + unregister_hotcpu_notifier(&qdf_cpuhp_notifier_block); +} +#else +static enum cpuhp_state registered_hotplug_state; + +static int qdf_cpuhp_up_handler(unsigned int cpu) +{ + __qdf_cpuhp_on_up(cpu); + + return 0; +} + +static int qdf_cpuhp_down_handler(unsigned int cpu) +{ + __qdf_cpuhp_on_down(cpu); + + return 0; +} + +static inline void qdf_cpuhp_register_callbacks(void) +{ + registered_hotplug_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "wlan/qca-qdf:online", + qdf_cpuhp_up_handler, + qdf_cpuhp_down_handler); +} + +static inline void qdf_cpuhp_unregister_callbacks(void) +{ + QDF_BUG(registered_hotplug_state); + if (registered_hotplug_state) + cpuhp_remove_state(registered_hotplug_state); +} +#endif /* KERNEL_VERSION(4, 6, 0) */ + +void __qdf_cpuhp_os_init(__qdf_cpuhp_emit on_up, __qdf_cpuhp_emit on_down) +{ + __qdf_cpuhp_on_up = on_up; + __qdf_cpuhp_on_down = on_down; + + qdf_cpuhp_register_callbacks(); +} + +void __qdf_cpuhp_os_deinit(void) +{ + qdf_cpuhp_unregister_callbacks(); +} + diff --git a/qdf/src/qdf_cpuhp.c b/qdf/src/qdf_cpuhp.c new file mode 100644 index 000000000000..214c92743e6e --- /dev/null +++ b/qdf/src/qdf_cpuhp.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: qdf_cpuhp (CPU hotplug) + * QCA driver framework (QDF) CPU hotplug APIs + */ + +#include "qdf_cpuhp.h" +#include "i_qdf_cpuhp.h" +#include "qdf_list.h" +#include "qdf_lock.h" + +static qdf_mutex_t qdf_cpuhp_lock; +static qdf_list_t qdf_cpuhp_handlers; + +struct qdf_cpuhp_handler { + qdf_list_node_t node; + void *context; + qdf_cpuhp_callback up_callback; + qdf_cpuhp_callback down_callback; +}; + +static void qdf_cpuhp_on_up(uint32_t cpu) +{ + QDF_STATUS status; + qdf_list_node_t *node; + + qdf_mutex_acquire(&qdf_cpuhp_lock); + + status = qdf_list_peek_front(&qdf_cpuhp_handlers, &node); + while (QDF_IS_STATUS_SUCCESS(status)) { + struct qdf_cpuhp_handler *handler = + qdf_container_of(node, struct qdf_cpuhp_handler, node); + if (handler->up_callback) + handler->up_callback(handler->context, cpu); + + status = qdf_list_peek_next(&qdf_cpuhp_handlers, node, &node); + } + + qdf_mutex_release(&qdf_cpuhp_lock); +} + +static void qdf_cpuhp_on_down(uint32_t cpu) +{ + QDF_STATUS status; + qdf_list_node_t *node; + + qdf_mutex_acquire(&qdf_cpuhp_lock); + + status = qdf_list_peek_front(&qdf_cpuhp_handlers, &node); + while (QDF_IS_STATUS_SUCCESS(status)) { + struct qdf_cpuhp_handler *handler = + qdf_container_of(node, struct qdf_cpuhp_handler, node); + if (handler->down_callback) + handler->down_callback(handler->context, cpu); + + status = qdf_list_peek_next(&qdf_cpuhp_handlers, node, &node); + } + + qdf_mutex_release(&qdf_cpuhp_lock); +} + +QDF_STATUS qdf_cpuhp_init(void) +{ + QDF_STATUS status; + + status = qdf_mutex_create(&qdf_cpuhp_lock); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + qdf_list_create(&qdf_cpuhp_handlers, 0); + + __qdf_cpuhp_os_init(qdf_cpuhp_on_up, qdf_cpuhp_on_down); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS qdf_cpuhp_deinit(void) +{ + __qdf_cpuhp_os_deinit(); + qdf_list_destroy(&qdf_cpuhp_handlers); + return qdf_mutex_destroy(&qdf_cpuhp_lock); +} + +QDF_STATUS qdf_cpuhp_register(struct qdf_cpuhp_handler **out_handler, + void *context, + qdf_cpuhp_callback up_callback, + qdf_cpuhp_callback down_callback) +{ + QDF_STATUS status; + struct qdf_cpuhp_handler *handler; + + *out_handler = NULL; + + handler = qdf_mem_malloc(sizeof(*handler)); + if (!handler) + return QDF_STATUS_E_NOMEM; + + handler->context = context; + handler->up_callback = up_callback; + handler->down_callback = down_callback; + + status = qdf_mutex_acquire(&qdf_cpuhp_lock); + if (QDF_IS_STATUS_ERROR(status)) + goto free_handler; + + status = qdf_list_insert_back(&qdf_cpuhp_handlers, &handler->node); + if (QDF_IS_STATUS_ERROR(status)) + goto release_lock; + + /* this can fail, but there isn't a good way to recover... */ + qdf_mutex_release(&qdf_cpuhp_lock); + + *out_handler = handler; + + return QDF_STATUS_SUCCESS; + +release_lock: + qdf_mutex_release(&qdf_cpuhp_lock); + +free_handler: + qdf_mem_free(handler); + + return status; +} + +void qdf_cpuhp_unregister(struct qdf_cpuhp_handler **out_handler) +{ + struct qdf_cpuhp_handler *handler = *out_handler; + + QDF_BUG(handler); + if (!handler) + return; + + qdf_mutex_acquire(&qdf_cpuhp_lock); + qdf_list_remove_node(&qdf_cpuhp_handlers, &handler->node); + qdf_mutex_release(&qdf_cpuhp_lock); + + qdf_mem_free(handler); + *out_handler = NULL; +} + |
