xref: /linux/drivers/s390/scsi/zfcp_diag.c (revision 26fbb4c8c7c3ee9a4c3b4de555a8587b5a19154e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * zfcp device driver
4  *
5  * Functions to handle diagnostics.
6  *
7  * Copyright IBM Corp. 2018
8  */
9 
10 #include <linux/spinlock.h>
11 #include <linux/jiffies.h>
12 #include <linux/string.h>
13 #include <linux/kernfs.h>
14 #include <linux/sysfs.h>
15 #include <linux/errno.h>
16 #include <linux/slab.h>
17 
18 #include "zfcp_diag.h"
19 #include "zfcp_ext.h"
20 #include "zfcp_def.h"
21 
22 static DECLARE_WAIT_QUEUE_HEAD(__zfcp_diag_publish_wait);
23 
24 /**
25  * zfcp_diag_adapter_setup() - Setup storage for adapter diagnostics.
26  * @adapter: the adapter to setup diagnostics for.
27  *
28  * Creates the data-structures to store the diagnostics for an adapter. This
29  * overwrites whatever was stored before at &zfcp_adapter->diagnostics!
30  *
31  * Return:
32  * * 0	     - Everyting is OK
33  * * -ENOMEM - Could not allocate all/parts of the data-structures;
34  *	       &zfcp_adapter->diagnostics remains unchanged
35  */
36 int zfcp_diag_adapter_setup(struct zfcp_adapter *const adapter)
37 {
38 	struct zfcp_diag_adapter *diag;
39 	struct zfcp_diag_header *hdr;
40 
41 	diag = kzalloc(sizeof(*diag), GFP_KERNEL);
42 	if (diag == NULL)
43 		return -ENOMEM;
44 
45 	diag->max_age = (5 * 1000); /* default value: 5 s */
46 
47 	/* setup header for port_data */
48 	hdr = &diag->port_data.header;
49 
50 	spin_lock_init(&hdr->access_lock);
51 	hdr->buffer = &diag->port_data.data;
52 	hdr->buffer_size = sizeof(diag->port_data.data);
53 	/* set the timestamp so that the first test on age will always fail */
54 	hdr->timestamp = jiffies - msecs_to_jiffies(diag->max_age);
55 
56 	/* setup header for config_data */
57 	hdr = &diag->config_data.header;
58 
59 	spin_lock_init(&hdr->access_lock);
60 	hdr->buffer = &diag->config_data.data;
61 	hdr->buffer_size = sizeof(diag->config_data.data);
62 	/* set the timestamp so that the first test on age will always fail */
63 	hdr->timestamp = jiffies - msecs_to_jiffies(diag->max_age);
64 
65 	adapter->diagnostics = diag;
66 	return 0;
67 }
68 
69 /**
70  * zfcp_diag_adapter_free() - Frees all adapter diagnostics allocations.
71  * @adapter: the adapter whose diagnostic structures should be freed.
72  *
73  * Frees all data-structures in the given adapter that store diagnostics
74  * information. Can savely be called with partially setup diagnostics.
75  */
76 void zfcp_diag_adapter_free(struct zfcp_adapter *const adapter)
77 {
78 	kfree(adapter->diagnostics);
79 	adapter->diagnostics = NULL;
80 }
81 
82 /**
83  * zfcp_diag_sysfs_setup() - Setup the sysfs-group for adapter-diagnostics.
84  * @adapter: target adapter to which the group should be added.
85  *
86  * Return: 0 on success; Something else otherwise (see sysfs_create_group()).
87  */
88 int zfcp_diag_sysfs_setup(struct zfcp_adapter *const adapter)
89 {
90 	int rc = sysfs_create_group(&adapter->ccw_device->dev.kobj,
91 				    &zfcp_sysfs_diag_attr_group);
92 	if (rc == 0)
93 		adapter->diagnostics->sysfs_established = 1;
94 
95 	return rc;
96 }
97 
98 /**
99  * zfcp_diag_sysfs_destroy() - Remove the sysfs-group for adapter-diagnostics.
100  * @adapter: target adapter from which the group should be removed.
101  */
102 void zfcp_diag_sysfs_destroy(struct zfcp_adapter *const adapter)
103 {
104 	if (adapter->diagnostics == NULL ||
105 	    !adapter->diagnostics->sysfs_established)
106 		return;
107 
108 	/*
109 	 * We need this state-handling so we can prevent warnings being printed
110 	 * on the kernel-console in case we have to abort a halfway done
111 	 * zfcp_adapter_enqueue(), in which the sysfs-group was not yet
112 	 * established. sysfs_remove_group() does this checking as well, but
113 	 * still prints a warning in case we try to remove a group that has not
114 	 * been established before
115 	 */
116 	adapter->diagnostics->sysfs_established = 0;
117 	sysfs_remove_group(&adapter->ccw_device->dev.kobj,
118 			   &zfcp_sysfs_diag_attr_group);
119 }
120 
121 
122 /**
123  * zfcp_diag_update_xdata() - Update a diagnostics buffer.
124  * @hdr: the meta data to update.
125  * @data: data to use for the update.
126  * @incomplete: flag stating whether the data in @data is incomplete.
127  */
128 void zfcp_diag_update_xdata(struct zfcp_diag_header *const hdr,
129 			    const void *const data, const bool incomplete)
130 {
131 	const unsigned long capture_timestamp = jiffies;
132 	unsigned long flags;
133 
134 	spin_lock_irqsave(&hdr->access_lock, flags);
135 
136 	/* make sure we never go into the past with an update */
137 	if (!time_after_eq(capture_timestamp, hdr->timestamp))
138 		goto out;
139 
140 	hdr->timestamp = capture_timestamp;
141 	hdr->incomplete = incomplete;
142 	memcpy(hdr->buffer, data, hdr->buffer_size);
143 out:
144 	spin_unlock_irqrestore(&hdr->access_lock, flags);
145 }
146 
147 /**
148  * zfcp_diag_update_port_data_buffer() - Implementation of
149  *					 &typedef zfcp_diag_update_buffer_func
150  *					 to collect and update Port Data.
151  * @adapter: Adapter to collect Port Data from.
152  *
153  * This call is SYNCHRONOUS ! It blocks till the respective command has
154  * finished completely, or has failed in some way.
155  *
156  * Return:
157  * * 0		- Successfully retrieved new Diagnostics and Updated the buffer;
158  *		  this also includes cases where data was retrieved, but
159  *		  incomplete; you'll have to check the flag ``incomplete``
160  *		  of &struct zfcp_diag_header.
161  * * see zfcp_fsf_exchange_port_data_sync() for possible error-codes (
162  *   excluding -EAGAIN)
163  */
164 int zfcp_diag_update_port_data_buffer(struct zfcp_adapter *const adapter)
165 {
166 	int rc;
167 
168 	rc = zfcp_fsf_exchange_port_data_sync(adapter->qdio, NULL);
169 	if (rc == -EAGAIN)
170 		rc = 0; /* signaling incomplete via struct zfcp_diag_header */
171 
172 	/* buffer-data was updated in zfcp_fsf_exchange_port_data_handler() */
173 
174 	return rc;
175 }
176 
177 /**
178  * zfcp_diag_update_config_data_buffer() - Implementation of
179  *					   &typedef zfcp_diag_update_buffer_func
180  *					   to collect and update Config Data.
181  * @adapter: Adapter to collect Config Data from.
182  *
183  * This call is SYNCHRONOUS ! It blocks till the respective command has
184  * finished completely, or has failed in some way.
185  *
186  * Return:
187  * * 0		- Successfully retrieved new Diagnostics and Updated the buffer;
188  *		  this also includes cases where data was retrieved, but
189  *		  incomplete; you'll have to check the flag ``incomplete``
190  *		  of &struct zfcp_diag_header.
191  * * see zfcp_fsf_exchange_config_data_sync() for possible error-codes (
192  *   excluding -EAGAIN)
193  */
194 int zfcp_diag_update_config_data_buffer(struct zfcp_adapter *const adapter)
195 {
196 	int rc;
197 
198 	rc = zfcp_fsf_exchange_config_data_sync(adapter->qdio, NULL);
199 	if (rc == -EAGAIN)
200 		rc = 0; /* signaling incomplete via struct zfcp_diag_header */
201 
202 	/* buffer-data was updated in zfcp_fsf_exchange_config_data_handler() */
203 
204 	return rc;
205 }
206 
207 static int __zfcp_diag_update_buffer(struct zfcp_adapter *const adapter,
208 				     struct zfcp_diag_header *const hdr,
209 				     zfcp_diag_update_buffer_func buffer_update,
210 				     unsigned long *const flags)
211 	__must_hold(hdr->access_lock)
212 {
213 	int rc;
214 
215 	if (hdr->updating == 1) {
216 		rc = wait_event_interruptible_lock_irq(__zfcp_diag_publish_wait,
217 						       hdr->updating == 0,
218 						       hdr->access_lock);
219 		rc = (rc == 0 ? -EAGAIN : -EINTR);
220 	} else {
221 		hdr->updating = 1;
222 		spin_unlock_irqrestore(&hdr->access_lock, *flags);
223 
224 		/* unlocked, because update function sleeps */
225 		rc = buffer_update(adapter);
226 
227 		spin_lock_irqsave(&hdr->access_lock, *flags);
228 		hdr->updating = 0;
229 
230 		/*
231 		 * every thread waiting here went via an interruptible wait,
232 		 * so its fine to only wake those
233 		 */
234 		wake_up_interruptible_all(&__zfcp_diag_publish_wait);
235 	}
236 
237 	return rc;
238 }
239 
240 static bool
241 __zfcp_diag_test_buffer_age_isfresh(const struct zfcp_diag_adapter *const diag,
242 				    const struct zfcp_diag_header *const hdr)
243 	__must_hold(hdr->access_lock)
244 {
245 	const unsigned long now = jiffies;
246 
247 	/*
248 	 * Should not happen (data is from the future).. if it does, still
249 	 * signal that it needs refresh
250 	 */
251 	if (!time_after_eq(now, hdr->timestamp))
252 		return false;
253 
254 	if (jiffies_to_msecs(now - hdr->timestamp) >= diag->max_age)
255 		return false;
256 
257 	return true;
258 }
259 
260 /**
261  * zfcp_diag_update_buffer_limited() - Collect diagnostics and update a
262  *				       diagnostics buffer rate limited.
263  * @adapter: Adapter to collect the diagnostics from.
264  * @hdr: buffer-header for which to update with the collected diagnostics.
265  * @buffer_update: Specific implementation for collecting and updating.
266  *
267  * This function will cause an update of the given @hdr by calling the also
268  * given @buffer_update function. If called by multiple sources at the same
269  * time, it will synchornize the update by only allowing one source to call
270  * @buffer_update and the others to wait for that source to complete instead
271  * (the wait is interruptible).
272  *
273  * Additionally this version is rate-limited and will only exit if either the
274  * buffer is fresh enough (within the limit) - it will do nothing if the buffer
275  * is fresh enough to begin with -, or if the source/thread that started this
276  * update is the one that made the update (to prevent endless loops).
277  *
278  * Return:
279  * * 0		- If the update was successfully published and/or the buffer is
280  *		  fresh enough
281  * * -EINTR	- If the thread went into the wait-state and was interrupted
282  * * whatever @buffer_update returns
283  */
284 int zfcp_diag_update_buffer_limited(struct zfcp_adapter *const adapter,
285 				    struct zfcp_diag_header *const hdr,
286 				    zfcp_diag_update_buffer_func buffer_update)
287 {
288 	unsigned long flags;
289 	int rc;
290 
291 	spin_lock_irqsave(&hdr->access_lock, flags);
292 
293 	for (rc = 0;
294 	     !__zfcp_diag_test_buffer_age_isfresh(adapter->diagnostics, hdr);
295 	     rc = 0) {
296 		rc = __zfcp_diag_update_buffer(adapter, hdr, buffer_update,
297 					       &flags);
298 		if (rc != -EAGAIN)
299 			break;
300 	}
301 
302 	spin_unlock_irqrestore(&hdr->access_lock, flags);
303 
304 	return rc;
305 }
306