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 bool chained; 57 int state; 58 unsigned long old_data, data; 59 unsigned long flags; 60 61 /* Find the NCSI channel */ 62 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); 63 if (!nc) 64 return -ENODEV; 65 66 /* Update the link status */ 67 lsc = (struct ncsi_aen_lsc_pkt *)h; 68 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); 73 ncm->data[2] = data; 74 ncm->data[4] = ntohl(lsc->oem_status); 75 76 netdev_info(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n", 77 nc->id, data & 0x1 ? "up" : "down"); 78 79 chained = !list_empty(&nc->link); 80 state = nc->state; 81 spin_unlock_irqrestore(&nc->lock, flags); 82 83 if (!((old_data ^ data) & 0x1) || chained) 84 return 0; 85 if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) && 86 !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1))) 87 return 0; 88 89 if (!(ndp->flags & NCSI_DEV_HWA) && 90 state == NCSI_CHANNEL_ACTIVE) 91 ndp->flags |= NCSI_DEV_RESHUFFLE; 92 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); 97 98 return ncsi_process_next_channel(ndp); 99 } 100 101 static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp, 102 struct ncsi_aen_pkt_hdr *h) 103 { 104 struct ncsi_channel *nc; 105 unsigned long flags; 106 107 /* Find the NCSI channel */ 108 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); 109 if (!nc) 110 return -ENODEV; 111 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); 116 return 0; 117 } 118 spin_unlock_irqrestore(&nc->lock, flags); 119 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); 124 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); 129 130 return ncsi_process_next_channel(ndp); 131 } 132 133 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp, 134 struct ncsi_aen_pkt_hdr *h) 135 { 136 struct ncsi_channel *nc; 137 struct ncsi_channel_mode *ncm; 138 struct ncsi_aen_hncdsc_pkt *hncdsc; 139 unsigned long flags; 140 141 /* Find the NCSI channel */ 142 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); 143 if (!nc) 144 return -ENODEV; 145 146 /* If the channel is active one, we need reconfigure it */ 147 spin_lock_irqsave(&nc->lock, flags); 148 ncm = &nc->modes[NCSI_MODE_LINK]; 149 hncdsc = (struct ncsi_aen_hncdsc_pkt *)h; 150 ncm->data[3] = ntohl(hncdsc->status); 151 netdev_info(ndp->ndev.dev, "NCSI: HNCDSC AEN - channel %u state %s\n", 152 nc->id, ncm->data[3] & 0x3 ? "up" : "down"); 153 if (!list_empty(&nc->link) || 154 nc->state != NCSI_CHANNEL_ACTIVE) { 155 spin_unlock_irqrestore(&nc->lock, flags); 156 return 0; 157 } 158 159 spin_unlock_irqrestore(&nc->lock, flags); 160 if (!(ndp->flags & NCSI_DEV_HWA) && !(ncm->data[3] & 0x1)) 161 ndp->flags |= NCSI_DEV_RESHUFFLE; 162 163 /* If this channel is the active one and the link doesn't 164 * work, we have to choose another channel to be active one. 165 * The logic here is exactly similar to what we do when link 166 * is down on the active channel. 167 * 168 * On the other hand, we need configure it when host driver 169 * state on the active channel becomes ready. 170 */ 171 ncsi_stop_channel_monitor(nc); 172 173 spin_lock_irqsave(&nc->lock, flags); 174 nc->state = (ncm->data[3] & 0x1) ? NCSI_CHANNEL_INACTIVE : 175 NCSI_CHANNEL_ACTIVE; 176 spin_unlock_irqrestore(&nc->lock, flags); 177 178 spin_lock_irqsave(&ndp->lock, flags); 179 list_add_tail_rcu(&nc->link, &ndp->channel_queue); 180 spin_unlock_irqrestore(&ndp->lock, flags); 181 182 ncsi_process_next_channel(ndp); 183 184 return 0; 185 } 186 187 static struct ncsi_aen_handler { 188 unsigned char type; 189 int payload; 190 int (*handler)(struct ncsi_dev_priv *ndp, 191 struct ncsi_aen_pkt_hdr *h); 192 } ncsi_aen_handlers[] = { 193 { NCSI_PKT_AEN_LSC, 12, ncsi_aen_handler_lsc }, 194 { NCSI_PKT_AEN_CR, 4, ncsi_aen_handler_cr }, 195 { NCSI_PKT_AEN_HNCDSC, 8, ncsi_aen_handler_hncdsc } 196 }; 197 198 int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb) 199 { 200 struct ncsi_aen_pkt_hdr *h; 201 struct ncsi_aen_handler *nah = NULL; 202 int i, ret; 203 204 /* Find the handler */ 205 h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb); 206 for (i = 0; i < ARRAY_SIZE(ncsi_aen_handlers); i++) { 207 if (ncsi_aen_handlers[i].type == h->type) { 208 nah = &ncsi_aen_handlers[i]; 209 break; 210 } 211 } 212 213 if (!nah) { 214 netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n", 215 h->type); 216 return -ENOENT; 217 } 218 219 ret = ncsi_validate_aen_pkt(h, nah->payload); 220 if (ret) { 221 netdev_warn(ndp->ndev.dev, 222 "NCSI: 'bad' packet ignored for AEN type 0x%x\n", 223 h->type); 224 goto out; 225 } 226 227 ret = nah->handler(ndp, h); 228 if (ret) 229 netdev_err(ndp->ndev.dev, 230 "NCSI: Handler for AEN type 0x%x returned %d\n", 231 h->type, ret); 232 out: 233 consume_skb(skb); 234 return ret; 235 } 236