Commit ddbcb794 authored by David S. Miller's avatar David S. Miller

Merge branch 'ncsi'

Gavin Shan says:

====================
NCSI Support

This series rebases on David's linux-net git repo ("master" branch). It's
to support NCSI stack on drivers/net/ethernet/faraday/ftgmac100.c. The
implementation is based on NCSI spec (version: 1.1.0):
https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf

As the following figure shows and defined in NCSI spec:

 * The NC-SI (aka NCSI) is defined as the interface between a (Base)
   Management Controller (BMC) and one or multiple Network Interface
   Controlers (NIC) on host side. The interface is responsible for providing
   external network connectivity for BMC.
 * Each BMC can connect to multiple packages, up to 8. Each package can have
   multiple channels, up to 32. Every package and channel are identified by
   3-bits and 5-bits in NCSI packet.
 * NCSI packet, encapsulated in ethernet frame, has 0x88F8 in the protocol
   field. The destination MAC address should be 0xFF's while the source MAC
   address can be arbitrary one.
 * NCSI packets are classified to command, response, AEN (Asynchronous Event Notification).
   Commands are sent from BMC to host (NIC) for configuration and
   information retrival. Responses, corresponding to commands, are sent from
   host to BMC for confirmation and requested information. One command should
   have one and only one response. AEN is sent from host to BMC for notification
   (e.g. link down on active channel) so that BMC can take appropriate action.

   +------------------+        +----------------------------------------------+
   |                  |        |                     Host                     |
   |        BMC       |        |                                              |
   |                  |        | +-------------------+  +-------------------+ |
   |    +---------+   |        | |     Package-A     |  |     Package-B     | |
   |    |         |   |        | +---------+---------+  +-------------------+ |
   |    |ftgmac100|   |        | | Channel | Channel |  | Channel | Channel | |
   +----+----+----+---+        +-+---------+---------+--+---------+---------+-+
             |                             |                      |
             |                             |                      |
             +-----------------------------+----------------------+

The series of patches is highlighted as:

The design for the patchset is highlighted as below:

 * The network driver uses 3 interfaces exported from NCSI stack:
   ncsi_register_dev() - Register (create) a associated NCSI device.
   ncsi_start_dev() - Bring up the NCSI device.
   ncsi_unregister_dev() - Destroy the registered NCSI device.
 * There are several data structures introduced for different objects:
   struct ncsi_dev - NCSI device seen by network device driver.
   struct ncsi_dev_priv - NCSI device seen by NCSI stack.
   struct ncsi_package - NCSI package which can have multiple channels.
   struct ncsi_channel - NCSI channel.
 * The NCSI stack is driven by workqueue and state machine internally.
 * The all available NCSI packages and channels are enumerated (probed) on
   the first call to ncsi_start_dev(). The NCSI topology won't change until
   the NCSI device is destroyed.
 * All available channels will be brought up When the hardware arbitration
   is enabled. Otherwise, only one channel is selected as active one. The
   NCSI internal is driven by state machine with help of a workqueue. In
   the meanwhile, there are 3 states for each channel which can be put into
   a queue requesting for configuration or suspending. Channels in the queue
   with inactive state set will be configured (bringup) while channels in
   the queue with active state will be suspended (teardown). The request
   configuration or suspending is being applied on the channel if it's in
   invisible state.
 * Failover, another inactive channel is selected as active, can happen when
   the hardware arbitration is disabled. The failover can be caused by timeout
   on link monitor and AEN.
 * NCSI stack should be configurable through netlink or another mechanism, it's
   not implemented in this patchset. It's something TBD.
 * The first NIC driver that is aware of NCSI: drivers/net/ethernet/faraday/ftgmac100.c

Changelog
=========
v2 -> v3:
 * Include (one line) change in include/uapi/linux/if_ether.h to fix build
   error.
v1 -> v2:
 * Support NCSI spec v1.1.0 (3 more commands and 4 hardware arbitration
   modes added).
 * Enable AEN packets according to the supported list.
 * Introduce NCSI channel states and processing queue in order to support
   the hardware arbitration.
 * The hardware arbitration is supported (tested with emulated environment).
 * Introduce link monitor with GLS (Get Link Status) command/response as part
   of the error handling defined in NCSI spec.
 * Support IPv6 address discovery when CONFIG_IPV6 is enabled.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5e31c701 fc6061cf
This diff is collapsed.
#ifndef __NET_NCSI_H
#define __NET_NCSI_H
/*
* The NCSI device states seen from external. More NCSI device states are
* only visible internally (in net/ncsi/internal.h). When the NCSI device
* is registered, it's in ncsi_dev_state_registered state. The state
* ncsi_dev_state_start is used to drive to choose active package and
* channel. After that, its state is changed to ncsi_dev_state_functional.
*
* The state ncsi_dev_state_stop helps to shut down the currently active
* package and channel while ncsi_dev_state_config helps to reconfigure
* them.
*/
enum {
ncsi_dev_state_registered = 0x0000,
ncsi_dev_state_functional = 0x0100,
ncsi_dev_state_probe = 0x0200,
ncsi_dev_state_config = 0x0300,
ncsi_dev_state_suspend = 0x0400,
};
struct ncsi_dev {
int state;
int link_up;
struct net_device *dev;
void (*handler)(struct ncsi_dev *ndev);
};
#ifdef CONFIG_NET_NCSI
struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
void (*notifier)(struct ncsi_dev *nd));
int ncsi_start_dev(struct ncsi_dev *nd);
void ncsi_unregister_dev(struct ncsi_dev *nd);
#else /* !CONFIG_NET_NCSI */
static inline struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
void (*notifier)(struct ncsi_dev *nd))
{
return NULL;
}
static inline int ncsi_start_dev(struct ncsi_dev *nd)
{
return -ENOTTY;
}
static inline void ncsi_unregister_dev(struct ncsi_dev *nd)
{
}
#endif /* CONFIG_NET_NCSI */
#endif /* __NET_NCSI_H */
......@@ -87,6 +87,7 @@
#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */
#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */
#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */
#define ETH_P_NCSI 0x88F8 /* NCSI protocol */
#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */
#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */
#define ETH_P_TDLS 0x890D /* TDLS */
......
......@@ -237,6 +237,7 @@ source "net/hsr/Kconfig"
source "net/switchdev/Kconfig"
source "net/l3mdev/Kconfig"
source "net/qrtr/Kconfig"
source "net/ncsi/Kconfig"
config RPS
bool
......
......@@ -79,3 +79,4 @@ ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
obj-y += l3mdev/
endif
obj-$(CONFIG_QRTR) += qrtr/
obj-$(CONFIG_NET_NCSI) += ncsi/
#
# Configuration for NCSI support
#
config NET_NCSI
bool "NCSI interface support"
depends on INET
---help---
This module provides NCSI (Network Controller Sideband Interface)
support. Enable this only if your system connects to a network
device via NCSI and the ethernet driver you're using supports
the protocol explicitly.
#
# Makefile for NCSI API
#
obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o
This diff is collapsed.
/*
* Copyright Gavin Shan, IBM Corporation 2016.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/ncsi.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include "internal.h"
#include "ncsi-pkt.h"
static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
const unsigned short payload)
{
u32 checksum;
__be32 *pchecksum;
if (h->common.revision != NCSI_PKT_REVISION)
return -EINVAL;
if (ntohs(h->common.length) != payload)
return -EINVAL;
/* Validate checksum, which might be zeroes if the
* sender doesn't support checksum according to NCSI
* specification.
*/
pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
if (ntohl(*pchecksum) == 0)
return 0;
checksum = ncsi_calculate_checksum((unsigned char *)h,
sizeof(*h) + payload - 4);
if (*pchecksum != htonl(checksum))
return -EINVAL;
return 0;
}
static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
struct ncsi_aen_pkt_hdr *h)
{
struct ncsi_aen_lsc_pkt *lsc;
struct ncsi_channel *nc;
struct ncsi_channel_mode *ncm;
unsigned long old_data;
unsigned long flags;
/* Find the NCSI channel */
ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
if (!nc)
return -ENODEV;
/* Update the link status */
ncm = &nc->modes[NCSI_MODE_LINK];
lsc = (struct ncsi_aen_lsc_pkt *)h;
old_data = ncm->data[2];
ncm->data[2] = ntohl(lsc->status);
ncm->data[4] = ntohl(lsc->oem_status);
if (!((old_data ^ ncm->data[2]) & 0x1) ||
!list_empty(&nc->link))
return 0;
if (!(nc->state == NCSI_CHANNEL_INACTIVE && (ncm->data[2] & 0x1)) &&
!(nc->state == NCSI_CHANNEL_ACTIVE && !(ncm->data[2] & 0x1)))
return 0;
if (!(ndp->flags & NCSI_DEV_HWA) &&
nc->state == NCSI_CHANNEL_ACTIVE)
ndp->flags |= NCSI_DEV_RESHUFFLE;
ncsi_stop_channel_monitor(nc);
spin_lock_irqsave(&ndp->lock, flags);
list_add_tail_rcu(&nc->link, &ndp->channel_queue);
spin_unlock_irqrestore(&ndp->lock, flags);
return ncsi_process_next_channel(ndp);
}
static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
struct ncsi_aen_pkt_hdr *h)
{
struct ncsi_channel *nc;
unsigned long flags;
/* Find the NCSI channel */
ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
if (!nc)
return -ENODEV;
if (!list_empty(&nc->link) ||
nc->state != NCSI_CHANNEL_ACTIVE)
return 0;
ncsi_stop_channel_monitor(nc);
spin_lock_irqsave(&ndp->lock, flags);
xchg(&nc->state, NCSI_CHANNEL_INACTIVE);
list_add_tail_rcu(&nc->link, &ndp->channel_queue);
spin_unlock_irqrestore(&ndp->lock, flags);
return ncsi_process_next_channel(ndp);
}
static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
struct ncsi_aen_pkt_hdr *h)
{
struct ncsi_channel *nc;
struct ncsi_channel_mode *ncm;
struct ncsi_aen_hncdsc_pkt *hncdsc;
unsigned long flags;
/* Find the NCSI channel */
ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
if (!nc)
return -ENODEV;
/* If the channel is active one, we need reconfigure it */
ncm = &nc->modes[NCSI_MODE_LINK];
hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
ncm->data[3] = ntohl(hncdsc->status);
if (!list_empty(&nc->link) ||
nc->state != NCSI_CHANNEL_ACTIVE ||
(ncm->data[3] & 0x1))
return 0;
if (ndp->flags & NCSI_DEV_HWA)
ndp->flags |= NCSI_DEV_RESHUFFLE;
/* If this channel is the active one and the link doesn't
* work, we have to choose another channel to be active one.
* The logic here is exactly similar to what we do when link
* is down on the active channel.
*/
ncsi_stop_channel_monitor(nc);
spin_lock_irqsave(&ndp->lock, flags);
list_add_tail_rcu(&nc->link, &ndp->channel_queue);
spin_unlock_irqrestore(&ndp->lock, flags);
ncsi_process_next_channel(ndp);
return 0;
}
static struct ncsi_aen_handler {
unsigned char type;
int payload;
int (*handler)(struct ncsi_dev_priv *ndp,
struct ncsi_aen_pkt_hdr *h);
} ncsi_aen_handlers[] = {
{ NCSI_PKT_AEN_LSC, 12, ncsi_aen_handler_lsc },
{ NCSI_PKT_AEN_CR, 4, ncsi_aen_handler_cr },
{ NCSI_PKT_AEN_HNCDSC, 4, ncsi_aen_handler_hncdsc }
};
int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
{
struct ncsi_aen_pkt_hdr *h;
struct ncsi_aen_handler *nah = NULL;
int i, ret;
/* Find the handler */
h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb);
for (i = 0; i < ARRAY_SIZE(ncsi_aen_handlers); i++) {
if (ncsi_aen_handlers[i].type == h->type) {
nah = &ncsi_aen_handlers[i];
break;
}
}
if (!nah) {
netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n",
h->type);
return -ENOENT;
}
ret = ncsi_validate_aen_pkt(h, nah->payload);
if (ret)
goto out;
ret = nah->handler(ndp, h);
out:
consume_skb(skb);
return ret;
}
/*
* Copyright Gavin Shan, IBM Corporation 2016.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/ncsi.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include "internal.h"
#include "ncsi-pkt.h"
u32 ncsi_calculate_checksum(unsigned char *data, int len)
{
u32 checksum = 0;
int i;
for (i = 0; i < len; i += 2)
checksum += (((u32)data[i] << 8) | data[i + 1]);
checksum = (~checksum + 1);
return checksum;
}
/* This function should be called after the data area has been
* populated completely.
*/
static void ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
struct ncsi_cmd_arg *nca)
{
u32 checksum;
__be32 *pchecksum;
h->mc_id = 0;
h->revision = NCSI_PKT_REVISION;
h->reserved = 0;
h->id = nca->id;
h->type = nca->type;
h->channel = NCSI_TO_CHANNEL(nca->package,
nca->channel);
h->length = htons(nca->payload);
h->reserved1[0] = 0;
h->reserved1[1] = 0;
/* Fill with calculated checksum */
checksum = ncsi_calculate_checksum((unsigned char *)h,
sizeof(*h) + nca->payload);
pchecksum = (__be32 *)((void *)h + sizeof(struct ncsi_pkt_hdr) +
nca->payload);
*pchecksum = htonl(checksum);
}
static int ncsi_cmd_handler_default(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_pkt *cmd;
cmd = (struct ncsi_cmd_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_sp(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_sp_pkt *cmd;
cmd = (struct ncsi_cmd_sp_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->hw_arbitration = nca->bytes[0];
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_dc(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_dc_pkt *cmd;
cmd = (struct ncsi_cmd_dc_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->ald = nca->bytes[0];
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_rc(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_rc_pkt *cmd;
cmd = (struct ncsi_cmd_rc_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_ae(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_ae_pkt *cmd;
cmd = (struct ncsi_cmd_ae_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->mc_id = nca->bytes[0];
cmd->mode = htonl(nca->dwords[1]);
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_sl(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_sl_pkt *cmd;
cmd = (struct ncsi_cmd_sl_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->mode = htonl(nca->dwords[0]);
cmd->oem_mode = htonl(nca->dwords[1]);
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_svf(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_svf_pkt *cmd;
cmd = (struct ncsi_cmd_svf_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->vlan = htons(nca->words[0]);
cmd->index = nca->bytes[2];
cmd->enable = nca->bytes[3];
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_ev(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_ev_pkt *cmd;
cmd = (struct ncsi_cmd_ev_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->mode = nca->bytes[0];
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_sma(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_sma_pkt *cmd;
int i;
cmd = (struct ncsi_cmd_sma_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
for (i = 0; i < 6; i++)
cmd->mac[i] = nca->bytes[i];
cmd->index = nca->bytes[6];
cmd->at_e = nca->bytes[7];
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_ebf(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_ebf_pkt *cmd;
cmd = (struct ncsi_cmd_ebf_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->mode = htonl(nca->dwords[0]);
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_egmf(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_egmf_pkt *cmd;
cmd = (struct ncsi_cmd_egmf_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->mode = htonl(nca->dwords[0]);
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static int ncsi_cmd_handler_snfc(struct sk_buff *skb,
struct ncsi_cmd_arg *nca)
{
struct ncsi_cmd_snfc_pkt *cmd;
cmd = (struct ncsi_cmd_snfc_pkt *)skb_put(skb, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->mode = nca->bytes[0];
ncsi_cmd_build_header(&cmd->cmd.common, nca);
return 0;
}
static struct ncsi_cmd_handler {
unsigned char type;
int payload;
int (*handler)(struct sk_buff *skb,
struct ncsi_cmd_arg *nca);
} ncsi_cmd_handlers[] = {
{ NCSI_PKT_CMD_CIS, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_SP, 4, ncsi_cmd_handler_sp },
{ NCSI_PKT_CMD_DP, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_EC, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_DC, 4, ncsi_cmd_handler_dc },
{ NCSI_PKT_CMD_RC, 4, ncsi_cmd_handler_rc },
{ NCSI_PKT_CMD_ECNT, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_DCNT, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_AE, 8, ncsi_cmd_handler_ae },
{ NCSI_PKT_CMD_SL, 8, ncsi_cmd_handler_sl },
{ NCSI_PKT_CMD_GLS, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_SVF, 4, ncsi_cmd_handler_svf },
{ NCSI_PKT_CMD_EV, 4, ncsi_cmd_handler_ev },
{ NCSI_PKT_CMD_DV, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_SMA, 8, ncsi_cmd_handler_sma },
{ NCSI_PKT_CMD_EBF, 4, ncsi_cmd_handler_ebf },
{ NCSI_PKT_CMD_DBF, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_EGMF, 4, ncsi_cmd_handler_egmf },
{ NCSI_PKT_CMD_DGMF, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_SNFC, 4, ncsi_cmd_handler_snfc },
{ NCSI_PKT_CMD_GVI, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_GC, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_GP, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_GCPS, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_GNS, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_GNPTS, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_GPS, 0, ncsi_cmd_handler_default },
{ NCSI_PKT_CMD_OEM, 0, NULL },
{ NCSI_PKT_CMD_PLDM, 0, NULL },
{ NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default }
};
static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
{
struct ncsi_dev_priv *ndp = nca->ndp;
struct ncsi_dev *nd = &ndp->ndev;
struct net_device *dev = nd->dev;
int hlen = LL_RESERVED_SPACE(dev);
int tlen = dev->needed_tailroom;
int len = hlen + tlen;
struct sk_buff *skb;
struct ncsi_request *nr;
nr = ncsi_alloc_request(ndp, nca->driven);
if (!nr)
return NULL;
/* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
* The packet needs padding if its payload is less than 26 bytes to
* meet 64 bytes minimal ethernet frame length.
*/
len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
if (nca->payload < 26)
len += 26;
else
len += nca->payload;
/* Allocate skb */
skb = alloc_skb(len, GFP_ATOMIC);
if (!skb) {
ncsi_free_request(nr);
return NULL;
}
nr->cmd = skb;
skb_reserve(skb, hlen);
skb_reset_network_header(skb);
skb->dev = dev;
skb->protocol = htons(ETH_P_NCSI);
return nr;
}
int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
{
struct ncsi_request *nr;
struct ethhdr *eh;
struct ncsi_cmd_handler *nch = NULL;
int i, ret;
/* Search for the handler */
for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
if (ncsi_cmd_handlers[i].type == nca->type) {
if (ncsi_cmd_handlers[i].handler)
nch = &ncsi_cmd_handlers[i];
else
nch = NULL;
break;
}
}
if (!nch) {
netdev_err(nca->ndp->ndev.dev,
"Cannot send packet with type 0x%02x\n", nca->type);
return -ENOENT;
}
/* Get packet payload length and allocate the request */
nca->payload = nch->payload;
nr = ncsi_alloc_command(nca);
if (!nr)
return -ENOMEM;
/* Prepare the packet */
nca->id = nr->id;
ret = nch->handler(nr->cmd, nca);
if (ret) {
ncsi_free_request(nr);
return ret;
}
/* Fill the ethernet header */
eh = (struct ethhdr *)skb_push(nr->cmd, sizeof(*eh));
eh->h_proto = htons(ETH_P_NCSI);
eth_broadcast_addr(eh->h_dest);
eth_broadcast_addr(eh->h_source);
/* Start the timer for the request that might not have
* corresponding response. Given NCSI is an internal
* connection a 1 second delay should be sufficient.
*/
nr->enabled = true;
mod_timer(&nr->timer, jiffies + 1 * HZ);
/* Send NCSI packet */
skb_get(nr->cmd);
ret = dev_queue_xmit(nr->cmd);
if (ret < 0) {
ncsi_free_request(nr);
return ret;
}
return 0;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment