18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * zfcp device driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Functions to handle diagnostics.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2018
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
118c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci#include <linux/kernfs.h>
148c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "zfcp_diag.h"
198c2ecf20Sopenharmony_ci#include "zfcp_ext.h"
208c2ecf20Sopenharmony_ci#include "zfcp_def.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(__zfcp_diag_publish_wait);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/**
258c2ecf20Sopenharmony_ci * zfcp_diag_adapter_setup() - Setup storage for adapter diagnostics.
268c2ecf20Sopenharmony_ci * @adapter: the adapter to setup diagnostics for.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * Creates the data-structures to store the diagnostics for an adapter. This
298c2ecf20Sopenharmony_ci * overwrites whatever was stored before at &zfcp_adapter->diagnostics!
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Return:
328c2ecf20Sopenharmony_ci * * 0	     - Everyting is OK
338c2ecf20Sopenharmony_ci * * -ENOMEM - Could not allocate all/parts of the data-structures;
348c2ecf20Sopenharmony_ci *	       &zfcp_adapter->diagnostics remains unchanged
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ciint zfcp_diag_adapter_setup(struct zfcp_adapter *const adapter)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct zfcp_diag_adapter *diag;
398c2ecf20Sopenharmony_ci	struct zfcp_diag_header *hdr;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	diag = kzalloc(sizeof(*diag), GFP_KERNEL);
428c2ecf20Sopenharmony_ci	if (diag == NULL)
438c2ecf20Sopenharmony_ci		return -ENOMEM;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	diag->max_age = (5 * 1000); /* default value: 5 s */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* setup header for port_data */
488c2ecf20Sopenharmony_ci	hdr = &diag->port_data.header;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	spin_lock_init(&hdr->access_lock);
518c2ecf20Sopenharmony_ci	hdr->buffer = &diag->port_data.data;
528c2ecf20Sopenharmony_ci	hdr->buffer_size = sizeof(diag->port_data.data);
538c2ecf20Sopenharmony_ci	/* set the timestamp so that the first test on age will always fail */
548c2ecf20Sopenharmony_ci	hdr->timestamp = jiffies - msecs_to_jiffies(diag->max_age);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* setup header for config_data */
578c2ecf20Sopenharmony_ci	hdr = &diag->config_data.header;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	spin_lock_init(&hdr->access_lock);
608c2ecf20Sopenharmony_ci	hdr->buffer = &diag->config_data.data;
618c2ecf20Sopenharmony_ci	hdr->buffer_size = sizeof(diag->config_data.data);
628c2ecf20Sopenharmony_ci	/* set the timestamp so that the first test on age will always fail */
638c2ecf20Sopenharmony_ci	hdr->timestamp = jiffies - msecs_to_jiffies(diag->max_age);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	adapter->diagnostics = diag;
668c2ecf20Sopenharmony_ci	return 0;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/**
708c2ecf20Sopenharmony_ci * zfcp_diag_adapter_free() - Frees all adapter diagnostics allocations.
718c2ecf20Sopenharmony_ci * @adapter: the adapter whose diagnostic structures should be freed.
728c2ecf20Sopenharmony_ci *
738c2ecf20Sopenharmony_ci * Frees all data-structures in the given adapter that store diagnostics
748c2ecf20Sopenharmony_ci * information. Can savely be called with partially setup diagnostics.
758c2ecf20Sopenharmony_ci */
768c2ecf20Sopenharmony_civoid zfcp_diag_adapter_free(struct zfcp_adapter *const adapter)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	kfree(adapter->diagnostics);
798c2ecf20Sopenharmony_ci	adapter->diagnostics = NULL;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/**
838c2ecf20Sopenharmony_ci * zfcp_diag_sysfs_setup() - Setup the sysfs-group for adapter-diagnostics.
848c2ecf20Sopenharmony_ci * @adapter: target adapter to which the group should be added.
858c2ecf20Sopenharmony_ci *
868c2ecf20Sopenharmony_ci * Return: 0 on success; Something else otherwise (see sysfs_create_group()).
878c2ecf20Sopenharmony_ci */
888c2ecf20Sopenharmony_ciint zfcp_diag_sysfs_setup(struct zfcp_adapter *const adapter)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	int rc = sysfs_create_group(&adapter->ccw_device->dev.kobj,
918c2ecf20Sopenharmony_ci				    &zfcp_sysfs_diag_attr_group);
928c2ecf20Sopenharmony_ci	if (rc == 0)
938c2ecf20Sopenharmony_ci		adapter->diagnostics->sysfs_established = 1;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return rc;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/**
998c2ecf20Sopenharmony_ci * zfcp_diag_sysfs_destroy() - Remove the sysfs-group for adapter-diagnostics.
1008c2ecf20Sopenharmony_ci * @adapter: target adapter from which the group should be removed.
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_civoid zfcp_diag_sysfs_destroy(struct zfcp_adapter *const adapter)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	if (adapter->diagnostics == NULL ||
1058c2ecf20Sopenharmony_ci	    !adapter->diagnostics->sysfs_established)
1068c2ecf20Sopenharmony_ci		return;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/*
1098c2ecf20Sopenharmony_ci	 * We need this state-handling so we can prevent warnings being printed
1108c2ecf20Sopenharmony_ci	 * on the kernel-console in case we have to abort a halfway done
1118c2ecf20Sopenharmony_ci	 * zfcp_adapter_enqueue(), in which the sysfs-group was not yet
1128c2ecf20Sopenharmony_ci	 * established. sysfs_remove_group() does this checking as well, but
1138c2ecf20Sopenharmony_ci	 * still prints a warning in case we try to remove a group that has not
1148c2ecf20Sopenharmony_ci	 * been established before
1158c2ecf20Sopenharmony_ci	 */
1168c2ecf20Sopenharmony_ci	adapter->diagnostics->sysfs_established = 0;
1178c2ecf20Sopenharmony_ci	sysfs_remove_group(&adapter->ccw_device->dev.kobj,
1188c2ecf20Sopenharmony_ci			   &zfcp_sysfs_diag_attr_group);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/**
1238c2ecf20Sopenharmony_ci * zfcp_diag_update_xdata() - Update a diagnostics buffer.
1248c2ecf20Sopenharmony_ci * @hdr: the meta data to update.
1258c2ecf20Sopenharmony_ci * @data: data to use for the update.
1268c2ecf20Sopenharmony_ci * @incomplete: flag stating whether the data in @data is incomplete.
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_civoid zfcp_diag_update_xdata(struct zfcp_diag_header *const hdr,
1298c2ecf20Sopenharmony_ci			    const void *const data, const bool incomplete)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	const unsigned long capture_timestamp = jiffies;
1328c2ecf20Sopenharmony_ci	unsigned long flags;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hdr->access_lock, flags);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* make sure we never go into the past with an update */
1378c2ecf20Sopenharmony_ci	if (!time_after_eq(capture_timestamp, hdr->timestamp))
1388c2ecf20Sopenharmony_ci		goto out;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	hdr->timestamp = capture_timestamp;
1418c2ecf20Sopenharmony_ci	hdr->incomplete = incomplete;
1428c2ecf20Sopenharmony_ci	memcpy(hdr->buffer, data, hdr->buffer_size);
1438c2ecf20Sopenharmony_ciout:
1448c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hdr->access_lock, flags);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/**
1488c2ecf20Sopenharmony_ci * zfcp_diag_update_port_data_buffer() - Implementation of
1498c2ecf20Sopenharmony_ci *					 &typedef zfcp_diag_update_buffer_func
1508c2ecf20Sopenharmony_ci *					 to collect and update Port Data.
1518c2ecf20Sopenharmony_ci * @adapter: Adapter to collect Port Data from.
1528c2ecf20Sopenharmony_ci *
1538c2ecf20Sopenharmony_ci * This call is SYNCHRONOUS ! It blocks till the respective command has
1548c2ecf20Sopenharmony_ci * finished completely, or has failed in some way.
1558c2ecf20Sopenharmony_ci *
1568c2ecf20Sopenharmony_ci * Return:
1578c2ecf20Sopenharmony_ci * * 0		- Successfully retrieved new Diagnostics and Updated the buffer;
1588c2ecf20Sopenharmony_ci *		  this also includes cases where data was retrieved, but
1598c2ecf20Sopenharmony_ci *		  incomplete; you'll have to check the flag ``incomplete``
1608c2ecf20Sopenharmony_ci *		  of &struct zfcp_diag_header.
1618c2ecf20Sopenharmony_ci * * see zfcp_fsf_exchange_port_data_sync() for possible error-codes (
1628c2ecf20Sopenharmony_ci *   excluding -EAGAIN)
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_ciint zfcp_diag_update_port_data_buffer(struct zfcp_adapter *const adapter)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	int rc;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	rc = zfcp_fsf_exchange_port_data_sync(adapter->qdio, NULL);
1698c2ecf20Sopenharmony_ci	if (rc == -EAGAIN)
1708c2ecf20Sopenharmony_ci		rc = 0; /* signaling incomplete via struct zfcp_diag_header */
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/* buffer-data was updated in zfcp_fsf_exchange_port_data_handler() */
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return rc;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/**
1788c2ecf20Sopenharmony_ci * zfcp_diag_update_config_data_buffer() - Implementation of
1798c2ecf20Sopenharmony_ci *					   &typedef zfcp_diag_update_buffer_func
1808c2ecf20Sopenharmony_ci *					   to collect and update Config Data.
1818c2ecf20Sopenharmony_ci * @adapter: Adapter to collect Config Data from.
1828c2ecf20Sopenharmony_ci *
1838c2ecf20Sopenharmony_ci * This call is SYNCHRONOUS ! It blocks till the respective command has
1848c2ecf20Sopenharmony_ci * finished completely, or has failed in some way.
1858c2ecf20Sopenharmony_ci *
1868c2ecf20Sopenharmony_ci * Return:
1878c2ecf20Sopenharmony_ci * * 0		- Successfully retrieved new Diagnostics and Updated the buffer;
1888c2ecf20Sopenharmony_ci *		  this also includes cases where data was retrieved, but
1898c2ecf20Sopenharmony_ci *		  incomplete; you'll have to check the flag ``incomplete``
1908c2ecf20Sopenharmony_ci *		  of &struct zfcp_diag_header.
1918c2ecf20Sopenharmony_ci * * see zfcp_fsf_exchange_config_data_sync() for possible error-codes (
1928c2ecf20Sopenharmony_ci *   excluding -EAGAIN)
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_ciint zfcp_diag_update_config_data_buffer(struct zfcp_adapter *const adapter)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	int rc;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	rc = zfcp_fsf_exchange_config_data_sync(adapter->qdio, NULL);
1998c2ecf20Sopenharmony_ci	if (rc == -EAGAIN)
2008c2ecf20Sopenharmony_ci		rc = 0; /* signaling incomplete via struct zfcp_diag_header */
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* buffer-data was updated in zfcp_fsf_exchange_config_data_handler() */
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return rc;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic int __zfcp_diag_update_buffer(struct zfcp_adapter *const adapter,
2088c2ecf20Sopenharmony_ci				     struct zfcp_diag_header *const hdr,
2098c2ecf20Sopenharmony_ci				     zfcp_diag_update_buffer_func buffer_update,
2108c2ecf20Sopenharmony_ci				     unsigned long *const flags)
2118c2ecf20Sopenharmony_ci	__must_hold(hdr->access_lock)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	int rc;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (hdr->updating == 1) {
2168c2ecf20Sopenharmony_ci		rc = wait_event_interruptible_lock_irq(__zfcp_diag_publish_wait,
2178c2ecf20Sopenharmony_ci						       hdr->updating == 0,
2188c2ecf20Sopenharmony_ci						       hdr->access_lock);
2198c2ecf20Sopenharmony_ci		rc = (rc == 0 ? -EAGAIN : -EINTR);
2208c2ecf20Sopenharmony_ci	} else {
2218c2ecf20Sopenharmony_ci		hdr->updating = 1;
2228c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&hdr->access_lock, *flags);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		/* unlocked, because update function sleeps */
2258c2ecf20Sopenharmony_ci		rc = buffer_update(adapter);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		spin_lock_irqsave(&hdr->access_lock, *flags);
2288c2ecf20Sopenharmony_ci		hdr->updating = 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		/*
2318c2ecf20Sopenharmony_ci		 * every thread waiting here went via an interruptible wait,
2328c2ecf20Sopenharmony_ci		 * so its fine to only wake those
2338c2ecf20Sopenharmony_ci		 */
2348c2ecf20Sopenharmony_ci		wake_up_interruptible_all(&__zfcp_diag_publish_wait);
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return rc;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic bool
2418c2ecf20Sopenharmony_ci__zfcp_diag_test_buffer_age_isfresh(const struct zfcp_diag_adapter *const diag,
2428c2ecf20Sopenharmony_ci				    const struct zfcp_diag_header *const hdr)
2438c2ecf20Sopenharmony_ci	__must_hold(hdr->access_lock)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	const unsigned long now = jiffies;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/*
2488c2ecf20Sopenharmony_ci	 * Should not happen (data is from the future).. if it does, still
2498c2ecf20Sopenharmony_ci	 * signal that it needs refresh
2508c2ecf20Sopenharmony_ci	 */
2518c2ecf20Sopenharmony_ci	if (!time_after_eq(now, hdr->timestamp))
2528c2ecf20Sopenharmony_ci		return false;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (jiffies_to_msecs(now - hdr->timestamp) >= diag->max_age)
2558c2ecf20Sopenharmony_ci		return false;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return true;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/**
2618c2ecf20Sopenharmony_ci * zfcp_diag_update_buffer_limited() - Collect diagnostics and update a
2628c2ecf20Sopenharmony_ci *				       diagnostics buffer rate limited.
2638c2ecf20Sopenharmony_ci * @adapter: Adapter to collect the diagnostics from.
2648c2ecf20Sopenharmony_ci * @hdr: buffer-header for which to update with the collected diagnostics.
2658c2ecf20Sopenharmony_ci * @buffer_update: Specific implementation for collecting and updating.
2668c2ecf20Sopenharmony_ci *
2678c2ecf20Sopenharmony_ci * This function will cause an update of the given @hdr by calling the also
2688c2ecf20Sopenharmony_ci * given @buffer_update function. If called by multiple sources at the same
2698c2ecf20Sopenharmony_ci * time, it will synchornize the update by only allowing one source to call
2708c2ecf20Sopenharmony_ci * @buffer_update and the others to wait for that source to complete instead
2718c2ecf20Sopenharmony_ci * (the wait is interruptible).
2728c2ecf20Sopenharmony_ci *
2738c2ecf20Sopenharmony_ci * Additionally this version is rate-limited and will only exit if either the
2748c2ecf20Sopenharmony_ci * buffer is fresh enough (within the limit) - it will do nothing if the buffer
2758c2ecf20Sopenharmony_ci * is fresh enough to begin with -, or if the source/thread that started this
2768c2ecf20Sopenharmony_ci * update is the one that made the update (to prevent endless loops).
2778c2ecf20Sopenharmony_ci *
2788c2ecf20Sopenharmony_ci * Return:
2798c2ecf20Sopenharmony_ci * * 0		- If the update was successfully published and/or the buffer is
2808c2ecf20Sopenharmony_ci *		  fresh enough
2818c2ecf20Sopenharmony_ci * * -EINTR	- If the thread went into the wait-state and was interrupted
2828c2ecf20Sopenharmony_ci * * whatever @buffer_update returns
2838c2ecf20Sopenharmony_ci */
2848c2ecf20Sopenharmony_ciint zfcp_diag_update_buffer_limited(struct zfcp_adapter *const adapter,
2858c2ecf20Sopenharmony_ci				    struct zfcp_diag_header *const hdr,
2868c2ecf20Sopenharmony_ci				    zfcp_diag_update_buffer_func buffer_update)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	unsigned long flags;
2898c2ecf20Sopenharmony_ci	int rc;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hdr->access_lock, flags);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	for (rc = 0;
2948c2ecf20Sopenharmony_ci	     !__zfcp_diag_test_buffer_age_isfresh(adapter->diagnostics, hdr);
2958c2ecf20Sopenharmony_ci	     rc = 0) {
2968c2ecf20Sopenharmony_ci		rc = __zfcp_diag_update_buffer(adapter, hdr, buffer_update,
2978c2ecf20Sopenharmony_ci					       &flags);
2988c2ecf20Sopenharmony_ci		if (rc != -EAGAIN)
2998c2ecf20Sopenharmony_ci			break;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hdr->access_lock, flags);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return rc;
3058c2ecf20Sopenharmony_ci}
306