Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net/smc: use a workqueue to defer llc send

SMC handles deferred work in tasklets. As tasklets cannot sleep this
can result in rare EBUSY conditions, so defer this work in a work queue.
The high level api functions do not defer work because they can sleep
until the llc send is actually completed.

Signed-off-by: Karsten Graul <kgraul@linux.ibm.com>
Signed-off-by: Ursula Braun <ubraun@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Karsten Graul and committed by
David S. Miller
2a4c57a9 b32cf4ab

+104 -43
+7 -3
net/smc/smc_core.c
··· 194 194 smc_ib_setup_per_ibdev(smcibdev); 195 195 get_random_bytes(rndvec, sizeof(rndvec)); 196 196 lnk->psn_initial = rndvec[0] + (rndvec[1] << 8) + (rndvec[2] << 16); 197 - smc_llc_link_init(lnk); 198 - rc = smc_wr_alloc_link_mem(lnk); 197 + rc = smc_llc_link_init(lnk); 199 198 if (rc) 200 199 goto free_lgr; 200 + rc = smc_wr_alloc_link_mem(lnk); 201 + if (rc) 202 + goto clear_llc_lnk; 201 203 rc = smc_ib_create_protection_domain(lnk); 202 204 if (rc) 203 205 goto free_link_mem; ··· 223 221 smc_ib_dealloc_protection_domain(lnk); 224 222 free_link_mem: 225 223 smc_wr_free_link_mem(lnk); 224 + clear_llc_lnk: 225 + smc_llc_link_clear(lnk); 226 226 free_lgr: 227 227 kfree(lgr); 228 228 out: ··· 270 266 static void smc_link_clear(struct smc_link *lnk) 271 267 { 272 268 lnk->peer_qpn = 0; 269 + smc_llc_link_clear(lnk); 273 270 smc_ib_modify_qp_reset(lnk); 274 271 smc_wr_free_link(lnk); 275 272 smc_ib_destroy_queue_pair(lnk); ··· 328 323 /* remove a link group */ 329 324 void smc_lgr_free(struct smc_link_group *lgr) 330 325 { 331 - smc_llc_link_flush(&lgr->lnk[SMC_SINGLE_LINK]); 332 326 smc_lgr_free_bufs(lgr); 333 327 smc_link_clear(&lgr->lnk[SMC_SINGLE_LINK]); 334 328 kfree(lgr);
+1
net/smc/smc_core.h
··· 96 96 u8 link_id; /* unique # within link group */ 97 97 98 98 enum smc_link_state state; /* state of link */ 99 + struct workqueue_struct *llc_wq; /* single thread work queue */ 99 100 struct completion llc_confirm; /* wait for rx of conf link */ 100 101 struct completion llc_confirm_resp; /* wait 4 rx of cnf lnk rsp */ 101 102 int llc_confirm_rc; /* rc from confirm link msg */
+94 -38
net/smc/smc_llc.c
··· 239 239 return rc; 240 240 } 241 241 242 + /* prepare an add link message */ 243 + static void smc_llc_prep_add_link(struct smc_llc_msg_add_link *addllc, 244 + struct smc_link *link, u8 mac[], 245 + union ib_gid *gid, 246 + enum smc_llc_reqresp reqresp) 247 + { 248 + memset(addllc, 0, sizeof(*addllc)); 249 + addllc->hd.common.type = SMC_LLC_ADD_LINK; 250 + addllc->hd.length = sizeof(struct smc_llc_msg_add_link); 251 + if (reqresp == SMC_LLC_RESP) { 252 + addllc->hd.flags |= SMC_LLC_FLAG_RESP; 253 + /* always reject more links for now */ 254 + addllc->hd.flags |= SMC_LLC_FLAG_ADD_LNK_REJ; 255 + addllc->hd.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH; 256 + } 257 + memcpy(addllc->sender_mac, mac, ETH_ALEN); 258 + memcpy(addllc->sender_gid, gid, SMC_GID_SIZE); 259 + } 260 + 242 261 /* send ADD LINK request or response */ 243 262 int smc_llc_send_add_link(struct smc_link *link, u8 mac[], 244 263 union ib_gid *gid, ··· 272 253 if (rc) 273 254 return rc; 274 255 addllc = (struct smc_llc_msg_add_link *)wr_buf; 275 - memset(addllc, 0, sizeof(*addllc)); 276 - addllc->hd.common.type = SMC_LLC_ADD_LINK; 277 - addllc->hd.length = sizeof(struct smc_llc_msg_add_link); 278 - if (reqresp == SMC_LLC_RESP) { 279 - addllc->hd.flags |= SMC_LLC_FLAG_RESP; 280 - /* always reject more links for now */ 281 - addllc->hd.flags |= SMC_LLC_FLAG_ADD_LNK_REJ; 282 - addllc->hd.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH; 283 - } 284 - memcpy(addllc->sender_mac, mac, ETH_ALEN); 285 - memcpy(addllc->sender_gid, gid, SMC_GID_SIZE); 256 + smc_llc_prep_add_link(addllc, link, mac, gid, reqresp); 286 257 /* send llc message */ 287 258 rc = smc_wr_tx_send(link, pend); 288 259 return rc; 260 + } 261 + 262 + /* prepare a delete link message */ 263 + static void smc_llc_prep_delete_link(struct smc_llc_msg_del_link *delllc, 264 + struct smc_link *link, 265 + enum smc_llc_reqresp reqresp) 266 + { 267 + memset(delllc, 0, sizeof(*delllc)); 268 + delllc->hd.common.type = SMC_LLC_DELETE_LINK; 269 + delllc->hd.length = sizeof(struct smc_llc_msg_add_link); 270 + if (reqresp == SMC_LLC_RESP) 271 + delllc->hd.flags |= SMC_LLC_FLAG_RESP; 272 + /* DEL_LINK_ALL because only 1 link supported */ 273 + delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; 274 + delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 275 + delllc->link_num = link->link_id; 289 276 } 290 277 291 278 /* send DELETE LINK request or response */ ··· 307 282 if (rc) 308 283 return rc; 309 284 delllc = (struct smc_llc_msg_del_link *)wr_buf; 310 - memset(delllc, 0, sizeof(*delllc)); 311 - delllc->hd.common.type = SMC_LLC_DELETE_LINK; 312 - delllc->hd.length = sizeof(struct smc_llc_msg_add_link); 313 - if (reqresp == SMC_LLC_RESP) 314 - delllc->hd.flags |= SMC_LLC_FLAG_RESP; 315 - /* DEL_LINK_ALL because only 1 link supported */ 316 - delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; 317 - delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 318 - delllc->link_num = link->link_id; 285 + smc_llc_prep_delete_link(delllc, link, reqresp); 319 286 /* send llc message */ 320 287 rc = smc_wr_tx_send(link, pend); 321 288 return rc; ··· 334 317 return rc; 335 318 } 336 319 337 - /* send a prepared message */ 338 - static int smc_llc_send_message(struct smc_link *link, void *llcbuf, int llclen) 320 + struct smc_llc_send_work { 321 + struct work_struct work; 322 + struct smc_link *link; 323 + int llclen; 324 + union smc_llc_msg llcbuf; 325 + }; 326 + 327 + /* worker that sends a prepared message */ 328 + static void smc_llc_send_message_work(struct work_struct *work) 339 329 { 330 + struct smc_llc_send_work *llcwrk = container_of(work, 331 + struct smc_llc_send_work, work); 340 332 struct smc_wr_tx_pend_priv *pend; 341 333 struct smc_wr_buf *wr_buf; 342 334 int rc; 343 335 344 - rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 336 + if (llcwrk->link->state == SMC_LNK_INACTIVE) 337 + goto out; 338 + rc = smc_llc_add_pending_send(llcwrk->link, &wr_buf, &pend); 345 339 if (rc) 346 - return rc; 347 - memcpy(wr_buf, llcbuf, llclen); 348 - /* send llc message */ 349 - rc = smc_wr_tx_send(link, pend); 350 - return rc; 340 + goto out; 341 + memcpy(wr_buf, &llcwrk->llcbuf, llcwrk->llclen); 342 + smc_wr_tx_send(llcwrk->link, pend); 343 + out: 344 + kfree(llcwrk); 345 + } 346 + 347 + /* copy llcbuf and schedule an llc send on link */ 348 + static int smc_llc_send_message(struct smc_link *link, void *llcbuf, int llclen) 349 + { 350 + struct smc_llc_send_work *wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC); 351 + 352 + if (!wrk) 353 + return -ENOMEM; 354 + INIT_WORK(&wrk->work, smc_llc_send_message_work); 355 + wrk->link = link; 356 + wrk->llclen = llclen; 357 + memcpy(&wrk->llcbuf, llcbuf, llclen); 358 + queue_work(link->llc_wq, &wrk->work); 359 + return 0; 351 360 } 352 361 353 362 /********************************* receive ***********************************/ ··· 424 381 } 425 382 426 383 if (lgr->role == SMC_SERV) { 427 - smc_llc_send_add_link(link, 384 + smc_llc_prep_add_link(llc, link, 428 385 link->smcibdev->mac[link->ibport - 1], 429 386 &link->smcibdev->gid[link->ibport - 1], 430 387 SMC_LLC_REQ); 431 388 432 389 } else { 433 - smc_llc_send_add_link(link, 390 + smc_llc_prep_add_link(llc, link, 434 391 link->smcibdev->mac[link->ibport - 1], 435 392 &link->smcibdev->gid[link->ibport - 1], 436 393 SMC_LLC_RESP); 437 394 } 395 + smc_llc_send_message(link, llc, sizeof(*llc)); 438 396 } 439 397 } 440 398 ··· 451 407 } else { 452 408 if (lgr->role == SMC_SERV) { 453 409 smc_lgr_forget(lgr); 454 - smc_llc_send_delete_link(link, SMC_LLC_REQ); 410 + smc_llc_prep_delete_link(llc, link, SMC_LLC_REQ); 411 + smc_llc_send_message(link, llc, sizeof(*llc)); 455 412 } else { 456 - smc_llc_send_delete_link(link, SMC_LLC_RESP); 413 + smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP); 414 + smc_llc_send_message(link, llc, sizeof(*llc)); 457 415 smc_lgr_terminate(lgr); 458 416 } 459 417 } ··· 605 559 } 606 560 next_interval = link->llc_testlink_time; 607 561 out: 608 - schedule_delayed_work(&link->llc_testlink_wrk, next_interval); 562 + queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk, 563 + next_interval); 609 564 } 610 565 611 - void smc_llc_link_init(struct smc_link *link) 566 + int smc_llc_link_init(struct smc_link *link) 612 567 { 568 + struct smc_link_group *lgr = container_of(link, struct smc_link_group, 569 + lnk[SMC_SINGLE_LINK]); 570 + link->llc_wq = alloc_ordered_workqueue("llc_wq-%x:%x)", WQ_MEM_RECLAIM, 571 + *((u32 *)lgr->id), 572 + link->link_id); 573 + if (!link->llc_wq) 574 + return -ENOMEM; 613 575 init_completion(&link->llc_confirm); 614 576 init_completion(&link->llc_confirm_resp); 615 577 init_completion(&link->llc_add); ··· 625 571 init_completion(&link->llc_confirm_rkey); 626 572 init_completion(&link->llc_testlink_resp); 627 573 INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work); 574 + return 0; 628 575 } 629 576 630 577 void smc_llc_link_active(struct smc_link *link, int testlink_time) ··· 633 578 link->state = SMC_LNK_ACTIVE; 634 579 if (testlink_time) { 635 580 link->llc_testlink_time = testlink_time * HZ; 636 - schedule_delayed_work(&link->llc_testlink_wrk, 637 - link->llc_testlink_time); 581 + queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk, 582 + link->llc_testlink_time); 638 583 } 639 584 } 640 585 ··· 646 591 } 647 592 648 593 /* called in worker context */ 649 - void smc_llc_link_flush(struct smc_link *link) 594 + void smc_llc_link_clear(struct smc_link *link) 650 595 { 651 - cancel_delayed_work_sync(&link->llc_testlink_wrk); 596 + flush_workqueue(link->llc_wq); 597 + destroy_workqueue(link->llc_wq); 652 598 } 653 599 654 600 /* register a new rtoken at the remote peer */
+2 -2
net/smc/smc_llc.h
··· 42 42 enum smc_llc_reqresp reqresp); 43 43 int smc_llc_send_delete_link(struct smc_link *link, 44 44 enum smc_llc_reqresp reqresp); 45 - void smc_llc_link_init(struct smc_link *link); 45 + int smc_llc_link_init(struct smc_link *link); 46 46 void smc_llc_link_active(struct smc_link *link, int testlink_time); 47 47 void smc_llc_link_inactive(struct smc_link *link); 48 - void smc_llc_link_flush(struct smc_link *link); 48 + void smc_llc_link_clear(struct smc_link *link); 49 49 int smc_llc_do_confirm_rkey(struct smc_link *link, 50 50 struct smc_buf_desc *rmb_desc); 51 51 int smc_llc_init(void) __init;