suricata
util-logopenfile-tile.c
Go to the documentation of this file.
1 /* Copyright (C) 2014 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Tom DeCanio <decanio.tom@gmail.com>
22  * \author Ken Steele, Tilera Corporation <suricata@tilera.com>
23  *
24  * File-like output for logging on Tilera PCIe cards (TILEncore-Gx)
25  * add the option to send logs across PCIe and then write the output
26  * files on the host system.
27  *
28  */
29 #include <sys/types.h>
30 
31 #include "suricata-common.h" /* errno.h, string.h, etc. */
32 #include "tm-modules.h" /* LogFileCtx */
33 #include "conf.h" /* ConfNode, etc. */
34 #include "util-atomic.h"
35 #include "util-logopenfile-tile.h"
36 
37 #ifdef __tile__
38 #include <gxio/trio.h>
39 #include <mde-version.h>
40 
41 #if MDE_VERSION_CODE >= MDE_VERSION(4,1,0)
42 #include <gxpci/gxpci.h>
43 #else
44 #include <gxpci.h>
45 #endif
46 
47 /*
48  * Tilera trio (PCIe) configuration.
49  */
50 static gxio_trio_context_t trio_context_body;
51 static gxio_trio_context_t* trio_context = &trio_context_body;
52 /*
53  * gxpci contexts used for log relay
54  */
55 static gxpci_context_t gxpci_context_body;
56 static gxpci_context_t *gxpci_context = &gxpci_context_body;
57 /* The TRIO index. */
58 static int trio_index = 0;
59 
60 /* The queue index of a packet queue. */
61 static unsigned int queue_index = 0;
62 
63 /* The local PCIe MAC index. */
64 static int loc_mac;
65 
66 /*
67  * Code for writing files over PCIe to host on Tilera TILEncore PCIe cards.
68  */
69 
70 #define OP_OPEN 1
71 #define OP_WRITE 2
72 #define OP_CLOSE 3
73 
74 /** Maximum number of commands in one PCIe function call */
75 #define MAX_CMDS_BATCH 64
76 
77 typedef struct {
78  uint32_t magic;
79  uint32_t fileno;
80  uint32_t op;
81  uint32_t seq;
82  uint32_t len;
83  uint32_t next_offset;
84  char buf[];
85 } __attribute__((__packed__)) PcieMsg;
86 
87 static int gxpci_fileno = 0;
88 static int pcie_initialized = 0;
89 /* Allocate a Huge page of memory, registered with Trio, into which
90  data to be sent over PCIe is written. Each write starts at wc_pos.
91 */
92 static char *log_mem = NULL;
93 static uint64_t wr_pos; /* write position within log_mem */
94 
95 static SCMutex raw_mutex __attribute__((aligned(64)));
96 static SCMutex pcie_mutex __attribute__((aligned(64)));
97 #define CHECK_SEQ_NUM 1
98 #ifdef CHECK_SEQ_NUM
99 static uint32_t raw_seq = 0;
100 #endif
101 static uint32_t comps_rcvd = 0;
102 /* Block of memory registered with PCIe DMA engine as a source for
103  * PCIe data transfers. Must be <= Huge Page size (16 MB).
104  * Must be large enough that it can't wrap before first PCIe transfer
105  * has completed.
106  */
107 #define PCIE_MEMORY_BLOCK_SIZE (4 * 1024 * 1024)
108 
109 /* Send a buffer over PCIe to Host memory.
110  * len must be smaller than one Packet Queue transfer block.
111  * TODO: Check errors
112  */
113 static void TilePcieDMABuf(void *buf, uint32_t len)
114 {
115  gxpci_comp_t comp[MAX_CMDS_BATCH];
116  gxpci_cmd_t cmd;
117  int result;
118  int credits;
119 
120  SCMutexLock(&pcie_mutex);
121 
122 #ifdef CHECK_SEQ_NUM
123  ((PcieMsg *)buf)->seq = ++raw_seq;
124  __insn_mf();
125 #endif
126 
127  /* Wait for credits to be available for more PCIe writes. */
128  do {
129  result = gxpci_get_comps(gxpci_context, comp, 0, MAX_CMDS_BATCH);
130  if (result) {
131  if (unlikely(result == GXPCI_ERESET)) {
132  SCLogInfo("gxpci channel is reset");
133  return;
134  } else {
135  __sync_fetch_and_add(&comps_rcvd, result);
136  }
137  }
138 
139  credits = gxpci_get_cmd_credits(gxpci_context);
140  if (unlikely(credits == GXPCI_ERESET)) {
141  SCLogInfo("gxpci channel is reset");
142  return;
143  }
144  } while (credits == 0);
145 
146  cmd.buffer = buf;
147  /* Round transfer size up to next host cache-line. This will
148  * transfer more data, but is more efficient.
149  */
150  cmd.size = (len + (CLS - 1)) & ~(CLS - 1);
151 
152  __insn_mf();
153 
154  /* Loop until the command is sent. */
155  do {
156  /* Send PCIe command to packet queue from tile to host. */
157  result = gxpci_pq_t2h_cmd(gxpci_context, &cmd);
158  if (result == 0)
159  break;
160  if (result == GXPCI_ERESET) {
161  SCLogInfo("gxpci channel is reset");
162  break;
163  }
164  /* Not enough credits to send command? */
165  if (result == GXPCI_ECREDITS)
166  continue;
167  } while (1);
168 
169  SCMutexUnlock(&pcie_mutex);
170 }
171 
172 /* Allocate a buffer for data that can be sent over PCIe. Reserves
173  * space at the beginning for the Pcie msg. The buffer is allocated
174  * from a 4MB pool on one huge page. The allocation simply walks
175  * throught the buffer sequentially. This removes the need to free
176  * the buffers, as they simply age out.
177  */
178 static PcieMsg *TilePcieAllocateBuffer(size_t size)
179 {
180  size += sizeof(PcieMsg);
181  /* Round up to cache-line size */
182  size = (size + (CLS - 1)) & ~(CLS - 1);
183 
184  PcieMsg *pmsg;
185  SCMutexLock(&raw_mutex);
186  pmsg = (PcieMsg *)&log_mem[wr_pos];
187  wr_pos += size;
188  if (wr_pos > PCIE_MEMORY_BLOCK_SIZE) {
189  /* Don't have enough space at the end of the memory block, so
190  * wrap to the start.
191  */
192  pmsg = (PcieMsg *)&log_mem[0];
193  wr_pos = size;
194 
195  }
196  SCMutexUnlock(&raw_mutex);
197 
198  return pmsg;
199 }
200 
201 static void PcieWriteOpen(PcieFile *fp, const char *path, const char append)
202 {
203  /* Need space for file name, file mode character and string termination */
204  const int buffer_size = strlen(path) + 2;
205 
206  /* Allocate space in the PCIe output buffer */
207  PcieMsg *p = TilePcieAllocateBuffer(buffer_size);
208 
209  p->magic = 5555;
210  p->fileno = fp->fileno;
211  p->op = OP_OPEN;
212  p->len = offsetof(PcieMsg, buf);
213  /* Format is one character Mode, followed by file path. */
214  p->len += snprintf(p->buf, buffer_size, "%c%s", append, path);
215 
216  TilePcieDMABuf(p, p->len);
217 }
218 
219 static int TilePcieWrite(const char *buffer, int buffer_len, LogFileCtx *log_ctx)
220 {
221  PcieFile *fp = log_ctx->pcie_fp;
222  /* Allocate space in the PCIe output buffer */
223  PcieMsg *p = TilePcieAllocateBuffer(buffer_len);
224 
225  p->magic = 5555;
226  p->fileno = fp->fileno;
227  p->op = OP_WRITE;
228  p->len = offsetof(PcieMsg, buf);
229  p->len += buffer_len;
230  p->next_offset = 0;
231 
232  /* Can remove the need for this memcpy later. */
233  memcpy(p->buf, buffer, buffer_len);
234 
235  TilePcieDMABuf(p, p->len);
236 
237  return buffer_len;
238 }
239 
240 static PcieFile *TileOpenPcieFpInternal(const char *path, const char append_char)
241 {
242  int result;
243  PcieFile *fp;
244 
245  /* Only initialize once */
246  if (SCAtomicCompareAndSwap(&pcie_initialized, 0, 1)) {
247  SCMutexInit(&raw_mutex, NULL);
248  SCMutexInit(&pcie_mutex, NULL);
249 
250  SCLogInfo("Initializing Tile-Gx PCIe index %d / %d, queue: %d",
251  trio_index, loc_mac, queue_index);
252 
253  result = gxio_trio_init(trio_context, trio_index);
254  if (result < 0) {
255  pcie_initialized = 0;
257  "gxio_trio_init() failed: %d: %s",
258  result, gxio_strerror(result));
259  return NULL;
260  }
261 
262  result = gxpci_init(trio_context, gxpci_context, trio_index, loc_mac);
263  if (result < 0) {
264  pcie_initialized = 0;
266  "gxpci_init() failed: %d: %s",
267  result, gxpci_strerror(result));
268  return NULL;
269  }
270 
271  /*
272  * This indicates that we need to allocate an ASID ourselves,
273  * instead of using one that is allocated somewhere else.
274  */
275  int asid = GXIO_ASID_NULL;
276 
277  result = gxpci_open_queue(gxpci_context, asid, GXPCI_PQ_T2H, 0,
278  queue_index, 0, 0);
279  if (result < 0) {
280  pcie_initialized = 0;
282  "gxpci_open_queue() failed: %d: %s",
283  result, gxpci_strerror(result));
284  return NULL;
285  }
286 
287  /*
288  * Allocate and register data buffer
289  */
290  size_t hugepagesz = tmc_alloc_get_huge_pagesize();
291  tmc_alloc_t alloc = TMC_ALLOC_INIT;
292  tmc_alloc_set_huge(&alloc);
293  tmc_alloc_set_home(&alloc, TMC_ALLOC_HOME_HASH);
294  tmc_alloc_set_pagesize_exact(&alloc, hugepagesz);
295  log_mem = tmc_alloc_map(&alloc, hugepagesz);
296  BUG_ON(PCIE_MEMORY_BLOCK_SIZE > hugepagesz);
297 
298  result = gxpci_iomem_register(gxpci_context, log_mem, hugepagesz);
299  if (result < 0) {
300  pcie_initialized = 0;
302  "gxpci_iomem_register() failed: %d: %s",
303  result, gxpci_strerror(result));
304  return NULL;
305  }
306  }
307  fp = SCMalloc(sizeof(PcieFile));
308  if (fp == NULL) {
310  "Failed to Allocate memory for PCIe file pointer");
311 
312  return NULL;
313  }
314 
315  /* Sequentially allocate File descriptor numbers. Not currently ever freed */
316  fp->fileno = SCAtomicFetchAndAdd(&gxpci_fileno, 1);
317  PcieWriteOpen(fp, path, append_char);
318 
319  return fp;
320 }
321 
322 /** \brief Close a PCIe file
323  * \param PCIe file desriptor
324  */
325 static void TileClosePcieFp(LogFileCtx *log_ctx)
326 {
327  SCLogInfo("Closing Tile-Gx PCIe: %s", log_ctx->filename);
328 
329  /* TODO: Need to count open files and close when reaches zero. */
330  SCMutexLock(&pcie_mutex);
331 
332  if (gxpci_context) {
333  gxpci_destroy(gxpci_context);
334  gxpci_context = NULL;
335  }
336 
337  SCMutexUnlock(&pcie_mutex);
338 
339  free(log_ctx->pcie_fp);
340 }
341 
342 /** \brief open the indicated file remotely over PCIe to a host
343  * \param path filesystem path to open
344  * \param append_setting open file with O_APPEND: "yes" or "no"
345  * \retval FILE* on success
346  * \retval NULL on error
347  */
348 PcieFile *TileOpenPcieFp(LogFileCtx *log_ctx, const char *path,
349  const char *append_setting)
350 {
351  PcieFile *ret = NULL;
352  if (ConfValIsTrue(append_setting)) {
353  ret = TileOpenPcieFpInternal(path, 'a');
354  } else {
355  ret = TileOpenPcieFpInternal(path, 'w');
356  }
357 
358  /* Override the default Write and Close functions
359  * with PCIe Write and Close functions.
360  */
361  log_ctx->Write = TilePcieWrite;
362  log_ctx->Close = TileClosePcieFp;
363 
364  if (ret == NULL)
365  SCLogError(SC_ERR_FOPEN, "Error opening PCIe file: \"%s\": %s",
366  path, strerror(errno));
367  return ret;
368 }
369 
370 #endif /* __tilegx__ */
PcieFile * pcie_fp
#define BUG_ON(x)
uint16_t fileno
#define unlikely(expr)
Definition: util-optimize.h:35
#define CLS
typedef __attribute__
DNP3 application header.
void(* Close)(struct LogFileCtx_ *fp)
#define SCMutexUnlock(mut)
uint32_t seq
#define SCMutexInit(mut, mutattr)
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
PcieFile * TileOpenPcieFp(LogFileCtx *log_ctx, const char *path, const char *append_setting)
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:566
int(* Write)(const char *buffer, int buffer_len, struct LogFileCtx_ *fp)
#define SCMalloc(a)
Definition: util-mem.h:174
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
#define SCMutex
#define SCMutexLock(mut)
uint8_t len