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