1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2016 Mellanox Technologies. All rights reserved. |
4 | * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> |
5 | */ |
6 | |
7 | #include "devl_internal.h" |
8 | |
9 | struct devlink_region { |
10 | struct devlink *devlink; |
11 | struct devlink_port *port; |
12 | struct list_head list; |
13 | union { |
14 | const struct devlink_region_ops *ops; |
15 | const struct devlink_port_region_ops *port_ops; |
16 | }; |
17 | struct mutex snapshot_lock; /* protects snapshot_list, |
18 | * max_snapshots and cur_snapshots |
19 | * consistency. |
20 | */ |
21 | struct list_head snapshot_list; |
22 | u32 max_snapshots; |
23 | u32 cur_snapshots; |
24 | u64 size; |
25 | }; |
26 | |
27 | struct devlink_snapshot { |
28 | struct list_head list; |
29 | struct devlink_region *region; |
30 | u8 *data; |
31 | u32 id; |
32 | }; |
33 | |
34 | static struct devlink_region * |
35 | devlink_region_get_by_name(struct devlink *devlink, const char *region_name) |
36 | { |
37 | struct devlink_region *region; |
38 | |
39 | list_for_each_entry(region, &devlink->region_list, list) |
40 | if (!strcmp(region->ops->name, region_name)) |
41 | return region; |
42 | |
43 | return NULL; |
44 | } |
45 | |
46 | static struct devlink_region * |
47 | devlink_port_region_get_by_name(struct devlink_port *port, |
48 | const char *region_name) |
49 | { |
50 | struct devlink_region *region; |
51 | |
52 | list_for_each_entry(region, &port->region_list, list) |
53 | if (!strcmp(region->ops->name, region_name)) |
54 | return region; |
55 | |
56 | return NULL; |
57 | } |
58 | |
59 | static struct devlink_snapshot * |
60 | devlink_region_snapshot_get_by_id(struct devlink_region *region, u32 id) |
61 | { |
62 | struct devlink_snapshot *snapshot; |
63 | |
64 | list_for_each_entry(snapshot, ®ion->snapshot_list, list) |
65 | if (snapshot->id == id) |
66 | return snapshot; |
67 | |
68 | return NULL; |
69 | } |
70 | |
71 | static int devlink_nl_region_snapshot_id_put(struct sk_buff *msg, |
72 | struct devlink *devlink, |
73 | struct devlink_snapshot *snapshot) |
74 | { |
75 | struct nlattr *snap_attr; |
76 | int err; |
77 | |
78 | snap_attr = nla_nest_start_noflag(skb: msg, attrtype: DEVLINK_ATTR_REGION_SNAPSHOT); |
79 | if (!snap_attr) |
80 | return -EINVAL; |
81 | |
82 | err = nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_REGION_SNAPSHOT_ID, value: snapshot->id); |
83 | if (err) |
84 | goto nla_put_failure; |
85 | |
86 | nla_nest_end(skb: msg, start: snap_attr); |
87 | return 0; |
88 | |
89 | nla_put_failure: |
90 | nla_nest_cancel(skb: msg, start: snap_attr); |
91 | return err; |
92 | } |
93 | |
94 | static int devlink_nl_region_snapshots_id_put(struct sk_buff *msg, |
95 | struct devlink *devlink, |
96 | struct devlink_region *region) |
97 | { |
98 | struct devlink_snapshot *snapshot; |
99 | struct nlattr *snapshots_attr; |
100 | int err; |
101 | |
102 | snapshots_attr = nla_nest_start_noflag(skb: msg, |
103 | attrtype: DEVLINK_ATTR_REGION_SNAPSHOTS); |
104 | if (!snapshots_attr) |
105 | return -EINVAL; |
106 | |
107 | list_for_each_entry(snapshot, ®ion->snapshot_list, list) { |
108 | err = devlink_nl_region_snapshot_id_put(msg, devlink, snapshot); |
109 | if (err) |
110 | goto nla_put_failure; |
111 | } |
112 | |
113 | nla_nest_end(skb: msg, start: snapshots_attr); |
114 | return 0; |
115 | |
116 | nla_put_failure: |
117 | nla_nest_cancel(skb: msg, start: snapshots_attr); |
118 | return err; |
119 | } |
120 | |
121 | static int devlink_nl_region_fill(struct sk_buff *msg, struct devlink *devlink, |
122 | enum devlink_command cmd, u32 portid, |
123 | u32 seq, int flags, |
124 | struct devlink_region *region) |
125 | { |
126 | void *hdr; |
127 | int err; |
128 | |
129 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
130 | if (!hdr) |
131 | return -EMSGSIZE; |
132 | |
133 | err = devlink_nl_put_handle(msg, devlink); |
134 | if (err) |
135 | goto nla_put_failure; |
136 | |
137 | if (region->port) { |
138 | err = nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_PORT_INDEX, |
139 | value: region->port->index); |
140 | if (err) |
141 | goto nla_put_failure; |
142 | } |
143 | |
144 | err = nla_put_string(skb: msg, attrtype: DEVLINK_ATTR_REGION_NAME, str: region->ops->name); |
145 | if (err) |
146 | goto nla_put_failure; |
147 | |
148 | err = nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_REGION_SIZE, |
149 | value: region->size, |
150 | padattr: DEVLINK_ATTR_PAD); |
151 | if (err) |
152 | goto nla_put_failure; |
153 | |
154 | err = nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_REGION_MAX_SNAPSHOTS, |
155 | value: region->max_snapshots); |
156 | if (err) |
157 | goto nla_put_failure; |
158 | |
159 | err = devlink_nl_region_snapshots_id_put(msg, devlink, region); |
160 | if (err) |
161 | goto nla_put_failure; |
162 | |
163 | genlmsg_end(skb: msg, hdr); |
164 | return 0; |
165 | |
166 | nla_put_failure: |
167 | genlmsg_cancel(skb: msg, hdr); |
168 | return err; |
169 | } |
170 | |
171 | static struct sk_buff * |
172 | devlink_nl_region_notify_build(struct devlink_region *region, |
173 | struct devlink_snapshot *snapshot, |
174 | enum devlink_command cmd, u32 portid, u32 seq) |
175 | { |
176 | struct devlink *devlink = region->devlink; |
177 | struct sk_buff *msg; |
178 | void *hdr; |
179 | int err; |
180 | |
181 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
182 | if (!msg) |
183 | return ERR_PTR(error: -ENOMEM); |
184 | |
185 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags: 0, cmd); |
186 | if (!hdr) { |
187 | err = -EMSGSIZE; |
188 | goto out_free_msg; |
189 | } |
190 | |
191 | err = devlink_nl_put_handle(msg, devlink); |
192 | if (err) |
193 | goto out_cancel_msg; |
194 | |
195 | if (region->port) { |
196 | err = nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_PORT_INDEX, |
197 | value: region->port->index); |
198 | if (err) |
199 | goto out_cancel_msg; |
200 | } |
201 | |
202 | err = nla_put_string(skb: msg, attrtype: DEVLINK_ATTR_REGION_NAME, |
203 | str: region->ops->name); |
204 | if (err) |
205 | goto out_cancel_msg; |
206 | |
207 | if (snapshot) { |
208 | err = nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_REGION_SNAPSHOT_ID, |
209 | value: snapshot->id); |
210 | if (err) |
211 | goto out_cancel_msg; |
212 | } else { |
213 | err = nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_REGION_SIZE, |
214 | value: region->size, padattr: DEVLINK_ATTR_PAD); |
215 | if (err) |
216 | goto out_cancel_msg; |
217 | } |
218 | genlmsg_end(skb: msg, hdr); |
219 | |
220 | return msg; |
221 | |
222 | out_cancel_msg: |
223 | genlmsg_cancel(skb: msg, hdr); |
224 | out_free_msg: |
225 | nlmsg_free(skb: msg); |
226 | return ERR_PTR(error: err); |
227 | } |
228 | |
229 | static void devlink_nl_region_notify(struct devlink_region *region, |
230 | struct devlink_snapshot *snapshot, |
231 | enum devlink_command cmd) |
232 | { |
233 | struct devlink *devlink = region->devlink; |
234 | struct sk_buff *msg; |
235 | |
236 | WARN_ON(cmd != DEVLINK_CMD_REGION_NEW && cmd != DEVLINK_CMD_REGION_DEL); |
237 | if (!xa_get_mark(&devlinks, index: devlink->index, DEVLINK_REGISTERED)) |
238 | return; |
239 | |
240 | msg = devlink_nl_region_notify_build(region, snapshot, cmd, portid: 0, seq: 0); |
241 | if (IS_ERR(ptr: msg)) |
242 | return; |
243 | |
244 | genlmsg_multicast_netns(family: &devlink_nl_family, net: devlink_net(devlink), skb: msg, |
245 | portid: 0, group: DEVLINK_MCGRP_CONFIG, GFP_KERNEL); |
246 | } |
247 | |
248 | void devlink_regions_notify_register(struct devlink *devlink) |
249 | { |
250 | struct devlink_region *region; |
251 | |
252 | list_for_each_entry(region, &devlink->region_list, list) |
253 | devlink_nl_region_notify(region, NULL, cmd: DEVLINK_CMD_REGION_NEW); |
254 | } |
255 | |
256 | void devlink_regions_notify_unregister(struct devlink *devlink) |
257 | { |
258 | struct devlink_region *region; |
259 | |
260 | list_for_each_entry_reverse(region, &devlink->region_list, list) |
261 | devlink_nl_region_notify(region, NULL, cmd: DEVLINK_CMD_REGION_DEL); |
262 | } |
263 | |
264 | /** |
265 | * __devlink_snapshot_id_increment - Increment number of snapshots using an id |
266 | * @devlink: devlink instance |
267 | * @id: the snapshot id |
268 | * |
269 | * Track when a new snapshot begins using an id. Load the count for the |
270 | * given id from the snapshot xarray, increment it, and store it back. |
271 | * |
272 | * Called when a new snapshot is created with the given id. |
273 | * |
274 | * The id *must* have been previously allocated by |
275 | * devlink_region_snapshot_id_get(). |
276 | * |
277 | * Returns 0 on success, or an error on failure. |
278 | */ |
279 | static int __devlink_snapshot_id_increment(struct devlink *devlink, u32 id) |
280 | { |
281 | unsigned long count; |
282 | void *p; |
283 | int err; |
284 | |
285 | xa_lock(&devlink->snapshot_ids); |
286 | p = xa_load(&devlink->snapshot_ids, index: id); |
287 | if (WARN_ON(!p)) { |
288 | err = -EINVAL; |
289 | goto unlock; |
290 | } |
291 | |
292 | if (WARN_ON(!xa_is_value(p))) { |
293 | err = -EINVAL; |
294 | goto unlock; |
295 | } |
296 | |
297 | count = xa_to_value(entry: p); |
298 | count++; |
299 | |
300 | err = xa_err(entry: __xa_store(&devlink->snapshot_ids, index: id, entry: xa_mk_value(v: count), |
301 | GFP_ATOMIC)); |
302 | unlock: |
303 | xa_unlock(&devlink->snapshot_ids); |
304 | return err; |
305 | } |
306 | |
307 | /** |
308 | * __devlink_snapshot_id_decrement - Decrease number of snapshots using an id |
309 | * @devlink: devlink instance |
310 | * @id: the snapshot id |
311 | * |
312 | * Track when a snapshot is deleted and stops using an id. Load the count |
313 | * for the given id from the snapshot xarray, decrement it, and store it |
314 | * back. |
315 | * |
316 | * If the count reaches zero, erase this id from the xarray, freeing it |
317 | * up for future re-use by devlink_region_snapshot_id_get(). |
318 | * |
319 | * Called when a snapshot using the given id is deleted, and when the |
320 | * initial allocator of the id is finished using it. |
321 | */ |
322 | static void __devlink_snapshot_id_decrement(struct devlink *devlink, u32 id) |
323 | { |
324 | unsigned long count; |
325 | void *p; |
326 | |
327 | xa_lock(&devlink->snapshot_ids); |
328 | p = xa_load(&devlink->snapshot_ids, index: id); |
329 | if (WARN_ON(!p)) |
330 | goto unlock; |
331 | |
332 | if (WARN_ON(!xa_is_value(p))) |
333 | goto unlock; |
334 | |
335 | count = xa_to_value(entry: p); |
336 | |
337 | if (count > 1) { |
338 | count--; |
339 | __xa_store(&devlink->snapshot_ids, index: id, entry: xa_mk_value(v: count), |
340 | GFP_ATOMIC); |
341 | } else { |
342 | /* If this was the last user, we can erase this id */ |
343 | __xa_erase(&devlink->snapshot_ids, index: id); |
344 | } |
345 | unlock: |
346 | xa_unlock(&devlink->snapshot_ids); |
347 | } |
348 | |
349 | /** |
350 | * __devlink_snapshot_id_insert - Insert a specific snapshot ID |
351 | * @devlink: devlink instance |
352 | * @id: the snapshot id |
353 | * |
354 | * Mark the given snapshot id as used by inserting a zero value into the |
355 | * snapshot xarray. |
356 | * |
357 | * This must be called while holding the devlink instance lock. Unlike |
358 | * devlink_snapshot_id_get, the initial reference count is zero, not one. |
359 | * It is expected that the id will immediately be used before |
360 | * releasing the devlink instance lock. |
361 | * |
362 | * Returns zero on success, or an error code if the snapshot id could not |
363 | * be inserted. |
364 | */ |
365 | static int __devlink_snapshot_id_insert(struct devlink *devlink, u32 id) |
366 | { |
367 | int err; |
368 | |
369 | xa_lock(&devlink->snapshot_ids); |
370 | if (xa_load(&devlink->snapshot_ids, index: id)) { |
371 | xa_unlock(&devlink->snapshot_ids); |
372 | return -EEXIST; |
373 | } |
374 | err = xa_err(entry: __xa_store(&devlink->snapshot_ids, index: id, entry: xa_mk_value(v: 0), |
375 | GFP_ATOMIC)); |
376 | xa_unlock(&devlink->snapshot_ids); |
377 | return err; |
378 | } |
379 | |
380 | /** |
381 | * __devlink_region_snapshot_id_get - get snapshot ID |
382 | * @devlink: devlink instance |
383 | * @id: storage to return snapshot id |
384 | * |
385 | * Allocates a new snapshot id. Returns zero on success, or a negative |
386 | * error on failure. Must be called while holding the devlink instance |
387 | * lock. |
388 | * |
389 | * Snapshot IDs are tracked using an xarray which stores the number of |
390 | * users of the snapshot id. |
391 | * |
392 | * Note that the caller of this function counts as a 'user', in order to |
393 | * avoid race conditions. The caller must release its hold on the |
394 | * snapshot by using devlink_region_snapshot_id_put. |
395 | */ |
396 | static int __devlink_region_snapshot_id_get(struct devlink *devlink, u32 *id) |
397 | { |
398 | return xa_alloc(xa: &devlink->snapshot_ids, id, entry: xa_mk_value(v: 1), |
399 | xa_limit_32b, GFP_KERNEL); |
400 | } |
401 | |
402 | /** |
403 | * __devlink_region_snapshot_create - create a new snapshot |
404 | * This will add a new snapshot of a region. The snapshot |
405 | * will be stored on the region struct and can be accessed |
406 | * from devlink. This is useful for future analyses of snapshots. |
407 | * Multiple snapshots can be created on a region. |
408 | * The @snapshot_id should be obtained using the getter function. |
409 | * |
410 | * Must be called only while holding the region snapshot lock. |
411 | * |
412 | * @region: devlink region of the snapshot |
413 | * @data: snapshot data |
414 | * @snapshot_id: snapshot id to be created |
415 | */ |
416 | static int |
417 | __devlink_region_snapshot_create(struct devlink_region *region, |
418 | u8 *data, u32 snapshot_id) |
419 | { |
420 | struct devlink *devlink = region->devlink; |
421 | struct devlink_snapshot *snapshot; |
422 | int err; |
423 | |
424 | lockdep_assert_held(®ion->snapshot_lock); |
425 | |
426 | /* check if region can hold one more snapshot */ |
427 | if (region->cur_snapshots == region->max_snapshots) |
428 | return -ENOSPC; |
429 | |
430 | if (devlink_region_snapshot_get_by_id(region, id: snapshot_id)) |
431 | return -EEXIST; |
432 | |
433 | snapshot = kzalloc(size: sizeof(*snapshot), GFP_KERNEL); |
434 | if (!snapshot) |
435 | return -ENOMEM; |
436 | |
437 | err = __devlink_snapshot_id_increment(devlink, id: snapshot_id); |
438 | if (err) |
439 | goto err_snapshot_id_increment; |
440 | |
441 | snapshot->id = snapshot_id; |
442 | snapshot->region = region; |
443 | snapshot->data = data; |
444 | |
445 | list_add_tail(new: &snapshot->list, head: ®ion->snapshot_list); |
446 | |
447 | region->cur_snapshots++; |
448 | |
449 | devlink_nl_region_notify(region, snapshot, cmd: DEVLINK_CMD_REGION_NEW); |
450 | return 0; |
451 | |
452 | err_snapshot_id_increment: |
453 | kfree(objp: snapshot); |
454 | return err; |
455 | } |
456 | |
457 | static void devlink_region_snapshot_del(struct devlink_region *region, |
458 | struct devlink_snapshot *snapshot) |
459 | { |
460 | struct devlink *devlink = region->devlink; |
461 | |
462 | lockdep_assert_held(®ion->snapshot_lock); |
463 | |
464 | devlink_nl_region_notify(region, snapshot, cmd: DEVLINK_CMD_REGION_DEL); |
465 | region->cur_snapshots--; |
466 | list_del(entry: &snapshot->list); |
467 | region->ops->destructor(snapshot->data); |
468 | __devlink_snapshot_id_decrement(devlink, id: snapshot->id); |
469 | kfree(objp: snapshot); |
470 | } |
471 | |
472 | int devlink_nl_region_get_doit(struct sk_buff *skb, struct genl_info *info) |
473 | { |
474 | struct devlink *devlink = info->user_ptr[0]; |
475 | struct devlink_port *port = NULL; |
476 | struct devlink_region *region; |
477 | const char *region_name; |
478 | struct sk_buff *msg; |
479 | unsigned int index; |
480 | int err; |
481 | |
482 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_REGION_NAME)) |
483 | return -EINVAL; |
484 | |
485 | if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) { |
486 | index = nla_get_u32(nla: info->attrs[DEVLINK_ATTR_PORT_INDEX]); |
487 | |
488 | port = devlink_port_get_by_index(devlink, port_index: index); |
489 | if (!port) |
490 | return -ENODEV; |
491 | } |
492 | |
493 | region_name = nla_data(nla: info->attrs[DEVLINK_ATTR_REGION_NAME]); |
494 | if (port) |
495 | region = devlink_port_region_get_by_name(port, region_name); |
496 | else |
497 | region = devlink_region_get_by_name(devlink, region_name); |
498 | |
499 | if (!region) |
500 | return -EINVAL; |
501 | |
502 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
503 | if (!msg) |
504 | return -ENOMEM; |
505 | |
506 | err = devlink_nl_region_fill(msg, devlink, cmd: DEVLINK_CMD_REGION_GET, |
507 | portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
508 | region); |
509 | if (err) { |
510 | nlmsg_free(skb: msg); |
511 | return err; |
512 | } |
513 | |
514 | return genlmsg_reply(skb: msg, info); |
515 | } |
516 | |
517 | static int devlink_nl_cmd_region_get_port_dumpit(struct sk_buff *msg, |
518 | struct netlink_callback *cb, |
519 | struct devlink_port *port, |
520 | int *idx, int start, int flags) |
521 | { |
522 | struct devlink_region *region; |
523 | int err = 0; |
524 | |
525 | list_for_each_entry(region, &port->region_list, list) { |
526 | if (*idx < start) { |
527 | (*idx)++; |
528 | continue; |
529 | } |
530 | err = devlink_nl_region_fill(msg, devlink: port->devlink, |
531 | cmd: DEVLINK_CMD_REGION_GET, |
532 | NETLINK_CB(cb->skb).portid, |
533 | seq: cb->nlh->nlmsg_seq, |
534 | flags, region); |
535 | if (err) |
536 | goto out; |
537 | (*idx)++; |
538 | } |
539 | |
540 | out: |
541 | return err; |
542 | } |
543 | |
544 | static int devlink_nl_region_get_dump_one(struct sk_buff *msg, |
545 | struct devlink *devlink, |
546 | struct netlink_callback *cb, |
547 | int flags) |
548 | { |
549 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); |
550 | struct devlink_region *region; |
551 | struct devlink_port *port; |
552 | unsigned long port_index; |
553 | int idx = 0; |
554 | int err; |
555 | |
556 | list_for_each_entry(region, &devlink->region_list, list) { |
557 | if (idx < state->idx) { |
558 | idx++; |
559 | continue; |
560 | } |
561 | err = devlink_nl_region_fill(msg, devlink, |
562 | cmd: DEVLINK_CMD_REGION_GET, |
563 | NETLINK_CB(cb->skb).portid, |
564 | seq: cb->nlh->nlmsg_seq, flags, |
565 | region); |
566 | if (err) { |
567 | state->idx = idx; |
568 | return err; |
569 | } |
570 | idx++; |
571 | } |
572 | |
573 | xa_for_each(&devlink->ports, port_index, port) { |
574 | err = devlink_nl_cmd_region_get_port_dumpit(msg, cb, port, idx: &idx, |
575 | start: state->idx, flags); |
576 | if (err) { |
577 | state->idx = idx; |
578 | return err; |
579 | } |
580 | } |
581 | |
582 | return 0; |
583 | } |
584 | |
585 | int devlink_nl_region_get_dumpit(struct sk_buff *skb, |
586 | struct netlink_callback *cb) |
587 | { |
588 | return devlink_nl_dumpit(msg: skb, cb, dump_one: devlink_nl_region_get_dump_one); |
589 | } |
590 | |
591 | int devlink_nl_region_del_doit(struct sk_buff *skb, struct genl_info *info) |
592 | { |
593 | struct devlink *devlink = info->user_ptr[0]; |
594 | struct devlink_snapshot *snapshot; |
595 | struct devlink_port *port = NULL; |
596 | struct devlink_region *region; |
597 | const char *region_name; |
598 | unsigned int index; |
599 | u32 snapshot_id; |
600 | |
601 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_REGION_NAME) || |
602 | GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_REGION_SNAPSHOT_ID)) |
603 | return -EINVAL; |
604 | |
605 | region_name = nla_data(nla: info->attrs[DEVLINK_ATTR_REGION_NAME]); |
606 | snapshot_id = nla_get_u32(nla: info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]); |
607 | |
608 | if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) { |
609 | index = nla_get_u32(nla: info->attrs[DEVLINK_ATTR_PORT_INDEX]); |
610 | |
611 | port = devlink_port_get_by_index(devlink, port_index: index); |
612 | if (!port) |
613 | return -ENODEV; |
614 | } |
615 | |
616 | if (port) |
617 | region = devlink_port_region_get_by_name(port, region_name); |
618 | else |
619 | region = devlink_region_get_by_name(devlink, region_name); |
620 | |
621 | if (!region) |
622 | return -EINVAL; |
623 | |
624 | mutex_lock(®ion->snapshot_lock); |
625 | snapshot = devlink_region_snapshot_get_by_id(region, id: snapshot_id); |
626 | if (!snapshot) { |
627 | mutex_unlock(lock: ®ion->snapshot_lock); |
628 | return -EINVAL; |
629 | } |
630 | |
631 | devlink_region_snapshot_del(region, snapshot); |
632 | mutex_unlock(lock: ®ion->snapshot_lock); |
633 | return 0; |
634 | } |
635 | |
636 | int devlink_nl_region_new_doit(struct sk_buff *skb, struct genl_info *info) |
637 | { |
638 | struct devlink *devlink = info->user_ptr[0]; |
639 | struct devlink_snapshot *snapshot; |
640 | struct devlink_port *port = NULL; |
641 | struct nlattr *snapshot_id_attr; |
642 | struct devlink_region *region; |
643 | const char *region_name; |
644 | unsigned int index; |
645 | u32 snapshot_id; |
646 | u8 *data; |
647 | int err; |
648 | |
649 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_REGION_NAME)) { |
650 | NL_SET_ERR_MSG(info->extack, "No region name provided" ); |
651 | return -EINVAL; |
652 | } |
653 | |
654 | region_name = nla_data(nla: info->attrs[DEVLINK_ATTR_REGION_NAME]); |
655 | |
656 | if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) { |
657 | index = nla_get_u32(nla: info->attrs[DEVLINK_ATTR_PORT_INDEX]); |
658 | |
659 | port = devlink_port_get_by_index(devlink, port_index: index); |
660 | if (!port) |
661 | return -ENODEV; |
662 | } |
663 | |
664 | if (port) |
665 | region = devlink_port_region_get_by_name(port, region_name); |
666 | else |
667 | region = devlink_region_get_by_name(devlink, region_name); |
668 | |
669 | if (!region) { |
670 | NL_SET_ERR_MSG(info->extack, "The requested region does not exist" ); |
671 | return -EINVAL; |
672 | } |
673 | |
674 | if (!region->ops->snapshot) { |
675 | NL_SET_ERR_MSG(info->extack, "The requested region does not support taking an immediate snapshot" ); |
676 | return -EOPNOTSUPP; |
677 | } |
678 | |
679 | mutex_lock(®ion->snapshot_lock); |
680 | |
681 | if (region->cur_snapshots == region->max_snapshots) { |
682 | NL_SET_ERR_MSG(info->extack, "The region has reached the maximum number of stored snapshots" ); |
683 | err = -ENOSPC; |
684 | goto unlock; |
685 | } |
686 | |
687 | snapshot_id_attr = info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]; |
688 | if (snapshot_id_attr) { |
689 | snapshot_id = nla_get_u32(nla: snapshot_id_attr); |
690 | |
691 | if (devlink_region_snapshot_get_by_id(region, id: snapshot_id)) { |
692 | NL_SET_ERR_MSG(info->extack, "The requested snapshot id is already in use" ); |
693 | err = -EEXIST; |
694 | goto unlock; |
695 | } |
696 | |
697 | err = __devlink_snapshot_id_insert(devlink, id: snapshot_id); |
698 | if (err) |
699 | goto unlock; |
700 | } else { |
701 | err = __devlink_region_snapshot_id_get(devlink, id: &snapshot_id); |
702 | if (err) { |
703 | NL_SET_ERR_MSG(info->extack, "Failed to allocate a new snapshot id" ); |
704 | goto unlock; |
705 | } |
706 | } |
707 | |
708 | if (port) |
709 | err = region->port_ops->snapshot(port, region->port_ops, |
710 | info->extack, &data); |
711 | else |
712 | err = region->ops->snapshot(devlink, region->ops, |
713 | info->extack, &data); |
714 | if (err) |
715 | goto err_snapshot_capture; |
716 | |
717 | err = __devlink_region_snapshot_create(region, data, snapshot_id); |
718 | if (err) |
719 | goto err_snapshot_create; |
720 | |
721 | if (!snapshot_id_attr) { |
722 | struct sk_buff *msg; |
723 | |
724 | snapshot = devlink_region_snapshot_get_by_id(region, |
725 | id: snapshot_id); |
726 | if (WARN_ON(!snapshot)) { |
727 | err = -EINVAL; |
728 | goto unlock; |
729 | } |
730 | |
731 | msg = devlink_nl_region_notify_build(region, snapshot, |
732 | cmd: DEVLINK_CMD_REGION_NEW, |
733 | portid: info->snd_portid, |
734 | seq: info->snd_seq); |
735 | err = PTR_ERR_OR_ZERO(ptr: msg); |
736 | if (err) |
737 | goto err_notify; |
738 | |
739 | err = genlmsg_reply(skb: msg, info); |
740 | if (err) |
741 | goto err_notify; |
742 | } |
743 | |
744 | mutex_unlock(lock: ®ion->snapshot_lock); |
745 | return 0; |
746 | |
747 | err_snapshot_create: |
748 | region->ops->destructor(data); |
749 | err_snapshot_capture: |
750 | __devlink_snapshot_id_decrement(devlink, id: snapshot_id); |
751 | mutex_unlock(lock: ®ion->snapshot_lock); |
752 | return err; |
753 | |
754 | err_notify: |
755 | devlink_region_snapshot_del(region, snapshot); |
756 | unlock: |
757 | mutex_unlock(lock: ®ion->snapshot_lock); |
758 | return err; |
759 | } |
760 | |
761 | static int devlink_nl_cmd_region_read_chunk_fill(struct sk_buff *msg, |
762 | u8 *chunk, u32 chunk_size, |
763 | u64 addr) |
764 | { |
765 | struct nlattr *chunk_attr; |
766 | int err; |
767 | |
768 | chunk_attr = nla_nest_start_noflag(skb: msg, attrtype: DEVLINK_ATTR_REGION_CHUNK); |
769 | if (!chunk_attr) |
770 | return -EINVAL; |
771 | |
772 | err = nla_put(skb: msg, attrtype: DEVLINK_ATTR_REGION_CHUNK_DATA, attrlen: chunk_size, data: chunk); |
773 | if (err) |
774 | goto nla_put_failure; |
775 | |
776 | err = nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_REGION_CHUNK_ADDR, value: addr, |
777 | padattr: DEVLINK_ATTR_PAD); |
778 | if (err) |
779 | goto nla_put_failure; |
780 | |
781 | nla_nest_end(skb: msg, start: chunk_attr); |
782 | return 0; |
783 | |
784 | nla_put_failure: |
785 | nla_nest_cancel(skb: msg, start: chunk_attr); |
786 | return err; |
787 | } |
788 | |
789 | #define DEVLINK_REGION_READ_CHUNK_SIZE 256 |
790 | |
791 | typedef int devlink_chunk_fill_t(void *cb_priv, u8 *chunk, u32 chunk_size, |
792 | u64 curr_offset, |
793 | struct netlink_ext_ack *extack); |
794 | |
795 | static int |
796 | devlink_nl_region_read_fill(struct sk_buff *skb, devlink_chunk_fill_t *cb, |
797 | void *cb_priv, u64 start_offset, u64 end_offset, |
798 | u64 *new_offset, struct netlink_ext_ack *extack) |
799 | { |
800 | u64 curr_offset = start_offset; |
801 | int err = 0; |
802 | u8 *data; |
803 | |
804 | /* Allocate and re-use a single buffer */ |
805 | data = kmalloc(DEVLINK_REGION_READ_CHUNK_SIZE, GFP_KERNEL); |
806 | if (!data) |
807 | return -ENOMEM; |
808 | |
809 | *new_offset = start_offset; |
810 | |
811 | while (curr_offset < end_offset) { |
812 | u32 data_size; |
813 | |
814 | data_size = min_t(u32, end_offset - curr_offset, |
815 | DEVLINK_REGION_READ_CHUNK_SIZE); |
816 | |
817 | err = cb(cb_priv, data, data_size, curr_offset, extack); |
818 | if (err) |
819 | break; |
820 | |
821 | err = devlink_nl_cmd_region_read_chunk_fill(msg: skb, chunk: data, chunk_size: data_size, addr: curr_offset); |
822 | if (err) |
823 | break; |
824 | |
825 | curr_offset += data_size; |
826 | } |
827 | *new_offset = curr_offset; |
828 | |
829 | kfree(objp: data); |
830 | |
831 | return err; |
832 | } |
833 | |
834 | static int |
835 | devlink_region_snapshot_fill(void *cb_priv, u8 *chunk, u32 chunk_size, |
836 | u64 curr_offset, |
837 | struct netlink_ext_ack __always_unused *extack) |
838 | { |
839 | struct devlink_snapshot *snapshot = cb_priv; |
840 | |
841 | memcpy(chunk, &snapshot->data[curr_offset], chunk_size); |
842 | |
843 | return 0; |
844 | } |
845 | |
846 | static int |
847 | devlink_region_port_direct_fill(void *cb_priv, u8 *chunk, u32 chunk_size, |
848 | u64 curr_offset, struct netlink_ext_ack *extack) |
849 | { |
850 | struct devlink_region *region = cb_priv; |
851 | |
852 | return region->port_ops->read(region->port, region->port_ops, extack, |
853 | curr_offset, chunk_size, chunk); |
854 | } |
855 | |
856 | static int |
857 | devlink_region_direct_fill(void *cb_priv, u8 *chunk, u32 chunk_size, |
858 | u64 curr_offset, struct netlink_ext_ack *extack) |
859 | { |
860 | struct devlink_region *region = cb_priv; |
861 | |
862 | return region->ops->read(region->devlink, region->ops, extack, |
863 | curr_offset, chunk_size, chunk); |
864 | } |
865 | |
866 | int devlink_nl_region_read_dumpit(struct sk_buff *skb, |
867 | struct netlink_callback *cb) |
868 | { |
869 | const struct genl_dumpit_info *info = genl_dumpit_info(cb); |
870 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); |
871 | struct nlattr *chunks_attr, *region_attr, *snapshot_attr; |
872 | u64 ret_offset, start_offset, end_offset = U64_MAX; |
873 | struct nlattr **attrs = info->info.attrs; |
874 | struct devlink_port *port = NULL; |
875 | devlink_chunk_fill_t *region_cb; |
876 | struct devlink_region *region; |
877 | const char *region_name; |
878 | struct devlink *devlink; |
879 | unsigned int index; |
880 | void *region_cb_priv; |
881 | void *hdr; |
882 | int err; |
883 | |
884 | start_offset = state->start_offset; |
885 | |
886 | devlink = devlink_get_from_attrs_lock(net: sock_net(sk: cb->skb->sk), attrs); |
887 | if (IS_ERR(ptr: devlink)) |
888 | return PTR_ERR(ptr: devlink); |
889 | |
890 | if (!attrs[DEVLINK_ATTR_REGION_NAME]) { |
891 | NL_SET_ERR_MSG(cb->extack, "No region name provided" ); |
892 | err = -EINVAL; |
893 | goto out_unlock; |
894 | } |
895 | |
896 | if (attrs[DEVLINK_ATTR_PORT_INDEX]) { |
897 | index = nla_get_u32(nla: attrs[DEVLINK_ATTR_PORT_INDEX]); |
898 | |
899 | port = devlink_port_get_by_index(devlink, port_index: index); |
900 | if (!port) { |
901 | err = -ENODEV; |
902 | goto out_unlock; |
903 | } |
904 | } |
905 | |
906 | region_attr = attrs[DEVLINK_ATTR_REGION_NAME]; |
907 | region_name = nla_data(nla: region_attr); |
908 | |
909 | if (port) |
910 | region = devlink_port_region_get_by_name(port, region_name); |
911 | else |
912 | region = devlink_region_get_by_name(devlink, region_name); |
913 | |
914 | if (!region) { |
915 | NL_SET_ERR_MSG_ATTR(cb->extack, region_attr, "Requested region does not exist" ); |
916 | err = -EINVAL; |
917 | goto out_unlock; |
918 | } |
919 | |
920 | snapshot_attr = attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]; |
921 | if (!snapshot_attr) { |
922 | if (!nla_get_flag(nla: attrs[DEVLINK_ATTR_REGION_DIRECT])) { |
923 | NL_SET_ERR_MSG(cb->extack, "No snapshot id provided" ); |
924 | err = -EINVAL; |
925 | goto out_unlock; |
926 | } |
927 | |
928 | if (!region->ops->read) { |
929 | NL_SET_ERR_MSG(cb->extack, "Requested region does not support direct read" ); |
930 | err = -EOPNOTSUPP; |
931 | goto out_unlock; |
932 | } |
933 | |
934 | if (port) |
935 | region_cb = &devlink_region_port_direct_fill; |
936 | else |
937 | region_cb = &devlink_region_direct_fill; |
938 | region_cb_priv = region; |
939 | } else { |
940 | struct devlink_snapshot *snapshot; |
941 | u32 snapshot_id; |
942 | |
943 | if (nla_get_flag(nla: attrs[DEVLINK_ATTR_REGION_DIRECT])) { |
944 | NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Direct region read does not use snapshot" ); |
945 | err = -EINVAL; |
946 | goto out_unlock; |
947 | } |
948 | |
949 | snapshot_id = nla_get_u32(nla: snapshot_attr); |
950 | snapshot = devlink_region_snapshot_get_by_id(region, id: snapshot_id); |
951 | if (!snapshot) { |
952 | NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Requested snapshot does not exist" ); |
953 | err = -EINVAL; |
954 | goto out_unlock; |
955 | } |
956 | region_cb = &devlink_region_snapshot_fill; |
957 | region_cb_priv = snapshot; |
958 | } |
959 | |
960 | if (attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR] && |
961 | attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]) { |
962 | if (!start_offset) |
963 | start_offset = |
964 | nla_get_u64(nla: attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR]); |
965 | |
966 | end_offset = nla_get_u64(nla: attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR]); |
967 | end_offset += nla_get_u64(nla: attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]); |
968 | } |
969 | |
970 | if (end_offset > region->size) |
971 | end_offset = region->size; |
972 | |
973 | /* return 0 if there is no further data to read */ |
974 | if (start_offset == end_offset) { |
975 | err = 0; |
976 | goto out_unlock; |
977 | } |
978 | |
979 | hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, seq: cb->nlh->nlmsg_seq, |
980 | family: &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, |
981 | cmd: DEVLINK_CMD_REGION_READ); |
982 | if (!hdr) { |
983 | err = -EMSGSIZE; |
984 | goto out_unlock; |
985 | } |
986 | |
987 | err = devlink_nl_put_handle(msg: skb, devlink); |
988 | if (err) |
989 | goto nla_put_failure; |
990 | |
991 | if (region->port) { |
992 | err = nla_put_u32(skb, attrtype: DEVLINK_ATTR_PORT_INDEX, |
993 | value: region->port->index); |
994 | if (err) |
995 | goto nla_put_failure; |
996 | } |
997 | |
998 | err = nla_put_string(skb, attrtype: DEVLINK_ATTR_REGION_NAME, str: region_name); |
999 | if (err) |
1000 | goto nla_put_failure; |
1001 | |
1002 | chunks_attr = nla_nest_start_noflag(skb, attrtype: DEVLINK_ATTR_REGION_CHUNKS); |
1003 | if (!chunks_attr) { |
1004 | err = -EMSGSIZE; |
1005 | goto nla_put_failure; |
1006 | } |
1007 | |
1008 | err = devlink_nl_region_read_fill(skb, cb: region_cb, cb_priv: region_cb_priv, |
1009 | start_offset, end_offset, new_offset: &ret_offset, |
1010 | extack: cb->extack); |
1011 | |
1012 | if (err && err != -EMSGSIZE) |
1013 | goto nla_put_failure; |
1014 | |
1015 | /* Check if there was any progress done to prevent infinite loop */ |
1016 | if (ret_offset == start_offset) { |
1017 | err = -EINVAL; |
1018 | goto nla_put_failure; |
1019 | } |
1020 | |
1021 | state->start_offset = ret_offset; |
1022 | |
1023 | nla_nest_end(skb, start: chunks_attr); |
1024 | genlmsg_end(skb, hdr); |
1025 | devl_unlock(devlink); |
1026 | devlink_put(devlink); |
1027 | return skb->len; |
1028 | |
1029 | nla_put_failure: |
1030 | genlmsg_cancel(skb, hdr); |
1031 | out_unlock: |
1032 | devl_unlock(devlink); |
1033 | devlink_put(devlink); |
1034 | return err; |
1035 | } |
1036 | |
1037 | /** |
1038 | * devl_region_create - create a new address region |
1039 | * |
1040 | * @devlink: devlink |
1041 | * @ops: region operations and name |
1042 | * @region_max_snapshots: Maximum supported number of snapshots for region |
1043 | * @region_size: size of region |
1044 | */ |
1045 | struct devlink_region *devl_region_create(struct devlink *devlink, |
1046 | const struct devlink_region_ops *ops, |
1047 | u32 region_max_snapshots, |
1048 | u64 region_size) |
1049 | { |
1050 | struct devlink_region *region; |
1051 | |
1052 | devl_assert_locked(devlink); |
1053 | |
1054 | if (WARN_ON(!ops) || WARN_ON(!ops->destructor)) |
1055 | return ERR_PTR(error: -EINVAL); |
1056 | |
1057 | if (devlink_region_get_by_name(devlink, region_name: ops->name)) |
1058 | return ERR_PTR(error: -EEXIST); |
1059 | |
1060 | region = kzalloc(size: sizeof(*region), GFP_KERNEL); |
1061 | if (!region) |
1062 | return ERR_PTR(error: -ENOMEM); |
1063 | |
1064 | region->devlink = devlink; |
1065 | region->max_snapshots = region_max_snapshots; |
1066 | region->ops = ops; |
1067 | region->size = region_size; |
1068 | INIT_LIST_HEAD(list: ®ion->snapshot_list); |
1069 | mutex_init(®ion->snapshot_lock); |
1070 | list_add_tail(new: ®ion->list, head: &devlink->region_list); |
1071 | devlink_nl_region_notify(region, NULL, cmd: DEVLINK_CMD_REGION_NEW); |
1072 | |
1073 | return region; |
1074 | } |
1075 | EXPORT_SYMBOL_GPL(devl_region_create); |
1076 | |
1077 | /** |
1078 | * devlink_region_create - create a new address region |
1079 | * |
1080 | * @devlink: devlink |
1081 | * @ops: region operations and name |
1082 | * @region_max_snapshots: Maximum supported number of snapshots for region |
1083 | * @region_size: size of region |
1084 | * |
1085 | * Context: Takes and release devlink->lock <mutex>. |
1086 | */ |
1087 | struct devlink_region * |
1088 | devlink_region_create(struct devlink *devlink, |
1089 | const struct devlink_region_ops *ops, |
1090 | u32 region_max_snapshots, u64 region_size) |
1091 | { |
1092 | struct devlink_region *region; |
1093 | |
1094 | devl_lock(devlink); |
1095 | region = devl_region_create(devlink, ops, region_max_snapshots, |
1096 | region_size); |
1097 | devl_unlock(devlink); |
1098 | return region; |
1099 | } |
1100 | EXPORT_SYMBOL_GPL(devlink_region_create); |
1101 | |
1102 | /** |
1103 | * devlink_port_region_create - create a new address region for a port |
1104 | * |
1105 | * @port: devlink port |
1106 | * @ops: region operations and name |
1107 | * @region_max_snapshots: Maximum supported number of snapshots for region |
1108 | * @region_size: size of region |
1109 | * |
1110 | * Context: Takes and release devlink->lock <mutex>. |
1111 | */ |
1112 | struct devlink_region * |
1113 | devlink_port_region_create(struct devlink_port *port, |
1114 | const struct devlink_port_region_ops *ops, |
1115 | u32 region_max_snapshots, u64 region_size) |
1116 | { |
1117 | struct devlink *devlink = port->devlink; |
1118 | struct devlink_region *region; |
1119 | int err = 0; |
1120 | |
1121 | ASSERT_DEVLINK_PORT_INITIALIZED(port); |
1122 | |
1123 | if (WARN_ON(!ops) || WARN_ON(!ops->destructor)) |
1124 | return ERR_PTR(error: -EINVAL); |
1125 | |
1126 | devl_lock(devlink); |
1127 | |
1128 | if (devlink_port_region_get_by_name(port, region_name: ops->name)) { |
1129 | err = -EEXIST; |
1130 | goto unlock; |
1131 | } |
1132 | |
1133 | region = kzalloc(size: sizeof(*region), GFP_KERNEL); |
1134 | if (!region) { |
1135 | err = -ENOMEM; |
1136 | goto unlock; |
1137 | } |
1138 | |
1139 | region->devlink = devlink; |
1140 | region->port = port; |
1141 | region->max_snapshots = region_max_snapshots; |
1142 | region->port_ops = ops; |
1143 | region->size = region_size; |
1144 | INIT_LIST_HEAD(list: ®ion->snapshot_list); |
1145 | mutex_init(®ion->snapshot_lock); |
1146 | list_add_tail(new: ®ion->list, head: &port->region_list); |
1147 | devlink_nl_region_notify(region, NULL, cmd: DEVLINK_CMD_REGION_NEW); |
1148 | |
1149 | devl_unlock(devlink); |
1150 | return region; |
1151 | |
1152 | unlock: |
1153 | devl_unlock(devlink); |
1154 | return ERR_PTR(error: err); |
1155 | } |
1156 | EXPORT_SYMBOL_GPL(devlink_port_region_create); |
1157 | |
1158 | /** |
1159 | * devl_region_destroy - destroy address region |
1160 | * |
1161 | * @region: devlink region to destroy |
1162 | */ |
1163 | void devl_region_destroy(struct devlink_region *region) |
1164 | { |
1165 | struct devlink *devlink = region->devlink; |
1166 | struct devlink_snapshot *snapshot, *ts; |
1167 | |
1168 | devl_assert_locked(devlink); |
1169 | |
1170 | /* Free all snapshots of region */ |
1171 | mutex_lock(®ion->snapshot_lock); |
1172 | list_for_each_entry_safe(snapshot, ts, ®ion->snapshot_list, list) |
1173 | devlink_region_snapshot_del(region, snapshot); |
1174 | mutex_unlock(lock: ®ion->snapshot_lock); |
1175 | |
1176 | list_del(entry: ®ion->list); |
1177 | mutex_destroy(lock: ®ion->snapshot_lock); |
1178 | |
1179 | devlink_nl_region_notify(region, NULL, cmd: DEVLINK_CMD_REGION_DEL); |
1180 | kfree(objp: region); |
1181 | } |
1182 | EXPORT_SYMBOL_GPL(devl_region_destroy); |
1183 | |
1184 | /** |
1185 | * devlink_region_destroy - destroy address region |
1186 | * |
1187 | * @region: devlink region to destroy |
1188 | * |
1189 | * Context: Takes and release devlink->lock <mutex>. |
1190 | */ |
1191 | void devlink_region_destroy(struct devlink_region *region) |
1192 | { |
1193 | struct devlink *devlink = region->devlink; |
1194 | |
1195 | devl_lock(devlink); |
1196 | devl_region_destroy(region); |
1197 | devl_unlock(devlink); |
1198 | } |
1199 | EXPORT_SYMBOL_GPL(devlink_region_destroy); |
1200 | |
1201 | /** |
1202 | * devlink_region_snapshot_id_get - get snapshot ID |
1203 | * |
1204 | * This callback should be called when adding a new snapshot, |
1205 | * Driver should use the same id for multiple snapshots taken |
1206 | * on multiple regions at the same time/by the same trigger. |
1207 | * |
1208 | * The caller of this function must use devlink_region_snapshot_id_put |
1209 | * when finished creating regions using this id. |
1210 | * |
1211 | * Returns zero on success, or a negative error code on failure. |
1212 | * |
1213 | * @devlink: devlink |
1214 | * @id: storage to return id |
1215 | */ |
1216 | int devlink_region_snapshot_id_get(struct devlink *devlink, u32 *id) |
1217 | { |
1218 | return __devlink_region_snapshot_id_get(devlink, id); |
1219 | } |
1220 | EXPORT_SYMBOL_GPL(devlink_region_snapshot_id_get); |
1221 | |
1222 | /** |
1223 | * devlink_region_snapshot_id_put - put snapshot ID reference |
1224 | * |
1225 | * This should be called by a driver after finishing creating snapshots |
1226 | * with an id. Doing so ensures that the ID can later be released in the |
1227 | * event that all snapshots using it have been destroyed. |
1228 | * |
1229 | * @devlink: devlink |
1230 | * @id: id to release reference on |
1231 | */ |
1232 | void devlink_region_snapshot_id_put(struct devlink *devlink, u32 id) |
1233 | { |
1234 | __devlink_snapshot_id_decrement(devlink, id); |
1235 | } |
1236 | EXPORT_SYMBOL_GPL(devlink_region_snapshot_id_put); |
1237 | |
1238 | /** |
1239 | * devlink_region_snapshot_create - create a new snapshot |
1240 | * This will add a new snapshot of a region. The snapshot |
1241 | * will be stored on the region struct and can be accessed |
1242 | * from devlink. This is useful for future analyses of snapshots. |
1243 | * Multiple snapshots can be created on a region. |
1244 | * The @snapshot_id should be obtained using the getter function. |
1245 | * |
1246 | * @region: devlink region of the snapshot |
1247 | * @data: snapshot data |
1248 | * @snapshot_id: snapshot id to be created |
1249 | */ |
1250 | int devlink_region_snapshot_create(struct devlink_region *region, |
1251 | u8 *data, u32 snapshot_id) |
1252 | { |
1253 | int err; |
1254 | |
1255 | mutex_lock(®ion->snapshot_lock); |
1256 | err = __devlink_region_snapshot_create(region, data, snapshot_id); |
1257 | mutex_unlock(lock: ®ion->snapshot_lock); |
1258 | return err; |
1259 | } |
1260 | EXPORT_SYMBOL_GPL(devlink_region_snapshot_create); |
1261 | |