summaryrefslogtreecommitdiff
path: root/drivers/edgetpu/edgetpu-device-group.h
blob: 57fe6e57a51fa570b8e06e9d02536c3f8ba8d40d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Implements utilities for virtual device group of EdgeTPU.
 *
 * Copyright (C) 2019 Google, Inc.
 */
#ifndef __EDGETPU_DEVICE_GROUP_H__
#define __EDGETPU_DEVICE_GROUP_H__

#include <linux/eventfd.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/rbtree.h>
#include <linux/refcount.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/types.h>

#include "edgetpu-internal.h"
#include "edgetpu-mailbox.h"
#include "edgetpu-mapping.h"
#include "edgetpu-mmu.h"
#include "edgetpu.h"

/* entry of edgetpu_device_group#clients */
struct edgetpu_list_group_client {
	struct list_head list;
	struct edgetpu_client *client;
};

enum edgetpu_device_group_status {
	/* Waiting to be finalized. */
	EDGETPU_DEVICE_GROUP_WAITING,
	/* Most operations can only apply on a finalized group. */
	EDGETPU_DEVICE_GROUP_FINALIZED,
	/*
	 * When a fatal error occurs, groups in FINALIZED status are transformed
	 * into this state. Operations on groups with this status mostly return
	 * ECANCELED. Once the client leaves an ERRORED group, the status is
	 * transitioned to DISBANDED.
	 */
	EDGETPU_DEVICE_GROUP_ERRORED,
	/* No operations except client leaving can be performed. */
	EDGETPU_DEVICE_GROUP_DISBANDED,
};

#define EDGETPU_EVENT_COUNT 2

/* eventfds registered for event notifications from kernel for a device group */
struct edgetpu_events {
	rwlock_t lock;
	struct eventfd_ctx *eventfds[EDGETPU_EVENT_COUNT];
};

struct edgetpu_device_group {
	/*
	 * Reference count.
	 * edgetpu_device_group_get() increases the counter by one and
	 * edgetpu_device_group_put() decreases it. This object will be freed
	 * when ref_count becomes zero.
	 */
	refcount_t ref_count;
	uint workload_id;
	struct edgetpu_dev *etdev;	/* the device opened by the leader */
	/*
	 * Whether mailbox attaching and detaching have effects on this group.
	 * This field is configured according to the priority field when
	 * creating this group.
	 */
	bool mailbox_detachable;
	/*
	 * Whether group->etdev is inaccessible.
	 * Some group operations will access device CSRs. If the device is known to be
	 * inaccessible (typically not powered on) then set this field to true to
	 * prevent HW interactions.
	 *
	 * Is not protected by @lock because this is only written when releasing the
	 * leader of this group.
	 */
	bool dev_inaccessible;
	/* Virtual context ID to be sent to the firmware. */
	u16 vcid;

	/* protects everything in the following comment block */
	struct mutex lock;
	/* fields protected by @lock */

	/* The only client in this group */
	struct edgetpu_client *client;
	enum edgetpu_device_group_status status;
	bool activated; /* whether this group's VII has ever been activated */
	struct edgetpu_vii vii;		/* VII mailbox */
	/*
	 * Context ID ranges from EDGETPU_CONTEXT_VII_BASE to
	 * EDGETPU_NCONTEXTS - 1.
	 * This equals EDGETPU_CONTEXT_INVALID or a token OR'ed with
	 * EDGETPU_CONTEXT_DOMAIN_TOKEN when the group has mailbox detached
	 * (means the group isn't in any context at this time).
	 */
	enum edgetpu_context_id context_id;
	/* The IOMMU domain being associated to this group */
	struct edgetpu_iommu_domain *etdomain;
	/*
	 * External mailboxes associated with this group, only valid if
	 * external mailbox allocated and enabled.
	 */
	struct edgetpu_external_mailbox *ext_mailbox;

	/* Mask of errors set for this group. */
	uint fatal_errors;

	/* List of DMA fences owned by this group */
	struct list_head dma_fence_list;

	/* end of fields protected by @lock */

	/* TPU IOVA mapped to host DRAM space */
	struct edgetpu_mapping_root host_mappings;
	/* TPU IOVA mapped to buffers backed by dma-buf */
	struct edgetpu_mapping_root dmabuf_mappings;
	struct edgetpu_events events;
	/* Mailbox attributes used to create this group */
	struct edgetpu_mailbox_attr mbox_attr;
};

/*
 * Entry of edgetpu_dev#groups.
 *
 * Files other than edgetpu-device-group.c shouldn't need to access this
 * structure. Use macro etdev_for_each_group to access the groups under an
 * etdev.
 */
struct edgetpu_list_group {
	struct list_head list;
	struct edgetpu_device_group *grp;
};

/* Macro to loop through etdev->groups. */
#define etdev_for_each_group(etdev, l, g)                                      \
	for (l = list_entry(etdev->groups.next, typeof(*l), list), g = l->grp; \
	     &l->list != &etdev->groups;                                       \
	     l = list_entry(l->list.next, typeof(*l), list), g = l->grp)

/* Loop through group->clients (hold group->lock prior). */
#define for_each_list_group_client(c, group) \
	list_for_each_entry(c, &group->clients, list)

/*
 * Returns if the group is waiting to be finalized.
 *
 * Caller holds @group->lock.
 */
static inline bool
edgetpu_device_group_is_waiting(const struct edgetpu_device_group *group)
{
	return group->status == EDGETPU_DEVICE_GROUP_WAITING;
}

/*
 * Returns if the group is finalized.
 *
 * Caller holds @group->lock.
 */
static inline bool
edgetpu_device_group_is_finalized(const struct edgetpu_device_group *group)
{
	return group->status == EDGETPU_DEVICE_GROUP_FINALIZED;
}

/*
 * Returns if the group is errored.
 *
 * Caller holds @group->lock.
 */
static inline bool
edgetpu_device_group_is_errored(const struct edgetpu_device_group *group)
{
	return group->status == EDGETPU_DEVICE_GROUP_ERRORED;
}

/*
 * Returns if the group is disbanded.
 *
 * Caller holds @group->lock.
 */
static inline bool
edgetpu_device_group_is_disbanded(const struct edgetpu_device_group *group)
{
	return group->status == EDGETPU_DEVICE_GROUP_DISBANDED;
}

/*
 * Returns -ECANCELED if the status of group is ERRORED, otherwise returns
 * -EINVAL.
 *
 * Caller holds @group->lock.
 */
static inline int edgetpu_group_errno(struct edgetpu_device_group *group)
{
	if (edgetpu_device_group_is_errored(group))
		return -ECANCELED;
	return -EINVAL;
}

/* Increases ref_count of @group by one and returns @group. */
static inline struct edgetpu_device_group *
edgetpu_device_group_get(struct edgetpu_device_group *group)
{
	WARN_ON_ONCE(!refcount_inc_not_zero(&group->ref_count));
	return group;
}

/*
 * Decreases ref_count of @group by one.
 *
 * If @group->ref_count becomes 0, @group will be freed.
 */
void edgetpu_device_group_put(struct edgetpu_device_group *group);

/*
 * Allocates a device group with @client as the group leader.
 *
 * @client must not already belong to another group.
 * @client->group will be set as the returned group on success.
 *
 * Call edgetpu_device_group_put() when the returned group is not needed.
 *
 * Returns allocated group, or a negative errno on error.
 * Returns -EINVAL if the client already belongs to a group.
 */
struct edgetpu_device_group *
edgetpu_device_group_alloc(struct edgetpu_client *client,
			   const struct edgetpu_mailbox_attr *attr);

/*
 * Let @client leave the group it belongs to.
 * The group will be marked as "disbanded".
 *
 * @client->group will be removed from @client->etdev->groups.
 * @client->group will be set as NULL.
 */
void edgetpu_device_group_leave(struct edgetpu_client *client);

/*
 * Finalizes the group.
 *
 * Returns 0 on success.
 */
int edgetpu_device_group_finalize(struct edgetpu_device_group *group);

/*
 * Maps buffer to a device group.
 *
 * @arg->device_address will be set as the mapped TPU VA on success.
 *
 * Returns zero on success or a negative errno on error.
 */
int edgetpu_device_group_map(struct edgetpu_device_group *group,
			     struct edgetpu_map_ioctl *arg);

/* Unmap a userspace buffer from a device group. */
int edgetpu_device_group_unmap(struct edgetpu_device_group *group,
			       tpu_addr_t tpu_addr,
			       edgetpu_map_flag_t flags);

/* Sync the buffer previously mapped by edgetpu_device_group_map. */
int edgetpu_device_group_sync_buffer(struct edgetpu_device_group *group,
				     const struct edgetpu_sync_ioctl *arg);

/* Clear all mappings for a device group. */
void edgetpu_mappings_clear_group(struct edgetpu_device_group *group);

/* Return total size of all mappings for the group in bytes */
size_t edgetpu_group_mappings_total_size(struct edgetpu_device_group *group);

/*
 * Return context ID for group MMU mappings.
 *
 * Caller holds @group->lock to prevent race, the context ID may be changed by
 * edgetpu_group_{detach/attach}_mailbox.
 */
static inline enum edgetpu_context_id
edgetpu_group_context_id_locked(struct edgetpu_device_group *group)
{
	return group->context_id;
}

/* dump mappings in @group */
void edgetpu_group_mappings_show(struct edgetpu_device_group *group,
				 struct seq_file *s);

/*
 * Maps the VII mailbox CSR.
 *
 * Returns 0 on success.
 */
int edgetpu_mmap_csr(struct edgetpu_device_group *group,
		     struct vm_area_struct *vma, bool is_external);
/*
 * Maps the cmd/resp queue memory.
 *
 * Returns 0 on success.
 */
int edgetpu_mmap_queue(struct edgetpu_device_group *group, enum gcip_mailbox_queue_type type,
		       struct vm_area_struct *vma, bool is_external);

/* Set group eventfd for event notification */
int edgetpu_group_set_eventfd(struct edgetpu_device_group *group, uint event_id,
			      int eventfd);

/* Unset previously-set group eventfd */
void edgetpu_group_unset_eventfd(struct edgetpu_device_group *group,
				 uint event_id);

/* Notify group of event */
void edgetpu_group_notify(struct edgetpu_device_group *group, uint event_id);

/* Is device in any group (and may be actively processing requests) */
bool edgetpu_in_any_group(struct edgetpu_dev *etdev);

/*
 * Enable or disable device group join lockout (as during f/w load).
 * Returns false if attempting to lockout group join but device is already
 * joined to a group.
 */
bool edgetpu_set_group_join_lockout(struct edgetpu_dev *etdev, bool lockout);

/* Notify @group about a fatal error for that group. */
void edgetpu_group_fatal_error_notify(struct edgetpu_device_group *group,
				      uint error_mask);
/* Notify all device groups of @etdev about a failure on the die */
void edgetpu_fatal_error_notify(struct edgetpu_dev *etdev, uint error_mask);

/* Return fatal error signaled bitmask for device group */
uint edgetpu_group_get_fatal_errors(struct edgetpu_device_group *group);

/*
 * Detach and release the mailbox resources of VII from @group.
 * Some group operations would be disabled when a group has no mailbox attached.
 *
 * Caller holds @group->lock.
 */
void edgetpu_group_detach_mailbox_locked(struct edgetpu_device_group *group);
/*
 * Before detaching the mailbox, send CLOSE_DEVICE KCI that claims the mailbox
 * is going to be unused.
 *
 * The KCI command is sent even when @group is configured as mailbox
 * non-detachable.
 */
void edgetpu_group_close_and_detach_mailbox(struct edgetpu_device_group *group);
/*
 * Request and attach the mailbox resources of VII to @group.
 *
 * Return 0 on success.
 *
 * Caller holds @group->lock.
 */
int edgetpu_group_attach_mailbox_locked(struct edgetpu_device_group *group);
/*
 * After (successfully) attaching the mailbox, send OPEN_DEVICE KCI.
 *
 * The KCI command is sent even when @group is configured as mailbox
 * non-detachable (because the mailbox was successfully "attached").
 */
int edgetpu_group_attach_and_open_mailbox(struct edgetpu_device_group *group);

/*
 * Checks whether @group has mailbox detached.
 *
 * Caller holds @group->lock.
 */
static inline bool
edgetpu_group_mailbox_detached_locked(const struct edgetpu_device_group *group)
{
	return group->context_id == EDGETPU_CONTEXT_INVALID ||
	       group->context_id & EDGETPU_CONTEXT_DOMAIN_TOKEN;
}

/*
 * Returns whether @group is finalized and has mailbox attached.
 *
 * Caller holds @group->lock.
 */
static inline bool
edgetpu_group_finalized_and_attached(const struct edgetpu_device_group *group)
{
	return edgetpu_device_group_is_finalized(group) &&
	       !edgetpu_group_mailbox_detached_locked(group);
}

#endif /* __EDGETPU_DEVICE_GROUP_H__ */