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 (devlink_nl_put_u64(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT,
 291			       reporter->error_count))
 292		goto reporter_nest_cancel;
 293	if (devlink_nl_put_u64(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT,
 294			       reporter->recovery_count))
 295		goto reporter_nest_cancel;
 296	if (reporter->ops->recover &&
 297	    devlink_nl_put_u64(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
 298			       reporter->graceful_period))
 299		goto reporter_nest_cancel;
 300	if (reporter->ops->recover &&
 301	    nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
 302		       reporter->auto_recover))
 303		goto reporter_nest_cancel;
 304	if (reporter->dump_fmsg &&
 305	    devlink_nl_put_u64(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,
 306			       jiffies_to_msecs(reporter->dump_ts)))
 307		goto reporter_nest_cancel;
 308	if (reporter->dump_fmsg &&
 309	    devlink_nl_put_u64(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS,
 310			       reporter->dump_real_ts))
 311		goto reporter_nest_cancel;
 312	if (reporter->ops->dump &&
 313	    nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP,
 314		       reporter->auto_dump))
 315		goto reporter_nest_cancel;
 316
 317	nla_nest_end(msg, reporter_attr);
 318	genlmsg_end(msg, hdr);
 319	return 0;
 320
 321reporter_nest_cancel:
 322	nla_nest_cancel(msg, reporter_attr);
 323genlmsg_cancel:
 324	genlmsg_cancel(msg, hdr);
 325	return -EMSGSIZE;
 326}
 327
 328static struct devlink_health_reporter *
 329devlink_health_reporter_get_from_attrs(struct devlink *devlink,
 330				       struct nlattr **attrs)
 331{
 332	struct devlink_port *devlink_port;
 333	char *reporter_name;
 334
 335	if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
 336		return NULL;
 337
 338	reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
 339	devlink_port = devlink_port_get_from_attrs(devlink, attrs);
 340	if (IS_ERR(devlink_port))
 341		return devlink_health_reporter_find_by_name(devlink,
 342							    reporter_name);
 343	else
 344		return devlink_port_health_reporter_find_by_name(devlink_port,
 345								 reporter_name);
 346}
 347
 348static struct devlink_health_reporter *
 349devlink_health_reporter_get_from_info(struct devlink *devlink,
 350				      struct genl_info *info)
 351{
 352	return devlink_health_reporter_get_from_attrs(devlink, info->attrs);
 353}
 354
 355int devlink_nl_health_reporter_get_doit(struct sk_buff *skb,
 356					struct genl_info *info)
 357{
 358	struct devlink *devlink = info->user_ptr[0];
 359	struct devlink_health_reporter *reporter;
 360	struct sk_buff *msg;
 361	int err;
 362
 363	reporter = devlink_health_reporter_get_from_info(devlink, info);
 364	if (!reporter)
 365		return -EINVAL;
 366
 367	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 368	if (!msg)
 369		return -ENOMEM;
 370
 371	err = devlink_nl_health_reporter_fill(msg, reporter,
 372					      DEVLINK_CMD_HEALTH_REPORTER_GET,
 373					      info->snd_portid, info->snd_seq,
 374					      0);
 375	if (err) {
 376		nlmsg_free(msg);
 377		return err;
 378	}
 379
 380	return genlmsg_reply(msg, info);
 381}
 382
 383static int devlink_nl_health_reporter_get_dump_one(struct sk_buff *msg,
 384						   struct devlink *devlink,
 385						   struct netlink_callback *cb,
 386						   int flags)
 387{
 388	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
 389	const struct genl_info *info = genl_info_dump(cb);
 390	struct devlink_health_reporter *reporter;
 391	unsigned long port_index_end = ULONG_MAX;
 392	struct nlattr **attrs = info->attrs;
 393	unsigned long port_index_start = 0;
 394	struct devlink_port *port;
 395	unsigned long port_index;
 396	int idx = 0;
 397	int err;
 398
 399	if (attrs && attrs[DEVLINK_ATTR_PORT_INDEX]) {
 400		port_index_start = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
 401		port_index_end = port_index_start;
 402		flags |= NLM_F_DUMP_FILTERED;
 403		goto per_port_dump;
 404	}
 405
 406	list_for_each_entry(reporter, &devlink->reporter_list, list) {
 407		if (idx < state->idx) {
 408			idx++;
 409			continue;
 410		}
 411		err = devlink_nl_health_reporter_fill(msg, reporter,
 412						      DEVLINK_CMD_HEALTH_REPORTER_GET,
 413						      NETLINK_CB(cb->skb).portid,
 414						      cb->nlh->nlmsg_seq,
 415						      flags);
 416		if (err) {
 417			state->idx = idx;
 418			return err;
 419		}
 420		idx++;
 421	}
 422per_port_dump:
 423	xa_for_each_range(&devlink->ports, port_index, port,
 424			  port_index_start, port_index_end) {
 425		list_for_each_entry(reporter, &port->reporter_list, list) {
 426			if (idx < state->idx) {
 427				idx++;
 428				continue;
 429			}
 430			err = devlink_nl_health_reporter_fill(msg, reporter,
 431							      DEVLINK_CMD_HEALTH_REPORTER_GET,
 432							      NETLINK_CB(cb->skb).portid,
 433							      cb->nlh->nlmsg_seq,
 434							      flags);
 435			if (err) {
 436				state->idx = idx;
 437				return err;
 438			}
 439			idx++;
 440		}
 441	}
 442
 443	return 0;
 444}
 445
 446int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
 447					  struct netlink_callback *cb)
 448{
 449	return devlink_nl_dumpit(skb, cb,
 450				 devlink_nl_health_reporter_get_dump_one);
 451}
 452
 453int devlink_nl_health_reporter_set_doit(struct sk_buff *skb,
 454					struct genl_info *info)
 455{
 456	struct devlink *devlink = info->user_ptr[0];
 457	struct devlink_health_reporter *reporter;
 458
 459	reporter = devlink_health_reporter_get_from_info(devlink, info);
 460	if (!reporter)
 461		return -EINVAL;
 462
 463	if (!reporter->ops->recover &&
 464	    (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
 465	     info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
 466		return -EOPNOTSUPP;
 467
 468	if (!reporter->ops->dump &&
 469	    info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
 470		return -EOPNOTSUPP;
 471
 472	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
 473		reporter->graceful_period =
 474			nla_get_u64(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]);
 475
 476	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
 477		reporter->auto_recover =
 478			nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]);
 479
 480	if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
 481		reporter->auto_dump =
 482		nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]);
 483
 484	return 0;
 485}
 486
 487static void devlink_recover_notify(struct devlink_health_reporter *reporter,
 488				   enum devlink_command cmd)
 489{
 490	struct devlink *devlink = reporter->devlink;
 491	struct devlink_obj_desc desc;
 492	struct sk_buff *msg;
 493	int err;
 494
 495	WARN_ON(cmd != DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
 496	ASSERT_DEVLINK_REGISTERED(devlink);
 497
 498	if (!devlink_nl_notify_need(devlink))
 499		return;
 500
 501	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 502	if (!msg)
 503		return;
 504
 505	err = devlink_nl_health_reporter_fill(msg, reporter, cmd, 0, 0, 0);
 506	if (err) {
 507		nlmsg_free(msg);
 508		return;
 509	}
 510
 511	devlink_nl_obj_desc_init(&desc, devlink);
 512	if (reporter->devlink_port)
 513		devlink_nl_obj_desc_port_set(&desc, reporter->devlink_port);
 514	devlink_nl_notify_send_desc(devlink, msg, &desc);
 515}
 516
 517void
 518devlink_health_reporter_recovery_done(struct devlink_health_reporter *reporter)
 519{
 520	reporter->recovery_count++;
 521	reporter->last_recovery_ts = jiffies;
 522}
 523EXPORT_SYMBOL_GPL(devlink_health_reporter_recovery_done);
 524
 525static int
 526devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
 527				void *priv_ctx, struct netlink_ext_ack *extack)
 528{
 529	int err;
 530
 531	if (reporter->health_state == DEVLINK_HEALTH_REPORTER_STATE_HEALTHY)
 532		return 0;
 533
 534	if (!reporter->ops->recover)
 535		return -EOPNOTSUPP;
 536
 537	err = reporter->ops->recover(reporter, priv_ctx, extack);
 538	if (err)
 539		return err;
 540
 541	devlink_health_reporter_recovery_done(reporter);
 542	reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
 543	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
 544
 545	return 0;
 546}
 547
 548static void
 549devlink_health_dump_clear(struct devlink_health_reporter *reporter)
 550{
 551	if (!reporter->dump_fmsg)
 552		return;
 553	devlink_fmsg_free(reporter->dump_fmsg);
 554	reporter->dump_fmsg = NULL;
 555}
 556
 557static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
 558				  void *priv_ctx,
 559				  struct netlink_ext_ack *extack)
 560{
 561	int err;
 562
 563	if (!reporter->ops->dump)
 564		return 0;
 565
 566	if (reporter->dump_fmsg)
 567		return 0;
 568
 569	reporter->dump_fmsg = devlink_fmsg_alloc();
 570	if (!reporter->dump_fmsg)
 571		return -ENOMEM;
 572
 573	devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
 574
 575	err = reporter->ops->dump(reporter, reporter->dump_fmsg,
 576				  priv_ctx, extack);
 577	if (err)
 578		goto dump_err;
 579
 580	devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
 581	err = reporter->dump_fmsg->err;
 582	if (err)
 583		goto dump_err;
 584
 585	reporter->dump_ts = jiffies;
 586	reporter->dump_real_ts = ktime_get_real_ns();
 587
 588	return 0;
 589
 590dump_err:
 591	devlink_health_dump_clear(reporter);
 592	return err;
 593}
 594
 595int devlink_health_report(struct devlink_health_reporter *reporter,
 596			  const char *msg, void *priv_ctx)
 597{
 598	enum devlink_health_reporter_state prev_health_state;
 599	struct devlink *devlink = reporter->devlink;
 600	unsigned long recover_ts_threshold;
 601	int ret;
 602
 603	/* write a log message of the current error */
 604	WARN_ON(!msg);
 605	trace_devlink_health_report(devlink, reporter->ops->name, msg);
 606	reporter->error_count++;
 607	prev_health_state = reporter->health_state;
 608	reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
 609	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
 610
 611	/* abort if the previous error wasn't recovered */
 612	recover_ts_threshold = reporter->last_recovery_ts +
 613			       msecs_to_jiffies(reporter->graceful_period);
 614	if (reporter->auto_recover &&
 615	    (prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
 616	     (reporter->last_recovery_ts && reporter->recovery_count &&
 617	      time_is_after_jiffies(recover_ts_threshold)))) {
 618		trace_devlink_health_recover_aborted(devlink,
 619						     reporter->ops->name,
 620						     reporter->health_state,
 621						     jiffies -
 622						     reporter->last_recovery_ts);
 623		return -ECANCELED;
 624	}
 625
 626	if (reporter->auto_dump) {
 627		devl_lock(devlink);
 628		/* store current dump of current error, for later analysis */
 629		devlink_health_do_dump(reporter, priv_ctx, NULL);
 630		devl_unlock(devlink);
 631	}
 632
 633	if (!reporter->auto_recover)
 634		return 0;
 635
 636	devl_lock(devlink);
 637	ret = devlink_health_reporter_recover(reporter, priv_ctx, NULL);
 638	devl_unlock(devlink);
 639
 640	return ret;
 641}
 642EXPORT_SYMBOL_GPL(devlink_health_report);
 643
 644void
 645devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
 646				     enum devlink_health_reporter_state state)
 647{
 648	if (WARN_ON(state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY &&
 649		    state != DEVLINK_HEALTH_REPORTER_STATE_ERROR))
 650		return;
 651
 652	if (reporter->health_state == state)
 653		return;
 654
 655	reporter->health_state = state;
 656	trace_devlink_health_reporter_state_update(reporter->devlink,
 657						   reporter->ops->name, state);
 658	devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
 659}
 660EXPORT_SYMBOL_GPL(devlink_health_reporter_state_update);
 661
 662int devlink_nl_health_reporter_recover_doit(struct sk_buff *skb,
 663					    struct genl_info *info)
 664{
 665	struct devlink *devlink = info->user_ptr[0];
 666	struct devlink_health_reporter *reporter;
 667
 668	reporter = devlink_health_reporter_get_from_info(devlink, info);
 669	if (!reporter)
 670		return -EINVAL;
 671
 672	return devlink_health_reporter_recover(reporter, NULL, info->extack);
 673}
 674
 675static void devlink_fmsg_err_if_binary(struct devlink_fmsg *fmsg)
 676{
 677	if (!fmsg->err && fmsg->putting_binary)
 678		fmsg->err = -EINVAL;
 679}
 680
 681static void devlink_fmsg_nest_common(struct devlink_fmsg *fmsg, int attrtype)
 682{
 683	struct devlink_fmsg_item *item;
 684
 685	if (fmsg->err)
 686		return;
 687
 688	item = kzalloc(sizeof(*item), GFP_KERNEL);
 689	if (!item) {
 690		fmsg->err = -ENOMEM;
 691		return;
 692	}
 693
 694	item->attrtype = attrtype;
 695	list_add_tail(&item->list, &fmsg->item_list);
 696}
 697
 698void devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
 699{
 700	devlink_fmsg_err_if_binary(fmsg);
 701	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
 702}
 703EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
 704
 705static void devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
 706{
 707	devlink_fmsg_err_if_binary(fmsg);
 708	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
 709}
 710
 711void devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
 712{
 713	devlink_fmsg_nest_end(fmsg);
 714}
 715EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
 716
 717#define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
 718
 719static void devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
 720{
 721	struct devlink_fmsg_item *item;
 722
 723	devlink_fmsg_err_if_binary(fmsg);
 724	if (fmsg->err)
 725		return;
 726
 727	if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE) {
 728		fmsg->err = -EMSGSIZE;
 729		return;
 730	}
 731
 732	item = kzalloc(sizeof(*item) + strlen(name) + 1, GFP_KERNEL);
 733	if (!item) {
 734		fmsg->err = -ENOMEM;
 735		return;
 736	}
 737
 738	item->nla_type = NLA_NUL_STRING;
 739	item->len = strlen(name) + 1;
 740	item->attrtype = DEVLINK_ATTR_FMSG_OBJ_NAME;
 741	memcpy(&item->value, name, item->len);
 742	list_add_tail(&item->list, &fmsg->item_list);
 743}
 744
 745void devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
 746{
 747	devlink_fmsg_err_if_binary(fmsg);
 748	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START);
 749	devlink_fmsg_put_name(fmsg, name);
 750}
 751EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start);
 752
 753void devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
 754{
 755	devlink_fmsg_nest_end(fmsg);
 756}
 757EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end);
 758
 759void devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
 760				      const char *name)
 761{
 762	devlink_fmsg_pair_nest_start(fmsg, name);
 763	devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_ARR_NEST_START);
 764}
 765EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_start);
 766
 767void devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
 768{
 769	devlink_fmsg_nest_end(fmsg);
 770	devlink_fmsg_nest_end(fmsg);
 771}
 772EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
 773
 774void devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg,
 775					 const char *name)
 776{
 777	devlink_fmsg_arr_pair_nest_start(fmsg, name);
 778	fmsg->putting_binary = true;
 779}
 780EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start);
 781
 782void devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg)
 783{
 784	if (fmsg->err)
 785		return;
 786
 787	if (!fmsg->putting_binary)
 788		fmsg->err = -EINVAL;
 789
 790	fmsg->putting_binary = false;
 791	devlink_fmsg_arr_pair_nest_end(fmsg);
 792}
 793EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end);
 794
 795static void devlink_fmsg_put_value(struct devlink_fmsg *fmsg,
 796				   const void *value, u16 value_len,
 797				   u8 value_nla_type)
 798{
 799	struct devlink_fmsg_item *item;
 800
 801	if (fmsg->err)
 802		return;
 803
 804	if (value_len > DEVLINK_FMSG_MAX_SIZE) {
 805		fmsg->err = -EMSGSIZE;
 806		return;
 807	}
 808
 809	item = kzalloc(sizeof(*item) + value_len, GFP_KERNEL);
 810	if (!item) {
 811		fmsg->err = -ENOMEM;
 812		return;
 813	}
 814
 815	item->nla_type = value_nla_type;
 816	item->len = value_len;
 817	item->attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
 818	memcpy(&item->value, value, item->len);
 819	list_add_tail(&item->list, &fmsg->item_list);
 820}
 821
 822static void devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
 823{
 824	devlink_fmsg_err_if_binary(fmsg);
 825	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
 826}
 827
 828static void devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
 829{
 830	devlink_fmsg_err_if_binary(fmsg);
 831	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
 832}
 833
 834void devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
 835{
 836	devlink_fmsg_err_if_binary(fmsg);
 837	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
 838}
 839EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
 840
 841static void devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
 842{
 843	devlink_fmsg_err_if_binary(fmsg);
 844	devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
 845}
 846
 847void devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
 848{
 849	devlink_fmsg_err_if_binary(fmsg);
 850	devlink_fmsg_put_value(fmsg, value, strlen(value) + 1, NLA_NUL_STRING);
 851}
 852EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
 853
 854void devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
 855			     u16 value_len)
 856{
 857	if (!fmsg->err && !fmsg->putting_binary)
 858		fmsg->err = -EINVAL;
 859
 860	devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
 861}
 862EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
 863
 864void devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
 865				bool value)
 866{
 867	devlink_fmsg_pair_nest_start(fmsg, name);
 868	devlink_fmsg_bool_put(fmsg, value);
 869	devlink_fmsg_pair_nest_end(fmsg);
 870}
 871EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
 872
 873void devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
 874			      u8 value)
 875{
 876	devlink_fmsg_pair_nest_start(fmsg, name);
 877	devlink_fmsg_u8_put(fmsg, value);
 878	devlink_fmsg_pair_nest_end(fmsg);
 879}
 880EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
 881
 882void devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
 883			       u32 value)
 884{
 885	devlink_fmsg_pair_nest_start(fmsg, name);
 886	devlink_fmsg_u32_put(fmsg, value);
 887	devlink_fmsg_pair_nest_end(fmsg);
 888}
 889EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
 890
 891void devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
 892			       u64 value)
 893{
 894	devlink_fmsg_pair_nest_start(fmsg, name);
 895	devlink_fmsg_u64_put(fmsg, value);
 896	devlink_fmsg_pair_nest_end(fmsg);
 897}
 898EXPORT_SYMBOL_GPL(devlink_fmsg_u64_pair_put);
 899
 900void devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
 901				  const char *value)
 902{
 903	devlink_fmsg_pair_nest_start(fmsg, name);
 904	devlink_fmsg_string_put(fmsg, value);
 905	devlink_fmsg_pair_nest_end(fmsg);
 906}
 907EXPORT_SYMBOL_GPL(devlink_fmsg_string_pair_put);
 908
 909void devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
 910				  const void *value, u32 value_len)
 911{
 912	u32 data_size;
 913	u32 offset;
 914
 915	devlink_fmsg_binary_pair_nest_start(fmsg, name);
 916
 917	for (offset = 0; offset < value_len; offset += data_size) {
 918		data_size = value_len - offset;
 919		if (data_size > DEVLINK_FMSG_MAX_SIZE)
 920			data_size = DEVLINK_FMSG_MAX_SIZE;
 921
 922		devlink_fmsg_binary_put(fmsg, value + offset, data_size);
 923	}
 924
 925	devlink_fmsg_binary_pair_nest_end(fmsg);
 926	fmsg->putting_binary = false;
 927}
 928EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
 929
 930static int
 931devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
 932{
 933	switch (msg->nla_type) {
 934	case NLA_FLAG:
 935	case NLA_U8:
 936	case NLA_U32:
 937	case NLA_U64:
 938	case NLA_NUL_STRING:
 939	case NLA_BINARY:
 940		return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
 941				  msg->nla_type);
 942	default:
 943		return -EINVAL;
 944	}
 945}
 946
 947static int
 948devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
 949{
 950	int attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
 951	u8 tmp;
 952
 953	switch (msg->nla_type) {
 954	case NLA_FLAG:
 955		/* Always provide flag data, regardless of its value */
 956		tmp = *(bool *)msg->value;
 957
 958		return nla_put_u8(skb, attrtype, tmp);
 959	case NLA_U8:
 960		return nla_put_u8(skb, attrtype, *(u8 *)msg->value);
 961	case NLA_U32:
 962		return nla_put_u32(skb, attrtype, *(u32 *)msg->value);
 963	case NLA_U64:
 964		return devlink_nl_put_u64(skb, attrtype, *(u64 *)msg->value);
 965	case NLA_NUL_STRING:
 966		return nla_put_string(skb, attrtype, (char *)&msg->value);
 967	case NLA_BINARY:
 968		return nla_put(skb, attrtype, msg->len, (void *)&msg->value);
 969	default:
 970		return -EINVAL;
 971	}
 972}
 973
 974static int
 975devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
 976			 int *start)
 977{
 978	struct devlink_fmsg_item *item;
 979	struct nlattr *fmsg_nlattr;
 980	int err = 0;
 981	int i = 0;
 982
 983	fmsg_nlattr = nla_nest_start_noflag(skb, DEVLINK_ATTR_FMSG);
 984	if (!fmsg_nlattr)
 985		return -EMSGSIZE;
 986
 987	list_for_each_entry(item, &fmsg->item_list, list) {
 988		if (i < *start) {
 989			i++;
 990			continue;
 991		}
 992
 993		switch (item->attrtype) {
 994		case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
 995		case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
 996		case DEVLINK_ATTR_FMSG_ARR_NEST_START:
 997		case DEVLINK_ATTR_FMSG_NEST_END:
 998			err = nla_put_flag(skb, item->attrtype);
 999			break;
1000		case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
1001			err = devlink_fmsg_item_fill_type(item, skb);
1002			if (err)
1003				break;
1004			err = devlink_fmsg_item_fill_data(item, skb);
1005			break;
1006		case DEVLINK_ATTR_FMSG_OBJ_NAME:
1007			err = nla_put_string(skb, item->attrtype,
1008					     (char *)&item->value);
1009			break;
1010		default:
1011			err = -EINVAL;
1012			break;
1013		}
1014		if (!err)
1015			*start = ++i;
1016		else
1017			break;
1018	}
1019
1020	nla_nest_end(skb, fmsg_nlattr);
1021	return err;
1022}
1023
1024static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
1025			    struct genl_info *info,
1026			    enum devlink_command cmd, int flags)
1027{
1028	struct nlmsghdr *nlh;
1029	struct sk_buff *skb;
1030	bool last = false;
1031	int index = 0;
1032	void *hdr;
1033	int err;
1034
1035	if (fmsg->err)
1036		return fmsg->err;
1037
1038	while (!last) {
1039		int tmp_index = index;
1040
1041		skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1042		if (!skb)
1043			return -ENOMEM;
1044
1045		hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
1046				  &devlink_nl_family, flags | NLM_F_MULTI, cmd);
1047		if (!hdr) {
1048			err = -EMSGSIZE;
1049			goto nla_put_failure;
1050		}
1051
1052		err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1053		if (!err)
1054			last = true;
1055		else if (err != -EMSGSIZE || tmp_index == index)
1056			goto nla_put_failure;
1057
1058		genlmsg_end(skb, hdr);
1059		err = genlmsg_reply(skb, info);
1060		if (err)
1061			return err;
1062	}
1063
1064	skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1065	if (!skb)
1066		return -ENOMEM;
1067	nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
1068			NLMSG_DONE, 0, flags | NLM_F_MULTI);
1069	if (!nlh) {
1070		err = -EMSGSIZE;
1071		goto nla_put_failure;
1072	}
1073
1074	return genlmsg_reply(skb, info);
1075
1076nla_put_failure:
1077	nlmsg_free(skb);
1078	return err;
1079}
1080
1081static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
1082			       struct netlink_callback *cb,
1083			       enum devlink_command cmd)
1084{
1085	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1086	int index = state->idx;
1087	int tmp_index = index;
1088	void *hdr;
1089	int err;
1090
1091	if (fmsg->err)
1092		return fmsg->err;
1093
1094	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
1095			  &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
1096	if (!hdr) {
1097		err = -EMSGSIZE;
1098		goto nla_put_failure;
1099	}
1100
1101	err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
1102	if ((err && err != -EMSGSIZE) || tmp_index == index)
1103		goto nla_put_failure;
1104
1105	state->idx = index;
1106	genlmsg_end(skb, hdr);
1107	return skb->len;
1108
1109nla_put_failure:
1110	genlmsg_cancel(skb, hdr);
1111	return err;
1112}
1113
1114int devlink_nl_health_reporter_diagnose_doit(struct sk_buff *skb,
1115					     struct genl_info *info)
1116{
1117	struct devlink *devlink = info->user_ptr[0];
1118	struct devlink_health_reporter *reporter;
1119	struct devlink_fmsg *fmsg;
1120	int err;
1121
1122	reporter = devlink_health_reporter_get_from_info(devlink, info);
1123	if (!reporter)
1124		return -EINVAL;
1125
1126	if (!reporter->ops->diagnose)
1127		return -EOPNOTSUPP;
1128
1129	fmsg = devlink_fmsg_alloc();
1130	if (!fmsg)
1131		return -ENOMEM;
1132
1133	devlink_fmsg_obj_nest_start(fmsg);
1134
1135	err = reporter->ops->diagnose(reporter, fmsg, info->extack);
1136	if (err)
1137		goto out;
1138
1139	devlink_fmsg_obj_nest_end(fmsg);
1140
1141	err = devlink_fmsg_snd(fmsg, info,
1142			       DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
1143
1144out:
1145	devlink_fmsg_free(fmsg);
1146	return err;
1147}
1148
1149static struct devlink_health_reporter *
1150devlink_health_reporter_get_from_cb_lock(struct netlink_callback *cb)
1151{
1152	const struct genl_info *info = genl_info_dump(cb);
1153	struct devlink_health_reporter *reporter;
1154	struct nlattr **attrs = info->attrs;
1155	struct devlink *devlink;
1156
1157	devlink = devlink_get_from_attrs_lock(sock_net(cb->skb->sk), attrs,
1158					      false);
1159	if (IS_ERR(devlink))
1160		return NULL;
1161
1162	reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
1163	if (!reporter) {
1164		devl_unlock(devlink);
1165		devlink_put(devlink);
1166	}
1167	return reporter;
1168}
1169
1170int devlink_nl_health_reporter_dump_get_dumpit(struct sk_buff *skb,
1171					       struct netlink_callback *cb)
1172{
1173	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
1174	struct devlink_health_reporter *reporter;
1175	struct devlink *devlink;
1176	int err;
1177
1178	reporter = devlink_health_reporter_get_from_cb_lock(cb);
1179	if (!reporter)
1180		return -EINVAL;
1181
1182	devlink = reporter->devlink;
1183	if (!reporter->ops->dump) {
1184		devl_unlock(devlink);
1185		devlink_put(devlink);
1186		return -EOPNOTSUPP;
1187	}
1188
1189	if (!state->idx) {
1190		err = devlink_health_do_dump(reporter, NULL, cb->extack);
1191		if (err)
1192			goto unlock;
1193		state->dump_ts = reporter->dump_ts;
1194	}
1195	if (!reporter->dump_fmsg || state->dump_ts != reporter->dump_ts) {
1196		NL_SET_ERR_MSG(cb->extack, "Dump trampled, please retry");
1197		err = -EAGAIN;
1198		goto unlock;
1199	}
1200
1201	err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb,
1202				  DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
1203unlock:
1204	devl_unlock(devlink);
1205	devlink_put(devlink);
1206	return err;
1207}
1208
1209int devlink_nl_health_reporter_dump_clear_doit(struct sk_buff *skb,
1210					       struct genl_info *info)
1211{
1212	struct devlink *devlink = info->user_ptr[0];
1213	struct devlink_health_reporter *reporter;
1214
1215	reporter = devlink_health_reporter_get_from_info(devlink, info);
1216	if (!reporter)
1217		return -EINVAL;
1218
1219	if (!reporter->ops->dump)
1220		return -EOPNOTSUPP;
1221
1222	devlink_health_dump_clear(reporter);
1223	return 0;
1224}
1225
1226int devlink_nl_health_reporter_test_doit(struct sk_buff *skb,
1227					 struct genl_info *info)
1228{
1229	struct devlink *devlink = info->user_ptr[0];
1230	struct devlink_health_reporter *reporter;
1231
1232	reporter = devlink_health_reporter_get_from_info(devlink, info);
1233	if (!reporter)
1234		return -EINVAL;
1235
1236	if (!reporter->ops->test)
1237		return -EOPNOTSUPP;
1238
1239	return reporter->ops->test(reporter, info->extack);
1240}