.. | .. |
---|
7 | 7 | */ |
---|
8 | 8 | |
---|
9 | 9 | #include <linux/spinlock.h> |
---|
| 10 | +#include <linux/mutex.h> |
---|
10 | 11 | #include <linux/slab.h> |
---|
11 | 12 | #include <asm/page.h> |
---|
12 | 13 | |
---|
.. | .. |
---|
17 | 18 | |
---|
18 | 19 | struct smcd_dev_list smcd_dev_list = { |
---|
19 | 20 | .list = LIST_HEAD_INIT(smcd_dev_list.list), |
---|
20 | | - .lock = __SPIN_LOCK_UNLOCKED(smcd_dev_list.lock) |
---|
| 21 | + .mutex = __MUTEX_INITIALIZER(smcd_dev_list.mutex) |
---|
21 | 22 | }; |
---|
22 | 23 | |
---|
23 | | -/* Test if an ISM communication is possible. */ |
---|
| 24 | +bool smc_ism_v2_capable; |
---|
| 25 | + |
---|
| 26 | +/* Test if an ISM communication is possible - same CPC */ |
---|
24 | 27 | int smc_ism_cantalk(u64 peer_gid, unsigned short vlan_id, struct smcd_dev *smcd) |
---|
25 | 28 | { |
---|
26 | 29 | return smcd->ops->query_remote_gid(smcd, peer_gid, vlan_id ? 1 : 0, |
---|
.. | .. |
---|
36 | 39 | pos->offset, data, len); |
---|
37 | 40 | |
---|
38 | 41 | return rc < 0 ? rc : 0; |
---|
| 42 | +} |
---|
| 43 | + |
---|
| 44 | +void smc_ism_get_system_eid(struct smcd_dev *smcd, u8 **eid) |
---|
| 45 | +{ |
---|
| 46 | + smcd->ops->get_system_eid(smcd, eid); |
---|
| 47 | +} |
---|
| 48 | + |
---|
| 49 | +u16 smc_ism_get_chid(struct smcd_dev *smcd) |
---|
| 50 | +{ |
---|
| 51 | + return smcd->ops->get_chid(smcd); |
---|
39 | 52 | } |
---|
40 | 53 | |
---|
41 | 54 | /* Set a connection using this DMBE. */ |
---|
.. | .. |
---|
146 | 159 | int smc_ism_unregister_dmb(struct smcd_dev *smcd, struct smc_buf_desc *dmb_desc) |
---|
147 | 160 | { |
---|
148 | 161 | struct smcd_dmb dmb; |
---|
| 162 | + int rc = 0; |
---|
| 163 | + |
---|
| 164 | + if (!dmb_desc->dma_addr) |
---|
| 165 | + return rc; |
---|
149 | 166 | |
---|
150 | 167 | memset(&dmb, 0, sizeof(dmb)); |
---|
151 | 168 | dmb.dmb_tok = dmb_desc->token; |
---|
.. | .. |
---|
153 | 170 | dmb.cpu_addr = dmb_desc->cpu_addr; |
---|
154 | 171 | dmb.dma_addr = dmb_desc->dma_addr; |
---|
155 | 172 | dmb.dmb_len = dmb_desc->len; |
---|
156 | | - return smcd->ops->unregister_dmb(smcd, &dmb); |
---|
| 173 | + rc = smcd->ops->unregister_dmb(smcd, &dmb); |
---|
| 174 | + if (!rc || rc == ISM_ERROR) { |
---|
| 175 | + dmb_desc->cpu_addr = NULL; |
---|
| 176 | + dmb_desc->dma_addr = 0; |
---|
| 177 | + } |
---|
| 178 | + |
---|
| 179 | + return rc; |
---|
157 | 180 | } |
---|
158 | 181 | |
---|
159 | 182 | int smc_ism_register_dmb(struct smc_link_group *lgr, int dmb_len, |
---|
.. | .. |
---|
187 | 210 | #define ISM_EVENT_REQUEST 0x0001 |
---|
188 | 211 | #define ISM_EVENT_RESPONSE 0x0002 |
---|
189 | 212 | #define ISM_EVENT_REQUEST_IR 0x00000001 |
---|
| 213 | +#define ISM_EVENT_CODE_SHUTDOWN 0x80 |
---|
190 | 214 | #define ISM_EVENT_CODE_TESTLINK 0x83 |
---|
| 215 | + |
---|
| 216 | +union smcd_sw_event_info { |
---|
| 217 | + u64 info; |
---|
| 218 | + struct { |
---|
| 219 | + u8 uid[SMC_LGR_ID_SIZE]; |
---|
| 220 | + unsigned short vlan_id; |
---|
| 221 | + u16 code; |
---|
| 222 | + }; |
---|
| 223 | +}; |
---|
191 | 224 | |
---|
192 | 225 | static void smcd_handle_sw_event(struct smc_ism_event_work *wrk) |
---|
193 | 226 | { |
---|
194 | | - union { |
---|
195 | | - u64 info; |
---|
196 | | - struct { |
---|
197 | | - u32 uid; |
---|
198 | | - unsigned short vlanid; |
---|
199 | | - u16 code; |
---|
200 | | - }; |
---|
201 | | - } ev_info; |
---|
| 227 | + union smcd_sw_event_info ev_info; |
---|
202 | 228 | |
---|
| 229 | + ev_info.info = wrk->event.info; |
---|
203 | 230 | switch (wrk->event.code) { |
---|
| 231 | + case ISM_EVENT_CODE_SHUTDOWN: /* Peer shut down DMBs */ |
---|
| 232 | + smc_smcd_terminate(wrk->smcd, wrk->event.tok, ev_info.vlan_id); |
---|
| 233 | + break; |
---|
204 | 234 | case ISM_EVENT_CODE_TESTLINK: /* Activity timer */ |
---|
205 | | - ev_info.info = wrk->event.info; |
---|
206 | 235 | if (ev_info.code == ISM_EVENT_REQUEST) { |
---|
207 | 236 | ev_info.code = ISM_EVENT_RESPONSE; |
---|
208 | 237 | wrk->smcd->ops->signal_event(wrk->smcd, |
---|
.. | .. |
---|
215 | 244 | } |
---|
216 | 245 | } |
---|
217 | 246 | |
---|
| 247 | +int smc_ism_signal_shutdown(struct smc_link_group *lgr) |
---|
| 248 | +{ |
---|
| 249 | + int rc; |
---|
| 250 | + union smcd_sw_event_info ev_info; |
---|
| 251 | + |
---|
| 252 | + if (lgr->peer_shutdown) |
---|
| 253 | + return 0; |
---|
| 254 | + |
---|
| 255 | + memcpy(ev_info.uid, lgr->id, SMC_LGR_ID_SIZE); |
---|
| 256 | + ev_info.vlan_id = lgr->vlan_id; |
---|
| 257 | + ev_info.code = ISM_EVENT_REQUEST; |
---|
| 258 | + rc = lgr->smcd->ops->signal_event(lgr->smcd, lgr->peer_gid, |
---|
| 259 | + ISM_EVENT_REQUEST_IR, |
---|
| 260 | + ISM_EVENT_CODE_SHUTDOWN, |
---|
| 261 | + ev_info.info); |
---|
| 262 | + return rc; |
---|
| 263 | +} |
---|
| 264 | + |
---|
218 | 265 | /* worker for SMC-D events */ |
---|
219 | 266 | static void smc_ism_event_work(struct work_struct *work) |
---|
220 | 267 | { |
---|
.. | .. |
---|
223 | 270 | |
---|
224 | 271 | switch (wrk->event.type) { |
---|
225 | 272 | case ISM_EVENT_GID: /* GID event, token is peer GID */ |
---|
226 | | - smc_smcd_terminate(wrk->smcd, wrk->event.tok); |
---|
| 273 | + smc_smcd_terminate(wrk->smcd, wrk->event.tok, VLAN_VID_MASK); |
---|
227 | 274 | break; |
---|
228 | 275 | case ISM_EVENT_DMB: |
---|
229 | 276 | break; |
---|
.. | .. |
---|
257 | 304 | return NULL; |
---|
258 | 305 | } |
---|
259 | 306 | |
---|
| 307 | + smcd->event_wq = alloc_ordered_workqueue("ism_evt_wq-%s)", |
---|
| 308 | + WQ_MEM_RECLAIM, name); |
---|
| 309 | + if (!smcd->event_wq) { |
---|
| 310 | + kfree(smcd->conn); |
---|
| 311 | + kfree(smcd); |
---|
| 312 | + return NULL; |
---|
| 313 | + } |
---|
| 314 | + |
---|
260 | 315 | smcd->dev.parent = parent; |
---|
261 | 316 | smcd->dev.release = smcd_release; |
---|
262 | 317 | device_initialize(&smcd->dev); |
---|
263 | 318 | dev_set_name(&smcd->dev, name); |
---|
264 | 319 | smcd->ops = ops; |
---|
265 | | - smc_pnetid_by_dev_port(parent, 0, smcd->pnetid); |
---|
| 320 | + if (smc_pnetid_by_dev_port(parent, 0, smcd->pnetid)) |
---|
| 321 | + smc_pnetid_by_table_smcd(smcd); |
---|
266 | 322 | |
---|
267 | 323 | spin_lock_init(&smcd->lock); |
---|
| 324 | + spin_lock_init(&smcd->lgr_lock); |
---|
268 | 325 | INIT_LIST_HEAD(&smcd->vlan); |
---|
269 | | - smcd->event_wq = alloc_ordered_workqueue("ism_evt_wq-%s)", |
---|
270 | | - WQ_MEM_RECLAIM, name); |
---|
| 326 | + INIT_LIST_HEAD(&smcd->lgr_list); |
---|
| 327 | + init_waitqueue_head(&smcd->lgrs_deleted); |
---|
271 | 328 | return smcd; |
---|
272 | 329 | } |
---|
273 | 330 | EXPORT_SYMBOL_GPL(smcd_alloc_dev); |
---|
274 | 331 | |
---|
275 | 332 | int smcd_register_dev(struct smcd_dev *smcd) |
---|
276 | 333 | { |
---|
277 | | - spin_lock(&smcd_dev_list.lock); |
---|
278 | | - list_add_tail(&smcd->list, &smcd_dev_list.list); |
---|
279 | | - spin_unlock(&smcd_dev_list.lock); |
---|
| 334 | + int rc; |
---|
280 | 335 | |
---|
281 | | - return device_add(&smcd->dev); |
---|
| 336 | + mutex_lock(&smcd_dev_list.mutex); |
---|
| 337 | + if (list_empty(&smcd_dev_list.list)) { |
---|
| 338 | + u8 *system_eid = NULL; |
---|
| 339 | + |
---|
| 340 | + smc_ism_get_system_eid(smcd, &system_eid); |
---|
| 341 | + if (system_eid[24] != '0' || system_eid[28] != '0') |
---|
| 342 | + smc_ism_v2_capable = true; |
---|
| 343 | + } |
---|
| 344 | + /* sort list: devices without pnetid before devices with pnetid */ |
---|
| 345 | + if (smcd->pnetid[0]) |
---|
| 346 | + list_add_tail(&smcd->list, &smcd_dev_list.list); |
---|
| 347 | + else |
---|
| 348 | + list_add(&smcd->list, &smcd_dev_list.list); |
---|
| 349 | + mutex_unlock(&smcd_dev_list.mutex); |
---|
| 350 | + |
---|
| 351 | + pr_warn_ratelimited("smc: adding smcd device %s with pnetid %.16s%s\n", |
---|
| 352 | + dev_name(&smcd->dev), smcd->pnetid, |
---|
| 353 | + smcd->pnetid_by_user ? " (user defined)" : ""); |
---|
| 354 | + |
---|
| 355 | + rc = device_add(&smcd->dev); |
---|
| 356 | + if (rc) { |
---|
| 357 | + mutex_lock(&smcd_dev_list.mutex); |
---|
| 358 | + list_del(&smcd->list); |
---|
| 359 | + mutex_unlock(&smcd_dev_list.mutex); |
---|
| 360 | + } |
---|
| 361 | + |
---|
| 362 | + return rc; |
---|
282 | 363 | } |
---|
283 | 364 | EXPORT_SYMBOL_GPL(smcd_register_dev); |
---|
284 | 365 | |
---|
285 | 366 | void smcd_unregister_dev(struct smcd_dev *smcd) |
---|
286 | 367 | { |
---|
287 | | - spin_lock(&smcd_dev_list.lock); |
---|
288 | | - list_del(&smcd->list); |
---|
289 | | - spin_unlock(&smcd_dev_list.lock); |
---|
| 368 | + pr_warn_ratelimited("smc: removing smcd device %s\n", |
---|
| 369 | + dev_name(&smcd->dev)); |
---|
| 370 | + mutex_lock(&smcd_dev_list.mutex); |
---|
| 371 | + list_del_init(&smcd->list); |
---|
| 372 | + mutex_unlock(&smcd_dev_list.mutex); |
---|
| 373 | + smcd->going_away = 1; |
---|
| 374 | + smc_smcd_terminate_all(smcd); |
---|
290 | 375 | flush_workqueue(smcd->event_wq); |
---|
291 | 376 | destroy_workqueue(smcd->event_wq); |
---|
292 | | - smc_smcd_terminate(smcd, 0); |
---|
293 | 377 | |
---|
294 | 378 | device_del(&smcd->dev); |
---|
295 | 379 | } |
---|
.. | .. |
---|
316 | 400 | { |
---|
317 | 401 | struct smc_ism_event_work *wrk; |
---|
318 | 402 | |
---|
| 403 | + if (smcd->going_away) |
---|
| 404 | + return; |
---|
319 | 405 | /* copy event to event work queue, and let it be handled there */ |
---|
320 | 406 | wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC); |
---|
321 | 407 | if (!wrk) |
---|
.. | .. |
---|
341 | 427 | |
---|
342 | 428 | spin_lock_irqsave(&smcd->lock, flags); |
---|
343 | 429 | conn = smcd->conn[dmbno]; |
---|
344 | | - if (conn) |
---|
| 430 | + if (conn && !conn->killed) |
---|
345 | 431 | tasklet_schedule(&conn->rx_tsklet); |
---|
346 | 432 | spin_unlock_irqrestore(&smcd->lock, flags); |
---|
347 | 433 | } |
---|
348 | 434 | EXPORT_SYMBOL_GPL(smcd_handle_irq); |
---|
| 435 | + |
---|
| 436 | +void __init smc_ism_init(void) |
---|
| 437 | +{ |
---|
| 438 | + smc_ism_v2_capable = false; |
---|
| 439 | +} |
---|