vectorscan/chimera/ch_scratch.c

317 lines
9.8 KiB
C

/*
* Copyright (c) 2018, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
* \brief Chimera: scratch space alloc.
*/
#include <string.h>
#include "allocator.h"
#include "ch.h"
#include "hs.h"
#include "hs_internal.h"
#include "ue2common.h"
#include "ch_alloc.h"
#include "ch_internal.h"
#include "ch_scratch.h"
#include "ch_database.h"
static
size_t getPatternDataSize(const ch_scratch_t *s) {
size_t numCapturingStructs =
s->patternCount * (s->maxCaptureGroups + 1);
return (sizeof(struct ch_patterndata) * s->patternCount) +
alignof(struct ch_capture) + // padding
(sizeof(struct ch_capture) * numCapturingStructs);
}
static
void initPatternData(const ch_scratch_t *s) {
// ch_capture array is aligned, directly after the patterndata array.
char *ptr = (char *)s->patternData +
(sizeof(struct ch_patterndata) * s->patternCount);
struct ch_capture *cap = (struct ch_capture *)
(ROUNDUP_PTR(ptr, alignof(struct ch_capture)));
for (u32 i = 0; i < s->patternCount; i++) {
struct ch_patterndata *pd = &s->patternData[i];
pd->match = cap;
DEBUG_PRINTF("pattern %u: pd=%p, match=%p\n", i, pd, pd->match);
cap += (s->maxCaptureGroups + 1);
}
}
static
ch_error_t alloc_scratch(const ch_scratch_t *proto, ch_scratch_t **scratch) {
size_t ovectorSize = (proto->maxCaptureGroups + 1) * sizeof(int) * 3;
size_t capturedSize =
sizeof(struct ch_capture) * (proto->maxCaptureGroups + 1);
size_t patternDataSize = getPatternDataSize(proto);
size_t activeSize = proto->activeSize;
size_t queueSize = proto->patternCount * sizeof(struct queue_item);
// max padding for alignment below.
size_t padding = alignof(int) + alignof(struct ch_capture) +
alignof(struct ch_patterndata) +
alignof(struct queue_item);
size_t allocSize = sizeof(ch_scratch_t) + ovectorSize + capturedSize +
patternDataSize + activeSize + queueSize + padding
+ 256; /* padding for cacheline alignment */
ch_scratch_t *s;
ch_scratch_t *s_tmp = ch_scratch_alloc(allocSize);
ch_error_t err = ch_check_alloc(s_tmp);
if (err != CH_SUCCESS) {
ch_scratch_free(s_tmp);
*scratch = NULL;
return err;
}
memset(s_tmp, 0, allocSize);
s = ROUNDUP_PTR(s_tmp, 64);
// Set ordinary members.
*s = *proto;
s->magic = CH_SCRATCH_MAGIC;
s->in_use = 0;
s->scratch_alloc = (char *)s_tmp;
// Set pointers internal to allocation.
char *ptr = (char *)s + sizeof(*s);
ptr = ROUNDUP_PTR(ptr, alignof(int));
s->ovector = (int *)ptr;
ptr += ovectorSize;
ptr = ROUNDUP_PTR(ptr, alignof(struct ch_capture));
s->captured = (struct ch_capture *)ptr;
ptr += capturedSize;
ptr = ROUNDUP_PTR(ptr, alignof(struct ch_patterndata));
s->patternData = (struct ch_patterndata *)ptr;
ptr += patternDataSize;
// Pre-fill pattern data, setting captureOffsets
initPatternData(s);
ptr = ROUNDUP_PTR(ptr, alignof(struct queue_item));
s->pq.item = (struct queue_item *)ptr;
ptr += queueSize;
s->active = (u8 *)ptr;
// Store size.
s->scratchSize = allocSize;
// We should never overrun our allocation.
assert((ptr + activeSize) - (char *)s <= (ptrdiff_t)allocSize);
*scratch = s;
return CH_SUCCESS;
}
HS_PUBLIC_API
ch_error_t HS_CDECL ch_alloc_scratch(const ch_database_t *hydb,
ch_scratch_t **scratch) {
if (!hydb || !scratch) {
DEBUG_PRINTF("invalid args\n");
return CH_INVALID;
}
DEBUG_PRINTF("hydb=%p, &scratch=%p\n", hydb, scratch);
ch_error_t rv = hydbIsValid(hydb);
if (rv != CH_SUCCESS) {
DEBUG_PRINTF("invalid database\n");
return rv;
}
if (*scratch != NULL) {
/* has to be aligned before we can do anything with it */
if (!ISALIGNED_CL(*scratch)) {
return CH_INVALID;
}
if ((*scratch)->magic != CH_SCRATCH_MAGIC) {
return CH_INVALID;
}
if (markScratchInUse(*scratch)) {
return CH_SCRATCH_IN_USE;
}
}
// We allocate a prototype of the scratch header to do our sizing with.
ch_scratch_t *proto;
ch_scratch_t *proto_tmp = ch_scratch_alloc(sizeof(ch_scratch_t) + 256);
ch_error_t proto_ret = ch_check_alloc(proto_tmp);
if (proto_ret != CH_SUCCESS) {
ch_scratch_free(proto_tmp);
ch_scratch_free(*scratch);
*scratch = NULL;
return proto_ret;
}
proto = ROUNDUP_PTR(proto_tmp, 64);
int resize = 0;
if (*scratch) {
*proto = **scratch;
} else {
memset(proto, 0, sizeof(*proto));
resize = 1;
}
proto->scratch_alloc = (char *)proto_tmp;
const struct ch_bytecode *db = ch_get_bytecode(hydb);
if (db->maxCaptureGroups > proto->maxCaptureGroups) {
proto->maxCaptureGroups = db->maxCaptureGroups;
resize = 1;
}
if (db->patternCount > proto->patternCount) {
proto->patternCount = db->patternCount;
proto->activeSize = db->activeSize;
resize = 1;
}
if (resize) {
if (*scratch) {
ch_scratch_free((*scratch)->scratch_alloc);
}
ch_error_t alloc_ret = alloc_scratch(proto, scratch);
ch_scratch_free(proto_tmp);
if (alloc_ret != CH_SUCCESS) {
*scratch = NULL;
return alloc_ret;
}
} else {
ch_scratch_free(proto_tmp);
unmarkScratchInUse(*scratch);
}
if (db->flags & CHIMERA_FLAG_NO_MULTIMATCH) {
return CH_SUCCESS;
}
// We may still have to realloc the underlying Hyperscan scratch.
rv = hs_alloc_scratch(getHyperscanDatabase(db),
&(*scratch)->multi_scratch);
if (rv != HS_SUCCESS) {
DEBUG_PRINTF("hs_alloc_scratch for multi_scratch failed\n");
hs_free_scratch((*scratch)->multi_scratch);
ch_scratch_free((*scratch)->scratch_alloc);
*scratch = NULL;
return rv;
}
return CH_SUCCESS;
}
HS_PUBLIC_API
ch_error_t HS_CDECL ch_clone_scratch(const ch_scratch_t *src,
ch_scratch_t **dest) {
if (!dest || !src || !ISALIGNED_CL(src) ||
src->magic != CH_SCRATCH_MAGIC) {
DEBUG_PRINTF("scratch invalid\n");
return CH_INVALID;
}
ch_error_t ret = alloc_scratch(src, dest);
if (ret != CH_SUCCESS) {
DEBUG_PRINTF("alloc_scratch failed\n");
*dest = NULL;
return ret;
}
if (src->multi_scratch) {
(*dest)->multi_scratch = NULL;
ret = hs_clone_scratch(src->multi_scratch, &(*dest)->multi_scratch);
if (ret != HS_SUCCESS) {
DEBUG_PRINTF("hs_clone_scratch(multi_scratch,...) failed\n");
ch_scratch_free(*dest);
return ret;
}
}
return CH_SUCCESS;
}
HS_PUBLIC_API
ch_error_t HS_CDECL ch_free_scratch(ch_scratch_t *scratch) {
ch_error_t ret = CH_SUCCESS;
if (scratch) {
/* has to be aligned before we can do anything with it */
if (!ISALIGNED_CL(scratch)) {
return CH_INVALID;
}
if (scratch->magic != CH_SCRATCH_MAGIC) {
return CH_INVALID;
}
if (markScratchInUse(scratch)) {
return CH_SCRATCH_IN_USE;
}
if (scratch->multi_scratch) {
ret = hs_free_scratch(scratch->multi_scratch);
}
scratch->magic = 0;
assert(scratch->scratch_alloc);
DEBUG_PRINTF("scratch %p is really at %p : freeing\n", scratch,
scratch->scratch_alloc);
ch_scratch_free(scratch->scratch_alloc);
}
return ret;
}
/** Not public, but used for info from our internal tools. Note that in the
* hybrid matcher the scratch is definitely not a contiguous memory region. */
HS_PUBLIC_API
ch_error_t HS_CDECL ch_scratch_size(const ch_scratch_t *scratch, size_t *size) {
ch_error_t ret = CH_SUCCESS;
if (!size || !scratch || !ISALIGNED_CL(scratch) ||
scratch->magic != CH_SCRATCH_MAGIC) {
return CH_INVALID;
} else {
size_t multi_size = 0;
if (scratch->multi_scratch) {
ret = hs_scratch_size(scratch->multi_scratch, &multi_size);
}
if (ret) {
multi_size = 0;
}
*size = scratch->scratchSize + multi_size;
}
return ret;
}