ncsi-aen.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. #include <linux/module.h>
  10. #include <linux/kernel.h>
  11. #include <linux/init.h>
  12. #include <linux/netdevice.h>
  13. #include <linux/skbuff.h>
  14. #include <net/ncsi.h>
  15. #include <net/net_namespace.h>
  16. #include <net/sock.h>
  17. #include "internal.h"
  18. #include "ncsi-pkt.h"
  19. static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
  20. const unsigned short payload)
  21. {
  22. u32 checksum;
  23. __be32 *pchecksum;
  24. if (h->common.revision != NCSI_PKT_REVISION)
  25. return -EINVAL;
  26. if (ntohs(h->common.length) != payload)
  27. return -EINVAL;
  28. /* Validate checksum, which might be zeroes if the
  29. * sender doesn't support checksum according to NCSI
  30. * specification.
  31. */
  32. pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
  33. if (ntohl(*pchecksum) == 0)
  34. return 0;
  35. checksum = ncsi_calculate_checksum((unsigned char *)h,
  36. sizeof(*h) + payload - 4);
  37. if (*pchecksum != htonl(checksum))
  38. return -EINVAL;
  39. return 0;
  40. }
  41. static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
  42. struct ncsi_aen_pkt_hdr *h)
  43. {
  44. struct ncsi_aen_lsc_pkt *lsc;
  45. struct ncsi_channel *nc;
  46. struct ncsi_channel_mode *ncm;
  47. bool chained;
  48. int state;
  49. unsigned long old_data, data;
  50. unsigned long flags;
  51. /* Find the NCSI channel */
  52. ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
  53. if (!nc)
  54. return -ENODEV;
  55. /* Update the link status */
  56. lsc = (struct ncsi_aen_lsc_pkt *)h;
  57. spin_lock_irqsave(&nc->lock, flags);
  58. ncm = &nc->modes[NCSI_MODE_LINK];
  59. old_data = ncm->data[2];
  60. data = ntohl(lsc->status);
  61. ncm->data[2] = data;
  62. ncm->data[4] = ntohl(lsc->oem_status);
  63. netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n",
  64. nc->id, data & 0x1 ? "up" : "down");
  65. chained = !list_empty(&nc->link);
  66. state = nc->state;
  67. spin_unlock_irqrestore(&nc->lock, flags);
  68. if (!((old_data ^ data) & 0x1) || chained)
  69. return 0;
  70. if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) &&
  71. !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1)))
  72. return 0;
  73. if (!(ndp->flags & NCSI_DEV_HWA) &&
  74. state == NCSI_CHANNEL_ACTIVE)
  75. ndp->flags |= NCSI_DEV_RESHUFFLE;
  76. ncsi_stop_channel_monitor(nc);
  77. spin_lock_irqsave(&ndp->lock, flags);
  78. list_add_tail_rcu(&nc->link, &ndp->channel_queue);
  79. spin_unlock_irqrestore(&ndp->lock, flags);
  80. return ncsi_process_next_channel(ndp);
  81. }
  82. static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
  83. struct ncsi_aen_pkt_hdr *h)
  84. {
  85. struct ncsi_channel *nc;
  86. unsigned long flags;
  87. /* Find the NCSI channel */
  88. ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
  89. if (!nc)
  90. return -ENODEV;
  91. spin_lock_irqsave(&nc->lock, flags);
  92. if (!list_empty(&nc->link) ||
  93. nc->state != NCSI_CHANNEL_ACTIVE) {
  94. spin_unlock_irqrestore(&nc->lock, flags);
  95. return 0;
  96. }
  97. spin_unlock_irqrestore(&nc->lock, flags);
  98. ncsi_stop_channel_monitor(nc);
  99. spin_lock_irqsave(&nc->lock, flags);
  100. nc->state = NCSI_CHANNEL_INVISIBLE;
  101. spin_unlock_irqrestore(&nc->lock, flags);
  102. spin_lock_irqsave(&ndp->lock, flags);
  103. nc->state = NCSI_CHANNEL_INACTIVE;
  104. list_add_tail_rcu(&nc->link, &ndp->channel_queue);
  105. spin_unlock_irqrestore(&ndp->lock, flags);
  106. return ncsi_process_next_channel(ndp);
  107. }
  108. static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
  109. struct ncsi_aen_pkt_hdr *h)
  110. {
  111. struct ncsi_channel *nc;
  112. struct ncsi_channel_mode *ncm;
  113. struct ncsi_aen_hncdsc_pkt *hncdsc;
  114. unsigned long flags;
  115. /* Find the NCSI channel */
  116. ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
  117. if (!nc)
  118. return -ENODEV;
  119. spin_lock_irqsave(&nc->lock, flags);
  120. ncm = &nc->modes[NCSI_MODE_LINK];
  121. hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
  122. ncm->data[3] = ntohl(hncdsc->status);
  123. spin_unlock_irqrestore(&nc->lock, flags);
  124. netdev_dbg(ndp->ndev.dev,
  125. "NCSI: host driver %srunning on channel %u\n",
  126. ncm->data[3] & 0x1 ? "" : "not ", nc->id);
  127. return 0;
  128. }
  129. static struct ncsi_aen_handler {
  130. unsigned char type;
  131. int payload;
  132. int (*handler)(struct ncsi_dev_priv *ndp,
  133. struct ncsi_aen_pkt_hdr *h);
  134. } ncsi_aen_handlers[] = {
  135. { NCSI_PKT_AEN_LSC, 12, ncsi_aen_handler_lsc },
  136. { NCSI_PKT_AEN_CR, 4, ncsi_aen_handler_cr },
  137. { NCSI_PKT_AEN_HNCDSC, 8, ncsi_aen_handler_hncdsc }
  138. };
  139. int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
  140. {
  141. struct ncsi_aen_pkt_hdr *h;
  142. struct ncsi_aen_handler *nah = NULL;
  143. int i, ret;
  144. /* Find the handler */
  145. h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb);
  146. for (i = 0; i < ARRAY_SIZE(ncsi_aen_handlers); i++) {
  147. if (ncsi_aen_handlers[i].type == h->type) {
  148. nah = &ncsi_aen_handlers[i];
  149. break;
  150. }
  151. }
  152. if (!nah) {
  153. netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n",
  154. h->type);
  155. return -ENOENT;
  156. }
  157. ret = ncsi_validate_aen_pkt(h, nah->payload);
  158. if (ret) {
  159. netdev_warn(ndp->ndev.dev,
  160. "NCSI: 'bad' packet ignored for AEN type 0x%x\n",
  161. h->type);
  162. goto out;
  163. }
  164. ret = nah->handler(ndp, h);
  165. if (ret)
  166. netdev_err(ndp->ndev.dev,
  167. "NCSI: Handler for AEN type 0x%x returned %d\n",
  168. h->type, ret);
  169. out:
  170. consume_skb(skb);
  171. return ret;
  172. }