| .. | .. |
|---|
| 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 | +} |
|---|