]>
git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blob - net/ncsi/ncsi-aen.c
2 * Copyright Gavin Shan, IBM Corporation 2016.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/netdevice.h>
14 #include <linux/skbuff.h>
17 #include <net/net_namespace.h>
23 static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr
*h
,
24 const unsigned short payload
)
29 if (h
->common
.revision
!= NCSI_PKT_REVISION
)
31 if (ntohs(h
->common
.length
) != payload
)
34 /* Validate checksum, which might be zeroes if the
35 * sender doesn't support checksum according to NCSI
38 pchecksum
= (__be32
*)((void *)(h
+ 1) + payload
- 4);
39 if (ntohl(*pchecksum
) == 0)
42 checksum
= ncsi_calculate_checksum((unsigned char *)h
,
43 sizeof(*h
) + payload
- 4);
44 if (*pchecksum
!= htonl(checksum
))
50 static int ncsi_aen_handler_lsc(struct ncsi_dev_priv
*ndp
,
51 struct ncsi_aen_pkt_hdr
*h
)
53 struct ncsi_channel
*nc
, *tmp
;
54 struct ncsi_channel_mode
*ncm
;
55 unsigned long old_data
, data
;
56 struct ncsi_aen_lsc_pkt
*lsc
;
57 struct ncsi_package
*np
;
58 bool had_link
, has_link
;
63 /* Find the NCSI channel */
64 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
68 /* Update the link status */
69 lsc
= (struct ncsi_aen_lsc_pkt
*)h
;
71 spin_lock_irqsave(&nc
->lock
, flags
);
72 ncm
= &nc
->modes
[NCSI_MODE_LINK
];
73 old_data
= ncm
->data
[2];
74 data
= ntohl(lsc
->status
);
76 ncm
->data
[4] = ntohl(lsc
->oem_status
);
78 had_link
= !!(old_data
& 0x1);
79 has_link
= !!(data
& 0x1);
81 netdev_dbg(ndp
->ndev
.dev
, "NCSI: LSC AEN - channel %u state %s\n",
82 nc
->id
, data
& 0x1 ? "up" : "down");
84 chained
= !list_empty(&nc
->link
);
86 spin_unlock_irqrestore(&nc
->lock
, flags
);
88 if (state
== NCSI_CHANNEL_INACTIVE
)
89 netdev_warn(ndp
->ndev
.dev
,
90 "NCSI: Inactive channel %u received AEN!\n",
93 if ((had_link
== has_link
) || chained
)
96 if (!ndp
->multi_package
&& !nc
->package
->multi_channel
) {
98 ndp
->flags
|= NCSI_DEV_RESHUFFLE
;
99 ncsi_stop_channel_monitor(nc
);
100 spin_lock_irqsave(&ndp
->lock
, flags
);
101 list_add_tail_rcu(&nc
->link
, &ndp
->channel_queue
);
102 spin_unlock_irqrestore(&ndp
->lock
, flags
);
103 return ncsi_process_next_channel(ndp
);
105 /* Configured channel came up */
110 ncm
= &nc
->modes
[NCSI_MODE_TX_ENABLE
];
111 if (ncsi_channel_is_last(ndp
, nc
)) {
112 /* No channels left, reconfigure */
113 return ncsi_reset_dev(&ndp
->ndev
);
114 } else if (ncm
->enable
) {
115 /* Need to failover Tx channel */
116 ncsi_update_tx_channel(ndp
, nc
->package
, nc
, NULL
);
118 } else if (has_link
&& nc
->package
->preferred_channel
== nc
) {
119 /* Return Tx to preferred channel */
120 ncsi_update_tx_channel(ndp
, nc
->package
, NULL
, nc
);
121 } else if (has_link
) {
122 NCSI_FOR_EACH_PACKAGE(ndp
, np
) {
123 NCSI_FOR_EACH_CHANNEL(np
, tmp
) {
124 /* Enable Tx on this channel if the current Tx
127 ncm
= &tmp
->modes
[NCSI_MODE_TX_ENABLE
];
129 !ncsi_channel_has_link(tmp
)) {
130 ncsi_update_tx_channel(ndp
, nc
->package
,
138 /* Leave configured channels active in a multi-channel scenario so
139 * AEN events are still received.
144 static int ncsi_aen_handler_cr(struct ncsi_dev_priv
*ndp
,
145 struct ncsi_aen_pkt_hdr
*h
)
147 struct ncsi_channel
*nc
;
150 /* Find the NCSI channel */
151 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
155 spin_lock_irqsave(&nc
->lock
, flags
);
156 if (!list_empty(&nc
->link
) ||
157 nc
->state
!= NCSI_CHANNEL_ACTIVE
) {
158 spin_unlock_irqrestore(&nc
->lock
, flags
);
161 spin_unlock_irqrestore(&nc
->lock
, flags
);
163 ncsi_stop_channel_monitor(nc
);
164 spin_lock_irqsave(&nc
->lock
, flags
);
165 nc
->state
= NCSI_CHANNEL_INVISIBLE
;
166 spin_unlock_irqrestore(&nc
->lock
, flags
);
168 spin_lock_irqsave(&ndp
->lock
, flags
);
169 nc
->state
= NCSI_CHANNEL_INACTIVE
;
170 list_add_tail_rcu(&nc
->link
, &ndp
->channel_queue
);
171 spin_unlock_irqrestore(&ndp
->lock
, flags
);
173 return ncsi_process_next_channel(ndp
);
176 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv
*ndp
,
177 struct ncsi_aen_pkt_hdr
*h
)
179 struct ncsi_channel
*nc
;
180 struct ncsi_channel_mode
*ncm
;
181 struct ncsi_aen_hncdsc_pkt
*hncdsc
;
184 /* Find the NCSI channel */
185 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
189 spin_lock_irqsave(&nc
->lock
, flags
);
190 ncm
= &nc
->modes
[NCSI_MODE_LINK
];
191 hncdsc
= (struct ncsi_aen_hncdsc_pkt
*)h
;
192 ncm
->data
[3] = ntohl(hncdsc
->status
);
193 spin_unlock_irqrestore(&nc
->lock
, flags
);
194 netdev_dbg(ndp
->ndev
.dev
,
195 "NCSI: host driver %srunning on channel %u\n",
196 ncm
->data
[3] & 0x1 ? "" : "not ", nc
->id
);
201 static struct ncsi_aen_handler
{
204 int (*handler
)(struct ncsi_dev_priv
*ndp
,
205 struct ncsi_aen_pkt_hdr
*h
);
206 } ncsi_aen_handlers
[] = {
207 { NCSI_PKT_AEN_LSC
, 12, ncsi_aen_handler_lsc
},
208 { NCSI_PKT_AEN_CR
, 4, ncsi_aen_handler_cr
},
209 { NCSI_PKT_AEN_HNCDSC
, 8, ncsi_aen_handler_hncdsc
}
212 int ncsi_aen_handler(struct ncsi_dev_priv
*ndp
, struct sk_buff
*skb
)
214 struct ncsi_aen_pkt_hdr
*h
;
215 struct ncsi_aen_handler
*nah
= NULL
;
218 /* Find the handler */
219 h
= (struct ncsi_aen_pkt_hdr
*)skb_network_header(skb
);
220 for (i
= 0; i
< ARRAY_SIZE(ncsi_aen_handlers
); i
++) {
221 if (ncsi_aen_handlers
[i
].type
== h
->type
) {
222 nah
= &ncsi_aen_handlers
[i
];
228 netdev_warn(ndp
->ndev
.dev
, "Invalid AEN (0x%x) received\n",
233 ret
= ncsi_validate_aen_pkt(h
, nah
->payload
);
235 netdev_warn(ndp
->ndev
.dev
,
236 "NCSI: 'bad' packet ignored for AEN type 0x%x\n",
241 ret
= nah
->handler(ndp
, h
);
243 netdev_err(ndp
->ndev
.dev
,
244 "NCSI: Handler for AEN type 0x%x returned %d\n",