File Index
Files and directories
Sign in
FILE

health.c

File preview with syntax highlighting.

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