@@ -86,6 +86,13 @@ struct kfd_sdma_activity_handler_workarea {
86
86
uint64_t sdma_activity_counter ;
87
87
};
88
88
89
+ struct temp_sdma_queue_list {
90
+ uint64_t rptr ;
91
+ uint64_t sdma_val ;
92
+ unsigned int queue_id ;
93
+ struct list_head list ;
94
+ };
95
+
89
96
static void kfd_sdma_activity_worker (struct work_struct * work )
90
97
{
91
98
struct kfd_sdma_activity_handler_workarea * workarea ;
@@ -96,6 +103,8 @@ static void kfd_sdma_activity_worker(struct work_struct *work)
96
103
struct qcm_process_device * qpd ;
97
104
struct device_queue_manager * dqm ;
98
105
int ret = 0 ;
106
+ struct temp_sdma_queue_list sdma_q_list ;
107
+ struct temp_sdma_queue_list * sdma_q , * next ;
99
108
100
109
workarea = container_of (work , struct kfd_sdma_activity_handler_workarea ,
101
110
sdma_activity_work );
@@ -109,41 +118,135 @@ static void kfd_sdma_activity_worker(struct work_struct *work)
109
118
qpd = & pdd -> qpd ;
110
119
if (!dqm || !qpd )
111
120
return ;
121
+ /*
122
+ * Total SDMA activity is current SDMA activity + past SDMA activity
123
+ * Past SDMA count is stored in pdd.
124
+ * To get the current activity counters for all active SDMA queues,
125
+ * we loop over all SDMA queues and get their counts from user-space.
126
+ *
127
+ * We cannot call get_user() with dqm_lock held as it can cause
128
+ * a circular lock dependency situation. To read the SDMA stats,
129
+ * we need to do the following:
130
+ *
131
+ * 1. Create a temporary list of SDMA queue nodes from the qpd->queues_list,
132
+ * with dqm_lock/dqm_unlock().
133
+ * 2. Call get_user() for each node in temporary list without dqm_lock.
134
+ * Save the SDMA count for each node and also add the count to the total
135
+ * SDMA count counter.
136
+ * Its possible, during this step, a few SDMA queue nodes got deleted
137
+ * from the qpd->queues_list.
138
+ * 3. Do a second pass over qpd->queues_list to check if any nodes got deleted.
139
+ * If any node got deleted, its SDMA count would be captured in the sdma
140
+ * past activity counter. So subtract the SDMA counter stored in step 2
141
+ * for this node from the total SDMA count.
142
+ */
143
+ INIT_LIST_HEAD (& sdma_q_list .list );
112
144
113
- mm = get_task_mm (pdd -> process -> lead_thread );
114
- if (!mm ) {
115
- return ;
145
+ /*
146
+ * Create the temp list of all SDMA queues
147
+ */
148
+ dqm_lock (dqm );
149
+
150
+ list_for_each_entry (q , & qpd -> queues_list , list ) {
151
+ if ((q -> properties .type != KFD_QUEUE_TYPE_SDMA ) &&
152
+ (q -> properties .type != KFD_QUEUE_TYPE_SDMA_XGMI ))
153
+ continue ;
154
+
155
+ sdma_q = kzalloc (sizeof (struct temp_sdma_queue_list ), GFP_KERNEL );
156
+ if (!sdma_q ) {
157
+ dqm_unlock (dqm );
158
+ goto cleanup ;
159
+ }
160
+
161
+ INIT_LIST_HEAD (& sdma_q -> list );
162
+ sdma_q -> rptr = (uint64_t )q -> properties .read_ptr ;
163
+ sdma_q -> queue_id = q -> properties .queue_id ;
164
+ list_add_tail (& sdma_q -> list , & sdma_q_list .list );
116
165
}
117
166
118
- use_mm (mm );
167
+ /*
168
+ * If the temp list is empty, then no SDMA queues nodes were found in
169
+ * qpd->queues_list. Return the past activity count as the total sdma
170
+ * count
171
+ */
172
+ if (list_empty (& sdma_q_list .list )) {
173
+ workarea -> sdma_activity_counter = pdd -> sdma_past_activity_counter ;
174
+ dqm_unlock (dqm );
175
+ return ;
176
+ }
119
177
120
- dqm_lock (dqm );
178
+ dqm_unlock (dqm );
121
179
122
180
/*
123
- * Total SDMA activity is current SDMA activity + past SDMA activity
181
+ * Get the usage count for each SDMA queue in temp_list.
124
182
*/
125
- workarea -> sdma_activity_counter = pdd -> sdma_past_activity_counter ;
183
+ mm = get_task_mm (pdd -> process -> lead_thread );
184
+ if (!mm )
185
+ goto cleanup ;
186
+
187
+ use_mm (mm );
188
+
189
+ list_for_each_entry (sdma_q , & sdma_q_list .list , list ) {
190
+ val = 0 ;
191
+ ret = read_sdma_queue_counter (sdma_q -> rptr , & val );
192
+ if (ret ) {
193
+ pr_debug ("Failed to read SDMA queue active counter for queue id: %d" ,
194
+ sdma_q -> queue_id );
195
+ } else {
196
+ sdma_q -> sdma_val = val ;
197
+ workarea -> sdma_activity_counter += val ;
198
+ }
199
+ }
200
+
201
+ unuse_mm (mm );
202
+ mmput (mm );
126
203
127
204
/*
128
- * Get the current activity counters for all active SDMA queues
205
+ * Do a second iteration over qpd_queues_list to check if any SDMA
206
+ * nodes got deleted while fetching SDMA counter.
129
207
*/
208
+ dqm_lock (dqm );
209
+
210
+ workarea -> sdma_activity_counter += pdd -> sdma_past_activity_counter ;
211
+
130
212
list_for_each_entry (q , & qpd -> queues_list , list ) {
131
- if ((q -> properties .type == KFD_QUEUE_TYPE_SDMA ) ||
132
- (q -> properties .type == KFD_QUEUE_TYPE_SDMA_XGMI )) {
133
- val = 0 ;
134
- ret = read_sdma_queue_counter (q , & val );
135
- if (ret )
136
- pr_debug ("Failed to read SDMA queue active "
137
- "counter for queue id: %d" ,
138
- q -> properties .queue_id );
139
- else
140
- workarea -> sdma_activity_counter += val ;
213
+ if (list_empty (& sdma_q_list .list ))
214
+ break ;
215
+
216
+ if ((q -> properties .type != KFD_QUEUE_TYPE_SDMA ) &&
217
+ (q -> properties .type != KFD_QUEUE_TYPE_SDMA_XGMI ))
218
+ continue ;
219
+
220
+ list_for_each_entry_safe (sdma_q , next , & sdma_q_list .list , list ) {
221
+ if (((uint64_t )q -> properties .read_ptr == sdma_q -> rptr ) &&
222
+ (sdma_q -> queue_id == q -> properties .queue_id )) {
223
+ list_del (& sdma_q -> list );
224
+ kfree (sdma_q );
225
+ break ;
226
+ }
141
227
}
142
228
}
143
229
144
230
dqm_unlock (dqm );
145
- unuse_mm (mm );
146
- mmput (mm );
231
+
232
+ /*
233
+ * If temp list is not empty, it implies some queues got deleted
234
+ * from qpd->queues_list during SDMA usage read. Subtract the SDMA
235
+ * count for each node from the total SDMA count.
236
+ */
237
+ list_for_each_entry_safe (sdma_q , next , & sdma_q_list .list , list ) {
238
+ workarea -> sdma_activity_counter -= sdma_q -> sdma_val ;
239
+ list_del (& sdma_q -> list );
240
+ kfree (sdma_q );
241
+ }
242
+
243
+ return ;
244
+
245
+ cleanup :
246
+ list_for_each_entry_safe (sdma_q , next , & sdma_q_list .list , list ) {
247
+ list_del (& sdma_q -> list );
248
+ kfree (sdma_q );
249
+ }
147
250
}
148
251
149
252
static ssize_t kfd_procfs_show (struct kobject * kobj , struct attribute * attr ,
0 commit comments