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 | */ |
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(size: 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(m: 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(m: 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 | */ |
74 | void zfcp_diag_adapter_free(struct zfcp_adapter *const adapter) |
75 | { |
76 | kfree(objp: 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 | */ |
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(lock: &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 | */ |
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 | */ |
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 | |
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(lock: &hdr->access_lock, flags: *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 |
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(j: 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 | */ |
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(diag: adapter->diagnostics, hdr); |
253 | rc = 0) { |
254 | rc = __zfcp_diag_update_buffer(adapter, hdr, buffer_update, |
255 | flags: &flags); |
256 | if (rc != -EAGAIN) |
257 | break; |
258 | } |
259 | |
260 | spin_unlock_irqrestore(lock: &hdr->access_lock, flags); |
261 | |
262 | return rc; |
263 | } |
264 | |