NACK: [SRU][F:linux-bluefield][PATCH v1 1/1] UBUNTU: SAUCE: Add ipmb_host.c driver

Asmaa Mnebhi asmaa at nvidia.com
Tue Jul 5 14:17:40 UTC 2022


Oh I see. Apologies, I misunderstood Zack's request. AFAIK, we have to report bugs targeting 5.15 as follows:
https://bugs.launchpad.net/elbridge/+filebug

I am not sure how to change the privacy setting from there.
Any guidelines would be appreciated.

Thanks.
Asmaa
-----Original Message-----
From: Stefan Bader <stefan.bader at canonical.com> 
Sent: Tuesday, July 5, 2022 10:11 AM
To: Asmaa Mnebhi <asmaa at nvidia.com>; Zachary Tahenakos <zachary.tahenakos at canonical.com>; kernel-team at lists.ubuntu.com
Subject: Re: NACK: [SRU][F:linux-bluefield][PATCH v1 1/1] UBUNTU: SAUCE: Add ipmb_host.c driver

On 05.07.22 15:10, Asmaa Mnebhi wrote:
> Hi Zach,
> 
> This is not how I sent the BugLink and I already discussed this with our IT a while ago. I don't really have control over how outlook controls this.
> This is how I sent the link:
> "Buglink: https://bugs.launchpad.net/bugs/1980532"

This is nothing that Outlook can be blamed for (not that one would like to blame it for a lot). But the bug report was not filed against the "Ubuntu" project and as such the whole report is like the project which was used, private.

-Stefan

> 
> This patch is targeting Elbridge 5.15 kernel, not 5.4 (which already has the ipmb_host.c driver). My understanding is that patches targeting 5.15 should be submitted the same way we did for 5.4? if the label is different for 5.15, please let me know what I should change it to.
> We had a meeting couple of weeks ago with a canonical team to go over the new kernel and how to submit patches for it but I don't think this part was mentioned.
> 
> Thanks.
> Asmaa
> 
> 
> -----Original Message-----
> From: Zachary Tahenakos <zachary.tahenakos at canonical.com>
> Sent: Tuesday, July 5, 2022 8:56 AM
> To: Asmaa Mnebhi <asmaa at nvidia.com>; kernel-team at lists.ubuntu.com
> Subject: NACK: [SRU][F:linux-bluefield][PATCH v1 1/1] UBUNTU: SAUCE: 
> Add ipmb_host.c driver
> 
> [You don't often get email from zachary.tahenakos at canonical.com. Learn 
> why this is important at https://aka.ms/LearnAboutSenderIdentification 
> ]
> 
> This already appears to be in f:bluefield, perhaps the wrong series?
> Also, the buglink is private.
> 
> -Zack
> 
> On 7/1/22 11:11 AM, Asmaa Mnebhi wrote:
>> BugLink:
>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbug
>> s 
>> .launchpad.net%2Fbugs%2F1980532&data=05%7C01%7Casmaa%40nvidia.com
>> % 
>> 7Ca19769bd9541412819fe08da5e85c30d%7C43083d15727340c1b7db39efd9ccc17a
>> % 
>> 7C0%7C0%7C637926225846070925%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAw
>> M 
>> DAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp
>> ;
>> sdata=sMtFpDuWpO9NJ6KPZqtn90D6CvVGVOE1w3OlzLrvRJ4%3D&reserved=0
>>
>> The ipmb_host.c driver allows sending IPMB request from the BF DPU to the BMC.
>>
>> Signed-off-by: Asmaa Mnebhi <asmaa at nvidia.com>
>> ---
>>    drivers/char/ipmi/Kconfig     |  10 +
>>    drivers/char/ipmi/Makefile    |   1 +
>>    drivers/char/ipmi/ipmb_host.c | 836 ++++++++++++++++++++++++++++++++++
>>    3 files changed, 847 insertions(+)
>>    create mode 100644 drivers/char/ipmi/ipmb_host.c
>>
>> diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig 
>> index 249b31197eea..2dc98a164784 100644
>> --- a/drivers/char/ipmi/Kconfig
>> +++ b/drivers/char/ipmi/Kconfig
>> @@ -168,3 +168,13 @@ config IPMB_DEVICE_INTERFACE
>>          Provides a driver for a device (Satellite MC) to
>>          receive requests and send responses back to the BMC via
>>          the IPMB interface. This module requires I2C support.
>> +
>> +config IPMB_HOST
>> +     tristate 'IPMB host handler'
>> +     depends on I2C
>> +     depends on I2C_SLAVE
>> +     help
>> +          Provides a driver for an IPMB interface to a BMC. This driver
>> +       is responsible of sending IPMI requests via the IPMB channel
>> +       and receiving the response.
>> +       This module requires I2C support.
>> diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile 
>> index 84f47d18007f..b5fff7e9695b 100644
>> --- a/drivers/char/ipmi/Makefile
>> +++ b/drivers/char/ipmi/Makefile
>> @@ -29,3 +29,4 @@ obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
>>    obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
>>    obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
>>    obj-$(CONFIG_IPMB_DEVICE_INTERFACE) += ipmb_dev_int.o
>> +obj-$(CONFIG_IPMB_HOST) += ipmb_host.o
>> diff --git a/drivers/char/ipmi/ipmb_host.c 
>> b/drivers/char/ipmi/ipmb_host.c new file mode 100644 index
>> 000000000000..f178a7df0389
>> --- /dev/null
>> +++ b/drivers/char/ipmi/ipmb_host.c
>> @@ -0,0 +1,836 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +
>> +/*
>> + * Copyright 2020, NVIDIA Corporation. All rights reserved.
>> + *
>> + * This was inspired by Brendan Higgins' bt-i2c driver.
>> + *
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/errno.h>
>> +#include <linux/init.h>
>> +#include <linux/ipmi_smi.h>
>> +#include <linux/i2c.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/moduleparam.h>
>> +#include <linux/mutex.h>
>> +#include <linux/sched/signal.h>
>> +#include <linux/slab.h>
>> +#include <linux/timer.h>
>> +#include <linux/types.h>
>> +
>> +#define      IPMB_TIMEOUT                    (msecs_to_jiffies(20000))
>> +
>> +static bool handshake_rsp = false;
>> +
>> +/*
>> + * The least we expect in an IPMB message is:
>> + * netfn_rs_lun, checksum1, rq_sa, rq_seq_rq_lun,
>> + * cmd and checksum2.
>> + */
>> +#define      IPMB_LEN_MIN                    6
>> +
>> +/*
>> + * Within the response message, we need at least
>> + * netfn_rq_lun, checksum1, rs_sa, rq_seq_rs_lun,
>> + * cmd, completion code and checksum2.
>> + */
>> +#define      IPMB_RESPONSE_LEN_MIN           7
>> +
>> +#define      IPMB_MSG_PAYLOAD_LEN_MAX        122
>> +
>> +#define      IPMB_SMI_MSG_PAYLOAD_LEN_MAX    (IPMI_MAX_MSG_LENGTH - 2)
>> +#define      IPMB_MAX_SMI_SIZE               125
>> +#define      IPMB_SMI_MSG_HEADER_SIZE        2
>> +
>> +#define      IPMB_SEQ_MAX                    64
>> +
>> +#define      MAX_BUF_SIZE                    122
>> +
>> +#define      WRITE_TIMEOUT                   25
>> +#define      RSP_QUEUE_MAX_LEN               256
>> +
>> +#define      NETFN_RSP_BIT                   0x4
>> +
>> +#define      GET_SEQ(lun_seq)                (lun_seq >> 2)
>> +
>> +struct ipmb_host_request {
>> +     /*
>> +      * u8 rs_sa;
>> +      * rs_sa (rq_sa for rsp) is not part of the msg struct because
>> +      * it is already integrated within the smbus message format.
>> +      * the first data byte in the smbus message is the netfunction.
>> +      */
>> +     u8 netfn_rs_lun;        /* netfn_rq_lun for rsp */
>> +     u8 checksum1;
>> +     u8 rq_sa;               /* rs_sa for rsp */
>> +     u8 rq_seq_rq_lun;       /* rq_seq_rs_lun for rsp */
>> +     u8 cmd;
>> +     u8 payload[IPMB_MSG_PAYLOAD_LEN_MAX];
>> +     /* checksum2 is the last element of the payload */ } __packed;
>> +
>> +struct ipmb_host_response {
>> +     /*
>> +      * u8 rq_sa;
>> +      * It is not part of the msg struct because
>> +      * it is already integrated within the smbus message format.
>> +      * the first data byte in the smbus message is the netfunction.
>> +      *
>> +      * 'len' is for internal use only. It is not part of the IPMB
>> +      * message.
>> +      */
>> +     u8 len;
>> +     u8 netfn_rq_lun;
>> +     u8 checksum1;
>> +     u8 rs_sa;
>> +     u8 rq_seq_rs_lun;
>> +     u8 cmd;
>> +     /* completion code is the first element of the payload */
>> +     u8 payload[IPMB_MSG_PAYLOAD_LEN_MAX];
>> +     /* checksum2 is the last element of the payload */ } __packed;
>> +
>> +/*
>> + * The ipmb_smi_msg struct is passed by the ipmi_smi_msg struct from
>> + * in the ipmi_msghandler module. So it needs to have the same 
>> +structure
>> + * as ipmi_smi_msg. Refer to the linux code and libraries for
>> + * more details.
>> + */
>> +struct ipmb_smi_msg {
>> +     u8 netfn_lun;
>> +     u8 cmd;
>> +     u8 payload[IPMB_SMI_MSG_PAYLOAD_LEN_MAX];
>> +} __packed;
>> +
>> +struct ipmb_seq_entry {
>> +     struct ipmi_smi_msg     *msg;
>> +     unsigned long           send_time;
>> +};
>> +
>> +struct ipmb_rsp_elem {
>> +     struct list_head          list;
>> +     struct ipmb_host_response rsp;
>> +};
>> +
>> +struct ipmb_master {
>> +     struct ipmi_device_id           ipmi_id;
>> +     /* Used to register this device as a slave device */
>> +     struct i2c_client               *client;
>> +     struct ipmi_smi                 *intf;
>> +     spinlock_t                      lock;
>> +     struct ipmb_seq_entry           seq_msg_map[IPMB_SEQ_MAX];
>> +     struct work_struct              ipmb_send_work;
>> +     struct ipmi_smi_msg             *msg_to_send;
>> +     /* Responder's I2C slave address */
>> +     u32                             rs_sa;
>> +
>> +     /* This is all for the response message */
>> +     size_t                          msg_idx;
>> +     struct ipmb_host_response       rsp;
>> +     struct list_head                rsp_queue;
>> +     atomic_t                        rsp_queue_len;
>> +     wait_queue_head_t               wait_queue;
>> +
>> +     bool                            slave_registered;
>> +};
>> +
>> +/* +1 is for the checksum integrated in payload */ #define 
>> +IPMB_MSG_HDR \
>> +     (sizeof(struct ipmb_host_request) - IPMB_MSG_PAYLOAD_LEN_MAX +
>> +1)
>> +
>> +#define IPMB_SMI_MSG_HDR \
>> +     (sizeof(struct ipmb_smi_msg) - IPMB_SMI_MSG_PAYLOAD_LEN_MAX)
>> +
>> +/*
>> + * ipmb_smi_msg contains a payload and 2 header fields: netfn_lun and cmd.
>> + * Its payload does not contain checksum2.
>> + *
>> + * 'struct ipmb_host_request' and 'struct ipmb_host_response' 
>> +contain a payload (including
>> + * checksum2) and 5 header fields: netfn_r*_lun, checksum1, r*_sa,
>> + * rq_seq_r*_lun, cmd. So we need to add one byte for each field 
>> +which
>> + * is present in the IPMB format and not in ipmb_smi_msg: checksum1,
>> + * r*_sa, rq_seq_r*_lun and checksum2.
>> + *
>> + * Note that 'len' in 'struct ipmb_host_response' is discarded as it 
>> +is for internal
>> + * use only and not part of the actual IPMB message.
>> + */
>> +static u8 ipmi_smi_to_ipmb_len(size_t smi_msg_size) {
>> +     return smi_msg_size + IPMB_MSG_HDR - IPMB_SMI_MSG_HDR; }
>> +
>> +/*
>> + * This function is the converse of the above.
>> + */
>> +static u8 ipmb_to_smi_len(u8 msg_len) {
>> +     return msg_len - (IPMB_MSG_HDR - IPMB_SMI_MSG_HDR); }
>> +
>> +/*
>> + * ipmb_handle_response puts the received response message in
>> + * a queue. The response will eventually be passed on to
>> + * ipmitool.
>> + */
>> +static int ipmb_handle_response(struct ipmb_master *master) {
>> +     struct ipmb_rsp_elem *queue_elem;
>> +     u8 seq;
>> +
>> +     /*
>> +      * If this is a delayed response received after the ipmb_receive_rsp
>> +      * timeout, discard it, don't add it to the queue.
>> +      */
>> +     seq = GET_SEQ(master->rsp.rq_seq_rs_lun);
>> +     if (!master->seq_msg_map[seq].msg)
>> +             return 0;
>> +
>> +     if (atomic_read(&master->rsp_queue_len) >=
>> +                     RSP_QUEUE_MAX_LEN)
>> +             return -EFAULT;
>> +
>> +     queue_elem = kmalloc(sizeof(*queue_elem), GFP_KERNEL);
>> +     if (!queue_elem)
>> +             return -ENOMEM;
>> +     memcpy(&queue_elem->rsp, &master->rsp,
>> +             sizeof(struct ipmb_host_response));
>> +
>> +     list_add(&queue_elem->list, &master->rsp_queue);
>> +     atomic_inc(&master->rsp_queue_len);
>> +     wake_up_all(&master->wait_queue);
>> +     return 0;
>> +}
>> +
>> +/*
>> + * All this function does is send the request msg via I2C by calling
>> + * i2c_master_send
>> + */
>> +static int ipmb_send_request(struct ipmb_master *master,
>> +                             struct ipmb_host_request *request, u8 
>> +i2c_msg_len) {
>> +     struct i2c_client *client = master->client;
>> +     unsigned long timeout, read_time;
>> +     u8 *buf = (u8 *) request;
>> +     int ret;
>> +     union i2c_smbus_data data;
>> +
>> +     /*
>> +      * skip netfn_rs_lun payload since it is passed as arg
>> +      * 5 to i2c_smbus_xfer.
>> +      */
>> +     data.block[0] = i2c_msg_len;
>> +     memcpy(&data.block[1], buf + 1, i2c_msg_len);
>> +
>> +     timeout = jiffies + msecs_to_jiffies(WRITE_TIMEOUT);
>> +     do {
>> +             read_time = jiffies;
>> +
>> +             ret = i2c_smbus_xfer(client->adapter, (u16)master->rs_sa,
>> +                                 client->flags, I2C_SMBUS_WRITE,
>> +                                 request->netfn_rs_lun,
>> +                                 I2C_SMBUS_BLOCK_DATA, &data);
>> +             if (ret == 0)
>> +                     return ret;
>> +             usleep_range(1000, 1500);
>> +     } while (time_before(read_time, timeout));
>> +
>> +     return ret;
>> +}
>> +
>> +static int ipmb_start_processing(void *data, struct ipmi_smi *intf) 
>> +{
>> +     struct ipmb_master *master = data;
>> +
>> +     master->intf = intf;
>> +
>> +     return 0;
>> +}
>> +
>> +static u8 ipmb_checksum1(u8 rs_sa, u8 netfn_rs_lun) {
>> +     u8 csum = rs_sa;
>> +
>> +     csum += netfn_rs_lun;
>> +     return -csum;
>> +}
>> +
>> +static u8 ipmb_checksum(u8 *data, int size, u8 start) {
>> +     u8 csum = start;
>> +
>> +     for (; size > 0; size--, data++)
>> +             csum += *data;
>> +
>> +     return -csum;
>> +}
>> +
>> +static void __ipmb_error_reply(struct ipmb_master *master,
>> +                             struct ipmi_smi_msg *msg,
>> +                             u8 completion_code) {
>> +     struct ipmb_smi_msg *response;
>> +     struct ipmb_smi_msg *request;
>> +
>> +     response = (struct ipmb_smi_msg *) msg->rsp;
>> +     request = (struct ipmb_smi_msg *) msg->data;
>> +
>> +     response->netfn_lun = request->netfn_lun | NETFN_RSP_BIT;
>> +     response->cmd = request->cmd;
>> +     response->payload[0] = completion_code;
>> +     msg->rsp_size = 3;
>> +     ipmi_smi_msg_received(master->intf, msg); }
>> +
>> +static void ipmb_error_reply(struct ipmb_master *master,
>> +                             struct ipmi_smi_msg *msg,
>> +                             u8 completion_code) {
>> +     unsigned long flags;
>> +
>> +     spin_lock_irqsave(&master->lock, flags);
>> +     __ipmb_error_reply(master, msg, completion_code);
>> +     spin_unlock_irqrestore(&master->lock, flags); }
>> +
>> +/*
>> + * This function gets the length of the payload.
>> + * Subtract one byte for each: netfn_rs_lun, checksum1,
>> + * rq_sa, rq_seq_rq_lun, cmd and checksum2  */ static size_t 
>> +ipmb_payload_len(size_t msg_len) {
>> +     return msg_len - 6;
>> +}
>> +
>> +static bool ipmb_assign_seq(struct ipmb_master *master,
>> +                             struct ipmi_smi_msg *msg, u8 *ret_seq) 
>> +{
>> +     struct ipmb_seq_entry *entry;
>> +     bool did_cleanup = false;
>> +     unsigned long flags;
>> +     u8 seq;
>> +
>> +     spin_lock_irqsave(&master->lock, flags);
>> +retry:
>> +     for (seq = 0; seq < IPMB_SEQ_MAX; seq++) {
>> +             if (!master->seq_msg_map[seq].msg) {
>> +                     master->seq_msg_map[seq].msg = msg;
>> +                     master->seq_msg_map[seq].send_time = jiffies;
>> +                     *ret_seq = seq;
>> +                     spin_unlock_irqrestore(&master->lock, flags);
>> +                     return true;
>> +             }
>> +     }
>> +
>> +     if (did_cleanup) {
>> +             spin_unlock_irqrestore(&master->lock, flags);
>> +             return false;
>> +     }
>> +
>> +     /*
>> +      * TODO: we should do cleanup at times other than only when we run out
>> +      * of sequence numbers.
>> +      */
>> +     for (seq = 0; seq < IPMB_SEQ_MAX; seq++) {
>> +             entry = &master->seq_msg_map[seq];
>> +             if (entry->msg &&
>> +                     time_after(entry->send_time + IPMB_TIMEOUT,
>> +                     jiffies)) {
>> +                     __ipmb_error_reply(master, entry->msg,
>> +                                     IPMI_TIMEOUT_ERR);
>> +                     entry->msg = NULL;
>> +             }
>> +     }
>> +     did_cleanup = true;
>> +     goto retry;
>> +}
>> +
>> +static struct ipmi_smi_msg *ipmb_find_msg(
>> +             struct ipmb_master *master, u8 seq) {
>> +     struct ipmi_smi_msg *msg;
>> +     unsigned long flags;
>> +
>> +     spin_lock_irqsave(&master->lock, flags);
>> +     msg = master->seq_msg_map[seq].msg;
>> +     spin_unlock_irqrestore(&master->lock, flags);
>> +     return msg;
>> +}
>> +
>> +static void ipmb_free_seq(struct ipmb_master *master, u8 seq) {
>> +     unsigned long flags;
>> +
>> +     spin_lock_irqsave(&master->lock, flags);
>> +     master->seq_msg_map[seq].msg = NULL;
>> +     spin_unlock_irqrestore(&master->lock, flags); }
>> +
>> +/*
>> + * When this function is called, it waits until receiving an
>> + * IPMI message in the response queue. If a response is found
>> + * in the queue, it will be copied to ipmb_rsp.
>> + * If no response is received after the timeout value, then
>> + * the function returns with an error code. It will return:
>> + * - 0 if there was no msg in the response queue after the timeout 
>> +elapsed
>> + * - A strictly positive number if a msg was found in the queue and
>> + *   ipmb_rsp was successfully populated.
>> + * - A negative value for errors.
>> + */
>> +static int ipmb_receive_rsp(struct ipmb_master *master,
>> +                             struct ipmb_host_response *ipmb_rsp) {
>> +     struct ipmb_rsp_elem    *queue_elem;
>> +     int                     ret = 1;
>> +     unsigned long           flags;
>> +
>> +     spin_lock_irqsave(&master->lock, flags);
>> +
>> +     if (list_empty(&master->rsp_queue)) {
>> +             spin_unlock_irqrestore(&master->lock, flags);
>> +
>> +             ret = wait_event_interruptible_timeout(master->wait_queue,
>> +                     !list_empty(&master->rsp_queue), IPMB_TIMEOUT);
>> +
>> +             if (ret <= 0)
>> +                     return ret;
>> +
>> +             spin_lock_irqsave(&master->lock, flags);
>> +     }
>> +
>> +     queue_elem = list_first_entry(&master->rsp_queue,
>> +                     struct ipmb_rsp_elem, list);
>> +
>> +     memcpy(ipmb_rsp, &queue_elem->rsp, sizeof(struct ipmb_host_response));
>> +     list_del(&queue_elem->list);
>> +     kfree(queue_elem);
>> +     atomic_dec(&master->rsp_queue_len);
>> +     spin_unlock_irqrestore(&master->lock, flags);
>> +
>> +     return ret;
>> +}
>> +
>> +/*
>> + * This function is called by ipmb_sender.
>> + * It checks whether the message to be sent has an acceptable size,
>> + * it assigns a sequence number to the msg
>> + * it calls ipmb_send_request to send the msg to the receiver
>> + * via I2C.
>> + */
>> +static void ipmb_send_workfn(struct work_struct *work) {
>> +     struct ipmb_master              *master;
>> +
>> +     struct ipmb_smi_msg             *smi_msg;
>> +     struct ipmb_host_request        ipmb_req_msg;
>> +     struct ipmi_smi_msg             *req_msg;
>> +
>> +     struct ipmb_smi_msg             *smi_rsp_msg;
>> +     struct ipmb_host_response       ipmb_rsp_msg;
>> +     struct ipmi_smi_msg             *rsp_msg;
>> +
>> +     size_t                          smi_msg_size;
>> +     u8                              msg_len;
>> +     unsigned long                   flags;
>> +     int                             rsp_msg_len;
>> +     u8                              *buf_rsp;
>> +     u8                              verify_checksum;
>> +     u8                              seq;
>> +     u8                              i2c_msg_len;
>> +
>> +     u8 *buf = (u8 *) &ipmb_req_msg;
>> +
>> +     memset(&ipmb_req_msg, 0, sizeof(struct ipmb_host_request));
>> +
>> +     master = container_of(work, struct ipmb_master,
>> +                          ipmb_send_work);
>> +
>> +     req_msg = master->msg_to_send;
>> +     smi_msg_size = req_msg->data_size;
>> +     smi_msg = (struct ipmb_smi_msg *) req_msg->data;
>> +
>> +     if (smi_msg_size > IPMB_MAX_SMI_SIZE) {
>> +             ipmb_error_reply(master, req_msg, IPMI_REQ_LEN_EXCEEDED_ERR);
>> +             return;
>> +     }
>> +
>> +     if (smi_msg_size < IPMB_SMI_MSG_HEADER_SIZE) {
>> +             ipmb_error_reply(master, req_msg, IPMI_REQ_LEN_INVALID_ERR);
>> +             return;
>> +     }
>> +
>> +     if (!ipmb_assign_seq(master, req_msg, &seq)) {
>> +             ipmb_error_reply(master, req_msg, IPMI_NODE_BUSY_ERR);
>> +             return;
>> +     }
>> +
>> +     ipmb_req_msg.rq_seq_rq_lun = seq << 2;
>> +
>> +     msg_len = ipmi_smi_to_ipmb_len(smi_msg_size);
>> +
>> +     /* Responder  */
>> +     ipmb_req_msg.netfn_rs_lun = smi_msg->netfn_lun;
>> +     ipmb_req_msg.checksum1 = ipmb_checksum1((u8)(master->rs_sa << 1),
>> +                                             
>> + ipmb_req_msg.netfn_rs_lun);
>> +
>> +     /* Requester is this device */
>> +     ipmb_req_msg.rq_sa = (u8)(master->client->addr << 1);
>> +     ipmb_req_msg.cmd = smi_msg->cmd;
>> +
>> +     memcpy(ipmb_req_msg.payload, smi_msg->payload,
>> +             ipmb_payload_len((size_t)msg_len));
>> +     ipmb_req_msg.payload[ipmb_payload_len((size_t)msg_len)] =
>> +             ipmb_checksum(buf + 2, msg_len - 2, 0);
>> +
>> +     /*
>> +      * subtract netfn_rs_lun payload since it is passed as arg
>> +      * 5 to i2c_smbus_xfer.
>> +      */
>> +     i2c_msg_len = ipmi_smi_to_ipmb_len(master->msg_to_send->data_size) - 1;
>> +     if (i2c_msg_len > I2C_SMBUS_BLOCK_MAX)
>> +             i2c_msg_len = I2C_SMBUS_BLOCK_MAX;
>> +
>> +     if (ipmb_send_request(master, &ipmb_req_msg, i2c_msg_len) < 0) {
>> +             ipmb_free_seq(master, (GET_SEQ(ipmb_req_msg.rq_seq_rq_lun)));
>> +             ipmb_error_reply(master, req_msg, IPMI_BUS_ERR);
>> +             spin_lock_irqsave(&master->lock, flags);
>> +             master->msg_to_send = NULL;
>> +             spin_unlock_irqrestore(&master->lock, flags);
>> +             return;
>> +     }
>> +
>> +     spin_lock_irqsave(&master->lock, flags);
>> +     master->msg_to_send = NULL;
>> +     spin_unlock_irqrestore(&master->lock, flags);
>> +
>> +     /* Done with sending request. Now handling response */
>> +
>> +     if (ipmb_receive_rsp(master, &ipmb_rsp_msg) <= 0) {
>> +             ipmb_free_seq(master, (GET_SEQ(ipmb_req_msg.rq_seq_rq_lun)));
>> +             ipmb_error_reply(master, req_msg, IPMI_TIMEOUT_ERR);
>> +             return;
>> +     }
>> +
>> +     buf_rsp = (u8 *) &ipmb_rsp_msg;
>> +     /* skip len */
>> +     buf_rsp += 1;
>> +     rsp_msg_len = ipmb_rsp_msg.len - 1;
>> +
>> +     if (rsp_msg_len < IPMB_LEN_MIN) {
>> +             ipmb_free_seq(master, (GET_SEQ(ipmb_req_msg.rq_seq_rq_lun)));
>> +             ipmb_error_reply(master, req_msg, IPMI_ERR_MSG_TRUNCATED);
>> +             return;
>> +     }
>> +
>> +     rsp_msg = ipmb_find_msg(master, (GET_SEQ(ipmb_rsp_msg.rq_seq_rs_lun)));
>> +     if (!rsp_msg) {
>> +             ipmb_free_seq(master, (GET_SEQ(ipmb_req_msg.rq_seq_rq_lun)));
>> +             ipmb_error_reply(master, req_msg, IPMI_ERR_UNSPECIFIED);
>> +             return;
>> +     }
>> +
>> +     ipmb_free_seq(master, (GET_SEQ(ipmb_rsp_msg.rq_seq_rs_lun)));
>> +
>> +     if (rsp_msg_len < IPMB_RESPONSE_LEN_MIN) {
>> +             ipmb_error_reply(master, rsp_msg, IPMI_ERR_MSG_TRUNCATED);
>> +             return;
>> +     }
>> +
>> +     verify_checksum = ipmb_checksum(buf_rsp, rsp_msg_len,
>> +                             (u8)(master->client->addr << 1));
>> +
>> +     if (verify_checksum) {
>> +             ipmb_error_reply(master, req_msg, IPMI_ERR_UNSPECIFIED);
>> +             return;
>> +     }
>> +
>> +     rsp_msg->rsp_size = ipmb_to_smi_len((u8) rsp_msg_len);
>> +     smi_rsp_msg = (struct ipmb_smi_msg *) rsp_msg->rsp;
>> +     smi_rsp_msg->netfn_lun = ipmb_rsp_msg.netfn_rq_lun;
>> +     smi_rsp_msg->cmd = ipmb_rsp_msg.cmd;
>> +     memcpy(smi_rsp_msg->payload, ipmb_rsp_msg.payload,
>> +             ipmb_payload_len((size_t) rsp_msg_len));
>> +
>> +     ipmi_smi_msg_received(master->intf, rsp_msg); }
>> +
>> +/*
>> + * Function called by smi_send in ipmi_msghandler.c
>> + * It passes request message from ipmitool program
>> + * to the host's kernel to the receiver via I2C.
>> + */
>> +static void ipmb_sender(void *data, struct ipmi_smi_msg *msg) {
>> +     struct ipmb_master *master = data;
>> +     unsigned long flags;
>> +
>> +     spin_lock_irqsave(&master->lock, flags);
>> +     if (master->msg_to_send) {
>> +             __ipmb_error_reply(master, msg, IPMI_NODE_BUSY_ERR);
>> +     } else {
>> +             master->msg_to_send = msg;
>> +             schedule_work(&master->ipmb_send_work);
>> +     }
>> +     spin_unlock_irqrestore(&master->lock, flags); }
>> +
>> +static void ipmb_request_events(void *data) { }
>> +
>> +static void ipmb_set_run_to_completion(void *data,
>> +                             bool run_to_completion) { }
>> +
>> +static void ipmb_poll(void *data)
>> +{
>> +}
>> +
>> +static struct ipmi_smi_handlers ipmb_smi_handlers = {
>> +     .owner                  = THIS_MODULE,
>> +     .start_processing       = ipmb_start_processing,
>> +     .sender                 = ipmb_sender,
>> +     .request_events         = ipmb_request_events,
>> +     .set_run_to_completion  = ipmb_set_run_to_completion,
>> +     .poll                   = ipmb_poll,
>> +};
>> +
>> +static bool is_ipmb_response(u8 netfn_rq_lun, size_t msg_len) {
>> +     /*
>> +      * First, check whether the message has the minimum IPMB response size
>> +      */
>> +     if (msg_len >= IPMB_RESPONSE_LEN_MIN) {
>> +             /*
>> +              * Then check whether this is an IPMB request or response.
>> +              * Responses have an odd netfn while requests have an even
>> +              * netfn.
>> +              */
>> +             if ((netfn_rq_lun & NETFN_RSP_BIT) == NETFN_RSP_BIT)
>> +                     return true;
>> +     }
>> +
>> +     return false;
>> +}
>> +
>> +/*
>> + * This is the callback function used to set this device as a slave
>> + * and to monitor and handle only IPMB responses.
>> + *
>> + * This driver's purpose is to:
>> + * 1) send IPMB requests,
>> + * 2) then wait until it receives a response back from the responder.
>> + *    This callback adds that response into a queue so that it is handled
>> + *    later in ipmb_receive_rsp.
>> + */
>> +static int ipmb_slave_cb(struct i2c_client *client,
>> +                     enum i2c_slave_event event, u8 *val) {
>> +     struct ipmb_master *master = i2c_get_clientdata(client);
>> +     u8 *buf;
>> +
>> +     if (!handshake_rsp) {
>> +             handshake_rsp = true;
>> +             return 0;
>> +     }
>> +
>> +     spin_lock(&master->lock);
>> +
>> +     switch (event) {
>> +     case I2C_SLAVE_WRITE_REQUESTED:
>> +             /*
>> +              * The length of the msg is stored at msg_idx 0,
>> +              * which maps to master->rsp.len. Skip it for now.
>> +              * The len will be populated once the whole buf is
>> +              * filled.
>> +              */
>> +             master->msg_idx = 1;
>> +             memset(&master->rsp, 0,
>> +                     sizeof(master->rsp));
>> +             break;
>> +
>> +     case I2C_SLAVE_WRITE_RECEIVED:
>> +             buf = (u8 *) &master->rsp;
>> +
>> +             if (master->msg_idx >= sizeof(struct ipmb_host_response))
>> +                     break;
>> +
>> +             buf[master->msg_idx++] = *val;
>> +             break;
>> +
>> +     case I2C_SLAVE_STOP:
>> +             master->rsp.len = master->msg_idx;
>> +             if (is_ipmb_response(master->rsp.netfn_rq_lun,
>> +                             master->msg_idx))
>> +                     ipmb_handle_response(master);
>> +             master->msg_idx = 0;
>> +             break;
>> +
>> +     default:
>> +             break;
>> +     }
>> +     spin_unlock(&master->lock);
>> +
>> +     return 0;
>> +}
>> +
>> +static unsigned short slave_add = 0x0; module_param(slave_add, 
>> +ushort, 0); MODULE_PARM_DESC(slave_add, "The i2c slave address of 
>> +the responding device");
>> +
>> +#define GET_DEVICE_ID_MSG_LEN 7
>> +
>> +static bool ipmb_detect(struct ipmb_master *master) {
>> +     struct ipmb_rsp_elem *q_elem, *tmp_q_elem;
>> +     struct ipmb_host_request request;
>> +     u8 *buf = (u8 *) &request;
>> +     struct device dev;
>> +     int retry = 2000;
>> +     u8 i2c_msg_len;
>> +     int ret;
>> +
>> +     /* Subtract rs sa and netfn */
>> +     i2c_msg_len = GET_DEVICE_ID_MSG_LEN - 2;
>> +
>> +     dev = master->client->dev;
>> +
>> +     request.netfn_rs_lun = IPMI_NETFN_APP_REQUEST << 2;
>> +     request.checksum1 = ipmb_checksum1((u8)(master->rs_sa << 1),
>> +                                             request.netfn_rs_lun);
>> +     request.rq_sa = (u8)(master->client->addr << 1);
>> +     request.rq_seq_rq_lun = 0;
>> +     request.cmd = IPMI_GET_DEVICE_ID_CMD;
>> +     request.payload[0] = ipmb_checksum(buf + 2, 3, 0);
>> +
>> +     ret = ipmb_send_request(master, &request, i2c_msg_len);
>> +     if (ret < 0) {
>> +             dev_err(&dev, "ERROR: ipmb_send_request failed during ipmb detection\n");
>> +             return false;
>> +     }
>> +
>> +     while(!handshake_rsp && (retry > 0)) {
>> +             mdelay(10);
>> +             retry--;
>> +     }
>> +
>> +     if (!retry) {
>> +             dev_err(&dev, "ERROR: Response timed out during ipmb detection\n");
>> +             return false;
>> +     }
>> +
>> +     list_for_each_entry_safe(q_elem, tmp_q_elem, &master->rsp_queue, list){
>> +             list_del(&q_elem->list);
>> +             kfree(q_elem);
>> +             atomic_dec(&master->rsp_queue_len);
>> +     }
>> +
>> +     return true;
>> +}
>> +
>> +static int ipmb_probe(struct i2c_client *client,
>> +                     const struct i2c_device_id *id) {
>> +     struct ipmb_master *master;
>> +     int ret;
>> +
>> +     master = devm_kzalloc(&client->dev, sizeof(struct ipmb_master),
>> +                          GFP_KERNEL);
>> +     if (!master)
>> +             return -ENOMEM;
>> +
>> +     spin_lock_init(&master->lock);
>> +     init_waitqueue_head(&master->wait_queue);
>> +     atomic_set(&master->rsp_queue_len, 0);
>> +     INIT_LIST_HEAD(&master->rsp_queue);
>> +
>> +     INIT_WORK(&master->ipmb_send_work, ipmb_send_workfn);
>> +
>> +     ret = device_property_read_u32(&client->dev, "slave-address",
>> +                                     &master->rs_sa);
>> +     if (ret) {
>> +             master->rs_sa = slave_add;
>> +             if (master->rs_sa == 0x0) {
>> +                     dev_err(&client->dev,
>> +                             "Failed to get the responder's address from user\n");
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     master->client = client;
>> +     i2c_set_clientdata(client, master);
>> +
>> +     ret = i2c_slave_register(client, ipmb_slave_cb);
>> +
>> +     if (ret)
>> +             return ret;
>> +
>> +     master->slave_registered = true;
>> +
>> +     /*
>> +      * Send a simple message "get device ID" to detect whether the BMC is responsive or not.
>> +      * This is necessary before calling ipmi_register_smi which executes a handshake with the
>> +      * slave device and can hold the lock for a very long if the BMC is not up. This long wait
>> +      * at boot time causes the system to crash.
>> +      */
>> +     if (!ipmb_detect(master)) {
>> +             dev_err(&client->dev, "Unable to get response from slave device at this time\n");
>> +             i2c_slave_unregister(client);
>> +             master->slave_registered = false;
>> +             return -ENXIO;
>> +     }
>> +
>> +     ret = ipmi_register_smi(&ipmb_smi_handlers, master,
>> +                             &client->dev,
>> +                             (unsigned char)master->rs_sa);
>> +
>> +     if (ret) {
>> +             dev_err(&client->dev, "ipmi_register_smi failed with ret = %d\n", ret);
>> +             i2c_slave_unregister(client);
>> +             master->slave_registered = false;
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static int ipmb_remove(struct i2c_client *client) {
>> +     struct ipmb_master *master;
>> +
>> +     master = i2c_get_clientdata(client);
>> +     if (!master)
>> +             return 0;
>> +
>> +     if (master->slave_registered) {
>> +             ipmi_unregister_smi(master->intf);
>> +             i2c_slave_unregister(client);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct i2c_device_id ipmb_i2c_id[] = {
>> +     {"ipmb-host", 0},
>> +     {},
>> +};
>> +MODULE_DEVICE_TABLE(i2c, ipmb_i2c_id);
>> +
>> +static struct i2c_driver ipmb_driver = {
>> +     .driver = {
>> +             .owner = THIS_MODULE,
>> +             .name = "ipmb-host",
>> +     },
>> +     .probe = ipmb_probe,
>> +     .remove = ipmb_remove,
>> +     .id_table = ipmb_i2c_id,
>> +};
>> +module_i2c_driver(ipmb_driver);
>> +
>> +MODULE_AUTHOR("Asmaa Mnebhi <asmaa at nvidia.com>"); 
>> +MODULE_DESCRIPTION("Host IPMB driver"); MODULE_LICENSE("GPL v2");
> 



More information about the kernel-team mailing list