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  /**
10   * struct devlink_resource - devlink resource
11   * @name: name of the resource
12   * @id: id, per devlink instance
13   * @size: size of the resource
14   * @size_new: updated size of the resource, reload is needed
15   * @size_valid: valid in case the total size of the resource is valid
16   *              including its children
17   * @parent: parent resource
18   * @size_params: size parameters
19   * @list: parent list
20   * @resource_list: list of child resources
21   * @occ_get: occupancy getter callback
22   * @occ_get_priv: occupancy getter callback priv
23   */
24  struct devlink_resource {
25  	const char *name;
26  	u64 id;
27  	u64 size;
28  	u64 size_new;
29  	bool size_valid;
30  	struct devlink_resource *parent;
31  	struct devlink_resource_size_params size_params;
32  	struct list_head list;
33  	struct list_head resource_list;
34  	devlink_resource_occ_get_t *occ_get;
35  	void *occ_get_priv;
36  };
37  
38  static struct devlink_resource *
devlink_resource_find(struct devlink * devlink,struct devlink_resource * resource,u64 resource_id)39  devlink_resource_find(struct devlink *devlink,
40  		      struct devlink_resource *resource, u64 resource_id)
41  {
42  	struct list_head *resource_list;
43  
44  	if (resource)
45  		resource_list = &resource->resource_list;
46  	else
47  		resource_list = &devlink->resource_list;
48  
49  	list_for_each_entry(resource, resource_list, list) {
50  		struct devlink_resource *child_resource;
51  
52  		if (resource->id == resource_id)
53  			return resource;
54  
55  		child_resource = devlink_resource_find(devlink, resource,
56  						       resource_id);
57  		if (child_resource)
58  			return child_resource;
59  	}
60  	return NULL;
61  }
62  
63  static void
devlink_resource_validate_children(struct devlink_resource * resource)64  devlink_resource_validate_children(struct devlink_resource *resource)
65  {
66  	struct devlink_resource *child_resource;
67  	bool size_valid = true;
68  	u64 parts_size = 0;
69  
70  	if (list_empty(&resource->resource_list))
71  		goto out;
72  
73  	list_for_each_entry(child_resource, &resource->resource_list, list)
74  		parts_size += child_resource->size_new;
75  
76  	if (parts_size > resource->size_new)
77  		size_valid = false;
78  out:
79  	resource->size_valid = size_valid;
80  }
81  
82  static int
devlink_resource_validate_size(struct devlink_resource * resource,u64 size,struct netlink_ext_ack * extack)83  devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
84  			       struct netlink_ext_ack *extack)
85  {
86  	u64 reminder;
87  	int err = 0;
88  
89  	if (size > resource->size_params.size_max) {
90  		NL_SET_ERR_MSG(extack, "Size larger than maximum");
91  		err = -EINVAL;
92  	}
93  
94  	if (size < resource->size_params.size_min) {
95  		NL_SET_ERR_MSG(extack, "Size smaller than minimum");
96  		err = -EINVAL;
97  	}
98  
99  	div64_u64_rem(size, resource->size_params.size_granularity, &reminder);
100  	if (reminder) {
101  		NL_SET_ERR_MSG(extack, "Wrong granularity");
102  		err = -EINVAL;
103  	}
104  
105  	return err;
106  }
107  
devlink_nl_resource_set_doit(struct sk_buff * skb,struct genl_info * info)108  int devlink_nl_resource_set_doit(struct sk_buff *skb, struct genl_info *info)
109  {
110  	struct devlink *devlink = info->user_ptr[0];
111  	struct devlink_resource *resource;
112  	u64 resource_id;
113  	u64 size;
114  	int err;
115  
116  	if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_ID) ||
117  	    GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_SIZE))
118  		return -EINVAL;
119  	resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
120  
121  	resource = devlink_resource_find(devlink, NULL, resource_id);
122  	if (!resource)
123  		return -EINVAL;
124  
125  	size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
126  	err = devlink_resource_validate_size(resource, size, info->extack);
127  	if (err)
128  		return err;
129  
130  	resource->size_new = size;
131  	devlink_resource_validate_children(resource);
132  	if (resource->parent)
133  		devlink_resource_validate_children(resource->parent);
134  	return 0;
135  }
136  
137  static int
devlink_resource_size_params_put(struct devlink_resource * resource,struct sk_buff * skb)138  devlink_resource_size_params_put(struct devlink_resource *resource,
139  				 struct sk_buff *skb)
140  {
141  	struct devlink_resource_size_params *size_params;
142  
143  	size_params = &resource->size_params;
144  	if (nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_GRAN,
145  			      size_params->size_granularity, DEVLINK_ATTR_PAD) ||
146  	    nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX,
147  			      size_params->size_max, DEVLINK_ATTR_PAD) ||
148  	    nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN,
149  			      size_params->size_min, DEVLINK_ATTR_PAD) ||
150  	    nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_UNIT, size_params->unit))
151  		return -EMSGSIZE;
152  	return 0;
153  }
154  
devlink_resource_occ_put(struct devlink_resource * resource,struct sk_buff * skb)155  static int devlink_resource_occ_put(struct devlink_resource *resource,
156  				    struct sk_buff *skb)
157  {
158  	if (!resource->occ_get)
159  		return 0;
160  	return nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_OCC,
161  				 resource->occ_get(resource->occ_get_priv),
162  				 DEVLINK_ATTR_PAD);
163  }
164  
devlink_resource_put(struct devlink * devlink,struct sk_buff * skb,struct devlink_resource * resource)165  static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
166  				struct devlink_resource *resource)
167  {
168  	struct devlink_resource *child_resource;
169  	struct nlattr *child_resource_attr;
170  	struct nlattr *resource_attr;
171  
172  	resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
173  	if (!resource_attr)
174  		return -EMSGSIZE;
175  
176  	if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
177  	    nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size,
178  			      DEVLINK_ATTR_PAD) ||
179  	    nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id,
180  			      DEVLINK_ATTR_PAD))
181  		goto nla_put_failure;
182  	if (resource->size != resource->size_new &&
183  	    nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW,
184  			      resource->size_new, DEVLINK_ATTR_PAD))
185  		goto nla_put_failure;
186  	if (devlink_resource_occ_put(resource, skb))
187  		goto nla_put_failure;
188  	if (devlink_resource_size_params_put(resource, skb))
189  		goto nla_put_failure;
190  	if (list_empty(&resource->resource_list))
191  		goto out;
192  
193  	if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
194  		       resource->size_valid))
195  		goto nla_put_failure;
196  
197  	child_resource_attr = nla_nest_start_noflag(skb,
198  						    DEVLINK_ATTR_RESOURCE_LIST);
199  	if (!child_resource_attr)
200  		goto nla_put_failure;
201  
202  	list_for_each_entry(child_resource, &resource->resource_list, list) {
203  		if (devlink_resource_put(devlink, skb, child_resource))
204  			goto resource_put_failure;
205  	}
206  
207  	nla_nest_end(skb, child_resource_attr);
208  out:
209  	nla_nest_end(skb, resource_attr);
210  	return 0;
211  
212  resource_put_failure:
213  	nla_nest_cancel(skb, child_resource_attr);
214  nla_put_failure:
215  	nla_nest_cancel(skb, resource_attr);
216  	return -EMSGSIZE;
217  }
218  
devlink_resource_fill(struct genl_info * info,enum devlink_command cmd,int flags)219  static int devlink_resource_fill(struct genl_info *info,
220  				 enum devlink_command cmd, int flags)
221  {
222  	struct devlink *devlink = info->user_ptr[0];
223  	struct devlink_resource *resource;
224  	struct nlattr *resources_attr;
225  	struct sk_buff *skb = NULL;
226  	struct nlmsghdr *nlh;
227  	bool incomplete;
228  	void *hdr;
229  	int i;
230  	int err;
231  
232  	resource = list_first_entry(&devlink->resource_list,
233  				    struct devlink_resource, list);
234  start_again:
235  	err = devlink_nl_msg_reply_and_new(&skb, info);
236  	if (err)
237  		return err;
238  
239  	hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
240  			  &devlink_nl_family, NLM_F_MULTI, cmd);
241  	if (!hdr) {
242  		nlmsg_free(skb);
243  		return -EMSGSIZE;
244  	}
245  
246  	if (devlink_nl_put_handle(skb, devlink))
247  		goto nla_put_failure;
248  
249  	resources_attr = nla_nest_start_noflag(skb,
250  					       DEVLINK_ATTR_RESOURCE_LIST);
251  	if (!resources_attr)
252  		goto nla_put_failure;
253  
254  	incomplete = false;
255  	i = 0;
256  	list_for_each_entry_from(resource, &devlink->resource_list, list) {
257  		err = devlink_resource_put(devlink, skb, resource);
258  		if (err) {
259  			if (!i)
260  				goto err_resource_put;
261  			incomplete = true;
262  			break;
263  		}
264  		i++;
265  	}
266  	nla_nest_end(skb, resources_attr);
267  	genlmsg_end(skb, hdr);
268  	if (incomplete)
269  		goto start_again;
270  send_done:
271  	nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
272  			NLMSG_DONE, 0, flags | NLM_F_MULTI);
273  	if (!nlh) {
274  		err = devlink_nl_msg_reply_and_new(&skb, info);
275  		if (err)
276  			return err;
277  		goto send_done;
278  	}
279  	return genlmsg_reply(skb, info);
280  
281  nla_put_failure:
282  	err = -EMSGSIZE;
283  err_resource_put:
284  	nlmsg_free(skb);
285  	return err;
286  }
287  
devlink_nl_resource_dump_doit(struct sk_buff * skb,struct genl_info * info)288  int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info)
289  {
290  	struct devlink *devlink = info->user_ptr[0];
291  
292  	if (list_empty(&devlink->resource_list))
293  		return -EOPNOTSUPP;
294  
295  	return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
296  }
297  
devlink_resources_validate(struct devlink * devlink,struct devlink_resource * resource,struct genl_info * info)298  int devlink_resources_validate(struct devlink *devlink,
299  			       struct devlink_resource *resource,
300  			       struct genl_info *info)
301  {
302  	struct list_head *resource_list;
303  	int err = 0;
304  
305  	if (resource)
306  		resource_list = &resource->resource_list;
307  	else
308  		resource_list = &devlink->resource_list;
309  
310  	list_for_each_entry(resource, resource_list, list) {
311  		if (!resource->size_valid)
312  			return -EINVAL;
313  		err = devlink_resources_validate(devlink, resource, info);
314  		if (err)
315  			return err;
316  	}
317  	return err;
318  }
319  
320  /**
321   * devl_resource_register - devlink resource register
322   *
323   * @devlink: devlink
324   * @resource_name: resource's name
325   * @resource_size: resource's size
326   * @resource_id: resource's id
327   * @parent_resource_id: resource's parent id
328   * @size_params: size parameters
329   *
330   * Generic resources should reuse the same names across drivers.
331   * Please see the generic resources list at:
332   * Documentation/networking/devlink/devlink-resource.rst
333   */
devl_resource_register(struct devlink * devlink,const char * resource_name,u64 resource_size,u64 resource_id,u64 parent_resource_id,const struct devlink_resource_size_params * size_params)334  int devl_resource_register(struct devlink *devlink,
335  			   const char *resource_name,
336  			   u64 resource_size,
337  			   u64 resource_id,
338  			   u64 parent_resource_id,
339  			   const struct devlink_resource_size_params *size_params)
340  {
341  	struct devlink_resource *resource;
342  	struct list_head *resource_list;
343  	bool top_hierarchy;
344  
345  	lockdep_assert_held(&devlink->lock);
346  
347  	top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
348  
349  	resource = devlink_resource_find(devlink, NULL, resource_id);
350  	if (resource)
351  		return -EINVAL;
352  
353  	resource = kzalloc(sizeof(*resource), GFP_KERNEL);
354  	if (!resource)
355  		return -ENOMEM;
356  
357  	if (top_hierarchy) {
358  		resource_list = &devlink->resource_list;
359  	} else {
360  		struct devlink_resource *parent_resource;
361  
362  		parent_resource = devlink_resource_find(devlink, NULL,
363  							parent_resource_id);
364  		if (parent_resource) {
365  			resource_list = &parent_resource->resource_list;
366  			resource->parent = parent_resource;
367  		} else {
368  			kfree(resource);
369  			return -EINVAL;
370  		}
371  	}
372  
373  	resource->name = resource_name;
374  	resource->size = resource_size;
375  	resource->size_new = resource_size;
376  	resource->id = resource_id;
377  	resource->size_valid = true;
378  	memcpy(&resource->size_params, size_params,
379  	       sizeof(resource->size_params));
380  	INIT_LIST_HEAD(&resource->resource_list);
381  	list_add_tail(&resource->list, resource_list);
382  
383  	return 0;
384  }
385  EXPORT_SYMBOL_GPL(devl_resource_register);
386  
387  /**
388   *	devlink_resource_register - devlink resource register
389   *
390   *	@devlink: devlink
391   *	@resource_name: resource's name
392   *	@resource_size: resource's size
393   *	@resource_id: resource's id
394   *	@parent_resource_id: resource's parent id
395   *	@size_params: size parameters
396   *
397   *	Generic resources should reuse the same names across drivers.
398   *	Please see the generic resources list at:
399   *	Documentation/networking/devlink/devlink-resource.rst
400   *
401   *	Context: Takes and release devlink->lock <mutex>.
402   */
devlink_resource_register(struct devlink * devlink,const char * resource_name,u64 resource_size,u64 resource_id,u64 parent_resource_id,const struct devlink_resource_size_params * size_params)403  int devlink_resource_register(struct devlink *devlink,
404  			      const char *resource_name,
405  			      u64 resource_size,
406  			      u64 resource_id,
407  			      u64 parent_resource_id,
408  			      const struct devlink_resource_size_params *size_params)
409  {
410  	int err;
411  
412  	devl_lock(devlink);
413  	err = devl_resource_register(devlink, resource_name, resource_size,
414  				     resource_id, parent_resource_id, size_params);
415  	devl_unlock(devlink);
416  	return err;
417  }
418  EXPORT_SYMBOL_GPL(devlink_resource_register);
419  
devlink_resource_unregister(struct devlink * devlink,struct devlink_resource * resource)420  static void devlink_resource_unregister(struct devlink *devlink,
421  					struct devlink_resource *resource)
422  {
423  	struct devlink_resource *tmp, *child_resource;
424  
425  	list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
426  				 list) {
427  		devlink_resource_unregister(devlink, child_resource);
428  		list_del(&child_resource->list);
429  		kfree(child_resource);
430  	}
431  }
432  
433  /**
434   * devl_resources_unregister - free all resources
435   *
436   * @devlink: devlink
437   */
devl_resources_unregister(struct devlink * devlink)438  void devl_resources_unregister(struct devlink *devlink)
439  {
440  	struct devlink_resource *tmp, *child_resource;
441  
442  	lockdep_assert_held(&devlink->lock);
443  
444  	list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
445  				 list) {
446  		devlink_resource_unregister(devlink, child_resource);
447  		list_del(&child_resource->list);
448  		kfree(child_resource);
449  	}
450  }
451  EXPORT_SYMBOL_GPL(devl_resources_unregister);
452  
453  /**
454   *	devlink_resources_unregister - free all resources
455   *
456   *	@devlink: devlink
457   *
458   *	Context: Takes and release devlink->lock <mutex>.
459   */
devlink_resources_unregister(struct devlink * devlink)460  void devlink_resources_unregister(struct devlink *devlink)
461  {
462  	devl_lock(devlink);
463  	devl_resources_unregister(devlink);
464  	devl_unlock(devlink);
465  }
466  EXPORT_SYMBOL_GPL(devlink_resources_unregister);
467  
468  /**
469   * devl_resource_size_get - get and update size
470   *
471   * @devlink: devlink
472   * @resource_id: the requested resource id
473   * @p_resource_size: ptr to update
474   */
devl_resource_size_get(struct devlink * devlink,u64 resource_id,u64 * p_resource_size)475  int devl_resource_size_get(struct devlink *devlink,
476  			   u64 resource_id,
477  			   u64 *p_resource_size)
478  {
479  	struct devlink_resource *resource;
480  
481  	lockdep_assert_held(&devlink->lock);
482  
483  	resource = devlink_resource_find(devlink, NULL, resource_id);
484  	if (!resource)
485  		return -EINVAL;
486  	*p_resource_size = resource->size_new;
487  	resource->size = resource->size_new;
488  	return 0;
489  }
490  EXPORT_SYMBOL_GPL(devl_resource_size_get);
491  
492  /**
493   * devl_resource_occ_get_register - register occupancy getter
494   *
495   * @devlink: devlink
496   * @resource_id: resource id
497   * @occ_get: occupancy getter callback
498   * @occ_get_priv: occupancy getter callback priv
499   */
devl_resource_occ_get_register(struct devlink * devlink,u64 resource_id,devlink_resource_occ_get_t * occ_get,void * occ_get_priv)500  void devl_resource_occ_get_register(struct devlink *devlink,
501  				    u64 resource_id,
502  				    devlink_resource_occ_get_t *occ_get,
503  				    void *occ_get_priv)
504  {
505  	struct devlink_resource *resource;
506  
507  	lockdep_assert_held(&devlink->lock);
508  
509  	resource = devlink_resource_find(devlink, NULL, resource_id);
510  	if (WARN_ON(!resource))
511  		return;
512  	WARN_ON(resource->occ_get);
513  
514  	resource->occ_get = occ_get;
515  	resource->occ_get_priv = occ_get_priv;
516  }
517  EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
518  
519  /**
520   *	devlink_resource_occ_get_register - register occupancy getter
521   *
522   *	@devlink: devlink
523   *	@resource_id: resource id
524   *	@occ_get: occupancy getter callback
525   *	@occ_get_priv: occupancy getter callback priv
526   *
527   *	Context: Takes and release devlink->lock <mutex>.
528   */
devlink_resource_occ_get_register(struct devlink * devlink,u64 resource_id,devlink_resource_occ_get_t * occ_get,void * occ_get_priv)529  void devlink_resource_occ_get_register(struct devlink *devlink,
530  				       u64 resource_id,
531  				       devlink_resource_occ_get_t *occ_get,
532  				       void *occ_get_priv)
533  {
534  	devl_lock(devlink);
535  	devl_resource_occ_get_register(devlink, resource_id,
536  				       occ_get, occ_get_priv);
537  	devl_unlock(devlink);
538  }
539  EXPORT_SYMBOL_GPL(devlink_resource_occ_get_register);
540  
541  /**
542   * devl_resource_occ_get_unregister - unregister occupancy getter
543   *
544   * @devlink: devlink
545   * @resource_id: resource id
546   */
devl_resource_occ_get_unregister(struct devlink * devlink,u64 resource_id)547  void devl_resource_occ_get_unregister(struct devlink *devlink,
548  				      u64 resource_id)
549  {
550  	struct devlink_resource *resource;
551  
552  	lockdep_assert_held(&devlink->lock);
553  
554  	resource = devlink_resource_find(devlink, NULL, resource_id);
555  	if (WARN_ON(!resource))
556  		return;
557  	WARN_ON(!resource->occ_get);
558  
559  	resource->occ_get = NULL;
560  	resource->occ_get_priv = NULL;
561  }
562  EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
563  
564  /**
565   *	devlink_resource_occ_get_unregister - unregister occupancy getter
566   *
567   *	@devlink: devlink
568   *	@resource_id: resource id
569   *
570   *	Context: Takes and release devlink->lock <mutex>.
571   */
devlink_resource_occ_get_unregister(struct devlink * devlink,u64 resource_id)572  void devlink_resource_occ_get_unregister(struct devlink *devlink,
573  					 u64 resource_id)
574  {
575  	devl_lock(devlink);
576  	devl_resource_occ_get_unregister(devlink, resource_id);
577  	devl_unlock(devlink);
578  }
579  EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);
580