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_aen_lsc_pkt
*lsc
;
54 struct ncsi_channel
*nc
;
55 struct ncsi_channel_mode
*ncm
;
58 unsigned long old_data
, data
;
61 /* Find the NCSI channel */
62 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
66 /* Update the link status */
67 lsc
= (struct ncsi_aen_lsc_pkt
*)h
;
69 spin_lock_irqsave(&nc
->lock
, flags
);
70 ncm
= &nc
->modes
[NCSI_MODE_LINK
];
71 old_data
= ncm
->data
[2];
72 data
= ntohl(lsc
->status
);
74 ncm
->data
[4] = ntohl(lsc
->oem_status
);
76 chained
= !list_empty(&nc
->link
);
78 spin_unlock_irqrestore(&nc
->lock
, flags
);
80 if (!((old_data
^ data
) & 0x1) || chained
)
82 if (!(state
== NCSI_CHANNEL_INACTIVE
&& (data
& 0x1)) &&
83 !(state
== NCSI_CHANNEL_ACTIVE
&& !(data
& 0x1)))
86 if (!(ndp
->flags
& NCSI_DEV_HWA
) &&
87 state
== NCSI_CHANNEL_ACTIVE
)
88 ndp
->flags
|= NCSI_DEV_RESHUFFLE
;
90 ncsi_stop_channel_monitor(nc
);
91 spin_lock_irqsave(&ndp
->lock
, flags
);
92 list_add_tail_rcu(&nc
->link
, &ndp
->channel_queue
);
93 spin_unlock_irqrestore(&ndp
->lock
, flags
);
95 return ncsi_process_next_channel(ndp
);
98 static int ncsi_aen_handler_cr(struct ncsi_dev_priv
*ndp
,
99 struct ncsi_aen_pkt_hdr
*h
)
101 struct ncsi_channel
*nc
;
104 /* Find the NCSI channel */
105 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
109 spin_lock_irqsave(&nc
->lock
, flags
);
110 if (!list_empty(&nc
->link
) ||
111 nc
->state
!= NCSI_CHANNEL_ACTIVE
) {
112 spin_unlock_irqrestore(&nc
->lock
, flags
);
115 spin_unlock_irqrestore(&nc
->lock
, flags
);
117 ncsi_stop_channel_monitor(nc
);
118 spin_lock_irqsave(&nc
->lock
, flags
);
119 nc
->state
= NCSI_CHANNEL_INVISIBLE
;
120 spin_unlock_irqrestore(&nc
->lock
, flags
);
122 spin_lock_irqsave(&ndp
->lock
, flags
);
123 nc
->state
= NCSI_CHANNEL_INACTIVE
;
124 list_add_tail_rcu(&nc
->link
, &ndp
->channel_queue
);
125 spin_unlock_irqrestore(&ndp
->lock
, flags
);
127 return ncsi_process_next_channel(ndp
);
130 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv
*ndp
,
131 struct ncsi_aen_pkt_hdr
*h
)
133 struct ncsi_channel
*nc
;
134 struct ncsi_channel_mode
*ncm
;
135 struct ncsi_aen_hncdsc_pkt
*hncdsc
;
138 /* Find the NCSI channel */
139 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
143 /* If the channel is active one, we need reconfigure it */
144 spin_lock_irqsave(&nc
->lock
, flags
);
145 ncm
= &nc
->modes
[NCSI_MODE_LINK
];
146 hncdsc
= (struct ncsi_aen_hncdsc_pkt
*)h
;
147 ncm
->data
[3] = ntohl(hncdsc
->status
);
148 if (!list_empty(&nc
->link
) ||
149 nc
->state
!= NCSI_CHANNEL_ACTIVE
) {
150 spin_unlock_irqrestore(&nc
->lock
, flags
);
154 spin_unlock_irqrestore(&nc
->lock
, flags
);
155 if (!(ndp
->flags
& NCSI_DEV_HWA
) && !(ncm
->data
[3] & 0x1))
156 ndp
->flags
|= NCSI_DEV_RESHUFFLE
;
158 /* If this channel is the active one and the link doesn't
159 * work, we have to choose another channel to be active one.
160 * The logic here is exactly similar to what we do when link
161 * is down on the active channel.
163 * On the other hand, we need configure it when host driver
164 * state on the active channel becomes ready.
166 ncsi_stop_channel_monitor(nc
);
168 spin_lock_irqsave(&nc
->lock
, flags
);
169 nc
->state
= (ncm
->data
[3] & 0x1) ? NCSI_CHANNEL_INACTIVE
:
171 spin_unlock_irqrestore(&nc
->lock
, flags
);
173 spin_lock_irqsave(&ndp
->lock
, flags
);
174 list_add_tail_rcu(&nc
->link
, &ndp
->channel_queue
);
175 spin_unlock_irqrestore(&ndp
->lock
, flags
);
177 ncsi_process_next_channel(ndp
);
182 static struct ncsi_aen_handler
{
185 int (*handler
)(struct ncsi_dev_priv
*ndp
,
186 struct ncsi_aen_pkt_hdr
*h
);
187 } ncsi_aen_handlers
[] = {
188 { NCSI_PKT_AEN_LSC
, 12, ncsi_aen_handler_lsc
},
189 { NCSI_PKT_AEN_CR
, 4, ncsi_aen_handler_cr
},
190 { NCSI_PKT_AEN_HNCDSC
, 4, ncsi_aen_handler_hncdsc
}
193 int ncsi_aen_handler(struct ncsi_dev_priv
*ndp
, struct sk_buff
*skb
)
195 struct ncsi_aen_pkt_hdr
*h
;
196 struct ncsi_aen_handler
*nah
= NULL
;
199 /* Find the handler */
200 h
= (struct ncsi_aen_pkt_hdr
*)skb_network_header(skb
);
201 for (i
= 0; i
< ARRAY_SIZE(ncsi_aen_handlers
); i
++) {
202 if (ncsi_aen_handlers
[i
].type
== h
->type
) {
203 nah
= &ncsi_aen_handlers
[i
];
209 netdev_warn(ndp
->ndev
.dev
, "Invalid AEN (0x%x) received\n",
214 ret
= ncsi_validate_aen_pkt(h
, nah
->payload
);
218 ret
= nah
->handler(ndp
, h
);