Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
   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 <net/genetlink.h>
   8#include <net/sock.h>
   9#include <trace/events/devlink.h>
  10#include "devl_internal.h"
  11
  12struct devlink_fmsg_item {
  13	struct list_head list;
  14	int attrtype;
  15	u8 nla_type;
  16	u16 len;
  17	int value[];
  18};
  19
  20struct devlink_fmsg {
  21	struct list_head item_list;
  22	int err; /* first error encountered on some devlink_fmsg_XXX() call */
  23	bool putting_binary; /* This flag forces enclosing of binary data
  24			      * in an array brackets. It forces using
  25			      * of designated API:
  26			      * devlink_fmsg_binary_pair_nest_start()
  27			      * devlink_fmsg_binary_pair_nest_end()
  28			      */
  29};
  30
  31static struct devlink_fmsg *devlink_fmsg_alloc(void)
  32{
  33	struct devlink_fmsg *fmsg;
  34
  35	fmsg = kzalloc(sizeof(*fmsg), GFP_KERNEL);
  36	if (!fmsg)
  37		return NULL;
  38
  39	INIT_LIST_HEAD(&fmsg->item_list);
  40
  41	return fmsg;
  42}
  43
  44static void devlink_fmsg_free(struct devlink_fmsg *fmsg)
  45{
  46	struct devlink_fmsg_item *item, *tmp;
  47
  48	list_for_each_entry_safe(item, tmp, &fmsg->item_list, list) {
  49		list_del(&item->list);
  50		kfree(item);
  51	}
  52	kfree(fmsg);
  53}
  54
  55struct devlink_health_reporter {
  56	struct list_head list;
  57	void *priv;
  58	const struct devlink_health_reporter_ops *ops;
  59	struct devlink *devlink;
  60	struct devlink_port *devlink_port;
  61	struct devlink_fmsg *dump_fmsg;
  62	u64 graceful_period;
  63	bool auto_recover;
  64	bool auto_dump;
  65	u8 health_state;
  66	u64 dump_ts;
  67	u64 dump_real_ts;
  68	u64 error_count;
  69	u64 recovery_count;
  70	u64 last_recovery_ts;
  71};
  72
  73void *
  74devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
  75{
  76	return reporter->priv;
  77}
  78EXPORT_SYMBOL_GPL(devlink_health_reporter_priv);
  79
  80static struct devlink_health_reporter *
  81__devlink_health_reporter_find_by_name(struct list_head *reporter_list,
  82				       const char *reporter_name)
  83{
  84	struct devlink_health_reporter *reporter;
  85
  86	list_for_each_entry(reporter, reporter_list, list)
  87		if (!strcmp(reporter->ops->name, reporter_name))
  88			return reporter;
  89	return NULL;
  90}
  91
  92static struct devlink_health_reporter *
  93devlink_health_reporter_find_by_name(struct devlink *devlink,
  94				     const char *reporter_name)
  95{
  96	return __devlink_health_reporter_find_by_name(&devlink->reporter_list,
  97						      reporter_name);
  98}
  99
 100static struct devlink_health_reporter *
 101devlink_port_health_reporter_find_by_name(struct devlink_port *devlink_port,
 102					  const char *reporter_name)
 103{
 104	return __devlink_health_reporter_find_by_name(&devlink_port->reporter_list,
 105						      reporter_name);
 106}
 107
 108static struct devlink_health_reporter *
 109__devlink_health_reporter_create(struct devlink *devlink,
 110				 const struct devlink_health_reporter_ops *ops,
 111				 u64 graceful_period, void *priv)
 112{
 113	struct devlink_health_reporter *reporter;
 114
 115	if (WARN_ON(graceful_period && !ops->recover))
 116		return ERR_PTR(-EINVAL);
 117
 118	reporter = kzalloc(sizeof(*reporter), GFP_KERNEL);
 119	if (!reporter)
 120		return ERR_PTR(-ENOMEM);
 121
 122	reporter->priv = priv;
 123	reporter->ops = ops;
 124	reporter->devlink = devlink;
 125	reporter->graceful_period = graceful_period;
 126	reporter->auto_recover = !!ops->recover;
 127	reporter->auto_dump = !!ops->dump;
 128	return reporter;
 129}
 130
 131/**
 132 * devl_port_health_reporter_create() - create devlink health reporter for
 133 *                                      specified port instance
 134 *
 135 * @port: devlink_port to which health reports will relate
 136 * @ops: devlink health reporter ops
 137 * @graceful_period: min time (in msec) between recovery attempts
 138 * @priv: driver priv pointer
 139 */
 140struct devlink_health_reporter *
 141devl_port_health_reporter_create(struct devlink_port *port,
 142				 const struct devlink_health_reporter_ops *ops,
 143				 u64 graceful_period, void *priv)
 144{
 145	struct devlink_health_reporter *reporter;
 146
 147	devl_assert_locked(port->devlink);
 148
 149	if (__devlink_health_reporter_find_by_name(&port->reporter_list,
 150						   ops->name))
 151		return ERR_PTR(-EEXIST);
 152
 153	reporter = __devlink_health_reporter_create(port->devlink, ops,
 154						    graceful_period, priv);
 155	if (IS_ERR(reporter))
 156		return reporter;
 157
 158	reporter->devlink_port = port;
 159	list_add_tail(&reporter->list, &port->reporter_list);
 160	return reporter;
 161}
 162EXPORT_SYMBOL_GPL(devl_port_health_reporter_create);
 163
 164struct devlink_health_reporter *
 165devlink_port_health_reporter_create(struct devlink_port *port,
 166				    const struct devlink_health_reporter_ops *ops,
 167				    u64 graceful_period, void *priv)
 168{
 169	struct devlink_health_reporter *reporter;
 170	struct devlink *devlink = port->devlink;
 171
 172	devl_lock(devlink);
 173	reporter = devl_port_health_reporter_create(port, ops,
 174						    graceful_period, priv);
 175	devl_unlock(devlink);
 176	return reporter;
 177}
 178EXPORT_SYMBOL_GPL(devlink_port_health_reporter_create);
 179
 180/**
 181 * devl_health_reporter_create - create devlink health reporter
 182 *
 183 * @devlink: devlink instance which the health reports will relate
 184 * @ops: devlink health reporter ops
 185 * @graceful_period: min time (in msec) between recovery attempts
 186 * @priv: driver priv pointer
 187 */
 188struct devlink_health_reporter *
 189devl_health_reporter_create(struct devlink *devlink,
 190			    const struct devlink_health_reporter_ops *ops,
 191			    u64 graceful_period, void *priv)
 192{
 193	struct devlink_health_reporter *reporter;
 194
 195	devl_assert_locked(devlink);
 196
 197	if (devlink_health_reporter_find_by_name(devlink, ops->name))
 198		return ERR_PTR(-EEXIST);
 199
 200	reporter = __devlink_health_reporter_create(devlink, ops,
 201						    graceful_period, priv);
 202	if (IS_ERR(reporter))
 203		return reporter;
 204
 205	list_add_tail(&reporter->list, &devlink->reporter_list);
 206	return reporter;
 207}
 208EXPORT_SYMBOL_GPL(devl_health_reporter_create);
 209
 210struct devlink_health_reporter *
 211devlink_health_reporter_create(struct devlink *devlink,
 212			       const struct devlink_health_reporter_ops *ops,
 213			       u64 graceful_period, void *priv)
 214{
 215	struct devlink_health_reporter *reporter;
 216
 217	devl_lock(devlink);
 218	reporter = devl_health_reporter_create(devlink, ops,
 219					       graceful_period, priv);
 220	devl_unlock(devlink);
 221	return reporter;
 222}
 223EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
 224
 225static void
 226devlink_health_reporter_free(struct devlink_health_reporter *reporter)
 227{
 228	if (reporter->dump_fmsg)
 229		devlink_fmsg_free(reporter->dump_fmsg);
 230	kfree(reporter);
 231}
 232
 233/**
 234 * devl_health_reporter_destroy() - destroy devlink health reporter
 235 *
 236 * @reporter: devlink health reporter to destroy
 237 */
 238void
 239devl_health_reporter_destroy(struct devlink_health_reporter *reporter)
 240{
 241	devl_assert_locked(reporter->devlink);
 242
 243	list_del(&reporter->list);
 244	devlink_health_reporter_free(reporter);
 245}
 246EXPORT_SYMBOL_GPL(devl_health_reporter_destroy);
 247
 248void
 249devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
 250{
 251	struct devlink *devlink = reporter->devlink;
 252
 253	devl_lock(devlink);
 254	devl_health_reporter_destroy(reporter);
 255	devl_unlock(devlink);
 256}
 257EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
 258
 259static int
 260devlink_nl_health_reporter_fill(struct sk_buff *msg,
 261				struct devlink_health_reporter *reporter,
 262				enum devlink_command cmd, u32 portid,
 263				u32 seq, int flags)
 264{
 265	struct devlink *devlink = reporter->devlink;
 266	struct nlattr *reporter_attr;
 267	void *hdr;
 268
 269	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
 270	if (!hdr)
 271		return -EMSGSIZE;
 272
 273	if (devlink_nl_put_handle(msg, devlink))
 274		goto genlmsg_cancel;
 275
 276	if (reporter->devlink_port) {
 277		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, reporter->devlink_port->index))
 278			goto genlmsg_cancel;
 279	}
 280	reporter_attr = nla_nest_start_noflag(msg,
 281					      DEVLINK_ATTR_HEALTH_REPORTER);
 282	if (!reporter_attr)
 283		goto genlmsg_cancel;
 284	if (nla_put_string(msg, DEVLINK_ATTR_HEALTH_REPORTER_NAME,
 285			   reporter->ops->name))
 286		goto reporter_nest_cancel;
 287	if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_STATE,
 288		       reporter->health_state))
 289		goto reporter_nest_cancel;
 290	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT,
 291			      reporter->error_count, DEVLINK_ATTR_PAD))
 292		goto reporter_nest_cancel;
 293	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT,
 294			      reporter->recovery_count, DEVLINK_ATTR_PAD))
 295		goto reporter_nest_cancel;
 296	if (reporter->ops->recover &&
 297	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
 298			      reporter->graceful_period,
 299			      DEVLINK_ATTR_PAD))
 300		goto reporter_nest_cancel;
 301	if (reporter->ops->recover &&
 302	    nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
 303		       reporter->auto_recover))
 304		goto reporter_nest_cancel;
 305	if (reporter->dump_fmsg &&
 306	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,
 307			      jiffies_to_msecs(reporter->dump_ts),
 308			      DEVLINK_ATTR_PAD))
 309		goto reporter_nest_cancel;
 310	if (reporter->dump_fmsg &&
 311	    nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS,
 312			      reporter->dump_real_ts, DEVLINK_ATTR_PAD))
 313		goto reporter_nest_cancel;
 314	if (reporter->ops->dump &&
 315	    nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP,
 316		       reporter->auto_dump))
 317		goto reporter_nest_cancel;
 318
 319	nla_nest_end(msg, reporter_attr);
 320	genlmsg_end(msg, hdr);
 321	return 0;
 322
 323reporter_nest_cancel:
 324	nla_nest_cancel(msg, reporter_attr);
 325genlmsg_cancel:
 326	genlmsg_cancel(msg, hdr);
 327	return -EMSGSIZE;
 328}
 329
 330static struct devlink_health_reporter *
 331devlink_health_reporter_get_from_attrs(struct devlink *devlink,
 332				       struct nlattr **attrs)
 333{
 334	struct devlink_port *devlink_port;
 335	char *reporter_name;
 336
 337	if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
 338		return NULL;
 339
 340	reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
 341	devlink_port = devlink_port_get_from_attrs(devlink, attrs);
 342	if (IS_ERR(devlink_port))
 343		return devlink_health_reporter_find_by_name(devlink,
 344							    reporter_name);
 345	else
 346		return devlink_port_health_reporter_find_by_name(devlink_port,
 347								 reporter_name);
 348}
 349
 350static struct devlink_health_reporter *
 351devlink_health_reporter_get_from_info(struct devlink *devlink,
 352				      struct genl_info *info)
 353{
 354	return devlink_health_reporter_get_from_attrs(devlink, info->attrs);
 355}
 356
 357int devlink_nl_health_reporter_get_doit(struct sk_buff *skb,
 358					struct genl_info *info)
 359{
 360	struct devlink *devlink = info->user_ptr[0];
 361	struct devlink_health_reporter *reporter;
 362	struct sk_buff *msg;
 363	int err;
 364
 365	reporter = devlink_health_reporter_get_from_info(devlink, info);
 366	if (!reporter)
 367		return -EINVAL;
 368
 369	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 370	if (!msg)
 371		return -ENOMEM;
 372
 373	err = devlink_nl_health_reporter_fill(msg, reporter,
 374					      DEVLINK_CMD_HEALTH_REPORTER_GET,
 375					      info->snd_portid, info->snd_seq,
 376					      0);
 377	if (err) {
 378		nlmsg_free(msg);
 379		return err;
 380	}
 381
 382	return genlmsg_reply(msg, info);
 383}
 384
 385static int devlink_nl_health_reporter_get_dump_one(struct sk_buff *msg,
 386						   struct devlink *devlink,
 387						   struct netlink_callback *cb,
 388						   int flags)
 389{
 390	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
 391	const struct genl_info *info = genl_info_dump(cb);
 392	struct devlink_health_reporter *reporter;
 393	unsigned long port_index_end = ULONG_MAX;
 394	struct nlattr **attrs = info->attrs;
 395	unsigned long port_index_start = 0;
 396	struct devlink_port *port;
 397	unsigned long port_index;
 398	int idx = 0;
 399	int err;
 400
 401	if (attrs && attrs[DEVLINK_ATTR_PORT_INDEX]) {
 402		port_index_start = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
 403		port_index_end = port_index_start;
 404		flags |= NLM_F_DUMP_FILTERED;
 405		goto per_port_dump;
 406	}
 407
 408	list_for_each_entry(reporter, &devlink->reporter_list, list) {
 409		if (idx < state->idx) {
 410			idx++;
 411			continue;
 412		}
 413		err = devlink_nl_health_reporter_fill(msg, reporter,
 414						      DEVLINK_CMD_HEALTH_REPORTER_GET,
 415						      NETLINK_CB(cb->skb).portid,
 416						      cb->nlh->nlmsg_seq,
 417						      flags);
 418		if (err) {
 419			state->idx = idx;
 420			return err;
 421		}
 422		idx++;
 423	}
 424per_port_dump:
 425	xa_for_each_range(&devlink->ports, port_index, port,
 426			  port_index_start, port_index_end) {
 427		list_for_each_entry(reporter, &port->reporter_list, list) {
 428			if (idx < state->idx) {
 429				idx++;
 430				continue;
 431			}
 432			err = devlink_nl_health_reporter_fill(msg, reporter,
 433							      DEVLINK_CMD_HEALTH_REPORTER_GET,
 434							      NETLINK_CB(cb->skb).portid,
 435							      cb->nlh->nlmsg_seq,
 436							      flags);
 437			if (err) {
 438				state->idx = idx;
 439				return err;
 440			}
 441			idx++;
 442		}
 443	}
 444
 445	return 0;
 446}
 447
 448int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
 449					  struct netlink_callback *cb)
 450{
 451	return devlink_nl_dumpit(skb, cb,
 452				 devlink_nl_health_reporter_get_dump_one);
 453}
 454
 455int devlink_nl_health_reporter_set_doit(struct sk_buff *skb,
 456					struct genl_info *info)
 457{
 458	struct devlink *devlink = info->user_ptr[0];
 459	struct devlink_health_reporter *reporter;
 460
 461	reporter = devlink_health_reporter_get_from_info(devlink, info);
 462	if (!reporter)
 463		return -EINVAL;
 464
 465	if (!reporter->ops->recover &&
 466	    (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
 467	     info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
 468		return -EOPNOTSUPP;
 469
 470	if (!reporter->ops->dump &&
 471	    info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
 472		return -EOPNOTSUPP;
 473
 474	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
 475		reporter->graceful_period =
 476			nla_get_u64(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]);
 477
 478	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
 479		reporter->auto_recover =
 480			nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]);
 481
 482	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
 483		reporter->auto_dump =
 484		nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]);
 485
 486	return 0;
 487}
 488
 489static void devlink_recover_notify(struct devlink_health_reporter *reporter,
 490				   enum devlink_command cmd)
 491{
 492	struct devlink *devlink = reporter->devlink;
 493	struct devlink_obj_desc desc;
 494	struct sk_buff *msg;
 495	int err;
 496
 497	WARN_ON(cmd != DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
 498	ASSERT_DEVLINK_REGISTERED(devlink);
 499
 500	if (!devlink_nl_notify_need(devlink))
 501		return;
 502
 503	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 504	if (!msg)
 505		return;
 506
 507	err = devlink_nl_health_reporter_fill(msg, reporter, cmd, 0, 0, 0);
 508	if (err) {
 509		nlmsg_free(msg);
 510		return;
 511	}
 512
 513	devlink_nl_obj_desc_init(&desc, devlink);
 514	if (reporter->devlink_port)
 515		devlink_nl_obj_desc_port_set(&desc, reporter->devlink_port);
 516	devlink_nl_notify_send_desc(devlink, msg, &desc);
 517}
 518
 519void
 520devlink_health_reporter_recovery_done(struct devlink_health_reporter *reporter)
 521{
 522	reporter->recovery_count++;
 523	reporter->last_recovery_ts = jiffies;
 524}
 525EXPORT_SYMBOL_GPL(devlink_health_reporter_recovery_done);
 526
 527static int
 528devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
 529				void *priv_ctx, struct netlink_ext_ack *extack)
 530{
 531	int err;
 532
 533	if (reporter->health_state == DEVLINK_HEALTH_REPORTER_STATE_HEALTHY)
 534		return 0;
 535
 536	if (!reporter->ops->recover)
 537		return -EOPNOTSUPP;
 538
 539	err = reporter->ops->recover(reporter, priv_ctx, extack);
 540	if (err)
 541		return err;
 542
 543	devlink_health_reporter_recovery_done(reporter);
 544	reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
 545	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
 546
 547	return 0;
 548}
 549
 550static void
 551devlink_health_dump_clear(struct devlink_health_reporter *reporter)
 552{
 553	if (!reporter->dump_fmsg)
 554		return;
 555	devlink_fmsg_free(reporter->dump_fmsg);
 556	reporter->dump_fmsg = NULL;
 557}
 558
 559static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
 560				  void *priv_ctx,
 561				  struct netlink_ext_ack *extack)
 562{
 563	int err;
 564
 565	if (!reporter->ops->dump)
 566		return 0;
 567
 568	if (reporter->dump_fmsg)
 569		return 0;
 570
 571	reporter->dump_fmsg = devlink_fmsg_alloc();
 572	if (!reporter->dump_fmsg)
 573		return -ENOMEM;
 574
 575	devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
 576
 577	err = reporter->ops->dump(reporter, reporter->dump_fmsg,
 578				  priv_ctx, extack);
 579	if (err)
 580		goto dump_err;
 581
 582	devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
 583	err = reporter->dump_fmsg->err;
 584	if (err)
 585		goto dump_err;
 586
 587	reporter->dump_ts = jiffies;
 588	reporter->dump_real_ts = ktime_get_real_ns();
 589
 590	return 0;
 591
 592dump_err:
 593	devlink_health_dump_clear(reporter);
 594	return err;
 595}
 596
 597int devlink_health_report(struct devlink_health_reporter *reporter,
 598			  const char *msg, void *priv_ctx)
 599{
 600	enum devlink_health_reporter_state prev_health_state;
 601	struct devlink *devlink = reporter->devlink;
 602	unsigned long recover_ts_threshold;
 603	int ret;
 604
 605	/* write a log message of the current error */
 606	WARN_ON(!msg);
 607	trace_devlink_health_report(devlink, reporter->ops->name, msg);
 608	reporter->error_count++;
 609	prev_health_state = reporter->health_state;
 610	reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
 611	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
 612
 613	/* abort if the previous error wasn't recovered */
 614	recover_ts_threshold = reporter->last_recovery_ts +
 615			       msecs_to_jiffies(reporter->graceful_period);
 616	if (reporter->auto_recover &&
 617	    (prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
 618	     (reporter->last_recovery_ts && reporter->recovery_count &&
 619	      time_is_after_jiffies(recover_ts_threshold)))) {
 620		trace_devlink_health_recover_aborted(devlink,
 621						     reporter->ops->name,
 622						     reporter->health_state,
 623						     jiffies -
 624						     reporter->last_recovery_ts);
 625		return -ECANCELED;
 626	}
 627
 628	if (reporter->auto_dump) {
 629		devl_lock(devlink);
 630		/* store current dump of current error, for later analysis */
 631		devlink_health_do_dump(reporter, priv_ctx, NULL);
 632		devl_unlock(devlink);
 633	}
 634
 635	if (!reporter->auto_recover)
 636		return 0;
 637
 638	devl_lock(devlink);
 639	ret = devlink_health_reporter_recover(reporter, priv_ctx, NULL);
 640	devl_unlock(devlink);
 641
 642	return ret;
 643}
 644EXPORT_SYMBOL_GPL(devlink_health_report);
 645
 646void
 647devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
 648				     enum devlink_health_reporter_state state)
 649{
 650	if (WARN_ON(state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY &&
 651		    state != DEVLINK_HEALTH_REPORTER_STATE_ERROR))
 652		return;
 653
 654	if (reporter->health_state == state)
 655		return;
 656
 657	reporter->health_state = state;
 658	trace_devlink_health_reporter_state_update(reporter->devlink,
 659						   reporter->ops->name, state);
 660	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
 661}
 662EXPORT_SYMBOL_GPL(devlink_health_reporter_state_update);
 663
 664int devlink_nl_health_reporter_recover_doit(struct sk_buff *skb,
 665					    struct genl_info *info)
 666{
 667	struct devlink *devlink = info->user_ptr[0];
 668	struct devlink_health_reporter *reporter;
 669
 670	reporter = devlink_health_reporter_get_from_info(devlink, info);
 671	if (!reporter)
 672		return -EINVAL;
 673
 674	return devlink_health_reporter_recover(reporter, NULL, info->extack);
 675}
 676
 677static void devlink_fmsg_err_if_binary(struct devlink_fmsg *fmsg)
 678{
 679	if (!fmsg->err && fmsg->putting_binary)
 680		fmsg->err = -EINVAL;
 681}
 682
 683static void devlink_fmsg_nest_common(struct devlink_fmsg *fmsg, int attrtype)
 684{
 685	struct devlink_fmsg_item *item;
 686
 687	if (fmsg->err)
 688		return;
 689
 690	item = kzalloc(sizeof(*item), GFP_KERNEL);
 691	if (!item) {
 692		fmsg->err = -ENOMEM;
 693		return;
 694	}
 695
 696	item->attrtype = attrtype;
 697	list_add_tail(&item->list, &fmsg->item_list);
 698}
 699
 700void devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
 701{
 702	devlink_fmsg_err_if_binary(fmsg);
 703	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
 704}
 705EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
 706
 707static void devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
 708{
 709	devlink_fmsg_err_if_binary(fmsg);
 710	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
 711}
 712
 713void devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
 714{
 715	devlink_fmsg_nest_end(fmsg);
 716}
 717EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
 718
 719#define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
 720
 721static void devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
 722{
 723	struct devlink_fmsg_item *item;
 724
 725	devlink_fmsg_err_if_binary(fmsg);
 726	if (fmsg->err)
 727		return;
 728
 729	if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE) {
 730		fmsg->err = -EMSGSIZE;
 731		return;
 732	}
 733
 734	item = kzalloc(sizeof(*item) + strlen(name) + 1, GFP_KERNEL);
 735	if (!item) {
 736		fmsg->err = -ENOMEM;
 737		return;
 738	}
 739
 740	item->nla_type = NLA_NUL_STRING;
 741	item->len = strlen(name) + 1;
 742	item->attrtype = DEVLINK_ATTR_FMSG_OBJ_NAME;
 743	memcpy(&item->value, name, item->len);
 744	list_add_tail(&item->list, &fmsg->item_list);
 745}
 746
 747void devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
 748{
 749	devlink_fmsg_err_if_binary(fmsg);
 750	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START);
 751	devlink_fmsg_put_name(fmsg, name);
 752}
 753EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start);
 754
 755void devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
 756{
 757	devlink_fmsg_nest_end(fmsg);
 758}
 759EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end);
 760
 761void devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
 762				      const char *name)
 763{
 764	devlink_fmsg_pair_nest_start(fmsg, name);
 765	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_ARR_NEST_START);
 766}
 767EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_start);
 768
 769void devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
 770{
 771	devlink_fmsg_nest_end(fmsg);
 772	devlink_fmsg_nest_end(fmsg);
 773}
 774EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
 775
 776void devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg,
 777					 const char *name)
 778{
 779	devlink_fmsg_arr_pair_nest_start(fmsg, name);
 780	fmsg->putting_binary = true;
 781}
 782EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start);
 783
 784void devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg)
 785{
 786	if (fmsg->err)
 787		return;
 788
 789	if (!fmsg->putting_binary)
 790		fmsg->err = -EINVAL;
 791
 792	fmsg->putting_binary = false;
 793	devlink_fmsg_arr_pair_nest_end(fmsg);
 794}
 795EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end);
 796
 797static void devlink_fmsg_put_value(struct devlink_fmsg *fmsg,
 798				   const void *value, u16 value_len,
 799				   u8 value_nla_type)
 800{
 801	struct devlink_fmsg_item *item;
 802
 803	if (fmsg->err)
 804		return;
 805
 806	if (value_len > DEVLINK_FMSG_MAX_SIZE) {
 807		fmsg->err = -EMSGSIZE;
 808		return;
 809	}
 810
 811	item = kzalloc(sizeof(*item) + value_len, GFP_KERNEL);
 812	if (!item) {
 813		fmsg->err = -ENOMEM;
 814		return;
 815	}
 816
 817	item->nla_type = value_nla_type;
 818	item->len = value_len;
 819	item->attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
 820	memcpy(&item->value, value, item->len);
 821	list_add_tail(&item->list, &fmsg->item_list);
 822}
 823
 824static void devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
 825{
 826	devlink_fmsg_err_if_binary(fmsg);
 827	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
 828}
 829
 830static void devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
 831{
 832	devlink_fmsg_err_if_binary(fmsg);
 833	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
 834}
 835
 836void devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
 837{
 838	devlink_fmsg_err_if_binary(fmsg);
 839	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
 840}
 841EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
 842
 843static void devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
 844{
 845	devlink_fmsg_err_if_binary(fmsg);
 846	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
 847}
 848
 849void devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
 850{
 851	devlink_fmsg_err_if_binary(fmsg);
 852	devlink_fmsg_put_value(fmsg, value, strlen(value) + 1, NLA_NUL_STRING);
 853}
 854EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
 855
 856void devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
 857			     u16 value_len)
 858{
 859	if (!fmsg->err && !fmsg->putting_binary)
 860		fmsg->err = -EINVAL;
 861
 862	devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
 863}
 864EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
 865
 866void devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
 867				bool value)
 868{
 869	devlink_fmsg_pair_nest_start(fmsg, name);
 870	devlink_fmsg_bool_put(fmsg, value);
 871	devlink_fmsg_pair_nest_end(fmsg);
 872}
 873EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
 874
 875void devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
 876			      u8 value)
 877{
 878	devlink_fmsg_pair_nest_start(fmsg, name);
 879	devlink_fmsg_u8_put(fmsg, value);
 880	devlink_fmsg_pair_nest_end(fmsg);
 881}
 882EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
 883
 884void devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
 885			       u32 value)
 886{
 887	devlink_fmsg_pair_nest_start(fmsg, name);
 888	devlink_fmsg_u32_put(fmsg, value);
 889	devlink_fmsg_pair_nest_end(fmsg);
 890}
 891EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
 892
 893void devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
 894			       u64 value)
 895{
 896	devlink_fmsg_pair_nest_start(fmsg, name);
 897	devlink_fmsg_u64_put(fmsg, value);
 898	devlink_fmsg_pair_nest_end(fmsg);
 899}
 900EXPORT_SYMBOL_GPL(devlink_fmsg_u64_pair_put);
 901
 902void devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
 903				  const char *value)
 904{
 905	devlink_fmsg_pair_nest_start(fmsg, name);
 906	devlink_fmsg_string_put(fmsg, value);
 907	devlink_fmsg_pair_nest_end(fmsg);
 908}
 909EXPORT_SYMBOL_GPL(devlink_fmsg_string_pair_put);
 910
 911void devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
 912				  const void *value, u32 value_len)
 913{
 914	u32 data_size;
 915	u32 offset;
 916
 917	devlink_fmsg_binary_pair_nest_start(fmsg, name);
 918
 919	for (offset = 0; offset < value_len; offset += data_size) {
 920		data_size = value_len - offset;
 921		if (data_size > DEVLINK_FMSG_MAX_SIZE)
 922			data_size = DEVLINK_FMSG_MAX_SIZE;
 923
 924		devlink_fmsg_binary_put(fmsg, value + offset, data_size);
 925	}
 926
 927	devlink_fmsg_binary_pair_nest_end(fmsg);
 928	fmsg->putting_binary = false;
 929}
 930EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
 931
 932static int
 933devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
 934{
 935	switch (msg->nla_type) {
 936	case NLA_FLAG:
 937	case NLA_U8:
 938	case NLA_U32:
 939	case NLA_U64:
 940	case NLA_NUL_STRING:
 941	case NLA_BINARY:
 942		return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
 943				  msg->nla_type);
 944	default:
 945		return -EINVAL;
 946	}
 947}
 948
 949static int
 950devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
 951{
 952	int attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
 953	u8 tmp;
 954
 955	switch (msg->nla_type) {
 956	case NLA_FLAG:
 957		/* Always provide flag data, regardless of its value */
 958		tmp = *(bool *)msg->value;
 959
 960		return nla_put_u8(skb, attrtype, tmp);
 961	case NLA_U8:
 962		return nla_put_u8(skb, attrtype, *(u8 *)msg->value);
 963	case NLA_U32:
 964		return nla_put_u32(skb, attrtype, *(u32 *)msg->value);
 965	case NLA_U64:
 966		return nla_put_u64_64bit(skb, attrtype, *(u64 *)msg->value,
 967					 DEVLINK_ATTR_PAD);
 968	case NLA_NUL_STRING:
 969		return nla_put_string(skb, attrtype, (char *)&msg->value);
 970	case NLA_BINARY:
 971		return nla_put(skb, attrtype, msg->len, (void *)&msg->value);
 972	default:
 973		return -EINVAL;
 974	}
 975}
 976
 977static int
 978devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
 979			 int *start)
 980{
 981	struct devlink_fmsg_item *item;
 982	struct nlattr *fmsg_nlattr;
 983	int err = 0;
 984	int i = 0;
 985
 986	fmsg_nlattr = nla_nest_start_noflag(skb, DEVLINK_ATTR_FMSG);
 987	if (!fmsg_nlattr)
 988		return -EMSGSIZE;
 989
 990	list_for_each_entry(item, &fmsg->item_list, list) {
 991		if (i < *start) {
 992			i++;
 993			continue;
 994		}
 995
 996		switch (item->attrtype) {
 997		case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
 998		case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
 999		case DEVLINK_ATTR_FMSG_ARR_NEST_START:
1000		case DEVLINK_ATTR_FMSG_NEST_END:
1001			err = nla_put_flag(skb, item->attrtype);
1002			break;
1003		case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
1004			err = devlink_fmsg_item_fill_type(item, skb);
1005			if (err)
1006				break;
1007			err = devlink_fmsg_item_fill_data(item, skb);
1008			break;
1009		case DEVLINK_ATTR_FMSG_OBJ_NAME:
1010			err = nla_put_string(skb, item->attrtype,
1011					     (char *)&item->value);
1012			break;
1013		default:
1014			err = -EINVAL;
1015			break;
1016		}
1017		if (!err)
1018			*start = ++i;
1019		else
1020			break;
1021	}
1022
1023	nla_nest_end(skb, fmsg_nlattr);
1024	return err;
1025}
1026
1027static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
1028			    struct genl_info *info,
1029			    enum devlink_command cmd, int flags)
1030{
1031	struct nlmsghdr *nlh;
1032	struct sk_buff *skb;
1033	bool last = false;
1034	int index = 0;
1035	void *hdr;
1036	int err;
1037
1038	if (fmsg->err)
1039		return fmsg->err;
1040
1041	while (!last) {
1042		int tmp_index = index;
1043
1044		skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1045		if (!skb)
1046			return -ENOMEM;
1047
1048		hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
1049				  &devlink_nl_family, flags | NLM_F_MULTI, cmd);
1050		if (!hdr) {
1051			err = -EMSGSIZE;
1052			goto nla_put_failure;
1053		}
1054
1055		err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1056		if (!err)
1057			last = true;
1058		else if (err != -EMSGSIZE || tmp_index == index)
1059			goto nla_put_failure;
1060
1061		genlmsg_end(skb, hdr);
1062		err = genlmsg_reply(skb, info);
1063		if (err)
1064			return err;
1065	}
1066
1067	skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1068	if (!skb)
1069		return -ENOMEM;
1070	nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
1071			NLMSG_DONE, 0, flags | NLM_F_MULTI);
1072	if (!nlh) {
1073		err = -EMSGSIZE;
1074		goto nla_put_failure;
1075	}
1076
1077	return genlmsg_reply(skb, info);
1078
1079nla_put_failure:
1080	nlmsg_free(skb);
1081	return err;
1082}
1083
1084static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1085			       struct netlink_callback *cb,
1086			       enum devlink_command cmd)
1087{
1088	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1089	int index = state->idx;
1090	int tmp_index = index;
1091	void *hdr;
1092	int err;
1093
1094	if (fmsg->err)
1095		return fmsg->err;
1096
1097	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
1098			  &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
1099	if (!hdr) {
1100		err = -EMSGSIZE;
1101		goto nla_put_failure;
1102	}
1103
1104	err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1105	if ((err && err != -EMSGSIZE) || tmp_index == index)
1106		goto nla_put_failure;
1107
1108	state->idx = index;
1109	genlmsg_end(skb, hdr);
1110	return skb->len;
1111
1112nla_put_failure:
1113	genlmsg_cancel(skb, hdr);
1114	return err;
1115}
1116
1117int devlink_nl_health_reporter_diagnose_doit(struct sk_buff *skb,
1118					     struct genl_info *info)
1119{
1120	struct devlink *devlink = info->user_ptr[0];
1121	struct devlink_health_reporter *reporter;
1122	struct devlink_fmsg *fmsg;
1123	int err;
1124
1125	reporter = devlink_health_reporter_get_from_info(devlink, info);
1126	if (!reporter)
1127		return -EINVAL;
1128
1129	if (!reporter->ops->diagnose)
1130		return -EOPNOTSUPP;
1131
1132	fmsg = devlink_fmsg_alloc();
1133	if (!fmsg)
1134		return -ENOMEM;
1135
1136	devlink_fmsg_obj_nest_start(fmsg);
1137
1138	err = reporter->ops->diagnose(reporter, fmsg, info->extack);
1139	if (err)
1140		goto out;
1141
1142	devlink_fmsg_obj_nest_end(fmsg);
1143
1144	err = devlink_fmsg_snd(fmsg, info,
1145			       DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
1146
1147out:
1148	devlink_fmsg_free(fmsg);
1149	return err;
1150}
1151
1152static struct devlink_health_reporter *
1153devlink_health_reporter_get_from_cb_lock(struct netlink_callback *cb)
1154{
1155	const struct genl_info *info = genl_info_dump(cb);
1156	struct devlink_health_reporter *reporter;
1157	struct nlattr **attrs = info->attrs;
1158	struct devlink *devlink;
1159
1160	devlink = devlink_get_from_attrs_lock(sock_net(cb->skb->sk), attrs,
1161					      false);
1162	if (IS_ERR(devlink))
1163		return NULL;
1164
1165	reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
1166	if (!reporter) {
1167		devl_unlock(devlink);
1168		devlink_put(devlink);
1169	}
1170	return reporter;
1171}
1172
1173int devlink_nl_health_reporter_dump_get_dumpit(struct sk_buff *skb,
1174					       struct netlink_callback *cb)
1175{
1176	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1177	struct devlink_health_reporter *reporter;
1178	struct devlink *devlink;
1179	int err;
1180
1181	reporter = devlink_health_reporter_get_from_cb_lock(cb);
1182	if (!reporter)
1183		return -EINVAL;
1184
1185	devlink = reporter->devlink;
1186	if (!reporter->ops->dump) {
1187		devl_unlock(devlink);
1188		devlink_put(devlink);
1189		return -EOPNOTSUPP;
1190	}
1191
1192	if (!state->idx) {
1193		err = devlink_health_do_dump(reporter, NULL, cb->extack);
1194		if (err)
1195			goto unlock;
1196		state->dump_ts = reporter->dump_ts;
1197	}
1198	if (!reporter->dump_fmsg || state->dump_ts != reporter->dump_ts) {
1199		NL_SET_ERR_MSG(cb->extack, "Dump trampled, please retry");
1200		err = -EAGAIN;
1201		goto unlock;
1202	}
1203
1204	err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb,
1205				  DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
1206unlock:
1207	devl_unlock(devlink);
1208	devlink_put(devlink);
1209	return err;
1210}
1211
1212int devlink_nl_health_reporter_dump_clear_doit(struct sk_buff *skb,
1213					       struct genl_info *info)
1214{
1215	struct devlink *devlink = info->user_ptr[0];
1216	struct devlink_health_reporter *reporter;
1217
1218	reporter = devlink_health_reporter_get_from_info(devlink, info);
1219	if (!reporter)
1220		return -EINVAL;
1221
1222	if (!reporter->ops->dump)
1223		return -EOPNOTSUPP;
1224
1225	devlink_health_dump_clear(reporter);
1226	return 0;
1227}
1228
1229int devlink_nl_health_reporter_test_doit(struct sk_buff *skb,
1230					 struct genl_info *info)
1231{
1232	struct devlink *devlink = info->user_ptr[0];
1233	struct devlink_health_reporter *reporter;
1234
1235	reporter = devlink_health_reporter_get_from_info(devlink, info);
1236	if (!reporter)
1237		return -EINVAL;
1238
1239	if (!reporter->ops->test)
1240		return -EOPNOTSUPP;
1241
1242	return reporter->ops->test(reporter, info->extack);
1243}