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 } 194