Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 2 Aug 2014 17:18:47 +0000 (UTC)
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r269435 - projects/ipfw/sys/netpfil/ipfw
Message-ID:  <201408021718.s72HIllo073252@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: melifaro
Date: Sat Aug  2 17:18:47 2014
New Revision: 269435
URL: http://svnweb.freebsd.org/changeset/base/269435

Log:
  * Fix case when returning more that 4096 bytes of data
  * Use different approach to ensure algo has enough space to store N elements:
    - explicitly ask algo (under UH_WLOCK) before/after insertion.  This (along
      with existing reallocation callbacks) really guarantees us that it is safe
      to insert N elements at once while holding UH_WLOCK+WLOCK.
    - remove old aflags/flags approach

Modified:
  projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Sat Aug  2 16:45:55 2014	(r269434)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Sat Aug  2 17:18:47 2014	(r269435)
@@ -305,6 +305,7 @@ struct sockopt_data {
 	size_t		kavail;		/* number of bytes available */
 	size_t		ktotal;		/* total bytes pushed */
 	struct sockopt	*sopt;		/* socket data */
+	caddr_t		sopt_val;	/* sopt user buffer */
 	size_t		valsize;	/* original data size */
 };
 

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Sat Aug  2 16:45:55 2014	(r269434)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Sat Aug  2 17:18:47 2014	(r269435)
@@ -1807,6 +1807,7 @@ ipfw_ctl3(struct sockopt *sopt)
 	}
 
 	sdata.sopt = sopt;
+	sdata.sopt_val = sopt->sopt_val;
 	sdata.valsize = valsize;
 
 	/*
@@ -1906,6 +1907,9 @@ ipfw_ctl3(struct sockopt *sopt)
 	else
 		ipfw_flush_sopt_data(&sdata);
 
+	/* Restore original pointer and set number of bytes written */
+	sopt->sopt_val = sdata.sopt_val;
+	sopt->sopt_valsize = sdata.ktotal;
 	if (sdata.kbuf != xbuf)
 		free(sdata.kbuf, M_TEMP);
 
@@ -2113,8 +2117,8 @@ ipfw_ctl(struct sockopt *sopt)
 			ti.type = IPFW_TABLE_CIDR;
 
 			error = (opt == IP_FW_TABLE_ADD) ?
-			    add_table_entry(chain, &ti, &tei) :
-			    del_table_entry(chain, &ti, &tei);
+			    add_table_entry(chain, &ti, &tei, 1) :
+			    del_table_entry(chain, &ti, &tei, 1);
 		}
 		break;
 
@@ -2239,12 +2243,13 @@ static int
 ipfw_flush_sopt_data(struct sockopt_data *sd)
 {
 	int error;
+	size_t sz;
 
-	if (sd->koff == 0)
+	if ((sz = sd->koff) == 0)
 		return (0);
 
 	if (sd->sopt->sopt_dir == SOPT_GET) {
-		error = sooptcopyout(sd->sopt, sd->kbuf, sd->koff);
+		error = sooptcopyout(sd->sopt, sd->kbuf, sz);
 		if (error != 0)
 			return (error);
 	}
@@ -2257,6 +2262,10 @@ ipfw_flush_sopt_data(struct sockopt_data
 	else
 		sd->kavail = sd->valsize - sd->ktotal;
 
+	/* Update sopt buffer */
+	sd->sopt->sopt_valsize = sd->kavail;
+	sd->sopt->sopt_val = sd->sopt_val + sd->ktotal;
+
 	return (0);
 }
 

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Sat Aug  2 16:45:55 2014	(r269434)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Sat Aug  2 17:18:47 2014	(r269435)
@@ -81,7 +81,6 @@ struct table_config {
 	uint8_t		spare;
 	uint32_t	count;		/* Number of records */
 	uint32_t	limit;		/* Max number of records */
-	uint64_t	flags;		/* state flags */
 	char		tablename[64];	/* table name */
 	struct table_algo	*ta;	/* Callbacks for given algo */
 	void		*astate;	/* algorithm state */
@@ -121,8 +120,8 @@ static int ipfw_manage_table_ent_v0(stru
 static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
     struct sockopt_data *sd);
 
-static int modify_table(struct ip_fw_chain *ch, struct table_config *tc,
-    struct table_algo *ta, void *ta_buf, uint64_t pflags);
+static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
+    struct table_info *ti, uint32_t count);
 static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
 
 static struct table_algo *find_table_algo(struct tables_config *tableconf,
@@ -132,10 +131,12 @@ static struct table_algo *find_table_alg
 #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
 #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
 
+#define	TA_BUF_SZ	128	/* On-stack buffer for add/delete state */
+
 
 int
 add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
-    struct tentry_info *tei)
+    struct tentry_info *tei, uint32_t count)
 {
 	struct table_config *tc;
 	struct table_algo *ta;
@@ -143,9 +144,8 @@ add_table_entry(struct ip_fw_chain *ch, 
 	uint16_t kidx;
 	int error;
 	uint32_t num;
-	uint64_t aflags;
-	ipfw_xtable_info xi;
-	char ta_buf[128];
+	ipfw_xtable_info *xi;
+	char ta_buf[TA_BUF_SZ];
 
 	IPFW_UH_WLOCK(ch);
 	ni = CHAIN_TO_NI(ch);
@@ -171,7 +171,6 @@ add_table_entry(struct ip_fw_chain *ch, 
 		/* Reference and unlock */
 		tc->no.refcnt++;
 		ta = tc->ta;
-		aflags = tc->flags;
 	}
 	IPFW_UH_WUNLOCK(ch);
 
@@ -180,10 +179,11 @@ add_table_entry(struct ip_fw_chain *ch, 
 		if ((tei->flags & TEI_FLAGS_COMPAT) == 0)
 			return (ESRCH);
 
-		memset(&xi, 0, sizeof(xi));
-		xi.vtype = IPFW_VTYPE_U32;
+		xi = malloc(sizeof(ipfw_xtable_info), M_TEMP, M_WAITOK|M_ZERO);
+		xi->vtype = IPFW_VTYPE_U32;
 
-		error = create_table_internal(ch, ti, NULL, &xi);
+		error = create_table_internal(ch, ti, NULL, xi);
+		free(xi, M_TEMP);
 
 		if (error != 0)
 			return (error);
@@ -203,22 +203,10 @@ add_table_entry(struct ip_fw_chain *ch, 
 		/* Reference and unlock */
 		tc->no.refcnt++;
 		ta = tc->ta;
-		aflags = tc->flags;
 
 		IPFW_UH_WUNLOCK(ch);
 	}
 
-	if (aflags != 0) {
-
-		/*
-		 * Previous add/delete call returned non-zero state.
-		 * Run appropriate handler.
-		 */
-		error = modify_table(ch, tc, ta, &ta_buf, aflags);
-		if (error != 0)
-			return (error);
-	}
-
 	/* Prepare record (allocate memory) */
 	memset(&ta_buf, 0, sizeof(ta_buf));
 	error = ta->prepare_add(ch, tei, &ta_buf);
@@ -227,17 +215,28 @@ add_table_entry(struct ip_fw_chain *ch, 
 
 	IPFW_UH_WLOCK(ch);
 
+	/*
+	 * Ensure we are able to add all entries without additional
+	 * memory allocations. May release/reacquire UH_WLOCK.
+	 */
+	kidx = tc->no.kidx;
+	error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), count);
+	if (error != 0) {
+		IPFW_UH_WUNLOCK(ch);
+		ta->flush_entry(ch, tei, &ta_buf);
+		return (error);
+	}
+
 	ni = CHAIN_TO_NI(ch);
 
 	/* Drop reference we've used in first search */
 	tc->no.refcnt--;
-	/* Update aflags since it can be changed after previous read */
-	aflags = tc->flags;
 	
 	/* Check limit before adding */
 	if (tc->limit != 0 && tc->count == tc->limit) {
 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0) {
 			IPFW_UH_WUNLOCK(ch);
+			ta->flush_entry(ch, tei, &ta_buf);
 			return (EFBIG);
 		}
 
@@ -256,15 +255,15 @@ add_table_entry(struct ip_fw_chain *ch, 
 	num = 0;
 
 	IPFW_WLOCK(ch);
-	error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf,
-	    &aflags, &num);
+	error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf, &num);
 	IPFW_WUNLOCK(ch);
 
 	/* Update number of records. */
-	if (error == 0)
+	if (error == 0) {
 		tc->count += num;
-
-	tc->flags = aflags;
+		/* Permit post-add algorithm grow/rehash. */
+		error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
+	}
 
 	IPFW_UH_WUNLOCK(ch);
 
@@ -276,7 +275,7 @@ add_table_entry(struct ip_fw_chain *ch, 
 
 int
 del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
-    struct tentry_info *tei)
+    struct tentry_info *tei, uint32_t count)
 {
 	struct table_config *tc;
 	struct table_algo *ta;
@@ -284,8 +283,7 @@ del_table_entry(struct ip_fw_chain *ch, 
 	uint16_t kidx;
 	int error;
 	uint32_t num;
-	uint64_t aflags;
-	char ta_buf[128];
+	char ta_buf[TA_BUF_SZ];
 
 	IPFW_UH_WLOCK(ch);
 	ni = CHAIN_TO_NI(ch);
@@ -299,33 +297,23 @@ del_table_entry(struct ip_fw_chain *ch, 
 		return (EINVAL);
 	}
 
-	aflags = tc->flags;
 	ta = tc->ta;
 
-	if (aflags != 0) {
-
-		/*
-		 * Give the chance to algo to shrink its state.
-		 */
-		tc->no.refcnt++;
+	/*
+	 * Give a chance for algorithm to shrink.
+	 * May release/reacquire UH_WLOCK.
+	 */
+	kidx = tc->no.kidx;
+	error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
+	if (error != 0) {
 		IPFW_UH_WUNLOCK(ch);
-		memset(&ta_buf, 0, sizeof(ta_buf));
-
-		error = modify_table(ch, tc, ta, &ta_buf, aflags);
-
-		IPFW_UH_WLOCK(ch);
-		tc->no.refcnt--;
-		aflags = tc->flags;
-
-		if (error != 0) {
-			IPFW_UH_WUNLOCK(ch);
-			return (error);
-		}
+		ta->flush_entry(ch, tei, &ta_buf);
+		return (error);
 	}
 
 	/*
 	 * We assume ta_buf size is enough for storing
-	 * prepare_del() key, so we're running under UH_LOCK here.
+	 * prepare_del() key, so we're running under UH_WLOCK here.
 	 */
 	memset(&ta_buf, 0, sizeof(ta_buf));
 	if ((error = ta->prepare_del(ch, tei, &ta_buf)) != 0) {
@@ -337,13 +325,14 @@ del_table_entry(struct ip_fw_chain *ch, 
 	num = 0;
 
 	IPFW_WLOCK(ch);
-	error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf,
-	    &aflags, &num);
+	error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf, &num);
 	IPFW_WUNLOCK(ch);
 
-	if (error == 0)
+	if (error == 0) {
 		tc->count -= num;
-	tc->flags = aflags;
+		/* Run post-del hook to permit shrinking */
+		error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
+	}
 
 	IPFW_UH_WUNLOCK(ch);
 
@@ -353,49 +342,88 @@ del_table_entry(struct ip_fw_chain *ch, 
 }
 
 /*
- * Runs callbacks to modify algo state (typically, table resize).
+ * Ensure that table @tc has enough space to add @count entries without
+ * need for reallocation.
  *
  * Callbacks order:
+ * 0) has_space() (UH_WLOCK) - checks if @count items can be added w/o resize.
+ *
  * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags.
  * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage
  * 3) modify (UH_WLOCK + WLOCK) - switch pointers
- * 4) flush_modify (no locks) - free state, if needed
+ * 4) flush_modify (UH_WLOCK) - free state, if needed
+ *
+ * Returns 0 on success.
  */
 static int
-modify_table(struct ip_fw_chain *ch, struct table_config *tc,
-    struct table_algo *ta, void *ta_buf, uint64_t pflags)
+check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
+    struct table_info *ti, uint32_t count)
 {
-	struct table_info *ti;
+	struct table_algo *ta;
+	uint64_t pflags;
+	char ta_buf[TA_BUF_SZ];
 	int error;
 
-	error = ta->prepare_mod(ta_buf, &pflags);
-	if (error != 0)
-		return (error);
-
-	IPFW_UH_WLOCK(ch);
-	ti = KIDX_TO_TI(ch, tc->no.kidx);
+	IPFW_UH_WLOCK_ASSERT(ch);
 
-	error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags);
+	error = 0;
+	ta = tc->ta;
+	/* Acquire reference not to loose @tc between locks/unlocks */
+	tc->no.refcnt++;
 
 	/*
-	 * prepare_mofify may return zero in @pflags to
-	 * indicate that modifications are not unnesessary.
+	 * TODO: think about avoiding race between large add/large delete
+	 * operation on algorithm which implements shrinking along with
+	 * growing.
 	 */
+	while (true) {
+		pflags = 0;
+		if (ta->has_space(tc->astate, ti, count, &pflags) != 0) {
+			tc->no.refcnt--;
+			return (0);
+		}
 
-	if (error == 0 && pflags != 0) {
-		/* Do actual modification */
-		IPFW_WLOCK(ch);
-		ta->modify(tc->astate, ti, ta_buf, pflags);
-		IPFW_WUNLOCK(ch);
-	}
+		/* We have to shrink/grow table */
+		IPFW_UH_WUNLOCK(ch);
+		memset(&ta_buf, 0, sizeof(ta_buf));
+		
+		if ((error = ta->prepare_mod(ta_buf, &pflags)) != 0) {
+			IPFW_UH_WLOCK(ch);
+			break;
+		}
 
-	IPFW_UH_WUNLOCK(ch);
+		IPFW_UH_WLOCK(ch);
 
-	ta->flush_mod(ta_buf);
+		/* Check if we still need to alter table */
+		ti = KIDX_TO_TI(ch, tc->no.kidx);
+		if (ta->has_space(tc->astate, ti, count, &pflags) != 0) {
 
+			/*
+			 * Other threads has already performed resize.
+			 * Flush our state and return/
+			 */
+			ta->flush_mod(ta_buf);
+			break;
+		}
+	
+		error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags);
+		if (error == 0) {
+			/* Do actual modification */
+			IPFW_WLOCK(ch);
+			ta->modify(tc->astate, ti, ta_buf, pflags);
+			IPFW_WUNLOCK(ch);
+		}
+
+		/* Anyway, flush data and retry */
+		ta->flush_mod(ta_buf);
+	}
+
+	tc->no.refcnt--;
 	return (error);
 }
 
+
+
 int
 ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
     struct sockopt_data *sd)
@@ -463,8 +491,8 @@ ipfw_manage_table_ent_v0(struct ip_fw_ch
 	ti.type = xent->type;
 
 	error = (op3->opcode == IP_FW_TABLE_XADD) ?
-	    add_table_entry(ch, &ti, &tei) :
-	    del_table_entry(ch, &ti, &tei);
+	    add_table_entry(ch, &ti, &tei, 1) :
+	    del_table_entry(ch, &ti, &tei, 1);
 
 	return (error);
 }
@@ -538,8 +566,8 @@ ipfw_manage_table_ent_v1(struct ip_fw_ch
 	ti.uidx = tent->idx;
 
 	error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ?
-	    add_table_entry(ch, &ti, &tei) :
-	    del_table_entry(ch, &ti, &tei);
+	    add_table_entry(ch, &ti, &tei, 1) :
+	    del_table_entry(ch, &ti, &tei, 1);
 
 	return (error);
 }
@@ -1614,16 +1642,28 @@ find_table_algo(struct tables_config *tc
 	return (tcfg->def_algo[ti->type]);
 }
 
+/*
+ * Register new table algo @ta.
+ * Stores algo id iside @idx.<F2>
+ *
+ * Returns 0 on success.
+ */
 int
 ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size,
     int *idx)
 {
 	struct tables_config *tcfg;
 	struct table_algo *ta_new;
+	size_t sz;
 
 	if (size > sizeof(struct table_algo))
 		return (EINVAL);
 
+	/* Check for the required on-stack size for add/del */
+	sz = roundup2(ta->ta_buf_size, sizeof(void *));
+	if (sz > TA_BUF_SZ)
+		return (EINVAL);
+
 	KASSERT(ta->type >= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE"));
 
 	ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO);
@@ -1646,6 +1686,9 @@ ipfw_add_table_algo(struct ip_fw_chain *
 	return (0);
 }
 
+/*
+ * Unregisters table algo using @idx as id.
+ */
 void
 ipfw_del_table_algo(struct ip_fw_chain *ch, int idx)
 {
@@ -1654,8 +1697,8 @@ ipfw_del_table_algo(struct ip_fw_chain *
 
 	tcfg = CHAIN_TO_TCFG(ch);
 
-	KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of rage 1..%d", idx, 
-	    tcfg->algo_count));
+	KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d",
+	    idx, tcfg->algo_count));
 
 	ta = tcfg->algo[idx];
 	KASSERT(ta != NULL, ("algo idx %d is NULL", idx));

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h	Sat Aug  2 16:45:55 2014	(r269434)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h	Sat Aug  2 17:18:47 2014	(r269435)
@@ -71,12 +71,14 @@ typedef int (ta_prepare_add)(struct ip_f
 typedef int (ta_prepare_del)(struct ip_fw_chain *ch, struct tentry_info *tei,
     void *ta_buf);
 typedef int (ta_add)(void *ta_state, struct table_info *ti,
-    struct tentry_info *tei, void *ta_buf, uint64_t *pflags, uint32_t *pnum);
+    struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
 typedef int (ta_del)(void *ta_state, struct table_info *ti,
-    struct tentry_info *tei, void *ta_buf, uint64_t *pflags, uint32_t *pnum);
+    struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
 typedef void (ta_flush_entry)(struct ip_fw_chain *ch, struct tentry_info *tei,
     void *ta_buf);
 
+typedef int (ta_has_space)(void *ta_state, struct table_info *ti,
+    uint32_t count, uint64_t *pflags);
 typedef int (ta_prepare_mod)(void *ta_buf, uint64_t *pflags);
 typedef int (ta_fill_mod)(void *ta_state, struct table_info *ti,
     void *ta_buf, uint64_t *pflags);
@@ -113,6 +115,7 @@ struct table_algo {
 	ta_del		*del;
 	ta_flush_entry	*flush_entry;
 	ta_find_tentry	*find_tentry;
+	ta_has_space	*has_space;
 	ta_prepare_mod	*prepare_mod;
 	ta_fill_mod	*fill_mod;
 	ta_modify	*modify;
@@ -151,9 +154,9 @@ int ipfw_flush_table(struct ip_fw_chain 
 int ipfw_list_table_algo(struct ip_fw_chain *ch, struct sockopt_data *sd);
 /* Exported to support legacy opcodes */
 int add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
-    struct tentry_info *tei);
+    struct tentry_info *tei, uint32_t count);
 int del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
-    struct tentry_info *tei);
+    struct tentry_info *tei, uint32_t count);
 int flush_table(struct ip_fw_chain *ch, struct tid_info *ti);
 
 int ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c	Sat Aug  2 16:45:55 2014	(r269434)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c	Sat Aug  2 17:18:47 2014	(r269435)
@@ -397,7 +397,7 @@ ta_prepare_add_cidr(struct ip_fw_chain *
 
 static int
 ta_add_cidr(void *ta_state, struct table_info *ti, struct tentry_info *tei,
-    void *ta_buf, uint64_t *pflags, uint32_t *pnum)
+    void *ta_buf, uint32_t *pnum)
 {
 	struct radix_node_head *rnh;
 	struct radix_node *rn;
@@ -489,7 +489,7 @@ ta_prepare_del_cidr(struct ip_fw_chain *
 
 static int
 ta_del_cidr(void *ta_state, struct table_info *ti, struct tentry_info *tei,
-    void *ta_buf, uint64_t *pflags, uint32_t *pnum)
+    void *ta_buf, uint32_t *pnum)
 {
 	struct radix_node_head *rnh;
 	struct radix_node *rn;
@@ -526,6 +526,20 @@ ta_flush_cidr_entry(struct ip_fw_chain *
 		free(tb->ent_ptr, M_IPFW_TBL);
 }
 
+static int
+ta_has_space_radix(void *ta_state, struct table_info *ti, uint32_t count,
+    uint64_t *pflags)
+{
+
+	/*
+	 * radix does not not require additional memory allocations
+	 * other than nodes itself. Adding new masks to the tree do
+	 * but we don't have any API to call (and we don't known which
+	 * sizes do we need).
+	 */
+	return (1);
+}
+
 struct table_algo cidr_radix = {
 	.name		= "cidr:radix",
 	.type		= IPFW_TABLE_CIDR,
@@ -541,6 +555,7 @@ struct table_algo cidr_radix = {
 	.foreach	= ta_foreach_radix,
 	.dump_tentry	= ta_dump_radix_tentry,
 	.find_tentry	= ta_find_radix_tentry,
+	.has_space	= ta_has_space_radix,
 };
 
 
@@ -1115,7 +1130,7 @@ ta_prepare_add_chash(struct ip_fw_chain 
 
 static int
 ta_add_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
-    void *ta_buf, uint64_t *pflags, uint32_t *pnum)
+    void *ta_buf, uint32_t *pnum)
 {
 	struct chash_cfg *ccfg;
 	struct chashbhead *head;
@@ -1172,16 +1187,11 @@ ta_add_chash(void *ta_state, struct tabl
 		tb->ent_ptr = NULL;
 		*pnum = 1;
 
-		/* Update counters and check if we need to grow hash */
-		if (tei->subtype == AF_INET) {
+		/* Update counters */
+		if (tei->subtype == AF_INET)
 			ccfg->items4++;
-			if (ccfg->items4 > ccfg->size4 && ccfg->size4 < 65536)
-				*pflags = (ccfg->size4 * 2) | (1UL << 32);
-		} else {
+		else
 			ccfg->items6++;
-			if (ccfg->items6 > ccfg->size6 && ccfg->size6 < 65536)
-				*pflags = ccfg->size6 * 2;
-		}
 	}
 
 	return (0);
@@ -1200,7 +1210,7 @@ ta_prepare_del_chash(struct ip_fw_chain 
 
 static int
 ta_del_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
-    void *ta_buf, uint64_t *pflags, uint32_t *pnum)
+    void *ta_buf, uint32_t *pnum)
 {
 	struct chash_cfg *ccfg;
 	struct chashbhead *head;
@@ -1263,8 +1273,39 @@ ta_flush_chash_entry(struct ip_fw_chain 
 struct mod_item {
 	void	*main_ptr;
 	size_t	size;
+	void	*main_ptr6;
+	size_t	size6;
 };
 
+static int
+ta_has_space_chash(void *ta_state, struct table_info *ti, uint32_t count,
+    uint64_t *pflags)
+{
+	struct chash_cfg *cfg;
+	uint64_t data;
+
+	/*
+	 * Since we don't know exact number of IPv4/IPv6 records in @count,
+	 * ignore non-zero @count value at all. Check current hash sizes
+	 * and return appropriate data.
+	 */
+
+	cfg = (struct chash_cfg *)ta_state;
+
+	data = 0;
+	if (cfg->items4 > cfg->size4 && cfg->size4 < 65536)
+		data |= (cfg->size4 * 2) << 16;
+	if (cfg->items6 > cfg->size6 && cfg->size6 < 65536)
+		data |= cfg->size6 * 2;
+
+	if (data != 0) {
+		*pflags = data;
+		return (0);
+	}
+
+	return (1);
+}
+
 /*
  * Allocate new, larger chash.
  */
@@ -1278,13 +1319,23 @@ ta_prepare_mod_chash(void *ta_buf, uint6
 	mi = (struct mod_item *)ta_buf;
 
 	memset(mi, 0, sizeof(struct mod_item));
-	mi->size = *pflags & 0xFFFFFFFF;
-	head = malloc(sizeof(struct chashbhead) * mi->size, M_IPFW,
-	    M_WAITOK | M_ZERO);
-	for (i = 0; i < mi->size; i++)
-		SLIST_INIT(&head[i]);
-
-	mi->main_ptr = head;
+	mi->size = (*pflags >> 16) & 0xFFFF;
+	mi->size6 = *pflags & 0xFFFF;
+	if (mi->size > 0) {
+		head = malloc(sizeof(struct chashbhead) * mi->size,
+		    M_IPFW, M_WAITOK | M_ZERO);
+		for (i = 0; i < mi->size; i++)
+			SLIST_INIT(&head[i]);
+		mi->main_ptr = head;
+	}
+
+	if (mi->size6 > 0) {
+		head = malloc(sizeof(struct chashbhead) * mi->size6,
+		    M_IPFW, M_WAITOK | M_ZERO);
+		for (i = 0; i < mi->size6; i++)
+			SLIST_INIT(&head[i]);
+		mi->main_ptr6 = head;
+	}
 
 	return (0);
 }
@@ -1301,7 +1352,6 @@ ta_fill_mod_chash(void *ta_state, struct
 	return (0);
 }
 
-
 /*
  * Switch old & new arrays.
  */
@@ -1310,54 +1360,62 @@ ta_modify_chash(void *ta_state, struct t
     uint64_t pflags)
 {
 	struct mod_item *mi;
-	struct chash_cfg *ccfg;
+	struct chash_cfg *cfg;
 	struct chashbhead *old_head, *new_head;
 	struct chashentry *ent, *ent_next;
 	int af, i, mlen;
 	uint32_t nhash;
-	size_t old_size;
+	size_t old_size, new_size;
 
 	mi = (struct mod_item *)ta_buf;
-	ccfg = (struct chash_cfg *)ta_state;
+	cfg = (struct chash_cfg *)ta_state;
 
 	/* Check which hash we need to grow and do we still need that */
-	if ((pflags >> 32) == 1) {
-		old_size = ccfg->size4;
+	if (mi->size > 0 && cfg->size4 < mi->size) {
+		new_head = (struct chashbhead *)mi->main_ptr;
+		new_size = mi->size;
+		old_size = cfg->size4;
 		old_head = ti->state;
-		mlen = ccfg->mask4;
+		mlen = cfg->mask4;
 		af = AF_INET;
-	} else {
-		old_size = ccfg->size6;
+
+		for (i = 0; i < old_size; i++) {
+			SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
+				nhash = hash_ent(ent, af, mlen, new_size);
+				SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
+			}
+		}
+
+		ti->state = new_head;
+		cfg->head4 = new_head;
+		cfg->size4 = mi->size;
+		mi->main_ptr = old_head;
+	}
+
+	if (mi->size6 > 0 && cfg->size6 < mi->size6) {
+		new_head = (struct chashbhead *)mi->main_ptr6;
+		new_size = mi->size6;
+		old_size = cfg->size6;
 		old_head = ti->xstate;
-		mlen = ccfg->mask6;
+		mlen = cfg->mask6;
 		af = AF_INET6;
-	}
 
-	if (old_size >= mi->size)
-		return (0);
-	
-	new_head = (struct chashbhead *)mi->main_ptr;
-	for (i = 0; i < old_size; i++) {
-		SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
-			nhash = hash_ent(ent, af, mlen, mi->size);
-			SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
+		for (i = 0; i < old_size; i++) {
+			SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
+				nhash = hash_ent(ent, af, mlen, new_size);
+				SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
+			}
 		}
-	}
 
-	if (af == AF_INET) {
-		ti->state = new_head;
-		ccfg->head4 = new_head;
-		ccfg->size4 = mi->size;
-	} else {
 		ti->xstate = new_head;
-		ccfg->head6 = new_head;
-		ccfg->size6 = mi->size;
+		cfg->head6 = new_head;
+		cfg->size6 = mi->size6;
+		mi->main_ptr6 = old_head;
 	}
 
-	ti->data = (ti->data & 0xFFFFFFFF00000000) | log2(ccfg->size4) << 8 | 
-	    log2(ccfg->size6);
-
-	mi->main_ptr = old_head;
+	/* Update lower 32 bits with new values */
+	ti->data &= 0xFFFFFFFF00000000;
+	ti->data |= log2(cfg->size4) << 8 | log2(cfg->size6);
 
 	return (0);
 }
@@ -1373,6 +1431,8 @@ ta_flush_mod_chash(void *ta_buf)
 	mi = (struct mod_item *)ta_buf;
 	if (mi->main_ptr != NULL)
 		free(mi->main_ptr, M_IPFW);
+	if (mi->main_ptr6 != NULL)
+		free(mi->main_ptr6, M_IPFW);
 }
 
 struct table_algo cidr_hash = {
@@ -1390,6 +1450,7 @@ struct table_algo cidr_hash = {
 	.dump_tentry	= ta_dump_chash_tentry,
 	.find_tentry	= ta_find_chash_tentry,
 	.print_config	= ta_print_chash_config,
+	.has_space	= ta_has_space_chash,
 	.prepare_mod	= ta_prepare_mod_chash,
 	.fill_mod	= ta_fill_mod_chash,
 	.modify		= ta_modify_chash,
@@ -1678,7 +1739,7 @@ ta_prepare_add_ifidx(struct ip_fw_chain 
 
 static int
 ta_add_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
-    void *ta_buf, uint64_t *pflags, uint32_t *pnum)
+    void *ta_buf, uint32_t *pnum)
 {
 	struct iftable_cfg *icfg;
 	struct ifentry *ife, *tmp;
@@ -1726,11 +1787,6 @@ ta_add_ifidx(void *ta_state, struct tabl
 	ipfw_iface_add_notify(icfg->ch, &ife->ic);
 	icfg->count++;
 
-	if (icfg->count + 1 == icfg->size) {
-		/* Notify core we need to grow */
-		*pflags = icfg->size + IFIDX_CHUNK;
-	}
-
 	tb->ife = NULL;
 	*pnum = 1;
 
@@ -1764,7 +1820,7 @@ ta_prepare_del_ifidx(struct ip_fw_chain 
  */
 static int
 ta_del_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
-    void *ta_buf, uint64_t *pflags, uint32_t *pnum)
+    void *ta_buf, uint32_t *pnum)
 {
 	struct iftable_cfg *icfg;
 	struct ifentry *ife;
@@ -1883,6 +1939,22 @@ struct mod_ifidx {
 	size_t	size;
 };
 
+static int
+ta_has_space_ifidx(void *ta_state, struct table_info *ti, uint32_t count,
+    uint64_t *pflags)
+{
+	struct iftable_cfg *cfg;
+
+	cfg = (struct iftable_cfg *)ta_state;
+
+	if (cfg->count + count > cfg->size) {
+		*pflags = roundup2(cfg->count + count, IFIDX_CHUNK);
+		return (0);
+	}
+
+	return (1);
+}
+
 /*
  * Allocate ned, larger runtime ifidx array.
  */
@@ -2049,6 +2121,7 @@ struct table_algo iface_idx = {
 	.foreach	= ta_foreach_ifidx,
 	.dump_tentry	= ta_dump_ifidx_tentry,
 	.find_tentry	= ta_find_ifidx_tentry,
+	.has_space	= ta_has_space_ifidx,
 	.prepare_mod	= ta_prepare_mod_ifidx,
 	.fill_mod	= ta_fill_mod_ifidx,
 	.modify		= ta_modify_ifidx,
@@ -2186,7 +2259,7 @@ ta_prepare_add_numarray(struct ip_fw_cha
 
 static int
 ta_add_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
-    void *ta_buf, uint64_t *pflags, uint32_t *pnum)
+    void *ta_buf, uint32_t *pnum)
 {
 	struct numarray_cfg *cfg;
 	struct ta_buf_numarray *tb;
@@ -2219,11 +2292,6 @@ ta_add_numarray(void *ta_state, struct t
 	KASSERT(res == 1, ("number %d already exists", tb->na.number));
 	cfg->used++;
 	ti->data = cfg->used;
-
-	if (cfg->used + 1 == cfg->size) {
-		/* Notify core we need to grow */
-		*pflags = cfg->size + NUMARRAY_CHUNK;
-	}
 	*pnum = 1;
 
 	return (0);
@@ -2235,7 +2303,7 @@ ta_add_numarray(void *ta_state, struct t
  */
 static int
 ta_del_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
-    void *ta_buf, uint64_t *pflags, uint32_t *pnum)
+    void *ta_buf, uint32_t *pnum)
 {
 	struct numarray_cfg *cfg;
 	struct ta_buf_numarray *tb;
@@ -2255,7 +2323,6 @@ ta_del_numarray(void *ta_state, struct t
 	KASSERT(res == 1, ("number %u does not exist", tb->na.number));
 	cfg->used--;
 	ti->data = cfg->used;
-
 	*pnum = 1;
 
 	return (0);
@@ -2274,8 +2341,24 @@ ta_flush_numarray_entry(struct ip_fw_cha
  * Table growing callbacks.
  */
 
+static int
+ta_has_space_numarray(void *ta_state, struct table_info *ti, uint32_t count,
+    uint64_t *pflags)
+{
+	struct numarray_cfg *cfg;
+
+	cfg = (struct numarray_cfg *)ta_state;
+
+	if (cfg->used + count > cfg->size) {
+		*pflags = roundup2(cfg->used + count, NUMARRAY_CHUNK);
+		return (0);
+	}
+
+	return (1);
+}
+
 /*
- * Allocate ned, larger runtime numarray array.
+ * Allocate new, larger runtime array.
  */
 static int
 ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags)
@@ -2415,6 +2498,7 @@ struct table_algo number_array = {
 	.foreach	= ta_foreach_numarray,
 	.dump_tentry	= ta_dump_numarray_tentry,
 	.find_tentry	= ta_find_numarray_tentry,
+	.has_space	= ta_has_space_numarray,
 	.prepare_mod	= ta_prepare_mod_numarray,
 	.fill_mod	= ta_fill_mod_numarray,
 	.modify		= ta_modify_numarray,
@@ -2437,8 +2521,8 @@ struct table_algo number_array = {
  *
  *
  * pflags:
- * [v4=1/v6=0][hsize]
- * [       32][   32]
+ * [hsize4][hsize6]
+ * [    16][    16]
  */
 
 struct fhashentry;
@@ -2858,7 +2942,7 @@ ta_prepare_add_fhash(struct ip_fw_chain 
 
 static int
 ta_add_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
-    void *ta_buf, uint64_t *pflags, uint32_t *pnum)
+    void *ta_buf, uint32_t *pnum)
 {
 	struct fhash_cfg *cfg;
 	struct fhashbhead *head;
@@ -2907,8 +2991,6 @@ ta_add_fhash(void *ta_state, struct tabl
 
 		/* Update counters and check if we need to grow hash */
 		cfg->items++;
-		if (cfg->items > cfg->size && cfg->size < 65536)
-			*pflags = cfg->size * 2;
 	}
 
 	return (0);
@@ -2927,7 +3009,7 @@ ta_prepare_del_fhash(struct ip_fw_chain 
 
 static int
 ta_del_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
-    void *ta_buf, uint64_t *pflags, uint32_t *pnum)
+    void *ta_buf, uint32_t *pnum)
 {
 	struct fhash_cfg *cfg;
 	struct fhashbhead *head;
@@ -2977,6 +3059,22 @@ ta_flush_fhash_entry(struct ip_fw_chain 
  * Hash growing callbacks.
  */
 
+static int
+ta_has_space_fhash(void *ta_state, struct table_info *ti, uint32_t count,
+    uint64_t *pflags)
+{
+	struct fhash_cfg *cfg;
+
+	cfg = (struct fhash_cfg *)ta_state;
+
+	if (cfg->items > cfg->size && cfg->size < 65536) {
+		*pflags = cfg->size * 2;
+		return (0);
+	}
+
+	return (1);
+}
+
 /*
  * Allocate new, larger fhash.
  */
@@ -3085,6 +3183,7 @@ struct table_algo flow_hash = {
 	.foreach	= ta_foreach_fhash,
 	.dump_tentry	= ta_dump_fhash_tentry,
 	.find_tentry	= ta_find_fhash_tentry,
+	.has_space	= ta_has_space_fhash,
 	.prepare_mod	= ta_prepare_mod_fhash,
 	.fill_mod	= ta_fill_mod_fhash,
 	.modify		= ta_modify_fhash,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201408021718.s72HIllo073252>