svn commit: r268440 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw

Gleb Smirnoff glebius at FreeBSD.org
Wed Jul 9 14:07:12 UTC 2014


On Tue, Jul 08, 2014 at 11:11:16PM +0000, Alexander V. Chernikov wrote:
A>   * Switch kernel to use per-cpu counters for rules.

...

A> ==============================================================================
A> --- projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Tue Jul  8 23:07:09 2014	(r268439)
A> +++ projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Tue Jul  8 23:11:15 2014	(r268440)
A> @@ -220,6 +220,44 @@ VNET_DECLARE(unsigned int, fw_tables_set
A>  
A>  struct tables_config;
A>  
A> +#ifdef _KERNEL
A> +typedef struct ip_fw_cntr {
A> +	uint64_t	pcnt;	   /* Packet counter		*/
A> +	uint64_t	bcnt;	   /* Byte counter		 */
A> +	uint64_t	timestamp;      /* tv_sec of last match	 */
A> +} ip_fw_cntr;

So, you decided to pack three counters into one structure instead of
using standard counter_u64_alloc() three times. And you create a
separate UMA_ZONE_PCPU zone for that. I already expressed concerns
about this plan, but if you wish so... let it be.

But further code gets uglier:

A> +struct ip_fw {
A> +	uint16_t	act_ofs;	/* offset of action in 32-bit units */
A> +	uint16_t	cmd_len;	/* # of 32-bit words in cmd	*/
A> +	uint16_t	rulenum;	/* rule number			*/
A> +	uint8_t		set;		/* rule set (0..31)		*/
A> +	uint8_t		flags;		/* currently unused		*/
A> +	counter_u64_t	cntr;		/* Pointer to rule counters	*/
A> +	uint32_t	timestamp;	/* tv_sec of last match		*/
A> +	uint32_t	id;		/* rule id			*/
A> +	struct ip_fw    *x_next;	/* linked list of rules		*/
A> +	struct ip_fw    *next_rule;	/* ptr to next [skipto] rule	*/
A> +
A> +	ipfw_insn	cmd[1];		/* storage for commands		*/
A> +};

Here we got 'counter_u64_t cntr' that actually points not to a counter(9)
allocation, but to 'struct ip_fw_cntr' from UMA_ZONE_PCPU zone.

A> +#define	IPFW_INC_RULE_COUNTER(_cntr, _bytes)	do {	\
A> +	counter_u64_add((_cntr)->cntr, 1);		\
A> +	counter_u64_add((_cntr)->cntr + 1, _bytes);	\
A> +	if ((_cntr)->timestamp != time_uptime)		\
A> +		(_cntr)->timestamp = time_uptime;	\
A> +	} while (0)

And you update second counter in the structure using (_cntr)->cntr + 1
that relies on internal structure alignment.

Regarding timestamp: doing comparison and then assignment on a percpu
memory isn't safe against scheduler preemption. I suppose the race is
harmless here.

Why didn't you made field in the 'struct ip_fw' of type 'struct ip_fw_cntr *'?
Then you could use zpcpu_get() to access private to this CPU ip_fw_cntr
and use it. The code would be free from type mismatches and structure
alignment requirements.


-- 
Totus tuus, Glebius.


More information about the svn-src-projects mailing list