1 /*
2 * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "qdf_delayed_work.h"
20 #include "qdf_status.h"
21 #include "qdf_trace.h"
22 #include "qdf_types.h"
23 #include "qdf_module.h"
24
25 #ifdef WLAN_DELAYED_WORK_DEBUG
26 #include "qdf_tracker.h"
27
28 #define qdf_dwork_tracker_bits 2 /* 4 buckets */
29 static qdf_tracker_declare(qdf_dwork_tracker, qdf_dwork_tracker_bits,
30 "delayed work leaks", "delayed work create",
31 "delayed work destroy");
32
qdf_delayed_work_feature_init(void)33 void qdf_delayed_work_feature_init(void)
34 {
35 qdf_tracker_init(&qdf_dwork_tracker);
36 }
37
qdf_delayed_work_feature_deinit(void)38 void qdf_delayed_work_feature_deinit(void)
39 {
40 qdf_tracker_deinit(&qdf_dwork_tracker);
41 }
42
qdf_delayed_work_check_for_leaks(void)43 void qdf_delayed_work_check_for_leaks(void)
44 {
45 qdf_tracker_check_for_leaks(&qdf_dwork_tracker);
46 }
47
qdf_dwork_dbg_track(struct qdf_delayed_work * dwork,const char * func,uint32_t line)48 static inline QDF_STATUS qdf_dwork_dbg_track(struct qdf_delayed_work *dwork,
49 const char *func, uint32_t line)
50 {
51 return qdf_tracker_track(&qdf_dwork_tracker, dwork, func, line);
52 }
53
qdf_dwork_dbg_untrack(struct qdf_delayed_work * dwork,const char * func,uint32_t line)54 static inline void qdf_dwork_dbg_untrack(struct qdf_delayed_work *dwork,
55 const char *func, uint32_t line)
56 {
57 qdf_tracker_untrack(&qdf_dwork_tracker, dwork, func, line);
58 }
59 #else
qdf_dwork_dbg_track(struct qdf_delayed_work * dwork,const char * func,uint32_t line)60 static inline QDF_STATUS qdf_dwork_dbg_track(struct qdf_delayed_work *dwork,
61 const char *func, uint32_t line)
62 {
63 return QDF_STATUS_SUCCESS;
64 }
65
qdf_dwork_dbg_untrack(struct qdf_delayed_work * dwork,const char * func,uint32_t line)66 static inline void qdf_dwork_dbg_untrack(struct qdf_delayed_work *dwork,
67 const char *func, uint32_t line)
68 { }
69 #endif /* WLAN_DELAYED_WORK_DEBUG */
70
__qdf_delayed_work_handler(struct work_struct * work)71 static void __qdf_delayed_work_handler(struct work_struct *work)
72 {
73 struct qdf_delayed_work *dwork =
74 container_of(work, struct qdf_delayed_work, dwork.work);
75
76 dwork->callback(dwork->context);
77 }
78
__qdf_delayed_work_create(struct qdf_delayed_work * dwork,qdf_delayed_work_cb callback,void * context,const char * func,uint32_t line)79 QDF_STATUS __qdf_delayed_work_create(struct qdf_delayed_work *dwork,
80 qdf_delayed_work_cb callback,
81 void *context,
82 const char *func, uint32_t line)
83 {
84 QDF_STATUS status;
85
86 QDF_BUG(dwork);
87 QDF_BUG(callback);
88 if (!dwork || !callback)
89 return QDF_STATUS_E_INVAL;
90
91 status = qdf_dwork_dbg_track(dwork, func, line);
92 if (QDF_IS_STATUS_ERROR(status))
93 return status;
94
95 INIT_DELAYED_WORK(&dwork->dwork, __qdf_delayed_work_handler);
96 dwork->callback = callback;
97 dwork->context = context;
98
99 return QDF_STATUS_SUCCESS;
100 }
101
102 qdf_export_symbol(__qdf_delayed_work_create);
103
__qdf_delayed_work_destroy(struct qdf_delayed_work * dwork,const char * func,uint32_t line)104 void __qdf_delayed_work_destroy(struct qdf_delayed_work *dwork,
105 const char *func, uint32_t line)
106 {
107 qdf_delayed_work_stop_sync(dwork);
108 qdf_dwork_dbg_untrack(dwork, func, line);
109 }
110
111 qdf_export_symbol(__qdf_delayed_work_destroy);
112
__qdf_delayed_work_start(struct qdf_delayed_work * dwork,uint32_t msec)113 bool __qdf_delayed_work_start(struct qdf_delayed_work *dwork, uint32_t msec)
114 {
115 return schedule_delayed_work(&dwork->dwork, msecs_to_jiffies(msec));
116 }
117
118 qdf_export_symbol(__qdf_delayed_work_start);
119
__qdf_delayed_work_stop_sync(struct qdf_delayed_work * dwork)120 bool __qdf_delayed_work_stop_sync(struct qdf_delayed_work *dwork)
121 {
122 return cancel_delayed_work_sync(&dwork->dwork);
123 }
124
125 qdf_export_symbol(__qdf_delayed_work_stop_sync);
126
__qdf_delayed_work_stop(struct qdf_delayed_work * dwork)127 bool __qdf_delayed_work_stop(struct qdf_delayed_work *dwork)
128 {
129 return cancel_delayed_work(&dwork->dwork);
130 }
131
132 qdf_export_symbol(__qdf_delayed_work_stop);
133