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