CSE-release/emapi_handler.c

697 lines
16 KiB
C
Raw Permalink Normal View History

2024-04-02 04:55:14 +00:00
/* SPDX-License-Identifier: Apache-2.0 */
/**
* @file emapi_handler.c
*
* @brief Code file for methods to respond to CXL Emulator API commands
*
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
*
* @date Feb 2024
* @author Barrett Edwards <code@jrlabs.io>
*
*/
/* INCLUDES ==================================================================*/
/* gettid()
*/
#define _GNU_SOURCE
#include <unistd.h>
/* printf()
*/
#include <stdio.h>
/* memset()
*/
#include <string.h>
/* struct timespec
* timespec_get()
*
*/
#include <time.h>
/* autl_prnt_buf()
*/
#include <arrayutils.h>
/* mctp_init()
* mctp_set_mh()
* mctp_run()
*/
#include <mctp.h>
#include <ptrqueue.h>
#include <timeutils.h>
#include <emapi.h>
2024-04-08 06:22:45 +00:00
#include <cxlstate.h>
2024-04-02 04:55:14 +00:00
#include "signals.h"
#include "options.h"
#include "state.h"
#include "emapi_handler.h"
/* MACROS ====================================================================*/
#ifdef CSE_VERBOSE
#define INIT unsigned step = 0;
#define ENTER if (opts[CLOP_VERBOSITY].u64 & CLVB_CALLSTACK) printf("%d:%s Enter\n", gettid(), __FUNCTION__);
#define STEP step++; if (opts[CLOP_VERBOSITY].u64 & CLVB_STEPS) printf("%d:%s STEP: %u\n", gettid(), __FUNCTION__, step);
#define HEX32(m, i) if (opts[CLOP_VERBOSITY].u64 & CLVB_STEPS) printf("%d:%s STEP: %u %s: 0x%x\n", gettid(), __FUNCTION__, step, m, i);
#define INT32(m, i) if (opts[CLOP_VERBOSITY].u64 & CLVB_STEPS) printf("%d:%s STEP: %u %s: %d\n", gettid(), __FUNCTION__, step, m, i);
#define EXIT(rc) if (opts[CLOP_VERBOSITY].u64 & CLVB_CALLSTACK) printf("%d:%s Exit: %d\n", gettid(), __FUNCTION__,rc);
#else
#define ENTER
#define EXIT(rc)
#define STEP
#define HEX32(m, i)
#define INT32(m, i)
#define INIT
#endif // CSE_VERBOSE
#define IFV(u) if (opts[CLOP_VERBOSITY].u64 & u)
#define ISO_TIME_BUF_LEN 32
/* ENUMERATIONS ==============================================================*/
/* STRUCTS ===================================================================*/
/* PROTOTYPES ================================================================*/
static int emop_conn_dev (struct mctp *m, struct mctp_action *ma);
static int emop_disconn_dev(struct mctp *m, struct mctp_action *ma);
static int emop_list_dev (struct mctp *m, struct mctp_action *ma);
static int emop_unsupported(struct mctp *m, struct mctp_action *ma);
/* GLOBAL VARIABLES ==========================================================*/
/* FUNCTIONS =================================================================*/
/**
* Handler for all CXL Emulator API Opcodes
*
* @return 0 upon success, 1 otherwise
*
* STEPS
* 1: Deserialize Header
* 2: Verify EM API Message Type
* 3: Handle Opcode
*/
int emapi_handler(struct mctp *m, struct mctp_action *ma)
{
INIT
struct emapi_hdr hdr;
int rv;
ENTER
// Initialize variables
rv = 1;
STEP // 1: Deserialize Header
if ( emapi_deserialize(&hdr, ma->req->payload, EMOB_HDR, NULL) == 0 )
goto fail;
STEP // 2: Verify EM API Message Type
if (hdr.type != EMMT_REQ)
goto fail;
STEP // 3: Handle Opcode
HEX32("Opcode", hdr.opcode);
switch(hdr.opcode)
{
case EMOP_EVENT: // 0x00
break;
case EMOP_LIST_DEV: // 0x01
rv = emop_list_dev(m, ma);
break;
case EMOP_CONN_DEV: // 0x02
rv = emop_conn_dev(m, ma);
break;
case EMOP_DISCON_DEV: // 0x03
rv = emop_disconn_dev(m, ma);
break;
default:
rv = emop_unsupported(m, ma);
break;
}
rv = 0;
goto end;
fail:
ma->completion_code = 1;
pq_push(m->acq, ma);
end:
EXIT(rv)
return rv;
}
/**
* Handler for EM API Connect Device Command
*
* @param m struct mctp*
* @param mm struct mctp_msg*
* @return 0 upon success, 1 otherwise
*
* STEPS
* 1: Initialize variables
* 2: Checkout Response mctp_msg buffer
* 3: Fill Response MCTP Header
* 4: Set buffer pointers
* 5: Deserialize Request Header
* 6: Deserialize Request Object
* 7: Extract parameters
* 8: Obtain lock on switch state
* 9: Validate Inputs
* 10: Perform Action
* 11: Prepare Response Object
* 12: Serialize Response Object
* 13: Set return code
* 14: Release lock on switch state
* 15: Fill Response Header
* 16: Serialize Header
* 17: Push Response mctp_msg onto Transmit Message Queue
* 18: Checkin mctp_msgs
*/
static int emop_conn_dev(struct mctp *m, struct mctp_action *ma)
{
INIT
char now[ISO_TIME_BUF_LEN];
struct emapi_msg reqm, rspm;
struct emapi_buf *reqb, *rspb;
unsigned rc;
int rv, len;
__u8 ppid, dev;
ENTER
STEP // 1: Initialize variables
rv = 1;
len = 0;
rc = FMRC_INVALID_INPUT;
isotime(now, ISO_TIME_BUF_LEN);
STEP // 2: Get response mctp_msg buffer
ma->rsp = pq_pop(m->msgs, 1);
if (ma->rsp == NULL)
goto fail;
STEP // 3: Fill Response MCTP Header: dst, src, owner, tag, and type
mctp_fill_msg_hdr(ma->rsp, ma->req->src, m->state.eid, 0, ma->req->tag);
ma->rsp->type = ma->req->type;
// 4: Set buffer pointers
reqb = (struct emapi_buf*) ma->req->payload;
rspb = (struct emapi_buf*) ma->rsp->payload;
STEP // 5: Deserialize Request Header
if ( emapi_deserialize(&reqm.hdr, reqb->hdr, EMOB_HDR, NULL) <= 0 )
goto fail;
STEP // 6: Deserialize Request Object
if ( emapi_deserialize(&reqm.obj, reqb->payload, emapi_emob_req(reqm.hdr.opcode), NULL) < 0 )
goto fail;
STEP // 7: Extract parameters
ppid = reqm.hdr.a;
dev = reqm.hdr.b;
IFV(CLVB_COMMANDS) printf("%s CMD: EM API Connect Device. PPID: %d Device: %d\n", now, ppid, dev);
STEP // 8: Obtain lock on switch state
2024-04-08 06:22:45 +00:00
pthread_mutex_lock(&cxls->mtx);
2024-04-02 04:55:14 +00:00
STEP // 9: Validate Inputs
2024-04-08 06:22:45 +00:00
if (ppid >= cxls->num_ports)
2024-04-02 04:55:14 +00:00
{
2024-04-08 06:22:45 +00:00
IFV(CLVB_ERRORS) printf("%s ERR: PPID out of range. PPID: %d Total: %d\n", now, ppid, cxls->num_ports);
2024-04-02 04:55:14 +00:00
goto send;
}
2024-04-08 06:22:45 +00:00
if (dev >= cxls->num_devices)
2024-04-02 04:55:14 +00:00
{
2024-04-08 06:22:45 +00:00
IFV(CLVB_ERRORS) printf("%s ERR: Device ID out of range. Device ID: %d Total: %d\n", now, dev, cxls->num_devices);
2024-04-02 04:55:14 +00:00
goto send;
}
2024-04-08 06:22:45 +00:00
if (cxls->devices[dev].name == NULL)
2024-04-02 04:55:14 +00:00
{
IFV(CLVB_ERRORS) printf("%s ERR: Device is NULL. Device ID: %d\n", now, dev);
goto send;
}
IFV(CLVB_ACTIONS) printf("%s ACT: Connecting Device %d to PPID %d\n", now, dev, ppid);
STEP // 10: Perform Action
2024-04-08 06:22:45 +00:00
cxls_connect(&cxls->ports[ppid], &cxls->devices[dev], cxls->dir);
2024-04-02 04:55:14 +00:00
STEP // 11: Prepare Response Object
STEP // 12: Serialize Response Object
len = emapi_serialize(rspb->payload, &rspm.obj, emapi_emob_rsp(reqm.hdr.opcode), NULL);
STEP // 13: Set return code
rc = EMRC_SUCCESS;
send:
STEP // 14: Release lock on switch state
2024-04-08 06:22:45 +00:00
pthread_mutex_unlock(&cxls->mtx);
2024-04-02 04:55:14 +00:00
if(len < 0)
goto fail;
STEP // 15: Fill Response Header
ma->rsp->len = emapi_fill_hdr(&rspm.hdr, EMMT_RSP, reqm.hdr.tag, rc, reqm.hdr.opcode, len, 0, 0);
STEP // 16: Serialize Header
emapi_serialize(rspb->hdr, &rspm.hdr, EMOB_HDR, NULL);
STEP // 17: Push mctp_action onto queue
pq_push(m->tmq, ma);
rv = 0;
goto end;
fail:
ma->completion_code = 1;
pq_push(m->acq, ma);
end:
EXIT(rc)
return rv;
}
/**
* Handler for EM API Disconnect Device Command
*
* @param m struct mctp*
* @param mm struct mctp_msg*
* @return 0 upon success, 1 otherwise
*
* STEPS
* 1: Initialize variables
* 2: Checkout Response mctp_msg buffer
* 3: Fill Response MCTP Header
* 4: Set buffer pointers
* 5: Deserialize Request Header
* 6: Deserialize Request Object
* 7: Extract parameters
* 8: Obtain lock on switch state
* 9: Validate Inputs
* 10: Perform Action
* 11: Prepare Response Object
* 12: Serialize Response Object
* 13: Set return code
* 14: Release lock on switch state
* 15: Fill Response Header
* 16: Serialize Header
* 17: Push Response mctp_msg onto Transmit Message Queue
* 18: Checkin mctp_msgs
*/
static int emop_disconn_dev(struct mctp *m, struct mctp_action *ma)
{
INIT
char now[ISO_TIME_BUF_LEN];
struct emapi_msg reqm, rspm;
struct emapi_buf *reqb, *rspb;
unsigned rc;
int rv, len;
__u8 ppid, all, start, end, i;
ENTER
STEP // 1: Initialize variables
rv = 1;
len = 0;
rc = FMRC_INVALID_INPUT;
isotime(now, ISO_TIME_BUF_LEN);
STEP // 2: Get response mctp_msg buffer
ma->rsp = pq_pop(m->msgs, 1);
if (ma->rsp == NULL)
goto fail;
STEP // 3: Fill Response MCTP Header: dst, src, owner, tag, and type
mctp_fill_msg_hdr(ma->rsp, ma->req->src, m->state.eid, 0, ma->req->tag);
ma->rsp->type = ma->req->type;
STEP // 4: Set buffer pointers
reqb = (struct emapi_buf*) ma->req->payload;
rspb = (struct emapi_buf*) ma->rsp->payload;
STEP // 5: Deserialize Request Header
if ( emapi_deserialize(&reqm.hdr, reqb->hdr, EMOB_HDR, NULL) <= 0 )
goto fail;
STEP // 6: Deserialize Request Object
if ( emapi_deserialize(&reqm.obj, reqb->payload, emapi_emob_req(reqm.hdr.opcode), NULL) < 0 )
goto fail;
STEP // 7: Extract parameters
ppid = reqm.hdr.a;
all = reqm.hdr.b;
IFV(CLVB_COMMANDS) printf("%s CMD: EM API Disconnect Device. PPID: %d All: %d\n", now, ppid, all);
STEP // 8: Obtain lock on switch state
2024-04-08 06:22:45 +00:00
pthread_mutex_lock(&cxls->mtx);
2024-04-02 04:55:14 +00:00
STEP // 9: Validate Inputs
if (all) {
start = 0;
2024-04-08 06:22:45 +00:00
end = cxls->num_ports;
2024-04-02 04:55:14 +00:00
}
else {
start = ppid;
end = ppid+1;
}
2024-04-08 06:22:45 +00:00
if (start >= cxls->num_ports)
2024-04-02 04:55:14 +00:00
{
2024-04-08 06:22:45 +00:00
IFV(CLVB_ERRORS) printf("%s ERR: PPID out of range. PPID: %d Total: %d\n", now, ppid, cxls->num_ports);
2024-04-02 04:55:14 +00:00
goto send;
}
STEP // 10: Perform Action
for ( i = start ; i < end ; i++ )
{
// Validate if port is connected
2024-04-08 06:22:45 +00:00
if (cxls->ports[i].prsnt == 1)
2024-04-02 04:55:14 +00:00
{
IFV(CLVB_ACTIONS) printf("%s ACT: Disconnecting PPID %d\n", now, i);
// Perform disconnect
2024-04-08 06:22:45 +00:00
cxls_disconnect(&cxls->ports[i]);
2024-04-02 04:55:14 +00:00
}
}
STEP // 11: Prepare Response Object
STEP // 12: Serialize Response Object
len = emapi_serialize(rspb->payload, &rspm.obj, emapi_emob_rsp(reqm.hdr.opcode), NULL);
STEP // 13: Set return code
rc = EMRC_SUCCESS;
send:
STEP // 14: Release lock on switch state
2024-04-08 06:22:45 +00:00
pthread_mutex_unlock(&cxls->mtx);
2024-04-02 04:55:14 +00:00
if (len < 0)
goto fail;
STEP // 15: Fill Response Header
ma->rsp->len = emapi_fill_hdr(&rspm.hdr, EMMT_RSP, reqm.hdr.tag, rc, reqm.hdr.opcode, len, 0, 0);
STEP // 16: Serialize Header
emapi_serialize(rspb->hdr, &rspm.hdr, EMOB_HDR, NULL);
STEP // 17: Push mctp_action onto queue
pq_push(m->tmq, ma);
rv = 0;
goto end;
fail:
ma->completion_code = 1;
pq_push(m->acq, ma);
end:
EXIT(rc)
return rv;
}
/**
* Handler for EM API List Devices Opcode
*
* @param m struct mctp*
* @param mm struct mctp_msg*
* @return 0 upon success, 1 otherwise
*
* STEPS
* 1: Initialize variables
* 2: Checkout Response mctp_msg buffer
* 3: Fill Response MCTP Header
* 4: Set buffer pointers
* 5: Deserialize Request Header
* 6: Deserialize Request Object
* 7: Extract parameters
* 8: Obtain lock on switch state
* 9: Validate Inputs
* 10: Perform Action
* 11: Prepare Response Object
* 12: Serialize Response Object
* 13: Set return code
* 14: Release lock on switch state
* 15: Fill Response Header
* 16: Serialize Header
* 17: Push Response mctp_msg onto Transmit Message Queue
* 18: Checkin mctp_msgs
*/
static int emop_list_dev(struct mctp *m, struct mctp_action *ma)
{
INIT
char now[ISO_TIME_BUF_LEN];
struct emapi_msg reqm, rspm;
struct emapi_buf *reqb, *rspb;
unsigned rc;
int rv, len;
unsigned i, count;
__u8 num_requested, start_num;
2024-04-08 06:22:45 +00:00
struct cxl_device *d;
2024-04-02 04:55:14 +00:00
ENTER
STEP // 1: Initialize variables
rv = 1;
len = 0;
rc = FMRC_INVALID_INPUT;
isotime(now, ISO_TIME_BUF_LEN);
count = 0;
STEP // 2: Get response mctp_msg buffer
ma->rsp = pq_pop(m->msgs, 1);
if (ma->rsp == NULL)
goto fail;
STEP // 3: Fill Response MCTP Header: dst, src, owner, tag, and type
mctp_fill_msg_hdr(ma->rsp, ma->req->src, m->state.eid, 0, ma->req->tag);
ma->rsp->type = ma->req->type;
STEP // 4: Set buffer pointers
reqb = (struct emapi_buf*) ma->req->payload;
rspb = (struct emapi_buf*) ma->rsp->payload;
STEP // 5: Deserialize Request Header
if ( emapi_deserialize(&reqm.hdr, reqb->hdr, EMOB_HDR, NULL) <= 0 )
goto fail;
STEP // 6: Deserialize Request Object
if ( emapi_deserialize(&reqm.obj, reqb->payload, emapi_emob_req(reqm.hdr.opcode), NULL) < 0 )
goto fail;
STEP // 7: Extract parameters
num_requested = reqm.hdr.a;
start_num = reqm.hdr.b;
IFV(CLVB_COMMANDS) printf("%s CMD: EM API list Devices. Start: %d Num: %d\n", now, start_num, num_requested);
STEP // 8: Obtain lock on switch state
2024-04-08 06:22:45 +00:00
pthread_mutex_lock(&cxls->mtx);
2024-04-02 04:55:14 +00:00
STEP // 9: Validate Inputs
if (num_requested == 0)
2024-04-08 06:22:45 +00:00
num_requested = (cxls->num_devices - start_num);
2024-04-02 04:55:14 +00:00
2024-04-08 06:22:45 +00:00
if (start_num >= cxls->num_devices)
2024-04-02 04:55:14 +00:00
{
IFV(CLVB_ERRORS) printf("%s ERR: Start num out of range. Start: %d Total: %d\n", now, start_num, num_requested);
goto send;
}
2024-04-08 06:22:45 +00:00
if ( (start_num + num_requested) >= cxls->num_devices)
num_requested = (cxls->num_devices - start_num);
2024-04-02 04:55:14 +00:00
STEP // 10: Perform Action
IFV(CLVB_ACTIONS) printf("%s ACT: Responding with %d devices\n", now, num_requested);
STEP // 11: Prepare Response Object
for ( i = 0 ; i < num_requested ; i++ )
{
2024-04-08 06:22:45 +00:00
d = &cxls->devices[start_num + i];
2024-04-02 04:55:14 +00:00
// Serialize the id number
rspb->payload[len+0] = start_num + i;
// Serialize the name string
if (d->name != NULL )
{
rspb->payload[len+1] = strlen(d->name) + 1;
memcpy(&rspb->payload[len+2], d->name, rspb->payload[len+1]);
}
else
rspb->payload[len+1] = 0;
len += (2 + rspb->payload[len+1]);
count++;
}
STEP // 12: Serialize Response Object
STEP // 13: Set return code
rc = EMRC_SUCCESS;
send:
STEP // 14: Release lock on switch state
2024-04-08 06:22:45 +00:00
pthread_mutex_unlock(&cxls->mtx);
2024-04-02 04:55:14 +00:00
STEP // 15: Fill Response Header
ma->rsp->len = emapi_fill_hdr(&rspm.hdr, EMMT_RSP, reqm.hdr.tag, rc, reqm.hdr.opcode, len, count, 0);
STEP // 16: Serialize Header
emapi_serialize(rspb->hdr, &rspm.hdr, EMOB_HDR, NULL);
STEP // 17: Push response mctp_msg onto queue
pq_push(m->tmq, ma);
rv = 0;
goto end;
fail:
ma->completion_code = 1;
pq_push(m->acq, ma);
end:
EXIT(rc)
return rv;
}
/**
* Handler for EM API List Devices Opcode
*
* @param m struct mctp*
* @param mm struct mctp_msg*
* @return 0 upon success, 1 otherwise
*
* STEPS
* 1: Initialize variables
* 2: Checkout Response mctp_msg buffer
* 3: Fill Response MCTP Header
* 4: Set buffer pointers
* 5: Deserialize Request Header
* 6: Deserialize Request Object
* 7: Extract parameters
* 8: Obtain lock on switch state
* 9: Validate Inputs
* 10: Perform Action
* 11: Prepare Response Object
* 12: Serialize Response Object
* 13: Set return code
* 14: Release lock on switch state
* 15: Fill Response Header
* 16: Serialize Header
* 17: Push Response mctp_msg onto Transmit Message Queue
* 18: Checkin mctp_msgs
*/
static int emop_unsupported(struct mctp *m, struct mctp_action *ma)
{
INIT
char now[ISO_TIME_BUF_LEN];
struct emapi_msg reqm, rspm;
struct emapi_buf *reqb, *rspb;
unsigned rc;
int rv;
ENTER
STEP // 1: Initialize variables
rv = 1;
rc = EMRC_UNSUPPORTED;
isotime(now, ISO_TIME_BUF_LEN);
STEP // 2: Get response mctp_msg buffer
ma->rsp = pq_pop(m->msgs, 1);
if (ma->rsp == NULL)
goto fail;
STEP // 3: Fill Response MCTP Header: dst, src, owner, tag, and type
mctp_fill_msg_hdr(ma->rsp, ma->req->src, m->state.eid, 0, ma->req->tag);
ma->rsp->type = ma->req->type;
STEP // 4: Set buffer pointers
reqb = (struct emapi_buf*) ma->req->payload;
rspb = (struct emapi_buf*) ma->rsp->payload;
STEP // 5: Deserialize Request Header
if ( emapi_deserialize(&reqm.hdr, reqb->hdr, EMOB_HDR, NULL) <= 0 )
goto fail;
STEP // 6: Deserialize Request Object
STEP // 7: Extract parameters
IFV(CLVB_COMMANDS) printf("%s ERR: Unsupported Opcode: 0x%04x\n", now, reqm.hdr.opcode);
STEP // 8: Obtain lock on switch state
STEP // 9: Validate Inputs
STEP // 10: Perform Action
STEP // 11: Prepare Response Object
STEP // 12: Serialize Response Object
STEP // 13: Set return code
STEP // 14: Release lock on switch state
STEP // 15: Fill Response Header
ma->rsp->len = emapi_fill_hdr(&rspm.hdr, EMMT_RSP, reqm.hdr.tag, rc, reqm.hdr.opcode, 0, 0, 0);
STEP // 16: Serialize Header
emapi_serialize(rspb->hdr, &rspm.hdr, EMOB_HDR, NULL);
STEP // 17: Push response mctp_msg onto queue
pq_push(m->tmq, ma);
rv = 0;
goto end;
fail:
ma->completion_code = 1;
pq_push(m->acq, ma);
end:
EXIT(rc)
return rv;
}