summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Moore <paul@paul-moore.com>2019-11-04 20:15:20 -0500
committerPaul Moore <paul@paul-moore.com>2019-11-11 17:42:54 -0500
commit19af04da86e9a4168a443f3563fc7aec8839edf0 (patch)
treed10a4b35e3c4923664536d61b22c9e09e4c4a64c
parent21b98d85e8bfdb701a5f9afd54ff5175af910a45 (diff)
downloadlibseccomp-19af04da86e9a4168a443f3563fc7aec8839edf0.tar.gz
db: add shadow transactions
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. Acked-by: Tom Hromatka <tom.hromatka@oracle.com> Signed-off-by: Paul Moore <paul@paul-moore.com>
-rw-r--r--src/db.c127
-rw-r--r--src/db.h1
2 files changed, 127 insertions, 1 deletions
diff --git a/src/db.c b/src/db.c
index 6a30c64..a40cb2b 100644
--- a/src/db.c
+++ b/src/db.c
@@ -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])
@@ -1134,6 +1137,7 @@ init_failure:
void db_col_release(struct db_filter_col *col)
{
unsigned int iter;
+ struct db_filter_snap *snap;
if (col == NULL)
return;
@@ -1141,6 +1145,13 @@ void db_col_release(struct db_filter_col *col)
/* 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]);
@@ -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)
@@ -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;
}
diff --git a/src/db.h b/src/db.h
index c181038..9dce65a 100644
--- a/src/db.h
+++ b/src/db.h
@@ -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;
};