From owner-svn-src-projects@FreeBSD.ORG Mon Aug 11 22:38:13 2014 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id D5104B36 for ; Mon, 11 Aug 2014 22:38:13 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id C3C272E5A for ; Mon, 11 Aug 2014 22:38:13 +0000 (UTC) Received: from melifaro (uid 1268) (envelope-from melifaro@FreeBSD.org) id 2e9f by svn.freebsd.org (DragonFly Mail Agent v0.9+); Mon, 11 Aug 2014 22:38:13 +0000 From: Alexander V. Chernikov Date: Mon, 11 Aug 2014 22:38:13 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r269843 - projects/ipfw/sys/netpfil/ipfw X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message-Id: <53e945d5.2e9f.260da81d@svn.freebsd.org> X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 11 Aug 2014 22:38:13 -0000 Author: melifaro Date: Mon Aug 11 22:38:13 2014 New Revision: 269843 URL: http://svnweb.freebsd.org/changeset/base/269843 Log: Simplify add/del_table_entry() by making their common pieces common functions. Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c ============================================================================== --- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Mon Aug 11 21:42:06 2014 (r269842) +++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Mon Aug 11 22:38:13 2014 (r269843) @@ -173,32 +173,59 @@ check_table_limit(struct table_config *t } /* - * Adds/updates one or more entries in table @ti. + * Convert algorithm callback return code into + * one of pre-defined states known by userland. + */ +static void +store_tei_result(struct tentry_info *tei, int do_add, int error, uint32_t num) +{ + int flag; + + flag = 0; + + switch (error) { + case 0: + if (do_add && num != 0) + flag = TEI_FLAGS_ADDED; + if (do_add == 0) + flag = TEI_FLAGS_DELETED; + break; + case ENOENT: + flag = TEI_FLAGS_NOTFOUND; + break; + case EEXIST: + flag = TEI_FLAGS_EXISTS; + break; + default: + flag = TEI_FLAGS_ERROR; + } + + tei->flags |= flag; +} + +/* + * Find and reference existing table optionally + * creating new one. * - * Returns 0 on success. + * Saves found table config/table algo into @ptc / @pta. + * Returns 0 if table was found/created and referenced + * or non-zero return code. */ -int -add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, - struct tentry_info *tei, uint8_t flags, uint32_t count) +static int +find_ref_table(struct ip_fw_chain *ch, struct tid_info *ti, + struct tentry_info *tei, uint32_t count, int do_add, + struct table_config **ptc, struct table_algo **pta) { + struct namedobj_instance *ni; struct table_config *tc; struct table_algo *ta; - struct namedobj_instance *ni; - uint16_t kidx; - int error, first_error, i, j, rerror, rollback; - uint32_t num, numadd; ipfw_xtable_info *xi; - struct tentry_info *ptei; - char ta_buf[TA_BUF_SZ]; - size_t ta_buf_sz; - caddr_t ta_buf_m, v, vv; + int error; IPFW_UH_WLOCK(ch); - ni = CHAIN_TO_NI(ch); - /* - * Find and reference existing table. - */ + ni = CHAIN_TO_NI(ch); + tc = NULL; ta = NULL; if ((tc = find_table(ni, ti)) != NULL) { /* check table type */ @@ -213,9 +240,10 @@ add_table_entry(struct ip_fw_chain *ch, } /* Try to exit early on limit hit */ - if ((error = check_table_limit(tc, tei)) != 0 && count == 1) { - IPFW_UH_WUNLOCK(ch); - return (EFBIG); + if (do_add != 0 && count == 1 && + check_table_limit(tc, tei) != 0) { + IPFW_UH_WUNLOCK(ch); + return (EFBIG); } /* Reference and unlock */ @@ -225,6 +253,9 @@ add_table_entry(struct ip_fw_chain *ch, IPFW_UH_WUNLOCK(ch); if (tc == NULL) { + if (do_add == 0) + return (ESRCH); + /* Compability mode: create new table for old clients */ if ((tei->flags & TEI_FLAGS_COMPAT) == 0) return (ESRCH); @@ -257,26 +288,101 @@ add_table_entry(struct ip_fw_chain *ch, IPFW_UH_WUNLOCK(ch); } - /* Allocate memory and prepare record(s) */ + *ptc = tc; + *pta = ta; + return (0); +} + +/* + * Rolls back already @added to @tc entries using state arrat @ta_buf_m. + * Assume the following layout: + * 1) ADD state (ta_buf_m[0] ... t_buf_m[added - 1]) for handling update cases + * 2) DEL state (ta_buf_m[count[ ... t_buf_m[count + added - 1]) + * for storing deleted state + */ +static void +rollback_added_entries(struct ip_fw_chain *ch, struct table_config *tc, + struct table_info *tinfo, struct tentry_info *tei, caddr_t ta_buf_m, + uint32_t count, uint32_t added) +{ + struct table_algo *ta; + struct tentry_info *ptei; + caddr_t v, vv; + size_t ta_buf_sz; + int error, i; + uint32_t num; + + IPFW_UH_WLOCK_ASSERT(ch); + + ta = tc->ta; + ta_buf_sz = ta->ta_buf_size; + v = ta_buf_m; + vv = v + count * ta_buf_sz; + for (i = 0; i < added; i++, v += ta_buf_sz, vv += ta_buf_sz) { + ptei = &tei[i]; + if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) { + + /* + * We have old value stored by previous + * call in @ptei->value. Do add once again + * to restore it. + */ + error = ta->add(tc->astate, tinfo, ptei, v, &num); + KASSERT(error == 0, ("rollback UPDATE fail")); + KASSERT(num == 0, ("rollback UPDATE fail2")); + continue; + } + + error = ta->prepare_del(ch, ptei, vv); + KASSERT(error == 0, ("pre-rollback INSERT failed")); + error = ta->del(tc->astate, tinfo, ptei, vv, &num); + KASSERT(error == 0, ("rollback INSERT failed")); + tc->count -= num; + } +} + +/* + * Prepares add/del state for all @count entries in @tei. + * Uses either stack buffer (@ta_buf) or allocates a new one. + * Stores pointer to allocated buffer back to @ta_buf. + * + * Returns 0 on success. + */ +static int +prepare_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta, + struct tentry_info *tei, uint32_t count, int do_add, caddr_t *ta_buf) +{ + caddr_t ta_buf_m, v; + size_t ta_buf_sz, sz; + struct tentry_info *ptei; + int error, i; + + error = 0; ta_buf_sz = ta->ta_buf_size; - rollback = 0; if (count == 1) { - memset(&ta_buf, 0, sizeof(ta_buf)); - ta_buf_m = ta_buf; + /* Sigle add/delete, use on-stack buffer */ + memset(*ta_buf, 0, TA_BUF_SZ); + ta_buf_m = *ta_buf; } else { /* - * Multiple adds, allocate larger buffer - * sufficient to hold both ADD state + * Multiple adds/deletes, allocate larger buffer + * + * Note we need 2xcount buffer for add case: + * we have hold both ADD state * and DELETE state (this may be needed * if we need to rollback all changes) */ - ta_buf_m = malloc(2 * count * ta_buf_sz, M_TEMP, + sz = count * ta_buf_sz; + ta_buf_m = malloc((do_add != 0) ? sz * 2 : sz, M_TEMP, M_WAITOK | M_ZERO); } + v = ta_buf_m; for (i = 0; i < count; i++, v += ta_buf_sz) { - error = ta->prepare_add(ch, &tei[i], v); + ptei = &tei[i]; + error = (do_add != 0) ? + ta->prepare_add(ch, ptei, v) : ta->prepare_del(ch, ptei, v); /* * Some syntax error (incorrect mask, or address, or @@ -284,9 +390,76 @@ add_table_entry(struct ip_fw_chain *ch, * settings. */ if (error != 0) - goto cleanup; + break; + } + + *ta_buf = ta_buf_m; + return (error); +} + +/* + * Flushes allocated state for each @count entries in @tei. + * Frees @ta_buf_m if differs from stack buffer @ta_buf. + */ +static void +flush_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta, + struct tentry_info *tei, uint32_t count, int do_add, int rollback, + caddr_t ta_buf_m, caddr_t ta_buf) +{ + caddr_t v; + size_t ta_buf_sz; + int i; + + ta_buf_sz = ta->ta_buf_size; + + /* Run cleaning callback anyway */ + v = ta_buf_m; + for (i = 0; i < count; i++, v += ta_buf_sz) + ta->flush_entry(ch, &tei[i], v); + + /* Clean up "deleted" state in case of rollback */ + if (rollback != 0) { + v = ta_buf_m + count * ta_buf_sz; + for (i = 0; i < count; i++, v += ta_buf_sz) + ta->flush_entry(ch, &tei[i], v); } + if (ta_buf_m != ta_buf) + free(ta_buf_m, M_TEMP); +} + +/* + * Adds/updates one or more entries in table @ti. + * + * Returns 0 on success. + */ +int +add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, + struct tentry_info *tei, uint8_t flags, uint32_t count) +{ + struct table_config *tc; + struct table_algo *ta; + uint16_t kidx; + int error, first_error, i, rollback; + uint32_t num, numadd; + struct tentry_info *ptei; + char ta_buf[TA_BUF_SZ]; + caddr_t ta_buf_m, v; + + /* + * Find and reference existing table. + */ + if ((error = find_ref_table(ch, ti, tei, count, 1, &tc, &ta)) != 0) + return (error); + + /* Allocate memory and prepare record(s) */ + rollback = 0; + /* Pass stack buffer by default */ + ta_buf_m = ta_buf; + error = prepare_batch_buffer(ch, ta, tei, count, 1, &ta_buf_m); + if (error != 0) + goto cleanup; + IPFW_UH_WLOCK(ch); /* @@ -300,8 +473,6 @@ add_table_entry(struct ip_fw_chain *ch, goto cleanup; } - ni = CHAIN_TO_NI(ch); - /* Drop reference we've used in first search */ tc->no.refcnt--; /* We've got valid table in @tc. Let's try to add data */ @@ -313,7 +484,7 @@ add_table_entry(struct ip_fw_chain *ch, IPFW_WLOCK(ch); v = ta_buf_m; - for (i = 0; i < count; i++, v += ta_buf_sz) { + for (i = 0; i < count; i++, v += ta->ta_buf_size) { ptei = &tei[i]; num = 0; /* check limit before adding */ @@ -321,14 +492,7 @@ add_table_entry(struct ip_fw_chain *ch, error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v, &num); /* Set status flag to inform userland */ - if (error == 0 && num != 0) - ptei->flags |= TEI_FLAGS_ADDED; - else if (error == ENOENT) - ptei->flags |= TEI_FLAGS_NOTFOUND; - else if (error == EEXIST) - ptei->flags |= TEI_FLAGS_EXISTS; - else - ptei->flags |= TEI_FLAGS_ERROR; + store_tei_result(ptei, 1, error, num); } if (error == 0) { /* Update number of records to ease limit checking */ @@ -348,39 +512,8 @@ add_table_entry(struct ip_fw_chain *ch, if ((flags & IPFW_CTF_ATOMIC) == 0) continue; - /* - * We need to rollback changes. - * This is tricky since some entries may have been - * updated, so we need to change their value back - * instead of deletion. - */ - rollback = 1; - v = ta_buf_m; - vv = v + count * ta_buf_sz; - for (j = 0; j < i; j++, v += ta_buf_sz, vv += ta_buf_sz) { - ptei = &tei[j]; - if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) { - - /* - * We have old value stored by previous - * call in @ptei->value. Do add once again - * to restore it. - */ - rerror = ta->add(tc->astate, - KIDX_TO_TI(ch, kidx), ptei, v, &num); - KASSERT(rerror == 0, ("rollback UPDATE fail")); - KASSERT(num == 0, ("rollback UPDATE fail2")); - continue; - } - - rerror = ta->prepare_del(ch, ptei, vv); - KASSERT(rerror == 0, ("pre-rollback INSERT failed")); - rerror = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, - vv, &num); - KASSERT(rerror == 0, ("rollback INSERT failed")); - tc->count -= num; - } - + rollback_added_entries(ch, tc, KIDX_TO_TI(ch, kidx), + tei, ta_buf_m, count, i); break; } @@ -396,20 +529,7 @@ add_table_entry(struct ip_fw_chain *ch, error = first_error; cleanup: - /* Run cleaning callback anyway */ - v = ta_buf_m; - for (i = 0; i < count; i++, v += ta_buf_sz) - ta->flush_entry(ch, &tei[i], v); - - /* Clean up "deleted" state in case of rollback */ - if (rollback != 0) { - vv = ta_buf_m + count * ta_buf_sz; - for (i = 0; i < count; i++, vv += ta_buf_sz) - ta->flush_entry(ch, &tei[i], vv); - } - - if (ta_buf_m != ta_buf) - free(ta_buf_m, M_TEMP); + flush_batch_buffer(ch, ta, tei, count, 1, rollback, ta_buf_m, ta_buf); return (error); } @@ -425,74 +545,25 @@ del_table_entry(struct ip_fw_chain *ch, { struct table_config *tc; struct table_algo *ta; - struct namedobj_instance *ni; struct tentry_info *ptei; uint16_t kidx; int error, first_error, i; uint32_t num, numdel; char ta_buf[TA_BUF_SZ]; - size_t ta_buf_sz; caddr_t ta_buf_m, v; - IPFW_UH_WLOCK(ch); - ni = CHAIN_TO_NI(ch); - if ((tc = find_table(ni, ti)) == NULL) { - IPFW_UH_WUNLOCK(ch); - return (ESRCH); - } - - if (tc->locked != 0) { - IPFW_UH_WUNLOCK(ch); - return (EACCES); - } - - if (tc->no.type != ti->type) { - IPFW_UH_WUNLOCK(ch); - return (EINVAL); - } - /* - * Give a chance for algorithm to shrink. - * May release/reacquire UH_WLOCK. + * Find and reference existing table. */ - kidx = tc->no.kidx; - error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); - if (error != 0) { - IPFW_UH_WUNLOCK(ch); + if ((error = find_ref_table(ch, ti, tei, count, 0, &tc, &ta)) != 0) return (error); - } - - /* Reference and unlock */ - tc->no.refcnt++; - ta = tc->ta; - - IPFW_UH_WUNLOCK(ch); /* Allocate memory and prepare record(s) */ - ta_buf_sz = ta->ta_buf_size; - if (count == 1) { - memset(&ta_buf, 0, sizeof(ta_buf)); - ta_buf_m = ta_buf; - } else { - - /* - * Multiple deletes, allocate larger buffer - * sufficient to hold delete state. - */ - ta_buf_m = malloc(count * ta_buf_sz, M_TEMP, - M_WAITOK | M_ZERO); - } - v = ta_buf_m; - for (i = 0; i < count; i++, v += ta_buf_sz) { - error = ta->prepare_del(ch, &tei[i], v); - - /* - * Some syntax error (incorrect mask, or address, or - * anything). Return error immediately. - */ - if (error != 0) - goto cleanup; - } + /* Pass stack buffer by default */ + ta_buf_m = ta_buf; + error = prepare_batch_buffer(ch, ta, tei, count, 0, &ta_buf_m); + if (error != 0) + goto cleanup; IPFW_UH_WLOCK(ch); @@ -505,18 +576,13 @@ del_table_entry(struct ip_fw_chain *ch, IPFW_WLOCK(ch); v = ta_buf_m; - for (i = 0; i < count; i++, v += ta_buf_sz) { + for (i = 0; i < count; i++, v += ta->ta_buf_size) { ptei = &tei[i]; num = 0; error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v, &num); /* Save state for userland */ - if (error == 0) - ptei->flags |= TEI_FLAGS_DELETED; - else if (error == ENOENT) - ptei->flags |= TEI_FLAGS_NOTFOUND; - else - ptei->flags |= TEI_FLAGS_ERROR; + store_tei_result(ptei, 0, error, num); if (error != 0 && first_error == 0) first_error = error; tc->count -= num; @@ -535,13 +601,7 @@ del_table_entry(struct ip_fw_chain *ch, error = first_error; cleanup: - /* Run cleaning callback anyway */ - v = ta_buf_m; - for (i = 0; i < count; i++, v += ta_buf_sz) - ta->flush_entry(ch, &tei[i], v); - - if (ta_buf_m != ta_buf) - free(ta_buf_m, M_TEMP); + flush_batch_buffer(ch, ta, tei, count, 0, 0, ta_buf_m, ta_buf); return (error); }