[RFC PATCH v2 1/2] Adding i2c interface for qemu

Padmakar p.kalghatgi at samsung.com
Mon Aug 2 10:22:01 PDT 2021


From: padmakar <p.kalghatgi at samsung.com>

The changes here includes the interface for i2c/smbus 
for nvme-mi protocol. We have used an address of 0x15
using which the guest VM can send and recieve the nvme-mi
commands. Since the nvme-mi device uses the I2C_SLAVE as
parent, we have used the send and recieve callbacks by
which the nvme-mi device will get the required notification.
With the callback approach, we have eliminated the use of 
threads. 

Please provide us with your valuable feedback on this patch.

---
 hw/nvme/nvme-mi.c | 152 ++++++++++++++++++++++--------------------------------
 hw/nvme/nvme-mi.h |  23 ++++++---
 2 files changed, 79 insertions(+), 96 deletions(-)

diff --git a/hw/nvme/nvme-mi.c b/hw/nvme/nvme-mi.c
index 5e93417..a90ce90 100644
--- a/hw/nvme/nvme-mi.c
+++ b/hw/nvme/nvme-mi.c
@@ -4,7 +4,7 @@
  * Copyright (c) 2021, Samsung Electronics co Ltd.
  *
  * Written by Padmakar Kalghatgi <p.kalghatgi at samsung.com>
- *            Arun Kumar Agasar <arun.ag at partner.samsung.com>
+ *            Arun Kumar Agasar <arun.kka at samsung.com>
  *            Saurav Kumar <saurav.29 at partner.samsung.com>
  *
  * This code is licensed under the GNU GPL v2 or later.
@@ -19,10 +19,8 @@
  * The nvme-mi device has to be used along with nvme device only
  *
  * Add options:
- *    -device  nvme-mi,bus=<nvme bus number>
- *    -device  vhost-vsock-pci, guest-cid=<vsock cid>
+ *    -device  nvme-mi,nvme=<nvme id>,address=0x15",
  *
- * the cid is used to connect to the vsock
  */
 
 #include "qemu/osdep.h"
@@ -41,8 +39,8 @@ static void nvme_mi_send_resp(NvmeMiCtrl *ctrl_mi, uint8_t *resp, uint32_t size)
 {
     uint32_t crc_value = crc32c(0xFFFFFFFF, resp, size);
     size += 4;
-    uint32_t retries = 5;
     uint32_t offset = 0;
+    uint32_t ofst = 0;
     uint32_t som = 1;
     uint32_t eom = 0;
     uint32_t pktseq = 0;
@@ -51,24 +49,17 @@ static void nvme_mi_send_resp(NvmeMiCtrl *ctrl_mi, uint8_t *resp, uint32_t size)
         uint32_t sizesent = size > mtus ? mtus : size;
         size -= sizesent;
         eom = size > 0 ? 0 : 1;
-        g_autofree uint8_t *buf = (uint8_t *)g_malloc(sizesent + 8);
+        g_autofree uint8_t *buf = (uint8_t *)g_malloc0(sizesent + 8);
         buf[2] = sizesent + 5;
         buf[7] = (som << 7) | (eom << 6) | (pktseq << 5);
         som = 0;
         memcpy(buf + 8, resp + offset, sizesent);
         offset += sizesent;
         if (size <= 0) {
-            memcpy(buf + 4 + offset , &crc_value, sizeof(crc_value));
-        }
-        retries = 5;
-        while (retries > 0) {
-            int32_t nsend = send(ctrl_mi->sock_desc, buf, sizesent + 8, 0);
-            if (nsend < 0) {
-                retries--;
-            } else {
-                break;
-            }
+            memcpy(buf + 8 + offset , &crc_value, sizeof(crc_value));
         }
+        memcpy(ctrl_mi->misendrecv.sendrecvbuf + ofst, buf, sizesent + 8);
+        ofst += sizesent + 8;
     }
 }
 
@@ -176,7 +167,6 @@ static void nvme_mi_controller_health_ds(NvmeMiCtrl *ctrl_mi,
          (dword1 & 0x2)) {
         nvme_mi_chds.cwarn.temp_above_or_under_thresh = 0x1;
     }
-    printf("size = %lu\n", sizeof(resp) + sizeof(NvmeMiCtrlHealthDs));
     g_autofree uint8_t *resp_buf = (uint8_t *)g_malloc(sizeof(resp) +
                                                        sizeof(NvmeMiCtrlHealthDs));
     resp.mgmt_resp = 1 << 0x10;
@@ -283,9 +273,8 @@ static void nvme_mi_vpd_read(NvmeMiCtrl *ctrl_mi, NvmeMIRequest *req)
         nvme_mi_send_resp(ctrl_mi, resp_buf, dlen + sizeof(resp));
     }
 }
-
-static void nvme_mi_vpd_write(NvmeMiCtrl *ctrl_mi, NvmeMIRequest *req,
-                              uint8_t *buf)
+static void nvme_mi_vpd_write(NvmeMiCtrl *ctrl_mi,
+                              NvmeMIRequest *req, uint8_t *buf)
 {
     uint16_t dofst = (req->dword0 & 0xFFFF);
     uint16_t dlen = (req->dword1 & 0xFFFF);
@@ -308,7 +297,7 @@ static void nvme_mi_nvm_subsys_health_status_poll(NvmeMiCtrl *ctrl_mi,
     NvmeMiNvmSubsysHspds nshds;
     nvme_mi_resp_hdr_init(&resp, false);
     for (uint32_t cntlid = 1; cntlid < ARRAY_SIZE(ctrl_mi->n->subsys->ctrls);
-         cntlid++) {
+                  cntlid++) {
 
         NvmeCtrl *ctrl = nvme_subsys_ctrl(ctrl_mi->n->subsys, cntlid);
         if (!ctrl) {
@@ -335,7 +324,8 @@ static void nvme_mi_nvm_subsys_health_status_poll(NvmeMiCtrl *ctrl_mi,
     }
 
 
-    g_autofree uint8_t *resp_buf = (uint8_t *)g_malloc(sizeof(resp) + sizeof(nshds));
+    g_autofree uint8_t *resp_buf = (uint8_t *)g_malloc(sizeof(resp) +
+                                                       sizeof(nshds));
     memcpy(resp_buf, &resp, sizeof(resp));
     memcpy(resp_buf + sizeof(resp), &nshds, sizeof(nshds));
     nvme_mi_send_resp(ctrl_mi, resp_buf, sizeof(resp_buf));
@@ -553,15 +543,12 @@ static void nvme_mi_admin_command(NvmeMiCtrl *ctrl_mi, void* req_arg)
         NvmeAdminMIRequest *req = (NvmeAdminMIRequest *) (msg);
         switch  (req->opc) {
         case NVME_ADM_MI_CMD_IDENTIFY:
-            printf("identify\n");
             nvme_mi_admin_identify(ctrl_mi, req);
             break;
         case NVME_ADM_MI_CMD_GET_LOG_PAGE:
-            printf("get log page\n");
             nvme_mi_admin_get_log_page(ctrl_mi, req);
             break;
         case NVME_ADM_MI_CMD_GET_FEATURES:
-            printf("get features\n");
             nvme_mi_admin_get_features(ctrl_mi, req);
             break;
         default:
@@ -578,92 +565,79 @@ static void nvme_mi_admin_command(NvmeMiCtrl *ctrl_mi, void* req_arg)
     return;
 }
 
-static void nvme_mi_init(NvmeMiCtrl *ctrl_mi)
+static uint8_t nvme_mi_i2c_recv(I2CSlave *s)
 {
-    pthread_t vsock_tid;
-    pthread_create(&vsock_tid, NULL, vsock_server_start, ctrl_mi);
-    pthread_detach(vsock_tid);
+    NvmeMiCtrl *mictrl = (NvmeMiCtrl *)s;
+    NvmeMiSendRecvStruct *misendrecv = &mictrl->misendrecv;
+    if (misendrecv->bsyncflag == true) {
+        return -1;
+    }
+    return misendrecv->sendrecvbuf[misendrecv->sendlen++];
 }
 
-void *vsock_server_start(void *arg)
+static int nvme_mi_i2c_send(I2CSlave *s, uint8_t data)
 {
-    NvmeMiCtrl *ctrl_mi = (NvmeMiCtrl *)arg;
-    int listenfd = 0, c = 0;
-    struct sockaddr_vm sa = {
-                        .svm_family = AF_VSOCK,
-                        .svm_cid = VMADDR_CID_ANY,
-                        .svm_port = 1342,
-                    };
-    struct sockaddr_vm client;
-    listenfd = socket(AF_VSOCK, SOCK_STREAM, 0);
-    if (listenfd == -1) {
-        pthread_exit(NULL);
-    }
-    if (bind(listenfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
-        pthread_exit(NULL);
+    NvmeMiCtrl *mictrl = (NvmeMiCtrl *)s;
+    NvmeMiSendRecvStruct *misendrecv = &mictrl->misendrecv;
+
+    misendrecv->bsyncflag = true;
+    misendrecv->sendlen = 0;
+    switch (misendrecv->len) {
+    case 1:
+        misendrecv->total_len = data;
+        break;
+    case 6:
+        misendrecv->eom = (data & 0x40) >> 6;
+        break;
     }
-    listen(listenfd, 1);
-    puts("Waiting for incoming connections...");
-    c = sizeof(struct sockaddr_vm);
-    while (1) {
-        ctrl_mi->sock_desc = accept(listenfd, (struct sockaddr *)&client,
-                                       (socklen_t *)&c);
-        if ((ctrl_mi->sock_desc) < 0) {
-            continue;
-        }
-
-        NvmeMiMctpHeader mctp_hdr;
-        uint32_t total_len = 0;
-        uint8_t *getdata_buf = NULL;
-        while (!mctp_hdr.EOM) {
-            uint8_t buf[7];
-            recv(ctrl_mi->sock_desc, buf, 7, 0);
-            mctp_hdr.byte_count = buf[1];
-            mctp_hdr.EOM = (buf[6] & 0x40) >> 6;
-            mctp_hdr.SOM = (buf[6] & 0x80) >> 7;
-            mctp_hdr.pckt_seq = (buf[6] & 0x20) >> 5;
-
-            uint32_t curr_len = total_len;
-            total_len = total_len + (mctp_hdr.byte_count - 5);
-
-            getdata_buf = (uint8_t *)g_realloc(getdata_buf, total_len);
-            recv(ctrl_mi->sock_desc, getdata_buf + curr_len,
-                        (mctp_hdr.byte_count - 5), 0);
+    misendrecv->sendrecvbuf[++misendrecv->len] = data;
+    if (misendrecv->len == misendrecv->total_len + 3) {
+        misendrecv->cmdbuffer = (uint8_t *)g_realloc(misendrecv->cmdbuffer,
+                                                     misendrecv->len - 5);
+        memcpy(misendrecv->cmdbuffer + misendrecv->offset,
+               misendrecv->sendrecvbuf + 8, misendrecv->len - 5);
+
+        misendrecv->offset = misendrecv->len - 5;
+        misendrecv->total_len = 0;
+        misendrecv->len = 0;
+
+        if (misendrecv->eom == 1) {
+            nvme_mi_admin_command(mictrl, misendrecv->cmdbuffer);
+            misendrecv->offset = 0;
+            misendrecv->bsyncflag = false;
         }
-        NvmeMiMessageHeader msghdr;
-        memcpy(&msghdr, getdata_buf, sizeof(NvmeMiMessageHeader));
-        nvme_mi_admin_command(ctrl_mi, getdata_buf);
     }
-    pthread_exit(0);
+    return 0;
 }
 
 static void nvme_mi_realize(DeviceState *dev, Error **errp)
 {
-    NvmeMiCtrl *ctrl_mi = (NvmeMiCtrl *)malloc(sizeof(NvmeMiCtrl));
-    BusState *s = qdev_get_parent_bus(dev);
-    ctrl_mi->n = NVME(s->parent);
-    ctrl_mi->mctp_unit_size = 64;
-    ctrl_mi->smbus_freq = 0x01;
-    ctrl_mi->sock_desc = 0;
-    memset(&ctrl_mi->vpd_data, 0, sizeof(ctrl_mi->vpd_data));
-
-    nvme_mi_init(ctrl_mi);
+    NvmeMiCtrl *s = (NvmeMiCtrl *)(dev);
+
+    s->smbus_freq = 100;
+    s->mctp_unit_size = 64;
 }
+static Property nvme_mi_props[] = {
+     DEFINE_PROP_LINK("nvme", NvmeMiCtrl, n, TYPE_NVME,
+                     NvmeCtrl *),
+    DEFINE_PROP_END_OF_LIST(),
+};
 
 static void nvme_mi_class_init(ObjectClass *oc, void *data)
 {
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(oc);
     DeviceClass *dc = DEVICE_CLASS(oc);
 
-    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
-
-    dc->bus_type = TYPE_NVME_BUS;
     dc->realize = nvme_mi_realize;
-    dc->desc = "nvme mi";
+    k->recv = nvme_mi_i2c_recv;
+    k->send = nvme_mi_i2c_send;
+
+    device_class_set_props(dc, nvme_mi_props);
 }
 
 static const TypeInfo nvme_mi = {
     .name = TYPE_NVME_MI,
-    .parent = TYPE_DEVICE,
+    .parent = TYPE_I2C_SLAVE,
     .instance_size = sizeof(NvmeMiCtrl),
     .class_init = nvme_mi_class_init,
 };
diff --git a/hw/nvme/nvme-mi.h b/hw/nvme/nvme-mi.h
index 4c155a0..92a20e6 100644
--- a/hw/nvme/nvme-mi.h
+++ b/hw/nvme/nvme-mi.h
@@ -5,7 +5,7 @@
  *
  * Authors:
  *   Padmakar Kalghatgi      <p.kalghatgi at samsung.com>
- *   Arun Kumar Agasar       <arun.ag at partner.samsung.com>
+ *   Arun Kumar Agasar       <arun.kka at samsung.com>
  *   Saurav Kumar            <saurav.29 at partner.samsung.com>
  *
  * This code is licensed under the GNU GPL v2 or later.
@@ -14,14 +14,12 @@
 #ifndef NVME_MI_H
 #define NVME_MI_H
 
-#include <sys/socket.h>
-#include <linux/vm_sockets.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdint.h>
 #include <stdbool.h>
-#include <pthread.h>
+#include "hw/i2c/i2c.h"
 
 #define TYPE_NVME_MI "nvme-mi"
 
@@ -32,17 +30,28 @@
 #define OPT_SUPP_CMD_LIST 4
 #define MGMT_EPT_BUFF_CMD_SUPP_LIST 5
 
+
+typedef struct NvmeMiSendRecvStruct {
+   uint32_t sendlen;
+   uint8_t len;
+   uint8_t total_len;
+   uint32_t offset;
+   uint8_t eom;
+   bool bsyncflag;
+   u_char sendrecvbuf[5000];
+   uint8_t *cmdbuffer;
+} NvmeMiSendRecvStruct;
 typedef struct NvmeMiVpdElements {
     long common_header;
 } NvmeMiVpdElements;
 
 typedef struct NvmeMiCtrl {
-   int32_t sock_desc;
+   I2CSlave parent_obj;
    uint32_t mctp_unit_size;
    uint32_t smbus_freq;
-   NvmeCtrl *n;
    NvmeMiVpdElements vpd_data;
-   u_char dummy[1000];
+   NvmeMiSendRecvStruct  misendrecv;
+   NvmeCtrl *n;
 } NvmeMiCtrl;
 
 enum NvmeMiMngmtInterfaceCmdSetsOpcodes {
-- 
2.7.0.windows.1




More information about the Linux-nvme mailing list