Skip to content

Commit

Permalink
db: add shadow transactions
Browse files Browse the repository at this point in the history
Creating a transaction can be very time consuming on large filters since we
create a duplicate filter tree iteratively using the rules supplied by the
caller.  In an effort to speed this up we introduce the idea of shadow
transactions where on a successful transaction commit we preserve the old
transaction checkpoint and bring it up to date with the current filter and
save it for future use.  The next time we start a new transaction we check
to see if a shadow transaction exists, if it does we use that instead of
creating a new transaction checkpoint from scratch.

Signed-off-by: Paul Moore <paul@paul-moore.com>
  • Loading branch information
pcmoore committed Nov 5, 2019
1 parent fb0dfd1 commit bc3a6c0
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 1 deletion.
127 changes: 126 additions & 1 deletion src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,9 @@ static void _db_snap_release(struct db_filter_snap *snap)
{
unsigned int iter;

if (snap == NULL)
return;

if (snap->filter_cnt > 0) {
for (iter = 0; iter < snap->filter_cnt; iter++) {
if (snap->filters[iter])
Expand Down Expand Up @@ -1134,13 +1137,21 @@ struct db_filter_col *db_col_init(uint32_t def_action)
void db_col_release(struct db_filter_col *col)
{
unsigned int iter;
struct db_filter_snap *snap;

if (col == NULL)
return;

/* set the state, just in case */
col->state = _DB_STA_FREED;

/* free any snapshots */
while (col->snapshots != NULL) {
snap = col->snapshots;
col->snapshots = snap->next;
_db_snap_release(snap);
}

/* free any filters */
for (iter = 0; iter < col->filter_cnt; iter++)
_db_release(col->filters[iter]);
Expand Down Expand Up @@ -2343,6 +2354,20 @@ int db_col_transaction_start(struct db_filter_col *col)
struct db_filter *filter_o, *filter_s;
struct db_api_rule_list *rule_o, *rule_s = NULL;

/* check to see if a shadow snapshot exists */
if (col->snapshots && col->snapshots->shadow) {
/* we have a shadow! this will be easy */

/* NOTE: we don't bother to do any verification of the shadow
* because we start a new transaction every time we add
* a new rule to the filter(s); if this ever changes we
* will need to add a mechanism to verify that the shadow
* transaction is current/correct */

col->snapshots->shadow = false;
return 0;
}

/* allocate the snapshot */
snap = zmalloc(sizeof(*snap));
if (snap == NULL)
Expand Down Expand Up @@ -2436,14 +2461,114 @@ void db_col_transaction_abort(struct db_filter_col *col)
* Commit the top most seccomp filter transaction
* @param col the filter collection
*
* This function commits the most recent seccomp filter transaction.
* This function commits the most recent seccomp filter transaction and
* attempts to create a shadow transaction that is a duplicate of the current
* filter to speed up future transactions.
*
*/
void db_col_transaction_commit(struct db_filter_col *col)
{
int rc;
unsigned int iter;
struct db_filter_snap *snap;
struct db_filter *filter_o, *filter_s;
struct db_api_rule_list *rule_o, *rule_s;

snap = col->snapshots;
if (snap == NULL)
return;

/* check for a shadow set by a higher transaction commit */
if (snap->shadow) {
/* leave the shadow intact, but drop the next snapshot */
if (snap->next) {
snap->next = snap->next->next;
_db_snap_release(snap->next);
}
return;
}

/* adjust the number of filters if needed */
if (col->filter_cnt > snap->filter_cnt) {
unsigned int tmp_i;
struct db_filter **tmp_f;

/* add filters */
tmp_f = realloc(snap->filters,
sizeof(struct db_filter *) * col->filter_cnt);
if (tmp_f == NULL)
goto shadow_err;
snap->filters = tmp_f;
do {
tmp_i = snap->filter_cnt;
snap->filters[tmp_i] =
_db_init(col->filters[tmp_i]->arch);
if (snap->filters[tmp_i] == NULL)
goto shadow_err;
snap->filter_cnt++;
} while (snap->filter_cnt < col->filter_cnt);
} else if (col->filter_cnt < snap->filter_cnt) {
/* remove filters */

/* NOTE: while we release the filters we no longer need, we
* don't bother to resize the filter array, we just
* adjust the filter counter, this *should* be harmless
* at the cost of a not reaping all the memory possible */

do {
_db_release(snap->filters[snap->filter_cnt--]);
} while (snap->filter_cnt > col->filter_cnt);
}

/* loop through each filter and update the rules on the snapshot */
for (iter = 0; iter < col->filter_cnt; iter++) {
filter_o = col->filters[iter];
filter_s = snap->filters[iter];

/* skip ahead to the new rule(s) */
rule_o = filter_o->rules;
rule_s = filter_s->rules;
if (rule_o == NULL)
/* nothing to shadow */
continue;
if (rule_s != NULL) {
do {
rule_o = rule_o->next;
rule_s = rule_s->next;
} while (rule_s != filter_s->rules);

/* did we actually add any rules? */
if (rule_o == filter_o->rules)
/* no, we are done in this case */
continue;
}

/* update the old snapshot to make it a shadow */
do {
/* duplicate the rule */
rule_s = db_rule_dup(rule_o);
if (rule_s == NULL)
goto shadow_err;

/* add the rule */
rc = _db_col_rule_add(filter_s, rule_s);
if (rc != 0) {
free(rule_s);
goto shadow_err;
}

/* next rule */
rule_o = rule_o->next;
} while (rule_o != filter_o->rules);
}

/* success, mark the snapshot as a shadow and return */
snap->shadow = true;
return;

shadow_err:
/* we failed making a shadow, cleanup and return */
col->snapshots = snap->next;
_db_snap_release(snap);
return;
}
1 change: 1 addition & 0 deletions src/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ struct db_filter_snap {
/* individual filters */
struct db_filter **filters;
unsigned int filter_cnt;
bool shadow;

struct db_filter_snap *next;
};
Expand Down

0 comments on commit bc3a6c0

Please sign in to comment.