FreeBSD/sys/netpfil/ipfw/ip_fw_table.c
/*
 * Adds/updates one or more entries in table @ti.
 *
 * Function may drop/reacquire UH wlock multiple times due to
 * items alloc, algorithm callbacks (check_space), value linkage
 * (new values, value storage realloc), etc..
 * Other processes like other adds (which may involve storage resize),
 * table swaps (which changes table data and may change algo type),
 * table modify (which may change value mask) may be executed
 * simultaneously so we need to deal with it.
 *
 * The following approach was implemented:
 * we have per-chain linked list, protected with UH lock.
 * add_table_entry prepares special on-stack structure wthich is passed
 * to its descendants. Users add this structure to this list before unlock.
 * After performing needed operations and acquiring UH lock back, each user
 * checks if structure has changed. If true, it rolls local state back and
 * returns without error to the caller.
 * add_table_entry() on its own checks if structure has changed and restarts
 * its operation from the beginning (goto restart).
 *
 * Functions which are modifying fields of interest (currently
 *   resize_shared_value_storage() and swap_tables() )
 * traverses given list while holding UH lock immediately before
 * performing their operations calling function provided be list entry
 * ( currently rollback_add_entry  ) which performs rollback for all necessary
 * state and sets appropriate values in structure indicating rollback
 * has happened.
 *
 * Algo interaction:
 * Function references @ti first to ensure table won't
 * disappear or change its type.
 * After that, prepare_add callback is called for each @tei entry.
 * Next, we try to add each entry under UH+WHLOCK
 * using add() callback.
 * Finally, we free all state by calling flush_entry callback
 * for each @tei.
 *
 * Returns 0 on success.
 */
Adds/updates one or more entries in table @ti.
Function may drop/reacquire UH wlock multiple times due to
items alloc, algorithm callbacks (check_space), value linkage
(new values, value storage realloc), etc
Other processes like other adds (which may involve storage resize),
table swaps (which changes table data and may change algo type),
table modify (which may change value mask) may be executed
simultaneously so we need to deal with it.

The following approach was implemented:
we have per-chain linked list, protected with UH lock.
add_table_entry prepares special on-stack structure wthich is passed
to its descendants. Users add this structure to this list before unlock.
After performing needed operations and acquiring UH lock back, each user
checks if structure has changed. If true, it rolls local state back and
returns without error to the caller.
add_table_entry() on its own checks if structure has changed and restarts
its operation from the beginning (goto restart).

Functions which are modifying fields of interest (currently
resize_shared_value_storage() and swap_tables() )
traverses given list while holding UH lock immediately before
performing their operations calling function provided be list entry
( currently rollback_add_entry  ) which performs rollback for all necessary
state and sets appropriate values in structure indicating rollback
has happened.

Algo interaction:
Function references @ti first to ensure table won't
disappear or change its type.
After that, prepare_add callback is called for each @tei entry.
Next, we try to add each entry under UH+WHLOCK
using add() callback.
Finally, we free all state by calling flush_entry callback
for each @tei.

Returns 0 on success.
Adds / updates one or more entries in table @ti . Function may drop / reacquire UH wlock multiple times due to items alloc algorithm callbacks ( check_space ) value linkage ( new values value storage realloc ) etc Other processes like other adds ( which may involve storage resize ) table swaps ( which changes table data and may change algo type ) table modify ( which may change value mask ) may be executed simultaneously so we need to deal with it . The following approach was implemented : we have per - chain linked list protected with UH lock . add_table_entry prepares special on - stack structure wthich is passed to its descendants . Users add this structure to this list before unlock . After performing needed operations and acquiring UH lock back each user checks if structure has changed . If true it rolls local state back and returns without error to the caller . add_table_entry () on its own checks if structure has changed and restarts its operation from the beginning ( goto restart ) . Functions which are modifying fields of interest ( currently resize_shared_value_storage () and swap_tables () ) traverses given list while holding UH lock immediately before performing their operations calling function provided be list entry ( currently rollback_add_entry ) which performs rollback for all necessary state and sets appropriate values in structure indicating rollback has happened . Algo interaction : Function references @ti first to ensure table won ' t disappear or change its type . After that prepare_add callback is called for each @tei entry . Next we try to add each entry under UH + WHLOCK using add () callback . Finally we free all state by calling flush_entry callback for each @tei . 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;
	struct tableop_state ts;
	char ta_buf[TA_BUF_SZ];
	caddr_t ta_buf_m, v;
	memset(&ts, 0, sizeof(ts));
	ta = NULL;
	IPFW_UH_WLOCK(ch);
restart:
	if (ts.modified != 0) {
		IPFW_UH_WUNLOCK(ch);
		flush_batch_buffer(ch, ta, tei, count, rollback,
		    ta_buf_m, ta_buf);
		memset(&ts, 0, sizeof(ts));
		ta = NULL;
		IPFW_UH_WLOCK(ch);
	}
	error = find_ref_table(ch, ti, tei, count, OP_ADD, &tc);
	if (error != 0) {
		IPFW_UH_WUNLOCK(ch);
		return (error);
	}
	ta = tc->ta;
	ts.ch = ch;
	ts.opstate.func = rollback_add_entry;
	ts.tc = tc;
	ts.vshared = tc->vshared;
	ts.vmask = tc->vmask;
	ts.ta = ta;
	ts.tei = tei;
	ts.count = count;
	rollback = 0;
	add_toperation_state(ch, &ts);
	IPFW_UH_WUNLOCK(ch);
	ta_buf_m = ta_buf;
	error = prepare_batch_buffer(ch, ta, tei, count, OP_ADD, &ta_buf_m);
	IPFW_UH_WLOCK(ch);
	del_toperation_state(ch, &ts);
	tc->no.refcnt--;
	if (error != 0)
		goto cleanup;
	if (ts.modified != 0)
		goto restart;
	error = ipfw_link_table_values(ch, &ts);
	if (error != 0)
		goto cleanup;
	if (ts.modified != 0)
		goto restart;
	kidx = tc->no.kidx;
	error = check_table_space(ch, &ts, tc, KIDX_TO_TI(ch, kidx), count);
	if (error != 0)
		goto cleanup;
	if (ts.modified != 0)
		goto restart;
	kidx = tc->no.kidx;
	ta = tc->ta;
	numadd = 0;
	first_error = 0;
	IPFW_WLOCK(ch);
	v = ta_buf_m;
	for (i = 0; i < count; i++, v += ta->ta_buf_size) {
		ptei = &tei[i];
		num = 0;
		if ((error = check_table_limit(tc, ptei)) == 0) {
			error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx),
			    ptei, v, &num);
			store_tei_result(ptei, OP_ADD, error, num);
		}
		if (error == 0) {
			tc->count += num;
			numadd += num;
			continue;
		}
		if (first_error == 0)
			first_error = error;
		if ((flags & IPFW_CTF_ATOMIC) == 0)
			continue;
		rollback_added_entries(ch, tc, KIDX_TO_TI(ch, kidx),
		    tei, ta_buf_m, count, i);
		rollback = 1;
		break;
	}
	IPFW_WUNLOCK(ch);
	ipfw_garbage_table_values(ch, tc, tei, count, rollback);
	if (numadd != 0)
		check_table_space(ch, NULL, tc, KIDX_TO_TI(ch, kidx), 0);
	error = first_error;
cleanup:
	IPFW_UH_WUNLOCK(ch);
	flush_batch_buffer(ch, ta, tei, count, rollback, ta_buf_m, ta_buf);
	return (error);
}
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 ; struct tableop_state ts ; char ta_buf [ TA_BUF_SZ ] ; caddr_t ta_buf_m , v ; memset ( & ts , 0 , sizeof ( ts ) ) ; ta = NULL ; IPFW_UH_WLOCK ( ch ) ; restart : if ( ts . modified != 0 ) { IPFW_UH_WUNLOCK ( ch ) ; flush_batch_buffer ( ch , ta , tei , count , rollback , ta_buf_m , ta_buf ) ; memset ( & ts , 0 , sizeof ( ts ) ) ; ta = NULL ; IPFW_UH_WLOCK ( ch ) ; } error = find_ref_table ( ch , ti , tei , count , OP_ADD , & tc ) ; if ( error != 0 ) { IPFW_UH_WUNLOCK ( ch ) ; return ( error ) ; } ta = tc -> ta ; ts . ch = ch ; ts . opstate . func = rollback_add_entry ; ts . tc = tc ; ts . vshared = tc -> vshared ; ts . vmask = tc -> vmask ; ts . ta = ta ; ts . tei = tei ; ts . count = count ; rollback = 0 ; add_toperation_state ( ch , & ts ) ; IPFW_UH_WUNLOCK ( ch ) ; ta_buf_m = ta_buf ; error = prepare_batch_buffer ( ch , ta , tei , count , OP_ADD , & ta_buf_m ) ; IPFW_UH_WLOCK ( ch ) ; del_toperation_state ( ch , & ts ) ; tc -> no . refcnt -- ; if ( error != 0 ) goto cleanup ; if ( ts . modified != 0 ) goto restart ; error = ipfw_link_table_values ( ch , & ts ) ; if ( error != 0 ) goto cleanup ; if ( ts . modified != 0 ) goto restart ; kidx = tc -> no . kidx ; error = check_table_space ( ch , & ts , tc , KIDX_TO_TI ( ch , kidx ) , count ) ; if ( error != 0 ) goto cleanup ; if ( ts . modified != 0 ) goto restart ; kidx = tc -> no . kidx ; ta = tc -> ta ; numadd = 0 ; first_error = 0 ; IPFW_WLOCK ( ch ) ; v = ta_buf_m ; for ( i = 0 ; i < count ; i ++ , v += ta -> ta_buf_size ) { ptei = & tei [ i ] ; num = 0 ; if ( ( error = check_table_limit ( tc , ptei ) ) == 0 ) { error = ta -> add ( tc -> astate , KIDX_TO_TI ( ch , kidx ) , ptei , v , & num ) ; store_tei_result ( ptei , OP_ADD , error , num ) ; } if ( error == 0 ) { tc -> count += num ; numadd += num ; continue ; } if ( first_error == 0 ) first_error = error ; if ( ( flags & IPFW_CTF_ATOMIC ) == 0 ) continue ; rollback_added_entries ( ch , tc , KIDX_TO_TI ( ch , kidx ) , tei , ta_buf_m , count , i ) ; rollback = 1 ; break ; } IPFW_WUNLOCK ( ch ) ; ipfw_garbage_table_values ( ch , tc , tei , count , rollback ) ; if ( numadd != 0 ) check_table_space ( ch , NULL , tc , KIDX_TO_TI ( ch , kidx ) , 0 ) ; error = first_error ; cleanup : IPFW_UH_WUNLOCK ( ch ) ; flush_batch_buffer ( ch , ta , tei , count , rollback , ta_buf_m , ta_buf ) ; return ( error ) ; }
Adds/updates one or more entries in table @ti.
Adds / updates one or more entries in table @ti .
/*
	 * Find and reference existing table.
	 */ /* Fill in tablestate */ /* Allocate memory and prepare record(s) */ /* Pass stack buffer by default */ /* Drop reference we've used in first search */ /* Check prepare_batch_buffer() error */ /*
	 * Check if table swap has happened.
	 * (so table algo might be changed).
	 * Restart operation to achieve consistent behavior.
	 */ /*
	 * Link all values values to shared/per-table value array.
	 *
	 * May release/reacquire UH_WLOCK.
	 */ /*
	 * Ensure we are able to add all entries without additional
	 * memory allocations. May release/reacquire UH_WLOCK.
	 */ /* We've got valid table in @tc. Let's try to add data */ /* check limit before adding */ /* Set status flag to inform userland */ /* Update number of records to ease limit checking */ /*
		 * Some error have happened. Check our atomicity
		 * settings: continue if atomicity is not required,
		 * rollback changes otherwise.
		 */ /* Permit post-add algorithm grow/rehash. */ /* Return first error to user, if any */