@@ -71,6 +71,7 @@ struct mlxsw_core {
71
71
struct list_head trans_list ;
72
72
spinlock_t trans_list_lock ; /* protects trans_list writes */
73
73
bool use_emad ;
74
+ bool enable_string_tlv ;
74
75
} emad ;
75
76
struct {
76
77
u8 * mapping ; /* lag_id+port_index to local_port mapping */
@@ -249,6 +250,25 @@ MLXSW_ITEM32(emad, op_tlv, class, 0x04, 0, 8);
249
250
*/
250
251
MLXSW_ITEM64 (emad , op_tlv , tid , 0x08 , 0 , 64 );
251
252
253
+ /* emad_string_tlv_type
254
+ * Type of the TLV.
255
+ * Must be set to 0x2 (string TLV).
256
+ */
257
+ MLXSW_ITEM32 (emad , string_tlv , type , 0x00 , 27 , 5 );
258
+
259
+ /* emad_string_tlv_len
260
+ * Length of the string TLV in u32.
261
+ */
262
+ MLXSW_ITEM32 (emad , string_tlv , len , 0x00 , 16 , 11 );
263
+
264
+ #define MLXSW_EMAD_STRING_TLV_STRING_LEN 128
265
+
266
+ /* emad_string_tlv_string
267
+ * String provided by the device's firmware in case of erroneous register access
268
+ */
269
+ MLXSW_ITEM_BUF (emad , string_tlv , string , 0x04 ,
270
+ MLXSW_EMAD_STRING_TLV_STRING_LEN );
271
+
252
272
/* emad_reg_tlv_type
253
273
* Type of the TLV.
254
274
* Must be set to 0x3 (register TLV).
@@ -304,6 +324,12 @@ static void mlxsw_emad_pack_reg_tlv(char *reg_tlv,
304
324
memcpy (reg_tlv + sizeof (u32 ), payload , reg -> len );
305
325
}
306
326
327
+ static void mlxsw_emad_pack_string_tlv (char * string_tlv )
328
+ {
329
+ mlxsw_emad_string_tlv_type_set (string_tlv , MLXSW_EMAD_TLV_TYPE_STRING );
330
+ mlxsw_emad_string_tlv_len_set (string_tlv , MLXSW_EMAD_STRING_TLV_LEN );
331
+ }
332
+
307
333
static void mlxsw_emad_pack_op_tlv (char * op_tlv ,
308
334
const struct mlxsw_reg_info * reg ,
309
335
enum mlxsw_core_reg_access_type type ,
@@ -345,7 +371,7 @@ static void mlxsw_emad_construct(struct sk_buff *skb,
345
371
const struct mlxsw_reg_info * reg ,
346
372
char * payload ,
347
373
enum mlxsw_core_reg_access_type type ,
348
- u64 tid )
374
+ u64 tid , bool enable_string_tlv )
349
375
{
350
376
char * buf ;
351
377
@@ -355,26 +381,82 @@ static void mlxsw_emad_construct(struct sk_buff *skb,
355
381
buf = skb_push (skb , reg -> len + sizeof (u32 ));
356
382
mlxsw_emad_pack_reg_tlv (buf , reg , payload );
357
383
384
+ if (enable_string_tlv ) {
385
+ buf = skb_push (skb , MLXSW_EMAD_STRING_TLV_LEN * sizeof (u32 ));
386
+ mlxsw_emad_pack_string_tlv (buf );
387
+ }
388
+
358
389
buf = skb_push (skb , MLXSW_EMAD_OP_TLV_LEN * sizeof (u32 ));
359
390
mlxsw_emad_pack_op_tlv (buf , reg , type , tid );
360
391
361
392
mlxsw_emad_construct_eth_hdr (skb );
362
393
}
363
394
395
+ struct mlxsw_emad_tlv_offsets {
396
+ u16 op_tlv ;
397
+ u16 string_tlv ;
398
+ u16 reg_tlv ;
399
+ };
400
+
401
+ static bool mlxsw_emad_tlv_is_string_tlv (const char * tlv )
402
+ {
403
+ u8 tlv_type = mlxsw_emad_string_tlv_type_get (tlv );
404
+
405
+ return tlv_type == MLXSW_EMAD_TLV_TYPE_STRING ;
406
+ }
407
+
408
+ static void mlxsw_emad_tlv_parse (struct sk_buff * skb )
409
+ {
410
+ struct mlxsw_emad_tlv_offsets * offsets =
411
+ (struct mlxsw_emad_tlv_offsets * ) skb -> cb ;
412
+
413
+ offsets -> op_tlv = MLXSW_EMAD_ETH_HDR_LEN ;
414
+ offsets -> string_tlv = 0 ;
415
+ offsets -> reg_tlv = MLXSW_EMAD_ETH_HDR_LEN +
416
+ MLXSW_EMAD_OP_TLV_LEN * sizeof (u32 );
417
+
418
+ /* If string TLV is present, it must come after the operation TLV. */
419
+ if (mlxsw_emad_tlv_is_string_tlv (skb -> data + offsets -> reg_tlv )) {
420
+ offsets -> string_tlv = offsets -> reg_tlv ;
421
+ offsets -> reg_tlv += MLXSW_EMAD_STRING_TLV_LEN * sizeof (u32 );
422
+ }
423
+ }
424
+
364
425
static char * mlxsw_emad_op_tlv (const struct sk_buff * skb )
365
426
{
366
- return ((char * ) (skb -> data + MLXSW_EMAD_ETH_HDR_LEN ));
427
+ struct mlxsw_emad_tlv_offsets * offsets =
428
+ (struct mlxsw_emad_tlv_offsets * ) skb -> cb ;
429
+
430
+ return ((char * ) (skb -> data + offsets -> op_tlv ));
431
+ }
432
+
433
+ static char * mlxsw_emad_string_tlv (const struct sk_buff * skb )
434
+ {
435
+ struct mlxsw_emad_tlv_offsets * offsets =
436
+ (struct mlxsw_emad_tlv_offsets * ) skb -> cb ;
437
+
438
+ if (!offsets -> string_tlv )
439
+ return NULL ;
440
+
441
+ return ((char * ) (skb -> data + offsets -> string_tlv ));
367
442
}
368
443
369
444
static char * mlxsw_emad_reg_tlv (const struct sk_buff * skb )
370
445
{
371
- return ((char * ) (skb -> data + MLXSW_EMAD_ETH_HDR_LEN +
372
- MLXSW_EMAD_OP_TLV_LEN * sizeof (u32 )));
446
+ struct mlxsw_emad_tlv_offsets * offsets =
447
+ (struct mlxsw_emad_tlv_offsets * ) skb -> cb ;
448
+
449
+ return ((char * ) (skb -> data + offsets -> reg_tlv ));
450
+ }
451
+
452
+ static char * mlxsw_emad_reg_payload (const char * reg_tlv )
453
+ {
454
+ return ((char * ) (reg_tlv + sizeof (u32 )));
373
455
}
374
456
375
- static char * mlxsw_emad_reg_payload (const char * op_tlv )
457
+ static char * mlxsw_emad_reg_payload_cmd (const char * mbox )
376
458
{
377
- return ((char * ) (op_tlv + (MLXSW_EMAD_OP_TLV_LEN + 1 ) * sizeof (u32 )));
459
+ return ((char * ) (mbox + (MLXSW_EMAD_OP_TLV_LEN + 1 ) * sizeof (u32 )));
378
460
}
379
461
380
462
static u64 mlxsw_emad_get_tid (const struct sk_buff * skb )
@@ -440,10 +522,31 @@ struct mlxsw_reg_trans {
440
522
const struct mlxsw_reg_info * reg ;
441
523
enum mlxsw_core_reg_access_type type ;
442
524
int err ;
525
+ char * emad_err_string ;
443
526
enum mlxsw_emad_op_tlv_status emad_status ;
444
527
struct rcu_head rcu ;
445
528
};
446
529
530
+ static void mlxsw_emad_process_string_tlv (const struct sk_buff * skb ,
531
+ struct mlxsw_reg_trans * trans )
532
+ {
533
+ char * string_tlv ;
534
+ char * string ;
535
+
536
+ string_tlv = mlxsw_emad_string_tlv (skb );
537
+ if (!string_tlv )
538
+ return ;
539
+
540
+ trans -> emad_err_string = kzalloc (MLXSW_EMAD_STRING_TLV_STRING_LEN ,
541
+ GFP_ATOMIC );
542
+ if (!trans -> emad_err_string )
543
+ return ;
544
+
545
+ string = mlxsw_emad_string_tlv_string_data (string_tlv );
546
+ strlcpy (trans -> emad_err_string , string ,
547
+ MLXSW_EMAD_STRING_TLV_STRING_LEN );
548
+ }
549
+
447
550
#define MLXSW_EMAD_TIMEOUT_DURING_FW_FLASH_MS 3000
448
551
#define MLXSW_EMAD_TIMEOUT_MS 200
449
552
@@ -535,12 +638,14 @@ static void mlxsw_emad_process_response(struct mlxsw_core *mlxsw_core,
535
638
mlxsw_emad_transmit_retry (mlxsw_core , trans );
536
639
} else {
537
640
if (err == 0 ) {
538
- char * op_tlv = mlxsw_emad_op_tlv (skb );
641
+ char * reg_tlv = mlxsw_emad_reg_tlv (skb );
539
642
540
643
if (trans -> cb )
541
644
trans -> cb (mlxsw_core ,
542
- mlxsw_emad_reg_payload (op_tlv ),
645
+ mlxsw_emad_reg_payload (reg_tlv ),
543
646
trans -> reg -> len , trans -> cb_priv );
647
+ } else {
648
+ mlxsw_emad_process_string_tlv (skb , trans );
544
649
}
545
650
mlxsw_emad_trans_finish (trans , err );
546
651
}
@@ -556,6 +661,8 @@ static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u8 local_port,
556
661
trace_devlink_hwmsg (priv_to_devlink (mlxsw_core ), true, 0 ,
557
662
skb -> data , skb -> len );
558
663
664
+ mlxsw_emad_tlv_parse (skb );
665
+
559
666
if (!mlxsw_emad_is_resp (skb ))
560
667
goto free_skb ;
561
668
@@ -631,14 +738,16 @@ static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core)
631
738
}
632
739
633
740
static struct sk_buff * mlxsw_emad_alloc (const struct mlxsw_core * mlxsw_core ,
634
- u16 reg_len )
741
+ u16 reg_len , bool enable_string_tlv )
635
742
{
636
743
struct sk_buff * skb ;
637
744
u16 emad_len ;
638
745
639
746
emad_len = (reg_len + sizeof (u32 ) + MLXSW_EMAD_ETH_HDR_LEN +
640
747
(MLXSW_EMAD_OP_TLV_LEN + MLXSW_EMAD_END_TLV_LEN ) *
641
748
sizeof (u32 ) + mlxsw_core -> driver -> txhdr_len );
749
+ if (enable_string_tlv )
750
+ emad_len += MLXSW_EMAD_STRING_TLV_LEN * sizeof (u32 );
642
751
if (emad_len > MLXSW_EMAD_MAX_FRAME_LEN )
643
752
return NULL ;
644
753
@@ -660,14 +769,20 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
660
769
mlxsw_reg_trans_cb_t * cb ,
661
770
unsigned long cb_priv , u64 tid )
662
771
{
772
+ bool enable_string_tlv ;
663
773
struct sk_buff * skb ;
664
774
int err ;
665
775
666
776
dev_dbg (mlxsw_core -> bus_info -> dev , "EMAD reg access (tid=%llx,reg_id=%x(%s),type=%s)\n" ,
667
777
tid , reg -> id , mlxsw_reg_id_str (reg -> id ),
668
778
mlxsw_core_reg_access_type_str (type ));
669
779
670
- skb = mlxsw_emad_alloc (mlxsw_core , reg -> len );
780
+ /* Since this can be changed during emad_reg_access, read it once and
781
+ * use the value all the way.
782
+ */
783
+ enable_string_tlv = mlxsw_core -> emad .enable_string_tlv ;
784
+
785
+ skb = mlxsw_emad_alloc (mlxsw_core , reg -> len , enable_string_tlv );
671
786
if (!skb )
672
787
return - ENOMEM ;
673
788
@@ -684,7 +799,8 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
684
799
trans -> reg = reg ;
685
800
trans -> type = type ;
686
801
687
- mlxsw_emad_construct (skb , reg , payload , type , trans -> tid );
802
+ mlxsw_emad_construct (skb , reg , payload , type , trans -> tid ,
803
+ enable_string_tlv );
688
804
mlxsw_core -> driver -> txhdr_construct (skb , & trans -> tx_info );
689
805
690
806
spin_lock_bh (& mlxsw_core -> emad .trans_list_lock );
@@ -1395,12 +1511,16 @@ static void mlxsw_core_event_listener_func(struct sk_buff *skb, u8 local_port,
1395
1511
struct mlxsw_event_listener_item * event_listener_item = priv ;
1396
1512
struct mlxsw_reg_info reg ;
1397
1513
char * payload ;
1398
- char * op_tlv = mlxsw_emad_op_tlv (skb );
1399
- char * reg_tlv = mlxsw_emad_reg_tlv (skb );
1514
+ char * reg_tlv ;
1515
+ char * op_tlv ;
1516
+
1517
+ mlxsw_emad_tlv_parse (skb );
1518
+ op_tlv = mlxsw_emad_op_tlv (skb );
1519
+ reg_tlv = mlxsw_emad_reg_tlv (skb );
1400
1520
1401
1521
reg .id = mlxsw_emad_op_tlv_register_id_get (op_tlv );
1402
1522
reg .len = (mlxsw_emad_reg_tlv_len_get (reg_tlv ) - 1 ) * sizeof (u32 );
1403
- payload = mlxsw_emad_reg_payload (op_tlv );
1523
+ payload = mlxsw_emad_reg_payload (reg_tlv );
1404
1524
event_listener_item -> el .func (& reg , payload , event_listener_item -> priv );
1405
1525
dev_kfree_skb (skb );
1406
1526
}
@@ -1618,8 +1738,11 @@ int mlxsw_reg_trans_write(struct mlxsw_core *mlxsw_core,
1618
1738
}
1619
1739
EXPORT_SYMBOL (mlxsw_reg_trans_write );
1620
1740
1741
+ #define MLXSW_REG_TRANS_ERR_STRING_SIZE 256
1742
+
1621
1743
static int mlxsw_reg_trans_wait (struct mlxsw_reg_trans * trans )
1622
1744
{
1745
+ char err_string [MLXSW_REG_TRANS_ERR_STRING_SIZE ];
1623
1746
struct mlxsw_core * mlxsw_core = trans -> core ;
1624
1747
int err ;
1625
1748
@@ -1637,9 +1760,17 @@ static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans)
1637
1760
mlxsw_core_reg_access_type_str (trans -> type ),
1638
1761
trans -> emad_status ,
1639
1762
mlxsw_emad_op_tlv_status_str (trans -> emad_status ));
1763
+
1764
+ snprintf (err_string , MLXSW_REG_TRANS_ERR_STRING_SIZE ,
1765
+ "(tid=%llx,reg_id=%x(%s)) %s (%s)\n" , trans -> tid ,
1766
+ trans -> reg -> id , mlxsw_reg_id_str (trans -> reg -> id ),
1767
+ mlxsw_emad_op_tlv_status_str (trans -> emad_status ),
1768
+ trans -> emad_err_string ? trans -> emad_err_string : "" );
1769
+
1640
1770
trace_devlink_hwerr (priv_to_devlink (mlxsw_core ),
1641
- trans -> emad_status ,
1642
- mlxsw_emad_op_tlv_status_str (trans -> emad_status ));
1771
+ trans -> emad_status , err_string );
1772
+
1773
+ kfree (trans -> emad_err_string );
1643
1774
}
1644
1775
1645
1776
list_del (& trans -> bulk_list );
@@ -1713,7 +1844,7 @@ static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core,
1713
1844
}
1714
1845
1715
1846
if (!err )
1716
- memcpy (payload , mlxsw_emad_reg_payload (out_mbox ),
1847
+ memcpy (payload , mlxsw_emad_reg_payload_cmd (out_mbox ),
1717
1848
reg -> len );
1718
1849
1719
1850
mlxsw_cmd_mbox_free (out_mbox );
@@ -2210,6 +2341,12 @@ u32 mlxsw_core_read_frc_l(struct mlxsw_core *mlxsw_core)
2210
2341
}
2211
2342
EXPORT_SYMBOL (mlxsw_core_read_frc_l );
2212
2343
2344
+ void mlxsw_core_emad_string_tlv_enable (struct mlxsw_core * mlxsw_core )
2345
+ {
2346
+ mlxsw_core -> emad .enable_string_tlv = true;
2347
+ }
2348
+ EXPORT_SYMBOL (mlxsw_core_emad_string_tlv_enable );
2349
+
2213
2350
static int __init mlxsw_core_module_init (void )
2214
2351
{
2215
2352
int err ;
0 commit comments