Skip to content

Commit 7a82ecf

Browse files
Gavin Shandavem330
authored andcommitted
net/ncsi: NCSI AEN packet handler
This introduces NCSI AEN packet handlers that result in (A) the currently active channel is reconfigured; (B) Currently active channel is deconfigured and disabled, another channel is chosen as active one and configured. Case (B) won't happen if hardware arbitration has been enabled, the channel that was in active state is suspended simply. Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com> Acked-by: Joel Stanley <joel@jms.id.au> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent e6f44ed commit 7a82ecf

File tree

5 files changed

+236
-2
lines changed

5 files changed

+236
-2
lines changed

net/ncsi/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#
22
# Makefile for NCSI API
33
#
4-
obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-manage.o
4+
obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o

net/ncsi/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,5 +323,6 @@ u32 ncsi_calculate_checksum(unsigned char *data, int len);
323323
int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
324324
int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
325325
struct packet_type *pt, struct net_device *orig_dev);
326+
int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb);
326327

327328
#endif /* __NCSI_INTERNAL_H__ */

net/ncsi/ncsi-aen.c

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* Copyright Gavin Shan, IBM Corporation 2016.
3+
*
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.
8+
*/
9+
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>
15+
16+
#include <net/ncsi.h>
17+
#include <net/net_namespace.h>
18+
#include <net/sock.h>
19+
20+
#include "internal.h"
21+
#include "ncsi-pkt.h"
22+
23+
static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
24+
const unsigned short payload)
25+
{
26+
u32 checksum;
27+
__be32 *pchecksum;
28+
29+
if (h->common.revision != NCSI_PKT_REVISION)
30+
return -EINVAL;
31+
if (ntohs(h->common.length) != payload)
32+
return -EINVAL;
33+
34+
/* Validate checksum, which might be zeroes if the
35+
* sender doesn't support checksum according to NCSI
36+
* specification.
37+
*/
38+
pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
39+
if (ntohl(*pchecksum) == 0)
40+
return 0;
41+
42+
checksum = ncsi_calculate_checksum((unsigned char *)h,
43+
sizeof(*h) + payload - 4);
44+
if (*pchecksum != htonl(checksum))
45+
return -EINVAL;
46+
47+
return 0;
48+
}
49+
50+
static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
51+
struct ncsi_aen_pkt_hdr *h)
52+
{
53+
struct ncsi_aen_lsc_pkt *lsc;
54+
struct ncsi_channel *nc;
55+
struct ncsi_channel_mode *ncm;
56+
unsigned long old_data;
57+
unsigned long flags;
58+
59+
/* Find the NCSI channel */
60+
ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
61+
if (!nc)
62+
return -ENODEV;
63+
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))
72+
return 0;
73+
if (!(nc->state == NCSI_CHANNEL_INACTIVE && (ncm->data[2] & 0x1)) &&
74+
!(nc->state == NCSI_CHANNEL_ACTIVE && !(ncm->data[2] & 0x1)))
75+
return 0;
76+
77+
if (!(ndp->flags & NCSI_DEV_HWA) &&
78+
nc->state == NCSI_CHANNEL_ACTIVE)
79+
ndp->flags |= NCSI_DEV_RESHUFFLE;
80+
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);
85+
86+
return ncsi_process_next_channel(ndp);
87+
}
88+
89+
static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
90+
struct ncsi_aen_pkt_hdr *h)
91+
{
92+
struct ncsi_channel *nc;
93+
unsigned long flags;
94+
95+
/* Find the NCSI channel */
96+
ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
97+
if (!nc)
98+
return -ENODEV;
99+
100+
if (!list_empty(&nc->link) ||
101+
nc->state != NCSI_CHANNEL_ACTIVE)
102+
return 0;
103+
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);
109+
110+
return ncsi_process_next_channel(ndp);
111+
}
112+
113+
static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
114+
struct ncsi_aen_pkt_hdr *h)
115+
{
116+
struct ncsi_channel *nc;
117+
struct ncsi_channel_mode *ncm;
118+
struct ncsi_aen_hncdsc_pkt *hncdsc;
119+
unsigned long flags;
120+
121+
/* Find the NCSI channel */
122+
ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
123+
if (!nc)
124+
return -ENODEV;
125+
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))
133+
return 0;
134+
135+
if (ndp->flags & NCSI_DEV_HWA)
136+
ndp->flags |= NCSI_DEV_RESHUFFLE;
137+
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.
142+
*/
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);
147+
148+
ncsi_process_next_channel(ndp);
149+
150+
return 0;
151+
}
152+
153+
static struct ncsi_aen_handler {
154+
unsigned char type;
155+
int payload;
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 }
162+
};
163+
164+
int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
165+
{
166+
struct ncsi_aen_pkt_hdr *h;
167+
struct ncsi_aen_handler *nah = NULL;
168+
int i, ret;
169+
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];
175+
break;
176+
}
177+
}
178+
179+
if (!nah) {
180+
netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n",
181+
h->type);
182+
return -ENOENT;
183+
}
184+
185+
ret = ncsi_validate_aen_pkt(h, nah->payload);
186+
if (ret)
187+
goto out;
188+
189+
ret = nah->handler(ndp, h);
190+
out:
191+
consume_skb(skb);
192+
return ret;
193+
}

net/ncsi/ncsi-pkt.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ struct ncsi_rsp_pkt_hdr {
3131
__be16 reason; /* Response reason */
3232
};
3333

34+
struct ncsi_aen_pkt_hdr {
35+
struct ncsi_pkt_hdr common; /* Common NCSI packet header */
36+
unsigned char reserved2[3]; /* Reserved */
37+
unsigned char type; /* AEN packet type */
38+
};
39+
3440
/* NCSI common command packet */
3541
struct ncsi_cmd_pkt {
3642
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
@@ -296,6 +302,30 @@ struct ncsi_rsp_gpuuid_pkt {
296302
__be32 checksum;
297303
};
298304

305+
/* AEN: Link State Change */
306+
struct ncsi_aen_lsc_pkt {
307+
struct ncsi_aen_pkt_hdr aen; /* AEN header */
308+
__be32 status; /* Link status */
309+
__be32 oem_status; /* OEM link status */
310+
__be32 checksum; /* Checksum */
311+
unsigned char pad[14];
312+
};
313+
314+
/* AEN: Configuration Required */
315+
struct ncsi_aen_cr_pkt {
316+
struct ncsi_aen_pkt_hdr aen; /* AEN header */
317+
__be32 checksum; /* Checksum */
318+
unsigned char pad[22];
319+
};
320+
321+
/* AEN: Host Network Controller Driver Status Change */
322+
struct ncsi_aen_hncdsc_pkt {
323+
struct ncsi_aen_pkt_hdr aen; /* AEN header */
324+
__be32 status; /* Status */
325+
__be32 checksum; /* Checksum */
326+
unsigned char pad[18];
327+
};
328+
299329
/* NCSI packet revision */
300330
#define NCSI_PKT_REVISION 0x01
301331

@@ -376,4 +406,10 @@ struct ncsi_rsp_gpuuid_pkt {
376406
#define NCSI_PKT_RSP_R_LENGTH 0x0005 /* Invalid payload length */
377407
#define NCSI_PKT_RSP_R_UNKNOWN 0x7fff /* Command type unsupported */
378408

409+
/* NCSI AEN packet type */
410+
#define NCSI_PKT_AEN 0xFF /* AEN Packet */
411+
#define NCSI_PKT_AEN_LSC 0x00 /* Link status change */
412+
#define NCSI_PKT_AEN_CR 0x01 /* Configuration required */
413+
#define NCSI_PKT_AEN_HNCDSC 0x02 /* HNC driver status change */
414+
379415
#endif /* __NCSI_PKT_H__ */

net/ncsi/ncsi-rsp.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,8 +980,12 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
980980
if (!ndp)
981981
return -ENODEV;
982982

983-
/* Find the handler */
983+
/* Check if it is AEN packet */
984984
hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
985+
if (hdr->type == NCSI_PKT_AEN)
986+
return ncsi_aen_handler(ndp, skb);
987+
988+
/* Find the handler */
985989
for (i = 0; i < ARRAY_SIZE(ncsi_rsp_handlers); i++) {
986990
if (ncsi_rsp_handlers[i].type == hdr->type) {
987991
if (ncsi_rsp_handlers[i].handler)

0 commit comments

Comments
 (0)