static void _handle_ack(struct cxl_afu_h *afu) { uint8_t data[sizeof(uint64_t)]; if (!afu) fatal_msg("NULL afu passed to libcxl.c:_handle_ack"); DPRINTF("MMIO ACK\n"); if ((afu->mmio.type == PSLSE_MMIO_READ64)| (afu->mmio.type == PSLSE_MMIO_EBREAD)) { if (get_bytes_silent(afu->fd, sizeof(uint64_t), data, 1000, 0) < 0) { warn_msg("Socket failure getting MMIO Ack"); _all_idle(afu); afu->mmio.data = 0xFEEDB00FFEEDB00FL; } else { memcpy(&(afu->mmio.data), data, sizeof(uint64_t)); afu->mmio.data = ntohll(afu->mmio.data); } } if (afu->mmio.type == PSLSE_MMIO_READ32) { if (get_bytes_silent(afu->fd, sizeof(uint32_t), data, 1000, 0) < 0) { warn_msg("Socket failure getting MMIO Read 32 data"); afu->mmio.data = 0xFEEDB00FL; _all_idle(afu); } else { memcpy(&(afu->mmio.data), data, sizeof(uint32_t)); debug_msg("KEM:0x%08x", afu->mmio.data); afu->mmio.data = ntohl(afu->mmio.data); debug_msg("KEM:0x%08x", afu->mmio.data); } } afu->mmio.state = LIBCXL_REQ_IDLE; }
// Attach to AFU static void _attach(struct psl *psl, struct client *client) { uint64_t wed; uint8_t ack; uint8_t buffer[MAX_LINE_CHARS]; size_t size; // FIXME: This only works for dedicate mode // Get wed value from application ack = PSLSE_DETACH; size = sizeof(uint64_t); if (get_bytes_silent(client->fd, size, buffer, psl->timeout, &(client->abort)) < 0) { warn_msg("Failed to get WED value from client"); client_drop(client, PSL_IDLE_CYCLES, CLIENT_NONE); goto attach_done; } memcpy((char *)&wed, (char *)buffer, sizeof(uint64_t)); wed = ntohll(wed); // Send start to AFU if (add_job(psl->job, PSL_JOB_START, wed) != NULL) { psl->idle_cycles = PSL_IDLE_CYCLES; ack = PSLSE_ATTACH; } attach_done: if (put_bytes(client->fd, 1, &ack, psl->dbg_fp, psl->dbg_id, client->context) < 0) { client_drop(client, PSL_IDLE_CYCLES, CLIENT_NONE); } }
// Handshake with client and attach to PSL static struct client *_client_connect(int *fd, char *ip) { struct client *client; uint8_t buffer[MAX_LINE_CHARS]; uint8_t ack[3]; uint16_t map; int rc; // Parse client handshake data ack[0] = PSLSE_DETACH; memset(buffer, '\0', MAX_LINE_CHARS); rc = get_bytes(*fd, 5, buffer, timeout, 0, fp, -1, -1); if ((rc < 0) || (strcmp((char *)buffer, "PSLSE"))) { info_msg("Connecting application is not PSLSE client\n"); info_msg("Expected: \"PSLSE\" Got: \"%s\"", buffer); put_bytes(*fd, 1, ack, fp, -1, -1); close_socket(fd); return NULL; } rc = get_bytes_silent(*fd, 2, buffer, timeout, 0); if ((rc < 0) || ((uint8_t) buffer[0] != PSLSE_VERSION_MAJOR) || ((uint8_t) buffer[1] != PSLSE_VERSION_MINOR)) { info_msg("Client is wrong version\n"); put_bytes(*fd, 1, ack, fp, -1, -1); close_socket(fd); return NULL; } // Initialize client struct client = (struct client *)calloc(1, sizeof(struct client)); client->fd = *fd; client->ip = ip; client->pending = 1; client->timeout = timeout; client->flushing = FLUSH_NONE; client->state = CLIENT_INIT; // Return acknowledge to client ack[0] = PSLSE_CONNECT; map = htons(afu_map); memcpy(&(ack[1]), &map, sizeof(map)); if (put_bytes(client->fd, 3, ack, fp, -1, -1) < 0) { _free_client(client); return NULL; } info_msg("%s connected", client->ip); return client; }
static int _handle_afu_error(struct cxl_afu_h *afu) { uint64_t error; uint16_t size; uint8_t data[sizeof(error)]; int i; if (!afu) fatal_msg("NULL afu passed to libcxl.c:_handle_afu_error"); DPRINTF("AFU ERROR\n"); if (get_bytes_silent(afu->fd, sizeof(error), data, 1000, 0) < 0) { warn_msg("Socket failure getting AFU ERROR"); _all_idle(afu); return -1; } memcpy(&error, data, sizeof(error)); error = ntohll(error); // Only track a single AFU error at a time pthread_mutex_lock(&(afu->event_lock)); i = 0; while (afu->events[i] != NULL) { if (afu->events[i]->header.type == CXL_EVENT_AFU_ERROR) { pthread_mutex_unlock(&(afu->event_lock)); return 0; } ++i; } assert(i < EVENT_QUEUE_MAX); size = sizeof(struct cxl_event_header) + sizeof(struct cxl_event_afu_error); afu->events[i] = (struct cxl_event *)calloc(1, size); afu->events[i]->header.type = CXL_EVENT_AFU_ERROR; afu->events[i]->header.size = size; afu->events[i]->header.process_element = afu->context; afu->events[i]->afu_error.error = error; do { i = write(afu->pipe[1], &(afu->events[i]->header.type), 1); } while ((i == 0) || (errno == EINTR)); pthread_mutex_unlock(&(afu->event_lock)); return i; }
// Handle data returning from client for memory read static void _handle_mem_read(struct cmd *cmd, struct cmd_event *event, int fd) { uint8_t data[MAX_LINE_CHARS]; uint64_t offset = event->addr & ~CACHELINE_MASK; // Client is returning data from memory read if (get_bytes_silent(fd, event->size, data, cmd->parms->timeout, event->abort) < 0) { event->resp = PSL_RESPONSE_DERROR; event->state = MEM_DONE; debug_cmd_update(cmd->dbg_fp, cmd->dbg_id, event->tag, event->context, event->resp); return; } memcpy((void *)&(event->data[offset]), (void *)&data, event->size); generate_cl_parity(event->data, event->parity); event->state = MEM_RECEIVED; }
static int _handle_interrupt(struct cxl_afu_h *afu) { uint16_t size, irq; uint8_t data[sizeof(irq)]; int i; if (!afu) fatal_msg("NULL afu passed to libcxl.c:_handle_interrupt"); DPRINTF("AFU INTERRUPT\n"); if (get_bytes_silent(afu->fd, sizeof(irq), data, 1000, 0) < 0) { warn_msg("Socket failure getting IRQ"); _all_idle(afu); return -1; } memcpy(&irq, data, sizeof(irq)); irq = ntohs(irq); // Only track a single interrupt at a time pthread_mutex_lock(&(afu->event_lock)); i = 0; while (afu->events[i] != NULL) { if (afu->events[i]->header.type == CXL_EVENT_AFU_INTERRUPT) { pthread_mutex_unlock(&(afu->event_lock)); return 0; } ++i; } assert(i < EVENT_QUEUE_MAX); size = sizeof(struct cxl_event_header) + sizeof(struct cxl_event_afu_interrupt); afu->events[i] = (struct cxl_event *)calloc(1, size); afu->events[i]->header.type = CXL_EVENT_AFU_INTERRUPT; afu->events[i]->header.size = size; afu->events[i]->header.process_element = afu->context; afu->events[i]->irq.irq = irq; do { i = write(afu->pipe[1], &(afu->events[i]->header.type), 1); } while ((i == 0) || (errno == EINTR)); pthread_mutex_unlock(&(afu->event_lock)); return i; }
static void *_client_loop(void *ptr) { struct client *client = (struct client *)ptr; uint8_t data[2]; int rc; pthread_mutex_lock(&lock); while (client->pending) { rc = bytes_ready(client->fd, client->timeout, &(client->abort)); if (rc == 0) { lock_delay(&lock); continue; } if ((rc < 0) || get_bytes(client->fd, 1, data, 10, &(client->abort), fp, -1, -1) < 0) { client_drop(client, PSL_IDLE_CYCLES, CLIENT_NONE); break; } if (data[0] == PSLSE_QUERY) { if (get_bytes_silent(client->fd, 1, data, timeout, &(client->abort)) < 0) { debug_msg("_client_loop failed PSLSE_QUERY"); client_drop(client, PSL_IDLE_CYCLES, CLIENT_NONE); break; } _query(client, data[0]); lock_delay(&lock); continue; } if (data[0] == PSLSE_MAX_INT) { if (get_bytes(client->fd, 2, data, timeout, &(client->abort), fp, -1, -1) < 0) { client_drop(client, PSL_IDLE_CYCLES, CLIENT_NONE); break; } _max_irqs(client, data[0]); lock_delay(&lock); continue; } if (data[0] == PSLSE_OPEN) { if (get_bytes_silent(client->fd, 2, data, timeout, &(client->abort)) < 0) { client_drop(client, PSL_IDLE_CYCLES, CLIENT_NONE); debug_msg("_client_loop: client associate failed; could not communicate with socket"); break; } _client_associate(client, data[0], (char)data[1]); debug_msg("_client_loop: client associated"); break; } client->pending = 0; break; lock_delay(&lock); } pthread_mutex_unlock(&lock); // Terminate thread pthread_exit(NULL); }
static int _pslse_connect(uint16_t * afu_map, int *fd) { FILE *fp; uint8_t buffer[MAX_LINE_CHARS]; struct sockaddr_in ssadr; struct hostent *he; char *host, *port_str; int port; // Get hostname and port of PSLSE server DPRINTF("AFU CONNECT\n"); fp = fopen("pslse_server.dat", "r"); if (!fp) { perror("fopen:pslse_server.dat"); goto connect_fail; } do { if (fgets((char *)buffer, MAX_LINE_CHARS - 1, fp) == NULL) { perror("fgets:pslse_server.dat"); fclose(fp); goto connect_fail; } } while (buffer[0] == '#'); fclose(fp); host = (char *)buffer; port_str = strchr((char *)buffer, ':'); *port_str = '\0'; port_str++; if (!host || !port_str) { warn_msg ("cxl_afu_open_dev:Invalid format in pslse_server.data"); goto connect_fail; } port = atoi(port_str); info_msg("Connecting to host '%s' port %d", host, port); // Connect to PSLSE server if ((he = gethostbyname(host)) == NULL) { herror("gethostbyname"); puts(host); goto connect_fail; } memset(&ssadr, 0, sizeof(ssadr)); memcpy(&ssadr.sin_addr, he->h_addr_list[0], he->h_length); ssadr.sin_family = AF_INET; ssadr.sin_port = htons(port); if ((*fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); goto connect_fail; } ssadr.sin_family = AF_INET; ssadr.sin_port = htons(port); if (connect(*fd, (struct sockaddr *)&ssadr, sizeof(ssadr)) < 0) { perror("connect"); goto connect_fail; } strcpy((char *)buffer, "PSLSE"); buffer[5] = (uint8_t) PSLSE_VERSION_MAJOR; buffer[6] = (uint8_t) PSLSE_VERSION_MINOR; if (put_bytes_silent(*fd, 7, buffer) != 7) { warn_msg("cxl_afu_open_dev:Failed to write to socket!"); goto connect_fail; } if (get_bytes_silent(*fd, 1, buffer, -1, 0) < 0) { warn_msg("cxl_afu_open_dev:Socket failed open acknowledge"); close_socket(fd); goto connect_fail; } if (buffer[0] != (uint8_t) PSLSE_CONNECT) { warn_msg("cxl_afu_open_dev:PSLSE bad acknowledge"); close_socket(fd); goto connect_fail; } if (get_bytes_silent(*fd, sizeof(uint16_t), buffer, 1000, 0) < 0) { warn_msg("cxl_afu_open_dev:afu_map"); close_socket(fd); goto connect_fail; } memcpy((char *)afu_map, (char *)buffer, 2); *afu_map = (long)ntohs(*afu_map); return 0; connect_fail: errno = ENODEV; return -1; }
static void *_psl_loop(void *ptr) { struct cxl_afu_h *afu = (struct cxl_afu_h *)ptr; uint8_t buffer[MAX_LINE_CHARS]; uint8_t size; uint64_t addr; uint16_t value; uint32_t lvalue; int rc; if (!afu) fatal_msg("NULL afu passed to libcxl.c:_psl_loop"); afu->opened = 1; while (afu->opened) { _delay_1ms(); // Send any requests to PSLSE over socket if (afu->int_req.state == LIBCXL_REQ_REQUEST) _req_max_int(afu); if (afu->attach.state == LIBCXL_REQ_REQUEST) _pslse_attach(afu); if (afu->mmio.state == LIBCXL_REQ_REQUEST) { switch (afu->mmio.type) { case PSLSE_MMIO_MAP: _mmio_map(afu); break; case PSLSE_MMIO_WRITE64: _mmio_write64(afu); break; case PSLSE_MMIO_WRITE32: _mmio_write32(afu); break; case PSLSE_MMIO_EBREAD: case PSLSE_MMIO_READ64: case PSLSE_MMIO_READ32: /*fall through */ _mmio_read(afu); break; default: break; } } // Process socket input from PSLSE rc = bytes_ready(afu->fd, 1000, 0); if (rc == 0) continue; if (rc < 0) { warn_msg("Socket failure testing bytes_ready"); _all_idle(afu); break; } if (get_bytes_silent(afu->fd, 1, buffer, 1000, 0) < 0) { warn_msg("Socket failure getting PSL event"); _all_idle(afu); break; } DPRINTF("PSL EVENT\n"); switch (buffer[0]) { case PSLSE_OPEN: if (get_bytes_silent(afu->fd, 1, buffer, 1000, 0) < 0) { warn_msg("Socket failure getting OPEN context"); _all_idle(afu); break; } afu->context = (uint16_t) buffer[0]; afu->open.state = LIBCXL_REQ_IDLE; break; case PSLSE_ATTACH: afu->attach.state = LIBCXL_REQ_IDLE; break; case PSLSE_DETACH: info_msg("detach response from from pslse"); afu->mapped = 0; afu->attached = 0; afu->opened = 0; afu->open.state = LIBCXL_REQ_IDLE; afu->attach.state = LIBCXL_REQ_IDLE; afu->mmio.state = LIBCXL_REQ_IDLE; afu->int_req.state = LIBCXL_REQ_IDLE; break; case PSLSE_MAX_INT: size = sizeof(uint16_t); if (get_bytes_silent(afu->fd, size, buffer, 1000, 0) < 0) { warn_msg ("Socket failure getting max interrupt acknowledge"); _all_idle(afu); break; } memcpy((char *)&value, (char *)buffer, sizeof(uint16_t)); afu->irqs_max = ntohs(value); afu->int_req.state = LIBCXL_REQ_IDLE; break; case PSLSE_QUERY: { size = sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t); if (get_bytes_silent(afu->fd, size, buffer, 1000, 0) < 0) { warn_msg("Socket failure getting PSLSE query"); _all_idle(afu); break; } memcpy((char *)&value, (char *)&(buffer[0]), 2); afu->irqs_min = (long)(value); memcpy((char *)&value, (char *)&(buffer[2]), 2); afu->irqs_max = (long)(value); memcpy((char *)&value, (char *)&(buffer[4]), 2); afu->modes_supported = (long)(value); memcpy((char *)&value, (char *)&(buffer[6]), 8); afu->mmio_len = (long)(value); memcpy((char *)&value, (char *)&(buffer[14]), 8); afu->mmio_off = (long)(value); memcpy((char *)&value, (char *)&(buffer[22]), 8); //afu->eb_len = (long)ntohll(value); afu->eb_len = (long)(value); memcpy((char *)&value, (char *)&(buffer[30]), 2); afu->cr_device = (long)ntohs(value); memcpy((char *)&value, (char *)&(buffer[32]), 2); afu->cr_vendor = (long)ntohs(value); memcpy((char *)&lvalue, (char *)&(buffer[34]), 4); afu->cr_class = ntohl(lvalue); //no better place to put this right now afu->prefault_mode = CXL_PREFAULT_MODE_NONE; break; } case PSLSE_MEMORY_READ: DPRINTF("AFU MEMORY READ\n"); if (get_bytes_silent(afu->fd, 1, buffer, 1000, 0) < 0) { warn_msg ("Socket failure getting memory read size"); _all_idle(afu); break; } size = (uint8_t) buffer[0]; if (get_bytes_silent(afu->fd, sizeof(uint64_t), buffer, -1, 0) < 0) { warn_msg ("Socket failure getting memory read addr"); _all_idle(afu); break; } memcpy((char *)&addr, (char *)buffer, sizeof(uint64_t)); addr = ntohll(addr); _handle_read(afu, addr, size); break; case PSLSE_MEMORY_WRITE: DPRINTF("AFU MEMORY WRITE\n"); if (get_bytes_silent(afu->fd, 1, buffer, 1000, 0) < 0) { warn_msg ("Socket failure getting memory write size"); _all_idle(afu); break; } size = (uint8_t) buffer[0]; if (get_bytes_silent(afu->fd, sizeof(uint64_t), buffer, -1, 0) < 0) { _all_idle(afu); break; } memcpy((char *)&addr, (char *)buffer, sizeof(uint64_t)); addr = ntohll(addr); if (get_bytes_silent(afu->fd, size, buffer, 1000, 0) < 0) { warn_msg ("Socket failure getting memory write data"); _all_idle(afu); break; } _handle_write(afu, addr, size, buffer); break; case PSLSE_MEMORY_TOUCH: DPRINTF("AFU MEMORY TOUCH\n"); if (get_bytes_silent(afu->fd, 1, buffer, 1000, 0) < 0) { warn_msg ("Socket failure getting memory touch size"); _all_idle(afu); break; } size = buffer[0]; if (get_bytes_silent(afu->fd, sizeof(uint64_t), buffer, -1, 0) < 0) { warn_msg ("Socket failure getting memory touch addr"); _all_idle(afu); break; } memcpy((char *)&addr, (char *)buffer, sizeof(uint64_t)); addr = ntohll(addr); _handle_touch(afu, addr, size); break; case PSLSE_MMIO_ACK: _handle_ack(afu); break; case PSLSE_INTERRUPT: if (_handle_interrupt(afu) < 0) { perror("Interrupt Failure"); goto psl_fail; } break; case PSLSE_AFU_ERROR: if (_handle_afu_error(afu) < 0) { perror("AFU ERROR Failure"); goto psl_fail; } break; default: break; } } psl_fail: afu->attached = 0; pthread_exit(NULL); }
// Attach to AFU static void _attach(struct psl *psl, struct client *client) { uint64_t wed; uint8_t ack; uint8_t buffer[MAX_LINE_CHARS]; size_t size; // FIXME: This only works for dedicate mode // might work for afu-directed now - lgt // Get wed value from application // always do the get // pass the wed only for dedicated ack = PSLSE_DETACH; size = sizeof(uint64_t); if (get_bytes_silent(client->fd, size, buffer, psl->timeout, &(client->abort)) < 0) { warn_msg("Failed to get WED value from client"); client_drop(client, PSL_IDLE_CYCLES, CLIENT_NONE); goto attach_done; } // but need to save wed if master|slave for future consumption // interestingly, I can always save it // add to client type. memcpy((char *)&wed, (char *)buffer, sizeof(uint64_t)); // wed came over in be format // since we are modeling the psl register here, we should leave it be #if defined PSL9lite || defined PSL9 client->wed = wed; // ntohll(wed); #else client->wed = ntohll(wed); #endif // Send start to AFU // only add PSL_JOB_START for dedicated and master clients. // send an empty wed in the case of master // lgt - new idea: // track number of clients in psl // if number of clients = 0, then add the start job // add llcmd add to client (loop through clients in send_com) // increment number of clients (decrement where we handle the completion of the detach) switch (client->type) { case 'd': if (psl->attached_clients == 0) { if (add_job(psl->job, PSL_JOB_START, client->wed) != NULL) { // if dedicated, we can ack PSLSE_ATTACH // if master, we might want to wait until after the llcmd add is complete // can I wait here for the START to finish? psl->idle_cycles = PSL_IDLE_CYCLES; ack = PSLSE_ATTACH; } } break; case 'm': case 's': if (psl->attached_clients < psl->max_clients) { if (psl->attached_clients == 0) { if (add_job(psl->job, PSL_JOB_START, 0L) != NULL) { // if master, we might want to wait until after the llcmd add is complete // can I wait here for the START to finish? } } psl->idle_cycles = PSL_IDLE_CYCLES; ack = PSLSE_ATTACH; } // running will be set by send/handle_aux2 routines break; default: // error? break; } psl->attached_clients++; info_msg( "Attached client context %d: current attached clients = %d: client type = %c\n", client->context, psl->attached_clients, client->type ); // for master and slave send llcmd add // master "wed" is 0x0005000000000000 can actually use client->context here as well since context = 0 // slave "wed" is 0x000500000000hhhh where hhhh is the "handle" from client->context // now - about those llcmds :-) // put these in a separate list associated with the job? psl->pe maybe... or another call to add_job? // new routine to job.c? add_cmd? // should a slave know their master? if (client->type == 'm' || client->type == 's') { wed = PSL_LLCMD_ADD; wed = wed | (uint64_t)client->context; // add_pe adds to the client if (add_pe(psl->job, PSL_JOB_LLCMD, wed) != NULL) { } } attach_done: if (put_bytes(client->fd, 1, &ack, psl->dbg_fp, psl->dbg_id, client->context) < 0) { client_drop(client, PSL_IDLE_CYCLES, CLIENT_NONE); } }