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
;
56 unsigned long old_data
;
59 /* Find the NCSI channel */
60 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
64 /* Update the link status */
65 ncm
= &nc
->modes
[NCSI_MODE_LINK
];
66 lsc
= (struct ncsi_aen_lsc_pkt
*)h
;
67 old_data
= ncm
->data
[2];
68 ncm
->data
[2] = ntohl(lsc
->status
);
69 ncm
->data
[4] = ntohl(lsc
->oem_status
);
70 if (!((old_data
^ ncm
->data
[2]) & 0x1) ||
71 !list_empty(&nc
->link
))
73 if (!(nc
->state
== NCSI_CHANNEL_INACTIVE
&& (ncm
->data
[2] & 0x1)) &&
74 !(nc
->state
== NCSI_CHANNEL_ACTIVE
&& !(ncm
->data
[2] & 0x1)))
77 if (!(ndp
->flags
& NCSI_DEV_HWA
) &&
78 nc
->state
== NCSI_CHANNEL_ACTIVE
)
79 ndp
->flags
|= NCSI_DEV_RESHUFFLE
;
81 ncsi_stop_channel_monitor(nc
);
82 spin_lock_irqsave(&ndp
->lock
, flags
);
83 list_add_tail_rcu(&nc
->link
, &ndp
->channel_queue
);
84 spin_unlock_irqrestore(&ndp
->lock
, flags
);
86 return ncsi_process_next_channel(ndp
);
89 static int ncsi_aen_handler_cr(struct ncsi_dev_priv
*ndp
,
90 struct ncsi_aen_pkt_hdr
*h
)
92 struct ncsi_channel
*nc
;
95 /* Find the NCSI channel */
96 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
100 if (!list_empty(&nc
->link
) ||
101 nc
->state
!= NCSI_CHANNEL_ACTIVE
)
104 ncsi_stop_channel_monitor(nc
);
105 spin_lock_irqsave(&ndp
->lock
, flags
);
106 xchg(&nc
->state
, NCSI_CHANNEL_INACTIVE
);
107 list_add_tail_rcu(&nc
->link
, &ndp
->channel_queue
);
108 spin_unlock_irqrestore(&ndp
->lock
, flags
);
110 return ncsi_process_next_channel(ndp
);
113 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv
*ndp
,
114 struct ncsi_aen_pkt_hdr
*h
)
116 struct ncsi_channel
*nc
;
117 struct ncsi_channel_mode
*ncm
;
118 struct ncsi_aen_hncdsc_pkt
*hncdsc
;
121 /* Find the NCSI channel */
122 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
126 /* If the channel is active one, we need reconfigure it */
127 ncm
= &nc
->modes
[NCSI_MODE_LINK
];
128 hncdsc
= (struct ncsi_aen_hncdsc_pkt
*)h
;
129 ncm
->data
[3] = ntohl(hncdsc
->status
);
130 if (!list_empty(&nc
->link
) ||
131 nc
->state
!= NCSI_CHANNEL_ACTIVE
||
132 (ncm
->data
[3] & 0x1))
135 if (ndp
->flags
& NCSI_DEV_HWA
)
136 ndp
->flags
|= NCSI_DEV_RESHUFFLE
;
138 /* If this channel is the active one and the link doesn't
139 * work, we have to choose another channel to be active one.
140 * The logic here is exactly similar to what we do when link
141 * is down on the active channel.
143 ncsi_stop_channel_monitor(nc
);
144 spin_lock_irqsave(&ndp
->lock
, flags
);
145 list_add_tail_rcu(&nc
->link
, &ndp
->channel_queue
);
146 spin_unlock_irqrestore(&ndp
->lock
, flags
);
148 ncsi_process_next_channel(ndp
);
153 static struct ncsi_aen_handler
{
156 int (*handler
)(struct ncsi_dev_priv
*ndp
,
157 struct ncsi_aen_pkt_hdr
*h
);
158 } ncsi_aen_handlers
[] = {
159 { NCSI_PKT_AEN_LSC
, 12, ncsi_aen_handler_lsc
},
160 { NCSI_PKT_AEN_CR
, 4, ncsi_aen_handler_cr
},
161 { NCSI_PKT_AEN_HNCDSC
, 4, ncsi_aen_handler_hncdsc
}
164 int ncsi_aen_handler(struct ncsi_dev_priv
*ndp
, struct sk_buff
*skb
)
166 struct ncsi_aen_pkt_hdr
*h
;
167 struct ncsi_aen_handler
*nah
= NULL
;
170 /* Find the handler */
171 h
= (struct ncsi_aen_pkt_hdr
*)skb_network_header(skb
);
172 for (i
= 0; i
< ARRAY_SIZE(ncsi_aen_handlers
); i
++) {
173 if (ncsi_aen_handlers
[i
].type
== h
->type
) {
174 nah
= &ncsi_aen_handlers
[i
];
180 netdev_warn(ndp
->ndev
.dev
, "Invalid AEN (0x%x) received\n",
185 ret
= ncsi_validate_aen_pkt(h
, nah
->payload
);
189 ret
= nah
->handler(ndp
, h
);