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 netdev_dbg(ndp
->ndev
.dev
, "NCSI: LSC AEN - channel %u state %s\n",
77 nc
->id
, data
& 0x1 ? "up" : "down");
79 chained
= !list_empty(&nc
->link
);
81 spin_unlock_irqrestore(&nc
->lock
, flags
);
83 if (!((old_data
^ data
) & 0x1) || chained
)
85 if (!(state
== NCSI_CHANNEL_INACTIVE
&& (data
& 0x1)) &&
86 !(state
== NCSI_CHANNEL_ACTIVE
&& !(data
& 0x1)))
89 if (!(ndp
->flags
& NCSI_DEV_HWA
) &&
90 state
== NCSI_CHANNEL_ACTIVE
)
91 ndp
->flags
|= NCSI_DEV_RESHUFFLE
;
93 ncsi_stop_channel_monitor(nc
);
94 spin_lock_irqsave(&ndp
->lock
, flags
);
95 list_add_tail_rcu(&nc
->link
, &ndp
->channel_queue
);
96 spin_unlock_irqrestore(&ndp
->lock
, flags
);
98 return ncsi_process_next_channel(ndp
);
101 static int ncsi_aen_handler_cr(struct ncsi_dev_priv
*ndp
,
102 struct ncsi_aen_pkt_hdr
*h
)
104 struct ncsi_channel
*nc
;
107 /* Find the NCSI channel */
108 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
112 spin_lock_irqsave(&nc
->lock
, flags
);
113 if (!list_empty(&nc
->link
) ||
114 nc
->state
!= NCSI_CHANNEL_ACTIVE
) {
115 spin_unlock_irqrestore(&nc
->lock
, flags
);
118 spin_unlock_irqrestore(&nc
->lock
, flags
);
120 ncsi_stop_channel_monitor(nc
);
121 spin_lock_irqsave(&nc
->lock
, flags
);
122 nc
->state
= NCSI_CHANNEL_INVISIBLE
;
123 spin_unlock_irqrestore(&nc
->lock
, flags
);
125 spin_lock_irqsave(&ndp
->lock
, flags
);
126 nc
->state
= NCSI_CHANNEL_INACTIVE
;
127 list_add_tail_rcu(&nc
->link
, &ndp
->channel_queue
);
128 spin_unlock_irqrestore(&ndp
->lock
, flags
);
130 return ncsi_process_next_channel(ndp
);
133 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv
*ndp
,
134 struct ncsi_aen_pkt_hdr
*h
)
136 struct ncsi_channel
*nc
;
137 struct ncsi_channel_mode
*ncm
;
138 struct ncsi_aen_hncdsc_pkt
*hncdsc
;
141 /* Find the NCSI channel */
142 ncsi_find_package_and_channel(ndp
, h
->common
.channel
, NULL
, &nc
);
146 spin_lock_irqsave(&nc
->lock
, flags
);
147 ncm
= &nc
->modes
[NCSI_MODE_LINK
];
148 hncdsc
= (struct ncsi_aen_hncdsc_pkt
*)h
;
149 ncm
->data
[3] = ntohl(hncdsc
->status
);
150 spin_unlock_irqrestore(&nc
->lock
, flags
);
151 netdev_dbg(ndp
->ndev
.dev
,
152 "NCSI: host driver %srunning on channel %u\n",
153 ncm
->data
[3] & 0x1 ? "" : "not ", nc
->id
);
158 static struct ncsi_aen_handler
{
161 int (*handler
)(struct ncsi_dev_priv
*ndp
,
162 struct ncsi_aen_pkt_hdr
*h
);
163 } ncsi_aen_handlers
[] = {
164 { NCSI_PKT_AEN_LSC
, 12, ncsi_aen_handler_lsc
},
165 { NCSI_PKT_AEN_CR
, 4, ncsi_aen_handler_cr
},
166 { NCSI_PKT_AEN_HNCDSC
, 8, ncsi_aen_handler_hncdsc
}
169 int ncsi_aen_handler(struct ncsi_dev_priv
*ndp
, struct sk_buff
*skb
)
171 struct ncsi_aen_pkt_hdr
*h
;
172 struct ncsi_aen_handler
*nah
= NULL
;
175 /* Find the handler */
176 h
= (struct ncsi_aen_pkt_hdr
*)skb_network_header(skb
);
177 for (i
= 0; i
< ARRAY_SIZE(ncsi_aen_handlers
); i
++) {
178 if (ncsi_aen_handlers
[i
].type
== h
->type
) {
179 nah
= &ncsi_aen_handlers
[i
];
185 netdev_warn(ndp
->ndev
.dev
, "Invalid AEN (0x%x) received\n",
190 ret
= ncsi_validate_aen_pkt(h
, nah
->payload
);
192 netdev_warn(ndp
->ndev
.dev
,
193 "NCSI: 'bad' packet ignored for AEN type 0x%x\n",
198 ret
= nah
->handler(ndp
, h
);
200 netdev_err(ndp
->ndev
.dev
,
201 "NCSI: Handler for AEN type 0x%x returned %d\n",