You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/util-logopenfile-tile.c

371 lines
11 KiB
C

/* Copyright (C) 2014 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Tom DeCanio <decanio.tom@gmail.com>
* \author Ken Steele, Tilera Corporation <suricata@tilera.com>
*
* File-like output for logging on Tilera PCIe cards (TILEncore-Gx)
* add the option to send logs across PCIe and then write the output
* files on the host system.
*
*/
#include <sys/types.h>
#include "suricata-common.h" /* errno.h, string.h, etc. */
#include "tm-modules.h" /* LogFileCtx */
#include "conf.h" /* ConfNode, etc. */
#include "util-atomic.h"
#include "util-logopenfile-tile.h"
#ifdef __tile__
#include <gxio/trio.h>
#include <mde-version.h>
#if MDE_VERSION_CODE >= MDE_VERSION(4,1,0)
#include <gxpci/gxpci.h>
#else
#include <gxpci.h>
#endif
/*
* Tilera trio (PCIe) configuration.
*/
static gxio_trio_context_t trio_context_body;
static gxio_trio_context_t* trio_context = &trio_context_body;
/*
* gxpci contexts used for log relay
*/
static gxpci_context_t gxpci_context_body;
static gxpci_context_t *gxpci_context = &gxpci_context_body;
/* The TRIO index. */
static int trio_index = 0;
/* The queue index of a packet queue. */
static unsigned int queue_index = 0;
/* The local PCIe MAC index. */
static int loc_mac;
/*
* Code for writing files over PCIe to host on Tilera TILEncore PCIe cards.
*/
#define OP_OPEN 1
#define OP_WRITE 2
#define OP_CLOSE 3
/** Maximum number of commands in one PCIe function call */
#define MAX_CMDS_BATCH 64
typedef struct {
uint32_t magic;
uint32_t fileno;
uint32_t op;
uint32_t seq;
uint32_t len;
uint32_t next_offset;
char buf[];
} __attribute__((__packed__)) PcieMsg;
static int gxpci_fileno = 0;
static int pcie_initialized = 0;
/* Allocate a Huge page of memory, registered with Trio, into which
data to be sent over PCIe is written. Each write starts at wc_pos.
*/
static char *log_mem = NULL;
static uint64_t wr_pos; /* write position within log_mem */
static SCMutex raw_mutex __attribute__((aligned(64)));
static SCMutex pcie_mutex __attribute__((aligned(64)));
#define CHECK_SEQ_NUM 1
#ifdef CHECK_SEQ_NUM
static uint32_t raw_seq = 0;
#endif
static uint32_t comps_rcvd = 0;
/* Block of memory registered with PCIe DMA engine as a source for
* PCIe data transfers. Must be <= Huge Page size (16 MB).
* Must be large enough that it can't wrap before first PCIe transfer
* has completed.
*/
#define PCIE_MEMORY_BLOCK_SIZE (4 * 1024 * 1024)
/* Send a buffer over PCIe to Host memory.
* len must be smaller than one Packet Queue transfer block.
* TODO: Check errors
*/
static void TilePcieDMABuf(void *buf, uint32_t len)
{
gxpci_comp_t comp[MAX_CMDS_BATCH];
gxpci_cmd_t cmd;
int result;
int credits;
SCMutexLock(&pcie_mutex);
#ifdef CHECK_SEQ_NUM
((PcieMsg *)buf)->seq = ++raw_seq;
__insn_mf();
#endif
/* Wait for credits to be available for more PCIe writes. */
do {
result = gxpci_get_comps(gxpci_context, comp, 0, MAX_CMDS_BATCH);
if (result) {
if (unlikely(result == GXPCI_ERESET)) {
SCLogInfo("gxpci channel is reset");
return;
} else {
__sync_fetch_and_add(&comps_rcvd, result);
}
}
credits = gxpci_get_cmd_credits(gxpci_context);
if (unlikely(credits == GXPCI_ERESET)) {
SCLogInfo("gxpci channel is reset");
return;
}
} while (credits == 0);
cmd.buffer = buf;
/* Round transfer size up to next host cache-line. This will
* transfer more data, but is more efficient.
*/
cmd.size = (len + (CLS - 1)) & ~(CLS - 1);
__insn_mf();
/* Loop until the command is sent. */
do {
/* Send PCIe command to packet queue from tile to host. */
result = gxpci_pq_t2h_cmd(gxpci_context, &cmd);
if (result == 0)
break;
if (result == GXPCI_ERESET) {
SCLogInfo("gxpci channel is reset");
break;
}
/* Not enough credits to send command? */
if (result == GXPCI_ECREDITS)
continue;
} while (1);
SCMutexUnlock(&pcie_mutex);
}
/* Allocate a buffer for data that can be sent over PCIe. Reserves
* space at the beginning for the Pcie msg. The buffer is allocated
* from a 4MB pool on one huge page. The allocation simply walks
* throught the buffer sequentially. This removes the need to free
* the buffers, as they simply age out.
*/
static PcieMsg *TilePcieAllocateBuffer(size_t size)
{
size += sizeof(PcieMsg);
/* Round up to cache-line size */
size = (size + (CLS - 1)) & ~(CLS - 1);
PcieMsg *pmsg;
SCMutexLock(&raw_mutex);
pmsg = (PcieMsg *)&log_mem[wr_pos];
wr_pos += size;
if (wr_pos > PCIE_MEMORY_BLOCK_SIZE) {
/* Don't have enough space at the end of the memory block, so
* wrap to the start.
*/
pmsg = (PcieMsg *)&log_mem[0];
wr_pos = size;
}
SCMutexUnlock(&raw_mutex);
return pmsg;
}
static void PcieWriteOpen(PcieFile *fp, const char *path, const char append)
{
/* Need space for file name, file mode character and string termination */
const int buffer_size = strlen(path) + 2;
/* Allocate space in the PCIe output buffer */
PcieMsg *p = TilePcieAllocateBuffer(buffer_size);
p->magic = 5555;
p->fileno = fp->fileno;
p->op = OP_OPEN;
p->len = offsetof(PcieMsg, buf);
/* Format is one character Mode, followed by file path. */
p->len += snprintf(p->buf, buffer_size, "%c%s", append, path);
TilePcieDMABuf(p, p->len);
}
static int TilePcieWrite(const char *buffer, int buffer_len, LogFileCtx *log_ctx)
{
PcieFile *fp = log_ctx->pcie_fp;
/* Allocate space in the PCIe output buffer */
PcieMsg *p = TilePcieAllocateBuffer(buffer_len);
p->magic = 5555;
p->fileno = fp->fileno;
p->op = OP_WRITE;
p->len = offsetof(PcieMsg, buf);
p->len += buffer_len;
p->next_offset = 0;
/* Can remove the need for this memcpy later. */
memcpy(p->buf, buffer, buffer_len);
TilePcieDMABuf(p, p->len);
return buffer_len;
}
static PcieFile *TileOpenPcieFpInternal(const char *path, const char append_char)
{
int result;
PcieFile *fp;
/* Only initialize once */
if (SCAtomicCompareAndSwap(&pcie_initialized, 0, 1)) {
SCMutexInit(&raw_mutex, NULL);
SCMutexInit(&pcie_mutex, NULL);
SCLogInfo("Initializing Tile-Gx PCIe index %d / %d, queue: %d",
trio_index, loc_mac, queue_index);
result = gxio_trio_init(trio_context, trio_index);
if (result < 0) {
pcie_initialized = 0;
SCLogError(SC_ERR_PCIE_INIT_FAILED,
"gxio_trio_init() failed: %d: %s",
result, gxio_strerror(result));
return NULL;
}
result = gxpci_init(trio_context, gxpci_context, trio_index, loc_mac);
if (result < 0) {
pcie_initialized = 0;
SCLogError(SC_ERR_PCIE_INIT_FAILED,
"gxpci_init() failed: %d: %s",
result, gxpci_strerror(result));
return NULL;
}
/*
* This indicates that we need to allocate an ASID ourselves,
* instead of using one that is allocated somewhere else.
*/
int asid = GXIO_ASID_NULL;
result = gxpci_open_queue(gxpci_context, asid, GXPCI_PQ_T2H, 0,
queue_index, 0, 0);
if (result < 0) {
pcie_initialized = 0;
SCLogError(SC_ERR_PCIE_INIT_FAILED,
"gxpci_open_queue() failed: %d: %s",
result, gxpci_strerror(result));
return NULL;
}
/*
* Allocate and register data buffer
*/
size_t hugepagesz = tmc_alloc_get_huge_pagesize();
tmc_alloc_t alloc = TMC_ALLOC_INIT;
tmc_alloc_set_huge(&alloc);
tmc_alloc_set_home(&alloc, TMC_ALLOC_HOME_HASH);
tmc_alloc_set_pagesize_exact(&alloc, hugepagesz);
log_mem = tmc_alloc_map(&alloc, hugepagesz);
BUG_ON(PCIE_MEMORY_BLOCK_SIZE > hugepagesz);
result = gxpci_iomem_register(gxpci_context, log_mem, hugepagesz);
if (result < 0) {
pcie_initialized = 0;
SCLogError(SC_ERR_PCIE_INIT_FAILED,
"gxpci_iomem_register() failed: %d: %s",
result, gxpci_strerror(result));
return NULL;
}
}
fp = SCMalloc(sizeof(PcieFile));
if (fp == NULL) {
SCLogError(SC_ERR_PCIE_INIT_FAILED,
"Failed to Allocate memory for PCIe file pointer");
return NULL;
}
/* Sequentially allocate File descriptor numbers. Not currently ever freed */
fp->fileno = SCAtomicFetchAndAdd(&gxpci_fileno, 1);
PcieWriteOpen(fp, path, append_char);
return fp;
}
/** \brief Close a PCIe file
* \param PCIe file desriptor
*/
static void TileClosePcieFp(LogFileCtx *log_ctx)
{
SCLogInfo("Closing Tile-Gx PCIe: %s", log_ctx->filename);
/* TODO: Need to count open files and close when reaches zero. */
SCMutexLock(&pcie_mutex);
if (gxpci_context) {
gxpci_destroy(gxpci_context);
gxpci_context = NULL;
}
SCMutexUnlock(&pcie_mutex);
free(log_ctx->pcie_fp);
}
/** \brief open the indicated file remotely over PCIe to a host
* \param path filesystem path to open
* \param append_setting open file with O_APPEND: "yes" or "no"
* \retval FILE* on success
* \retval NULL on error
*/
PcieFile *TileOpenPcieFp(LogFileCtx *log_ctx, const char *path,
const char *append_setting)
{
PcieFile *ret = NULL;
if (ConfValIsTrue(append_setting)) {
ret = TileOpenPcieFpInternal(path, 'a');
} else {
ret = TileOpenPcieFpInternal(path, 'w');
}
/* Override the default Write and Close functions
* with PCIe Write and Close functions.
*/
log_ctx->Write = TilePcieWrite;
log_ctx->Close = TileClosePcieFp;
if (ret == NULL)
SCLogError(SC_ERR_FOPEN, "Error opening PCIe file: \"%s\": %s",
path, strerror(errno));
return ret;
}
#endif /* __tilegx__ */