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