diff options
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/core.c | 494 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/core.h | 13 |
2 files changed, 334 insertions, 173 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index a14e422fae4d..b0a0b01bb4ef 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c | |||
@@ -44,7 +44,7 @@ | |||
44 | #include <linux/seq_file.h> | 44 | #include <linux/seq_file.h> |
45 | #include <linux/u64_stats_sync.h> | 45 | #include <linux/u64_stats_sync.h> |
46 | #include <linux/netdevice.h> | 46 | #include <linux/netdevice.h> |
47 | #include <linux/wait.h> | 47 | #include <linux/completion.h> |
48 | #include <linux/skbuff.h> | 48 | #include <linux/skbuff.h> |
49 | #include <linux/etherdevice.h> | 49 | #include <linux/etherdevice.h> |
50 | #include <linux/types.h> | 50 | #include <linux/types.h> |
@@ -96,11 +96,9 @@ struct mlxsw_core { | |||
96 | struct list_head rx_listener_list; | 96 | struct list_head rx_listener_list; |
97 | struct list_head event_listener_list; | 97 | struct list_head event_listener_list; |
98 | struct { | 98 | struct { |
99 | struct sk_buff *resp_skb; | 99 | atomic64_t tid; |
100 | u64 tid; | 100 | struct list_head trans_list; |
101 | wait_queue_head_t wait; | 101 | spinlock_t trans_list_lock; /* protects trans_list writes */ |
102 | bool trans_active; | ||
103 | struct mutex lock; /* One EMAD transaction at a time. */ | ||
104 | bool use_emad; | 102 | bool use_emad; |
105 | } emad; | 103 | } emad; |
106 | struct mlxsw_core_pcpu_stats __percpu *pcpu_stats; | 104 | struct mlxsw_core_pcpu_stats __percpu *pcpu_stats; |
@@ -293,7 +291,7 @@ static void mlxsw_emad_pack_reg_tlv(char *reg_tlv, | |||
293 | static void mlxsw_emad_pack_op_tlv(char *op_tlv, | 291 | static void mlxsw_emad_pack_op_tlv(char *op_tlv, |
294 | const struct mlxsw_reg_info *reg, | 292 | const struct mlxsw_reg_info *reg, |
295 | enum mlxsw_core_reg_access_type type, | 293 | enum mlxsw_core_reg_access_type type, |
296 | struct mlxsw_core *mlxsw_core) | 294 | u64 tid) |
297 | { | 295 | { |
298 | mlxsw_emad_op_tlv_type_set(op_tlv, MLXSW_EMAD_TLV_TYPE_OP); | 296 | mlxsw_emad_op_tlv_type_set(op_tlv, MLXSW_EMAD_TLV_TYPE_OP); |
299 | mlxsw_emad_op_tlv_len_set(op_tlv, MLXSW_EMAD_OP_TLV_LEN); | 297 | mlxsw_emad_op_tlv_len_set(op_tlv, MLXSW_EMAD_OP_TLV_LEN); |
@@ -309,7 +307,7 @@ static void mlxsw_emad_pack_op_tlv(char *op_tlv, | |||
309 | MLXSW_EMAD_OP_TLV_METHOD_WRITE); | 307 | MLXSW_EMAD_OP_TLV_METHOD_WRITE); |
310 | mlxsw_emad_op_tlv_class_set(op_tlv, | 308 | mlxsw_emad_op_tlv_class_set(op_tlv, |
311 | MLXSW_EMAD_OP_TLV_CLASS_REG_ACCESS); | 309 | MLXSW_EMAD_OP_TLV_CLASS_REG_ACCESS); |
312 | mlxsw_emad_op_tlv_tid_set(op_tlv, mlxsw_core->emad.tid); | 310 | mlxsw_emad_op_tlv_tid_set(op_tlv, tid); |
313 | } | 311 | } |
314 | 312 | ||
315 | static int mlxsw_emad_construct_eth_hdr(struct sk_buff *skb) | 313 | static int mlxsw_emad_construct_eth_hdr(struct sk_buff *skb) |
@@ -331,7 +329,7 @@ static void mlxsw_emad_construct(struct sk_buff *skb, | |||
331 | const struct mlxsw_reg_info *reg, | 329 | const struct mlxsw_reg_info *reg, |
332 | char *payload, | 330 | char *payload, |
333 | enum mlxsw_core_reg_access_type type, | 331 | enum mlxsw_core_reg_access_type type, |
334 | struct mlxsw_core *mlxsw_core) | 332 | u64 tid) |
335 | { | 333 | { |
336 | char *buf; | 334 | char *buf; |
337 | 335 | ||
@@ -342,7 +340,7 @@ static void mlxsw_emad_construct(struct sk_buff *skb, | |||
342 | mlxsw_emad_pack_reg_tlv(buf, reg, payload); | 340 | mlxsw_emad_pack_reg_tlv(buf, reg, payload); |
343 | 341 | ||
344 | buf = skb_push(skb, MLXSW_EMAD_OP_TLV_LEN * sizeof(u32)); | 342 | buf = skb_push(skb, MLXSW_EMAD_OP_TLV_LEN * sizeof(u32)); |
345 | mlxsw_emad_pack_op_tlv(buf, reg, type, mlxsw_core); | 343 | mlxsw_emad_pack_op_tlv(buf, reg, type, tid); |
346 | 344 | ||
347 | mlxsw_emad_construct_eth_hdr(skb); | 345 | mlxsw_emad_construct_eth_hdr(skb); |
348 | } | 346 | } |
@@ -379,58 +377,16 @@ static bool mlxsw_emad_is_resp(const struct sk_buff *skb) | |||
379 | return (mlxsw_emad_op_tlv_r_get(op_tlv) == MLXSW_EMAD_OP_TLV_RESPONSE); | 377 | return (mlxsw_emad_op_tlv_r_get(op_tlv) == MLXSW_EMAD_OP_TLV_RESPONSE); |
380 | } | 378 | } |
381 | 379 | ||
382 | #define MLXSW_EMAD_TIMEOUT_MS 200 | 380 | static int mlxsw_emad_process_status(char *op_tlv, |
383 | 381 | enum mlxsw_emad_op_tlv_status *p_status) | |
384 | static int __mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core, | ||
385 | struct sk_buff *skb, | ||
386 | const struct mlxsw_tx_info *tx_info) | ||
387 | { | ||
388 | int err; | ||
389 | int ret; | ||
390 | |||
391 | mlxsw_core->emad.trans_active = true; | ||
392 | |||
393 | err = mlxsw_core_skb_transmit(mlxsw_core, skb, tx_info); | ||
394 | if (err) { | ||
395 | dev_err(mlxsw_core->bus_info->dev, "Failed to transmit EMAD (tid=%llx)\n", | ||
396 | mlxsw_core->emad.tid); | ||
397 | dev_kfree_skb(skb); | ||
398 | goto trans_inactive_out; | ||
399 | } | ||
400 | |||
401 | ret = wait_event_timeout(mlxsw_core->emad.wait, | ||
402 | !(mlxsw_core->emad.trans_active), | ||
403 | msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_MS)); | ||
404 | if (!ret) { | ||
405 | dev_warn(mlxsw_core->bus_info->dev, "EMAD timed-out (tid=%llx)\n", | ||
406 | mlxsw_core->emad.tid); | ||
407 | err = -EIO; | ||
408 | goto trans_inactive_out; | ||
409 | } | ||
410 | |||
411 | return 0; | ||
412 | |||
413 | trans_inactive_out: | ||
414 | mlxsw_core->emad.trans_active = false; | ||
415 | return err; | ||
416 | } | ||
417 | |||
418 | static int mlxsw_emad_process_status(struct mlxsw_core *mlxsw_core, | ||
419 | char *op_tlv) | ||
420 | { | 382 | { |
421 | enum mlxsw_emad_op_tlv_status status; | 383 | *p_status = mlxsw_emad_op_tlv_status_get(op_tlv); |
422 | u64 tid; | ||
423 | |||
424 | status = mlxsw_emad_op_tlv_status_get(op_tlv); | ||
425 | tid = mlxsw_emad_op_tlv_tid_get(op_tlv); | ||
426 | 384 | ||
427 | switch (status) { | 385 | switch (*p_status) { |
428 | case MLXSW_EMAD_OP_TLV_STATUS_SUCCESS: | 386 | case MLXSW_EMAD_OP_TLV_STATUS_SUCCESS: |
429 | return 0; | 387 | return 0; |
430 | case MLXSW_EMAD_OP_TLV_STATUS_BUSY: | 388 | case MLXSW_EMAD_OP_TLV_STATUS_BUSY: |
431 | case MLXSW_EMAD_OP_TLV_STATUS_MESSAGE_RECEIPT_ACK: | 389 | case MLXSW_EMAD_OP_TLV_STATUS_MESSAGE_RECEIPT_ACK: |
432 | dev_warn(mlxsw_core->bus_info->dev, "Reg access status again (tid=%llx,status=%x(%s))\n", | ||
433 | tid, status, mlxsw_emad_op_tlv_status_str(status)); | ||
434 | return -EAGAIN; | 390 | return -EAGAIN; |
435 | case MLXSW_EMAD_OP_TLV_STATUS_VERSION_NOT_SUPPORTED: | 391 | case MLXSW_EMAD_OP_TLV_STATUS_VERSION_NOT_SUPPORTED: |
436 | case MLXSW_EMAD_OP_TLV_STATUS_UNKNOWN_TLV: | 392 | case MLXSW_EMAD_OP_TLV_STATUS_UNKNOWN_TLV: |
@@ -441,70 +397,150 @@ static int mlxsw_emad_process_status(struct mlxsw_core *mlxsw_core, | |||
441 | case MLXSW_EMAD_OP_TLV_STATUS_RESOURCE_NOT_AVAILABLE: | 397 | case MLXSW_EMAD_OP_TLV_STATUS_RESOURCE_NOT_AVAILABLE: |
442 | case MLXSW_EMAD_OP_TLV_STATUS_INTERNAL_ERROR: | 398 | case MLXSW_EMAD_OP_TLV_STATUS_INTERNAL_ERROR: |
443 | default: | 399 | default: |
444 | dev_err(mlxsw_core->bus_info->dev, "Reg access status failed (tid=%llx,status=%x(%s))\n", | ||
445 | tid, status, mlxsw_emad_op_tlv_status_str(status)); | ||
446 | return -EIO; | 400 | return -EIO; |
447 | } | 401 | } |
448 | } | 402 | } |
449 | 403 | ||
450 | static int mlxsw_emad_process_status_skb(struct mlxsw_core *mlxsw_core, | 404 | static int |
451 | struct sk_buff *skb) | 405 | mlxsw_emad_process_status_skb(struct sk_buff *skb, |
406 | enum mlxsw_emad_op_tlv_status *p_status) | ||
452 | { | 407 | { |
453 | return mlxsw_emad_process_status(mlxsw_core, mlxsw_emad_op_tlv(skb)); | 408 | return mlxsw_emad_process_status(mlxsw_emad_op_tlv(skb), p_status); |
409 | } | ||
410 | |||
411 | struct mlxsw_reg_trans { | ||
412 | struct list_head list; | ||
413 | struct list_head bulk_list; | ||
414 | struct mlxsw_core *core; | ||
415 | struct sk_buff *tx_skb; | ||
416 | struct mlxsw_tx_info tx_info; | ||
417 | struct delayed_work timeout_dw; | ||
418 | unsigned int retries; | ||
419 | u64 tid; | ||
420 | struct completion completion; | ||
421 | atomic_t active; | ||
422 | mlxsw_reg_trans_cb_t *cb; | ||
423 | unsigned long cb_priv; | ||
424 | const struct mlxsw_reg_info *reg; | ||
425 | enum mlxsw_core_reg_access_type type; | ||
426 | int err; | ||
427 | enum mlxsw_emad_op_tlv_status emad_status; | ||
428 | struct rcu_head rcu; | ||
429 | }; | ||
430 | |||
431 | #define MLXSW_EMAD_TIMEOUT_MS 200 | ||
432 | |||
433 | static void mlxsw_emad_trans_timeout_schedule(struct mlxsw_reg_trans *trans) | ||
434 | { | ||
435 | unsigned long timeout = msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_MS); | ||
436 | |||
437 | mlxsw_core_schedule_dw(&trans->timeout_dw, timeout); | ||
454 | } | 438 | } |
455 | 439 | ||
456 | static int mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core, | 440 | static int mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core, |
457 | struct sk_buff *skb, | 441 | struct mlxsw_reg_trans *trans) |
458 | const struct mlxsw_tx_info *tx_info) | ||
459 | { | 442 | { |
460 | struct sk_buff *trans_skb; | 443 | struct sk_buff *skb; |
461 | int n_retry; | ||
462 | int err; | 444 | int err; |
463 | 445 | ||
464 | n_retry = 0; | 446 | skb = skb_copy(trans->tx_skb, GFP_KERNEL); |
465 | retry: | 447 | if (!skb) |
466 | /* We copy the EMAD to a new skb, since we might need | 448 | return -ENOMEM; |
467 | * to retransmit it in case of failure. | 449 | |
468 | */ | 450 | atomic_set(&trans->active, 1); |
469 | trans_skb = skb_copy(skb, GFP_KERNEL); | 451 | err = mlxsw_core_skb_transmit(mlxsw_core, skb, &trans->tx_info); |
470 | if (!trans_skb) { | 452 | if (err) { |
471 | err = -ENOMEM; | 453 | dev_kfree_skb(skb); |
472 | goto out; | 454 | return err; |
473 | } | 455 | } |
456 | mlxsw_emad_trans_timeout_schedule(trans); | ||
457 | return 0; | ||
458 | } | ||
474 | 459 | ||
475 | err = __mlxsw_emad_transmit(mlxsw_core, trans_skb, tx_info); | 460 | static void mlxsw_emad_trans_finish(struct mlxsw_reg_trans *trans, int err) |
476 | if (!err) { | 461 | { |
477 | struct sk_buff *resp_skb = mlxsw_core->emad.resp_skb; | 462 | struct mlxsw_core *mlxsw_core = trans->core; |
463 | |||
464 | dev_kfree_skb(trans->tx_skb); | ||
465 | spin_lock_bh(&mlxsw_core->emad.trans_list_lock); | ||
466 | list_del_rcu(&trans->list); | ||
467 | spin_unlock_bh(&mlxsw_core->emad.trans_list_lock); | ||
468 | trans->err = err; | ||
469 | complete(&trans->completion); | ||
470 | } | ||
471 | |||
472 | static void mlxsw_emad_transmit_retry(struct mlxsw_core *mlxsw_core, | ||
473 | struct mlxsw_reg_trans *trans) | ||
474 | { | ||
475 | int err; | ||
478 | 476 | ||
479 | err = mlxsw_emad_process_status_skb(mlxsw_core, resp_skb); | 477 | if (trans->retries < MLXSW_EMAD_MAX_RETRY) { |
480 | if (err) | 478 | trans->retries++; |
481 | dev_kfree_skb(resp_skb); | 479 | err = mlxsw_emad_transmit(trans->core, trans); |
482 | if (!err || err != -EAGAIN) | 480 | if (err == 0) |
483 | goto out; | 481 | return; |
482 | } else { | ||
483 | err = -EIO; | ||
484 | } | 484 | } |
485 | if (n_retry++ < MLXSW_EMAD_MAX_RETRY) | 485 | mlxsw_emad_trans_finish(trans, err); |
486 | goto retry; | 486 | } |
487 | 487 | ||
488 | out: | 488 | static void mlxsw_emad_trans_timeout_work(struct work_struct *work) |
489 | dev_kfree_skb(skb); | 489 | { |
490 | mlxsw_core->emad.tid++; | 490 | struct mlxsw_reg_trans *trans = container_of(work, |
491 | return err; | 491 | struct mlxsw_reg_trans, |
492 | timeout_dw.work); | ||
493 | |||
494 | if (!atomic_dec_and_test(&trans->active)) | ||
495 | return; | ||
496 | |||
497 | mlxsw_emad_transmit_retry(trans->core, trans); | ||
492 | } | 498 | } |
493 | 499 | ||
500 | static void mlxsw_emad_process_response(struct mlxsw_core *mlxsw_core, | ||
501 | struct mlxsw_reg_trans *trans, | ||
502 | struct sk_buff *skb) | ||
503 | { | ||
504 | int err; | ||
505 | |||
506 | if (!atomic_dec_and_test(&trans->active)) | ||
507 | return; | ||
508 | |||
509 | err = mlxsw_emad_process_status_skb(skb, &trans->emad_status); | ||
510 | if (err == -EAGAIN) { | ||
511 | mlxsw_emad_transmit_retry(mlxsw_core, trans); | ||
512 | } else { | ||
513 | if (err == 0) { | ||
514 | char *op_tlv = mlxsw_emad_op_tlv(skb); | ||
515 | |||
516 | if (trans->cb) | ||
517 | trans->cb(mlxsw_core, | ||
518 | mlxsw_emad_reg_payload(op_tlv), | ||
519 | trans->reg->len, trans->cb_priv); | ||
520 | } | ||
521 | mlxsw_emad_trans_finish(trans, err); | ||
522 | } | ||
523 | } | ||
524 | |||
525 | /* called with rcu read lock held */ | ||
494 | static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u8 local_port, | 526 | static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u8 local_port, |
495 | void *priv) | 527 | void *priv) |
496 | { | 528 | { |
497 | struct mlxsw_core *mlxsw_core = priv; | 529 | struct mlxsw_core *mlxsw_core = priv; |
530 | struct mlxsw_reg_trans *trans; | ||
498 | 531 | ||
499 | if (mlxsw_emad_is_resp(skb) && | 532 | if (!mlxsw_emad_is_resp(skb)) |
500 | mlxsw_core->emad.trans_active && | 533 | goto free_skb; |
501 | mlxsw_emad_get_tid(skb) == mlxsw_core->emad.tid) { | 534 | |
502 | mlxsw_core->emad.resp_skb = skb; | 535 | list_for_each_entry_rcu(trans, &mlxsw_core->emad.trans_list, list) { |
503 | mlxsw_core->emad.trans_active = false; | 536 | if (mlxsw_emad_get_tid(skb) == trans->tid) { |
504 | wake_up(&mlxsw_core->emad.wait); | 537 | mlxsw_emad_process_response(mlxsw_core, trans, skb); |
505 | } else { | 538 | break; |
506 | dev_kfree_skb(skb); | 539 | } |
507 | } | 540 | } |
541 | |||
542 | free_skb: | ||
543 | dev_kfree_skb(skb); | ||
508 | } | 544 | } |
509 | 545 | ||
510 | static const struct mlxsw_rx_listener mlxsw_emad_rx_listener = { | 546 | static const struct mlxsw_rx_listener mlxsw_emad_rx_listener = { |
@@ -531,18 +567,19 @@ static int mlxsw_emad_traps_set(struct mlxsw_core *mlxsw_core) | |||
531 | 567 | ||
532 | static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) | 568 | static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) |
533 | { | 569 | { |
570 | u64 tid; | ||
534 | int err; | 571 | int err; |
535 | 572 | ||
536 | /* Set the upper 32 bits of the transaction ID field to a random | 573 | /* Set the upper 32 bits of the transaction ID field to a random |
537 | * number. This allows us to discard EMADs addressed to other | 574 | * number. This allows us to discard EMADs addressed to other |
538 | * devices. | 575 | * devices. |
539 | */ | 576 | */ |
540 | get_random_bytes(&mlxsw_core->emad.tid, 4); | 577 | get_random_bytes(&tid, 4); |
541 | mlxsw_core->emad.tid = mlxsw_core->emad.tid << 32; | 578 | tid <<= 32; |
579 | atomic64_set(&mlxsw_core->emad.tid, tid); | ||
542 | 580 | ||
543 | init_waitqueue_head(&mlxsw_core->emad.wait); | 581 | INIT_LIST_HEAD(&mlxsw_core->emad.trans_list); |
544 | mlxsw_core->emad.trans_active = false; | 582 | spin_lock_init(&mlxsw_core->emad.trans_list_lock); |
545 | mutex_init(&mlxsw_core->emad.lock); | ||
546 | 583 | ||
547 | err = mlxsw_core_rx_listener_register(mlxsw_core, | 584 | err = mlxsw_core_rx_listener_register(mlxsw_core, |
548 | &mlxsw_emad_rx_listener, | 585 | &mlxsw_emad_rx_listener, |
@@ -600,6 +637,59 @@ static struct sk_buff *mlxsw_emad_alloc(const struct mlxsw_core *mlxsw_core, | |||
600 | return skb; | 637 | return skb; |
601 | } | 638 | } |
602 | 639 | ||
640 | static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core, | ||
641 | const struct mlxsw_reg_info *reg, | ||
642 | char *payload, | ||
643 | enum mlxsw_core_reg_access_type type, | ||
644 | struct mlxsw_reg_trans *trans, | ||
645 | struct list_head *bulk_list, | ||
646 | mlxsw_reg_trans_cb_t *cb, | ||
647 | unsigned long cb_priv, u64 tid) | ||
648 | { | ||
649 | struct sk_buff *skb; | ||
650 | int err; | ||
651 | |||
652 | dev_dbg(mlxsw_core->bus_info->dev, "EMAD reg access (tid=%llx,reg_id=%x(%s),type=%s)\n", | ||
653 | trans->tid, reg->id, mlxsw_reg_id_str(reg->id), | ||
654 | mlxsw_core_reg_access_type_str(type)); | ||
655 | |||
656 | skb = mlxsw_emad_alloc(mlxsw_core, reg->len); | ||
657 | if (!skb) | ||
658 | return -ENOMEM; | ||
659 | |||
660 | list_add_tail(&trans->bulk_list, bulk_list); | ||
661 | trans->core = mlxsw_core; | ||
662 | trans->tx_skb = skb; | ||
663 | trans->tx_info.local_port = MLXSW_PORT_CPU_PORT; | ||
664 | trans->tx_info.is_emad = true; | ||
665 | INIT_DELAYED_WORK(&trans->timeout_dw, mlxsw_emad_trans_timeout_work); | ||
666 | trans->tid = tid; | ||
667 | init_completion(&trans->completion); | ||
668 | trans->cb = cb; | ||
669 | trans->cb_priv = cb_priv; | ||
670 | trans->reg = reg; | ||
671 | trans->type = type; | ||
672 | |||
673 | mlxsw_emad_construct(skb, reg, payload, type, trans->tid); | ||
674 | mlxsw_core->driver->txhdr_construct(skb, &trans->tx_info); | ||
675 | |||
676 | spin_lock_bh(&mlxsw_core->emad.trans_list_lock); | ||
677 | list_add_tail_rcu(&trans->list, &mlxsw_core->emad.trans_list); | ||
678 | spin_unlock_bh(&mlxsw_core->emad.trans_list_lock); | ||
679 | err = mlxsw_emad_transmit(mlxsw_core, trans); | ||
680 | if (err) | ||
681 | goto err_out; | ||
682 | return 0; | ||
683 | |||
684 | err_out: | ||
685 | spin_lock_bh(&mlxsw_core->emad.trans_list_lock); | ||
686 | list_del_rcu(&trans->list); | ||
687 | spin_unlock_bh(&mlxsw_core->emad.trans_list_lock); | ||
688 | list_del(&trans->bulk_list); | ||
689 | dev_kfree_skb(trans->tx_skb); | ||
690 | return err; | ||
691 | } | ||
692 | |||
603 | /***************** | 693 | /***************** |
604 | * Core functions | 694 | * Core functions |
605 | *****************/ | 695 | *****************/ |
@@ -689,24 +779,6 @@ static const struct file_operations mlxsw_core_rx_stats_dbg_ops = { | |||
689 | .llseek = seq_lseek | 779 | .llseek = seq_lseek |
690 | }; | 780 | }; |
691 | 781 | ||
692 | static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core, | ||
693 | const char *buf, size_t size) | ||
694 | { | ||
695 | __be32 *m = (__be32 *) buf; | ||
696 | int i; | ||
697 | int count = size / sizeof(__be32); | ||
698 | |||
699 | for (i = count - 1; i >= 0; i--) | ||
700 | if (m[i]) | ||
701 | break; | ||
702 | i++; | ||
703 | count = i ? i : 1; | ||
704 | for (i = 0; i < count; i += 4) | ||
705 | dev_dbg(mlxsw_core->bus_info->dev, "%04x - %08x %08x %08x %08x\n", | ||
706 | i * 4, be32_to_cpu(m[i]), be32_to_cpu(m[i + 1]), | ||
707 | be32_to_cpu(m[i + 2]), be32_to_cpu(m[i + 3])); | ||
708 | } | ||
709 | |||
710 | int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver) | 782 | int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver) |
711 | { | 783 | { |
712 | spin_lock(&mlxsw_core_driver_list_lock); | 784 | spin_lock(&mlxsw_core_driver_list_lock); |
@@ -1264,56 +1336,112 @@ void mlxsw_core_event_listener_unregister(struct mlxsw_core *mlxsw_core, | |||
1264 | } | 1336 | } |
1265 | EXPORT_SYMBOL(mlxsw_core_event_listener_unregister); | 1337 | EXPORT_SYMBOL(mlxsw_core_event_listener_unregister); |
1266 | 1338 | ||
1339 | static u64 mlxsw_core_tid_get(struct mlxsw_core *mlxsw_core) | ||
1340 | { | ||
1341 | return atomic64_inc_return(&mlxsw_core->emad.tid); | ||
1342 | } | ||
1343 | |||
1267 | static int mlxsw_core_reg_access_emad(struct mlxsw_core *mlxsw_core, | 1344 | static int mlxsw_core_reg_access_emad(struct mlxsw_core *mlxsw_core, |
1268 | const struct mlxsw_reg_info *reg, | 1345 | const struct mlxsw_reg_info *reg, |
1269 | char *payload, | 1346 | char *payload, |
1270 | enum mlxsw_core_reg_access_type type) | 1347 | enum mlxsw_core_reg_access_type type, |
1348 | struct list_head *bulk_list, | ||
1349 | mlxsw_reg_trans_cb_t *cb, | ||
1350 | unsigned long cb_priv) | ||
1271 | { | 1351 | { |
1352 | u64 tid = mlxsw_core_tid_get(mlxsw_core); | ||
1353 | struct mlxsw_reg_trans *trans; | ||
1272 | int err; | 1354 | int err; |
1273 | char *op_tlv; | ||
1274 | struct sk_buff *skb; | ||
1275 | struct mlxsw_tx_info tx_info = { | ||
1276 | .local_port = MLXSW_PORT_CPU_PORT, | ||
1277 | .is_emad = true, | ||
1278 | }; | ||
1279 | 1355 | ||
1280 | skb = mlxsw_emad_alloc(mlxsw_core, reg->len); | 1356 | trans = kzalloc(sizeof(*trans), GFP_KERNEL); |
1281 | if (!skb) | 1357 | if (!trans) |
1282 | return -ENOMEM; | 1358 | return -ENOMEM; |
1283 | 1359 | ||
1284 | mlxsw_emad_construct(skb, reg, payload, type, mlxsw_core); | 1360 | err = mlxsw_emad_reg_access(mlxsw_core, reg, payload, type, trans, |
1285 | mlxsw_core->driver->txhdr_construct(skb, &tx_info); | 1361 | bulk_list, cb, cb_priv, tid); |
1362 | if (err) { | ||
1363 | kfree(trans); | ||
1364 | return err; | ||
1365 | } | ||
1366 | return 0; | ||
1367 | } | ||
1286 | 1368 | ||
1287 | dev_dbg(mlxsw_core->bus_info->dev, "EMAD send (tid=%llx)\n", | 1369 | int mlxsw_reg_trans_query(struct mlxsw_core *mlxsw_core, |
1288 | mlxsw_core->emad.tid); | 1370 | const struct mlxsw_reg_info *reg, char *payload, |
1289 | mlxsw_core_buf_dump_dbg(mlxsw_core, skb->data, skb->len); | 1371 | struct list_head *bulk_list, |
1372 | mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv) | ||
1373 | { | ||
1374 | return mlxsw_core_reg_access_emad(mlxsw_core, reg, payload, | ||
1375 | MLXSW_CORE_REG_ACCESS_TYPE_QUERY, | ||
1376 | bulk_list, cb, cb_priv); | ||
1377 | } | ||
1378 | EXPORT_SYMBOL(mlxsw_reg_trans_query); | ||
1290 | 1379 | ||
1291 | err = mlxsw_emad_transmit(mlxsw_core, skb, &tx_info); | 1380 | int mlxsw_reg_trans_write(struct mlxsw_core *mlxsw_core, |
1292 | if (!err) { | 1381 | const struct mlxsw_reg_info *reg, char *payload, |
1293 | op_tlv = mlxsw_emad_op_tlv(mlxsw_core->emad.resp_skb); | 1382 | struct list_head *bulk_list, |
1294 | memcpy(payload, mlxsw_emad_reg_payload(op_tlv), | 1383 | mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv) |
1295 | reg->len); | 1384 | { |
1385 | return mlxsw_core_reg_access_emad(mlxsw_core, reg, payload, | ||
1386 | MLXSW_CORE_REG_ACCESS_TYPE_WRITE, | ||
1387 | bulk_list, cb, cb_priv); | ||
1388 | } | ||
1389 | EXPORT_SYMBOL(mlxsw_reg_trans_write); | ||
1296 | 1390 | ||
1297 | dev_dbg(mlxsw_core->bus_info->dev, "EMAD recv (tid=%llx)\n", | 1391 | static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans) |
1298 | mlxsw_core->emad.tid - 1); | 1392 | { |
1299 | mlxsw_core_buf_dump_dbg(mlxsw_core, | 1393 | struct mlxsw_core *mlxsw_core = trans->core; |
1300 | mlxsw_core->emad.resp_skb->data, | 1394 | int err; |
1301 | mlxsw_core->emad.resp_skb->len); | ||
1302 | 1395 | ||
1303 | dev_kfree_skb(mlxsw_core->emad.resp_skb); | 1396 | wait_for_completion(&trans->completion); |
1304 | } | 1397 | cancel_delayed_work_sync(&trans->timeout_dw); |
1398 | err = trans->err; | ||
1305 | 1399 | ||
1400 | if (trans->retries) | ||
1401 | dev_warn(mlxsw_core->bus_info->dev, "EMAD retries (%d/%d) (tid=%llx)\n", | ||
1402 | trans->retries, MLXSW_EMAD_MAX_RETRY, trans->tid); | ||
1403 | if (err) | ||
1404 | dev_err(mlxsw_core->bus_info->dev, "EMAD reg access failed (tid=%llx,reg_id=%x(%s),type=%s,status=%x(%s))\n", | ||
1405 | trans->tid, trans->reg->id, | ||
1406 | mlxsw_reg_id_str(trans->reg->id), | ||
1407 | mlxsw_core_reg_access_type_str(trans->type), | ||
1408 | trans->emad_status, | ||
1409 | mlxsw_emad_op_tlv_status_str(trans->emad_status)); | ||
1410 | |||
1411 | list_del(&trans->bulk_list); | ||
1412 | kfree_rcu(trans, rcu); | ||
1306 | return err; | 1413 | return err; |
1307 | } | 1414 | } |
1308 | 1415 | ||
1416 | int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list) | ||
1417 | { | ||
1418 | struct mlxsw_reg_trans *trans; | ||
1419 | struct mlxsw_reg_trans *tmp; | ||
1420 | int sum_err = 0; | ||
1421 | int err; | ||
1422 | |||
1423 | list_for_each_entry_safe(trans, tmp, bulk_list, bulk_list) { | ||
1424 | err = mlxsw_reg_trans_wait(trans); | ||
1425 | if (err && sum_err == 0) | ||
1426 | sum_err = err; /* first error to be returned */ | ||
1427 | } | ||
1428 | return sum_err; | ||
1429 | } | ||
1430 | EXPORT_SYMBOL(mlxsw_reg_trans_bulk_wait); | ||
1431 | |||
1309 | static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core, | 1432 | static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core, |
1310 | const struct mlxsw_reg_info *reg, | 1433 | const struct mlxsw_reg_info *reg, |
1311 | char *payload, | 1434 | char *payload, |
1312 | enum mlxsw_core_reg_access_type type) | 1435 | enum mlxsw_core_reg_access_type type) |
1313 | { | 1436 | { |
1437 | enum mlxsw_emad_op_tlv_status status; | ||
1314 | int err, n_retry; | 1438 | int err, n_retry; |
1315 | char *in_mbox, *out_mbox, *tmp; | 1439 | char *in_mbox, *out_mbox, *tmp; |
1316 | 1440 | ||
1441 | dev_dbg(mlxsw_core->bus_info->dev, "Reg cmd access (reg_id=%x(%s),type=%s)\n", | ||
1442 | reg->id, mlxsw_reg_id_str(reg->id), | ||
1443 | mlxsw_core_reg_access_type_str(type)); | ||
1444 | |||
1317 | in_mbox = mlxsw_cmd_mbox_alloc(); | 1445 | in_mbox = mlxsw_cmd_mbox_alloc(); |
1318 | if (!in_mbox) | 1446 | if (!in_mbox) |
1319 | return -ENOMEM; | 1447 | return -ENOMEM; |
@@ -1324,7 +1452,8 @@ static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core, | |||
1324 | goto free_in_mbox; | 1452 | goto free_in_mbox; |
1325 | } | 1453 | } |
1326 | 1454 | ||
1327 | mlxsw_emad_pack_op_tlv(in_mbox, reg, type, mlxsw_core); | 1455 | mlxsw_emad_pack_op_tlv(in_mbox, reg, type, |
1456 | mlxsw_core_tid_get(mlxsw_core)); | ||
1328 | tmp = in_mbox + MLXSW_EMAD_OP_TLV_LEN * sizeof(u32); | 1457 | tmp = in_mbox + MLXSW_EMAD_OP_TLV_LEN * sizeof(u32); |
1329 | mlxsw_emad_pack_reg_tlv(tmp, reg, payload); | 1458 | mlxsw_emad_pack_reg_tlv(tmp, reg, payload); |
1330 | 1459 | ||
@@ -1332,60 +1461,61 @@ static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core, | |||
1332 | retry: | 1461 | retry: |
1333 | err = mlxsw_cmd_access_reg(mlxsw_core, in_mbox, out_mbox); | 1462 | err = mlxsw_cmd_access_reg(mlxsw_core, in_mbox, out_mbox); |
1334 | if (!err) { | 1463 | if (!err) { |
1335 | err = mlxsw_emad_process_status(mlxsw_core, out_mbox); | 1464 | err = mlxsw_emad_process_status(out_mbox, &status); |
1336 | if (err == -EAGAIN && n_retry++ < MLXSW_EMAD_MAX_RETRY) | 1465 | if (err) { |
1337 | goto retry; | 1466 | if (err == -EAGAIN && n_retry++ < MLXSW_EMAD_MAX_RETRY) |
1467 | goto retry; | ||
1468 | dev_err(mlxsw_core->bus_info->dev, "Reg cmd access status failed (status=%x(%s))\n", | ||
1469 | status, mlxsw_emad_op_tlv_status_str(status)); | ||
1470 | } | ||
1338 | } | 1471 | } |
1339 | 1472 | ||
1340 | if (!err) | 1473 | if (!err) |
1341 | memcpy(payload, mlxsw_emad_reg_payload(out_mbox), | 1474 | memcpy(payload, mlxsw_emad_reg_payload(out_mbox), |
1342 | reg->len); | 1475 | reg->len); |
1343 | 1476 | ||
1344 | mlxsw_core->emad.tid++; | ||
1345 | mlxsw_cmd_mbox_free(out_mbox); | 1477 | mlxsw_cmd_mbox_free(out_mbox); |
1346 | free_in_mbox: | 1478 | free_in_mbox: |
1347 | mlxsw_cmd_mbox_free(in_mbox); | 1479 | mlxsw_cmd_mbox_free(in_mbox); |
1480 | if (err) | ||
1481 | dev_err(mlxsw_core->bus_info->dev, "Reg cmd access failed (reg_id=%x(%s),type=%s)\n", | ||
1482 | reg->id, mlxsw_reg_id_str(reg->id), | ||
1483 | mlxsw_core_reg_access_type_str(type)); | ||
1348 | return err; | 1484 | return err; |
1349 | } | 1485 | } |
1350 | 1486 | ||
1487 | static void mlxsw_core_reg_access_cb(struct mlxsw_core *mlxsw_core, | ||
1488 | char *payload, size_t payload_len, | ||
1489 | unsigned long cb_priv) | ||
1490 | { | ||
1491 | char *orig_payload = (char *) cb_priv; | ||
1492 | |||
1493 | memcpy(orig_payload, payload, payload_len); | ||
1494 | } | ||
1495 | |||
1351 | static int mlxsw_core_reg_access(struct mlxsw_core *mlxsw_core, | 1496 | static int mlxsw_core_reg_access(struct mlxsw_core *mlxsw_core, |
1352 | const struct mlxsw_reg_info *reg, | 1497 | const struct mlxsw_reg_info *reg, |
1353 | char *payload, | 1498 | char *payload, |
1354 | enum mlxsw_core_reg_access_type type) | 1499 | enum mlxsw_core_reg_access_type type) |
1355 | { | 1500 | { |
1356 | u64 cur_tid; | 1501 | LIST_HEAD(bulk_list); |
1357 | int err; | 1502 | int err; |
1358 | 1503 | ||
1359 | if (mutex_lock_interruptible(&mlxsw_core->emad.lock)) { | ||
1360 | dev_err(mlxsw_core->bus_info->dev, "Reg access interrupted (reg_id=%x(%s),type=%s)\n", | ||
1361 | reg->id, mlxsw_reg_id_str(reg->id), | ||
1362 | mlxsw_core_reg_access_type_str(type)); | ||
1363 | return -EINTR; | ||
1364 | } | ||
1365 | |||
1366 | cur_tid = mlxsw_core->emad.tid; | ||
1367 | dev_dbg(mlxsw_core->bus_info->dev, "Reg access (tid=%llx,reg_id=%x(%s),type=%s)\n", | ||
1368 | cur_tid, reg->id, mlxsw_reg_id_str(reg->id), | ||
1369 | mlxsw_core_reg_access_type_str(type)); | ||
1370 | |||
1371 | /* During initialization EMAD interface is not available to us, | 1504 | /* During initialization EMAD interface is not available to us, |
1372 | * so we default to command interface. We switch to EMAD interface | 1505 | * so we default to command interface. We switch to EMAD interface |
1373 | * after setting the appropriate traps. | 1506 | * after setting the appropriate traps. |
1374 | */ | 1507 | */ |
1375 | if (!mlxsw_core->emad.use_emad) | 1508 | if (!mlxsw_core->emad.use_emad) |
1376 | err = mlxsw_core_reg_access_cmd(mlxsw_core, reg, | 1509 | return mlxsw_core_reg_access_cmd(mlxsw_core, reg, |
1377 | payload, type); | ||
1378 | else | ||
1379 | err = mlxsw_core_reg_access_emad(mlxsw_core, reg, | ||
1380 | payload, type); | 1510 | payload, type); |
1381 | 1511 | ||
1512 | err = mlxsw_core_reg_access_emad(mlxsw_core, reg, | ||
1513 | payload, type, &bulk_list, | ||
1514 | mlxsw_core_reg_access_cb, | ||
1515 | (unsigned long) payload); | ||
1382 | if (err) | 1516 | if (err) |
1383 | dev_err(mlxsw_core->bus_info->dev, "Reg access failed (tid=%llx,reg_id=%x(%s),type=%s)\n", | 1517 | return err; |
1384 | cur_tid, reg->id, mlxsw_reg_id_str(reg->id), | 1518 | return mlxsw_reg_trans_bulk_wait(&bulk_list); |
1385 | mlxsw_core_reg_access_type_str(type)); | ||
1386 | |||
1387 | mutex_unlock(&mlxsw_core->emad.lock); | ||
1388 | return err; | ||
1389 | } | 1519 | } |
1390 | 1520 | ||
1391 | int mlxsw_reg_query(struct mlxsw_core *mlxsw_core, | 1521 | int mlxsw_reg_query(struct mlxsw_core *mlxsw_core, |
@@ -1536,6 +1666,24 @@ void mlxsw_core_port_fini(struct mlxsw_core_port *mlxsw_core_port) | |||
1536 | } | 1666 | } |
1537 | EXPORT_SYMBOL(mlxsw_core_port_fini); | 1667 | EXPORT_SYMBOL(mlxsw_core_port_fini); |
1538 | 1668 | ||
1669 | static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core, | ||
1670 | const char *buf, size_t size) | ||
1671 | { | ||
1672 | __be32 *m = (__be32 *) buf; | ||
1673 | int i; | ||
1674 | int count = size / sizeof(__be32); | ||
1675 | |||
1676 | for (i = count - 1; i >= 0; i--) | ||
1677 | if (m[i]) | ||
1678 | break; | ||
1679 | i++; | ||
1680 | count = i ? i : 1; | ||
1681 | for (i = 0; i < count; i += 4) | ||
1682 | dev_dbg(mlxsw_core->bus_info->dev, "%04x - %08x %08x %08x %08x\n", | ||
1683 | i * 4, be32_to_cpu(m[i]), be32_to_cpu(m[i + 1]), | ||
1684 | be32_to_cpu(m[i + 2]), be32_to_cpu(m[i + 3])); | ||
1685 | } | ||
1686 | |||
1539 | int mlxsw_cmd_exec(struct mlxsw_core *mlxsw_core, u16 opcode, u8 opcode_mod, | 1687 | int mlxsw_cmd_exec(struct mlxsw_core *mlxsw_core, u16 opcode, u8 opcode_mod, |
1540 | u32 in_mod, bool out_mbox_direct, | 1688 | u32 in_mod, bool out_mbox_direct, |
1541 | char *in_mbox, size_t in_mbox_size, | 1689 | char *in_mbox, size_t in_mbox_size, |
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index b41ebf8cad72..436bc49df6ab 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h | |||
@@ -109,6 +109,19 @@ void mlxsw_core_event_listener_unregister(struct mlxsw_core *mlxsw_core, | |||
109 | const struct mlxsw_event_listener *el, | 109 | const struct mlxsw_event_listener *el, |
110 | void *priv); | 110 | void *priv); |
111 | 111 | ||
112 | typedef void mlxsw_reg_trans_cb_t(struct mlxsw_core *mlxsw_core, char *payload, | ||
113 | size_t payload_len, unsigned long cb_priv); | ||
114 | |||
115 | int mlxsw_reg_trans_query(struct mlxsw_core *mlxsw_core, | ||
116 | const struct mlxsw_reg_info *reg, char *payload, | ||
117 | struct list_head *bulk_list, | ||
118 | mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv); | ||
119 | int mlxsw_reg_trans_write(struct mlxsw_core *mlxsw_core, | ||
120 | const struct mlxsw_reg_info *reg, char *payload, | ||
121 | struct list_head *bulk_list, | ||
122 | mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv); | ||
123 | int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list); | ||
124 | |||
112 | int mlxsw_reg_query(struct mlxsw_core *mlxsw_core, | 125 | int mlxsw_reg_query(struct mlxsw_core *mlxsw_core, |
113 | const struct mlxsw_reg_info *reg, char *payload); | 126 | const struct mlxsw_reg_info *reg, char *payload); |
114 | int mlxsw_reg_write(struct mlxsw_core *mlxsw_core, | 127 | int mlxsw_reg_write(struct mlxsw_core *mlxsw_core, |