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

Add option on Tile-Gx for logging for fast.log alerts over PCIe When running on a TILEncore-Gx PCIe card, setting the filetype of fast.log to pcie, will open a connection over PCIe to a host application caleld tile-pcie-logd, that receives the alert strings and writes them to a file on the host. The file name to open is also passed over the PCIe link. This allows running Suricata on the TILEncore-Gx PCIe card, but have the alerts logged to the host system's file system efficiently. The PCIe API that is used is the Tilera Packet Queue (PQ) API which can access PCIe from User Space, thus avoiding system calls. Created util-logopenfile-tile.c and util-logopen-tile.h for the TILE specific PCIe logging functionality. Using Write() and Close() function pointers in LogFileCtx, which default to standard write and close for files and sockets, but are changed to PCIe write and close functions when a PCIe channel is openned for logging. Moved Logging contex out of tm-modules.h into util-logopenfile.h, where it makes more sense. This required including util-logopenfile.h into a couple of alert-*.c files, which previously were getting the definitions from tm-modules.h. The source and Makefile for tile-pcie-logd are added in contrib/tile-pcie-logd. By default, the file name for fast.log specified in suricata.yaml is used as the filename on the host. An optional argument to tile-pcie-logd, --prefix=, can be added to prepend the supplied file path. For example, is the file in suricata.yaml is specified as "/var/log/fast.log" and --prefix="/tmp", then the file will be written to "/tmp/var/log/fast.log". Check for TILERA_ROOT environment variable before building tile_pcie_logd Building tile_pcie_logd on x86 requires the Tilera MDE for its PCIe libraries and API header files. Configure now checs for TILERA_ROOT before enabling builing tile_pcie_logd in contrib/tile_pcie_logd
13 years ago
/* 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)) {
Add option on Tile-Gx for logging for fast.log alerts over PCIe When running on a TILEncore-Gx PCIe card, setting the filetype of fast.log to pcie, will open a connection over PCIe to a host application caleld tile-pcie-logd, that receives the alert strings and writes them to a file on the host. The file name to open is also passed over the PCIe link. This allows running Suricata on the TILEncore-Gx PCIe card, but have the alerts logged to the host system's file system efficiently. The PCIe API that is used is the Tilera Packet Queue (PQ) API which can access PCIe from User Space, thus avoiding system calls. Created util-logopenfile-tile.c and util-logopen-tile.h for the TILE specific PCIe logging functionality. Using Write() and Close() function pointers in LogFileCtx, which default to standard write and close for files and sockets, but are changed to PCIe write and close functions when a PCIe channel is openned for logging. Moved Logging contex out of tm-modules.h into util-logopenfile.h, where it makes more sense. This required including util-logopenfile.h into a couple of alert-*.c files, which previously were getting the definitions from tm-modules.h. The source and Makefile for tile-pcie-logd are added in contrib/tile-pcie-logd. By default, the file name for fast.log specified in suricata.yaml is used as the filename on the host. An optional argument to tile-pcie-logd, --prefix=, can be added to prepend the supplied file path. For example, is the file in suricata.yaml is specified as "/var/log/fast.log" and --prefix="/tmp", then the file will be written to "/tmp/var/log/fast.log". Check for TILERA_ROOT environment variable before building tile_pcie_logd Building tile_pcie_logd on x86 requires the Tilera MDE for its PCIe libraries and API header files. Configure now checs for TILERA_ROOT before enabling builing tile_pcie_logd in contrib/tile_pcie_logd
13 years ago
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__ */