static bool _add_timer_event_callback( struct itimerspec *interval, timer_callback callback, void *user_data ) { assert( interval != NULL ); assert( callback != NULL ); timer_read_begin(); struct timer_info *timer = get_timer_info(); timer_read_end(); assert( timer != NULL ); debug( "Adding a timer event callback ( interval = %u.%09u, initial expiration = %u.%09u, callback = %p, user_data = %p ).", interval->it_interval.tv_sec, interval->it_interval.tv_nsec, interval->it_value.tv_sec, interval->it_value.tv_nsec, callback, user_data ); timer_callback_info *cb; struct timespec now; cb = xmalloc( sizeof( timer_callback_info ) ); memset( cb, 0, sizeof( timer_callback_info ) ); cb->function = callback; cb->user_data = user_data; if ( clock_gettime( CLOCK_MONOTONIC, &now ) != 0 ) { error( "Failed to retrieve monotonic time ( %s [%d] ).", strerror( errno ), errno ); xfree( cb ); return false; } cb->interval = interval->it_interval; if ( VALID_TIMESPEC( &interval->it_value ) ) { ADD_TIMESPEC( &now, &interval->it_value, &cb->expires_at ); } else if ( VALID_TIMESPEC( &interval->it_interval ) ) { ADD_TIMESPEC( &now, &interval->it_interval, &cb->expires_at ); } else { error( "Timer must not be zero when a timer event is added." ); xfree( cb ); return false; } debug( "Set an initial expiration time to %u.%09u.", now.tv_sec, now.tv_nsec ); assert( timer->timer_callbacks != NULL ); insert_timer_callback( timer, cb ); return true; }
static void on_timer( timer_callback_info *callback, struct timespec *now ) { assert( callback != NULL ); assert( callback->function != NULL ); debug( "Executing a timer event ( function = %p, expires_at = %u.%09u, interval = %u.%09u, user_data = %p ).", callback->function, callback->expires_at.tv_sec, callback->expires_at.tv_nsec, callback->interval.tv_sec, callback->interval.tv_nsec, callback->user_data ); if ( VALID_TIMESPEC( &callback->expires_at ) ) { callback->function( callback->user_data ); if ( VALID_TIMESPEC( &callback->interval ) ) { ADD_TIMESPEC( &callback->expires_at, &callback->interval, &callback->expires_at ); if ( TIMESPEC_LESS_THEN( &callback->expires_at, now ) ) { callback->expires_at.tv_sec = now->tv_sec; callback->expires_at.tv_nsec = now->tv_nsec; } } else { callback->expires_at.tv_sec = 0; callback->expires_at.tv_nsec = 0; callback->function = NULL; } debug( "Set expires_at value to %u.%09u.", callback->expires_at.tv_sec, callback->expires_at.tv_nsec ); } else { error( "Invalid expires_at value." ); } }
static void handle_error( uint64_t datapath_id, uint32_t transaction_id, uint16_t type, uint16_t code, const buffer *data, void *user_data ) { UNUSED( user_data ); UNUSED( data ); warn( "Error ( type = %#x, code = %#x ) from switch %#" PRIx64 " for %#x received.", type, code, datapath_id, transaction_id ); transaction_entry *entry = lookup_transaction_entry_by_xid( transaction_id ); if ( entry == NULL ) { entry = lookup_transaction_entry_by_barrier_xid( transaction_id ); if ( entry == NULL ) { warn( "An error for untracked transaction received " "( type = %#x, code = %#x, transaction_id = %#x, datapath_id = %#" PRIx64 " ).", type, code, transaction_id, datapath_id ); return; } entry->completed = true; } entry->error_received = true; bool ret = get_monotonic_time( &entry->expires_at ); if ( !ret ) { return; } ADD_TIMESPEC( &entry->expires_at, &TRANSACTION_END_MARGIN, &entry->expires_at ); debug( "Set transaction timeout to %d.%09d.", ( int ) entry->expires_at.tv_sec, ( int ) entry->expires_at.tv_nsec ); }
static bool add_transaction_entry( transaction_entry *entry ) { debug( "Adding a transaction entry ( entry = %p, transaction_db = %p ).", entry, transaction_db ); assert( entry != NULL ); assert( transaction_db != NULL ); assert( transaction_db->xid != NULL ); assert( transaction_db->barrier_xid != NULL ); bool ret = get_monotonic_time( &entry->expires_at ); if ( !ret ) { return false; } ADD_TIMESPEC( &entry->expires_at, &TRANSACTION_TIMEOUT, &entry->expires_at ); dump_transaction_entry( debug, entry ); if ( lookup_transaction_entry_by_xid( entry->xid ) == NULL ) { void *duplicated = insert_hash_entry( transaction_db->xid, &entry->xid, entry ); assert( duplicated == NULL ); } else { error( "Duplicated transaction entry found ( entry = %p, xid = %#x ).", entry, entry->xid ); dump_transaction_entry( error, entry ); return false; } if ( lookup_transaction_entry_by_barrier_xid( entry->barrier_xid ) == NULL ) { void *duplicated = insert_hash_entry( transaction_db->barrier_xid, &entry->barrier_xid, entry ); assert( duplicated == NULL ); } else { error( "Duplicated transaction entry found ( entry = %p, barrier_xid = %#x ).", entry, entry->barrier_xid ); dump_transaction_entry( error, entry ); delete_transaction_entry_by_xid( entry->xid ); return false; } debug( "A transaction is added." ); dump_transaction_entry( debug, entry ); return true; }
static void handle_barrier_reply( uint64_t datapath_id, uint32_t transaction_id, void *user_data ) { UNUSED( user_data ); debug( "Barrier reply from switch %#" PRIx64 " for %#x received.", datapath_id, transaction_id ); transaction_entry *entry = lookup_transaction_entry_by_barrier_xid( transaction_id ); if ( entry == NULL ) { warn( "A barrier reply for untracked transaction received ( transaction_id = %#x, datapath_id = %#" PRIx64 " ).", transaction_id, datapath_id ); return; } entry->completed = true; bool ret = get_monotonic_time( &entry->expires_at ); if ( !ret ) { return; } ADD_TIMESPEC( &entry->expires_at, &TRANSACTION_END_MARGIN, &entry->expires_at ); debug( "Set transaction timeout to %d.%09d.", ( int ) entry->expires_at.tv_sec, ( int ) entry->expires_at.tv_nsec ); }
static void age_transaction_entries( void *user_data ) { UNUSED( user_data ); debug( "Aging transaction entries ( %p ).", transaction_db ); assert( transaction_db != NULL ); assert( transaction_db->xid != NULL ); struct timespec now; bool ret = get_monotonic_time( &now ); if ( !ret ) { return; } hash_entry *e; hash_iterator iter; init_hash_iterator( transaction_db->xid, &iter ); while ( ( e = iterate_hash_next( &iter ) ) != NULL ) { transaction_entry *entry = e->value; if( entry == NULL ) { continue; } debug( "Checking an entry ( %p ).", entry ); dump_transaction_entry( debug, entry ); if ( TIMESPEC_LESS_THAN( &entry->expires_at, &now ) ) { struct ofp_header *header = entry->message->data; header->xid = htonl( entry->original_xid ); if ( entry->completed ) { if ( !entry->error_received && entry->succeeded_callback != NULL ) { debug( "Calling succeeded callback ( %p ).", entry->succeeded_callback ); entry->succeeded_callback( entry->datapath_id, entry->message, entry->succeeded_user_data ); } else if ( entry->error_received && entry->failed_callback != NULL ) { debug( "Calling failed callback ( %p ).", entry->failed_callback ); entry->failed_callback( entry->datapath_id, entry->message, entry->failed_user_data ); } } else { if ( !entry->timeout ) { debug( "Transaction timeout. Wait for final marin to elapse." ); // Wait for a moment after deleting all messages in send queue delete_openflow_messages( entry->datapath_id ); entry->expires_at = now; ADD_TIMESPEC( &entry->expires_at, &TRANSACTION_END_MARGIN, &entry->expires_at ); entry->timeout = true; continue; } else { warn( "Transaction timeout ( xid = %#x, barrier_xid = %#x, original_xid = %#x, expires_at = %d.%09d ).", entry->xid, entry->barrier_xid, entry->original_xid, ( int ) entry->expires_at.tv_sec, ( int ) entry->expires_at.tv_nsec ); if ( entry->failed_callback != NULL ) { entry->failed_callback( entry->datapath_id, entry->message, entry->failed_user_data ); } } } delete_transaction_entry_by_xid( entry->xid ); free_transaction_entry( entry ); } } debug( "Aging completed ( %p ).", transaction_db ); }