Ignore:
Timestamp:
Nov 24, 2021, 9:47:56 PM (2 years ago)
Author:
Michael Brooks <mlbrooks@…>
Branches:
ADT, ast-experimental, enum, master, pthread-emulation, qualifiedEnum
Children:
5235d49
Parents:
94647b0 (diff), 3cc1111 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/concurrency/kernel.cfa

    r94647b0 r7770cc8  
    3434#include "strstream.hfa"
    3535#include "device/cpu.hfa"
     36#include "io/types.hfa"
    3637
    3738//Private includes
     
    124125static void __wake_one(cluster * cltr);
    125126
    126 static void mark_idle (__cluster_proc_list & idles, processor & proc);
     127static void idle_sleep(processor * proc, io_future_t & future, char buf[]);
     128static bool mark_idle (__cluster_proc_list & idles, processor & proc);
    127129static void mark_awake(__cluster_proc_list & idles, processor & proc);
    128 static [unsigned idle, unsigned total, * processor] query_idles( & __cluster_proc_list idles );
    129130
    130131extern void __cfa_io_start( processor * );
    131132extern bool __cfa_io_drain( processor * );
    132 extern void __cfa_io_flush( processor * );
     133extern bool __cfa_io_flush( processor *, bool wait );
    133134extern void __cfa_io_stop ( processor * );
    134135static inline bool __maybe_io_drain( processor * );
     136
     137#if defined(IO_URING_IDLE) && defined(CFA_HAVE_LINUX_IO_URING_H)
     138        extern bool __kernel_read(processor * proc, io_future_t & future, char buf[], int fd);
     139#endif
    135140
    136141extern void __disable_interrupts_hard();
     
    148153        /* paranoid */ verify( __preemption_enabled() );
    149154}
     155
    150156
    151157//=============================================================================================
     
    163169        verify(this);
    164170
     171        io_future_t future; // used for idle sleep when io_uring is present
     172        future.self.ptr = 1p;  // mark it as already fulfilled so we know if there is a pending request or not
     173        char buf[sizeof(uint64_t)];
     174
    165175        __cfa_io_start( this );
    166176
     
    196206
    197207                        if( !readyThread ) {
    198                                 __cfa_io_flush( this );
     208                                __cfa_io_flush( this, false );
     209
    199210                                readyThread = __next_thread_slow( this->cltr );
    200211                        }
     
    210221
    211222                                // Push self to idle stack
    212                                 mark_idle(this->cltr->procs, * this);
     223                                if(!mark_idle(this->cltr->procs, * this)) continue MAIN_LOOP;
    213224
    214225                                // Confirm the ready-queue is empty
     
    226237                                }
    227238
    228                                 #if !defined(__CFA_NO_STATISTICS__)
    229                                         if(this->print_halts) {
    230                                                 __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl());
     239                                idle_sleep( this, future, buf );
     240
     241                                // We were woken up, remove self from idle
     242                                mark_awake(this->cltr->procs, * this);
     243
     244                                // DON'T just proceed, start looking again
     245                                continue MAIN_LOOP;
     246                        }
     247
     248                        /* paranoid */ verify( readyThread );
     249
     250                        // Reset io dirty bit
     251                        this->io.dirty = false;
     252
     253                        // We found a thread run it
     254                        __run_thread(this, readyThread);
     255
     256                        // Are we done?
     257                        if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;
     258
     259                        if(this->io.pending && !this->io.dirty) {
     260                                __cfa_io_flush( this, false );
     261                        }
     262
     263                        #else
     264                                #warning new kernel loop
     265                        SEARCH: {
     266                                /* paranoid */ verify( ! __preemption_enabled() );
     267
     268                                // First, lock the scheduler since we are searching for a thread
     269                                ready_schedule_lock();
     270
     271                                // Try to get the next thread
     272                                readyThread = pop_fast( this->cltr );
     273                                if(readyThread) { ready_schedule_unlock(); break SEARCH; }
     274
     275                                // If we can't find a thread, might as well flush any outstanding I/O
     276                                if(this->io.pending) { __cfa_io_flush( this, false ); }
     277
     278                                // Spin a little on I/O, just in case
     279                                for(5) {
     280                                        __maybe_io_drain( this );
     281                                        readyThread = pop_fast( this->cltr );
     282                                        if(readyThread) { ready_schedule_unlock(); break SEARCH; }
     283                                }
     284
     285                                // no luck, try stealing a few times
     286                                for(5) {
     287                                        if( __maybe_io_drain( this ) ) {
     288                                                readyThread = pop_fast( this->cltr );
     289                                        } else {
     290                                                readyThread = pop_slow( this->cltr );
    231291                                        }
    232                                 #endif
    233 
    234                                 __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle);
     292                                        if(readyThread) { ready_schedule_unlock(); break SEARCH; }
     293                                }
     294
     295                                // still no luck, search for a thread
     296                                readyThread = pop_search( this->cltr );
     297                                if(readyThread) { ready_schedule_unlock(); break SEARCH; }
     298
     299                                // Don't block if we are done
     300                                if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) {
     301                                        ready_schedule_unlock();
     302                                        break MAIN_LOOP;
     303                                }
     304
     305                                __STATS( __tls_stats()->ready.sleep.halts++; )
     306
     307                                // Push self to idle stack
     308                                ready_schedule_unlock();
     309                                if(!mark_idle(this->cltr->procs, * this)) goto SEARCH;
     310                                ready_schedule_lock();
     311
     312                                // Confirm the ready-queue is empty
     313                                __maybe_io_drain( this );
     314                                readyThread = pop_search( this->cltr );
     315                                ready_schedule_unlock();
     316
     317                                if( readyThread ) {
     318                                        // A thread was found, cancel the halt
     319                                        mark_awake(this->cltr->procs, * this);
     320
     321                                        __STATS( __tls_stats()->ready.sleep.cancels++; )
     322
     323                                        // continue the main loop
     324                                        break SEARCH;
     325                                }
     326
     327                                __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )
     328                                __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle_fd);
    235329
    236330                                {
    237331                                        eventfd_t val;
    238                                         ssize_t ret = read( this->idle, &val, sizeof(val) );
     332                                        ssize_t ret = read( this->idle_fd, &val, sizeof(val) );
    239333                                        if(ret < 0) {
    240334                                                switch((int)errno) {
     
    252346                                }
    253347
    254                                 #if !defined(__CFA_NO_STATISTICS__)
    255                                         if(this->print_halts) {
    256                                                 __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl());
    257                                         }
    258                                 #endif
     348                                        __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl()); )
    259349
    260350                                // We were woken up, remove self from idle
     
    265355                        }
    266356
    267                         /* paranoid */ verify( readyThread );
    268 
    269                         // Reset io dirty bit
    270                         this->io.dirty = false;
    271 
    272                         // We found a thread run it
    273                         __run_thread(this, readyThread);
    274 
    275                         // Are we done?
    276                         if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;
    277 
    278                         if(this->io.pending && !this->io.dirty) {
    279                                 __cfa_io_flush( this );
    280                         }
    281 
    282                         #else
    283                                 #warning new kernel loop
    284                         SEARCH: {
    285                                 /* paranoid */ verify( ! __preemption_enabled() );
    286 
    287                                 // First, lock the scheduler since we are searching for a thread
    288                                 ready_schedule_lock();
    289 
    290                                 // Try to get the next thread
    291                                 readyThread = pop_fast( this->cltr );
    292                                 if(readyThread) { ready_schedule_unlock(); break SEARCH; }
    293 
    294                                 // If we can't find a thread, might as well flush any outstanding I/O
    295                                 if(this->io.pending) { __cfa_io_flush( this ); }
    296 
    297                                 // Spin a little on I/O, just in case
    298                                 for(5) {
    299                                         __maybe_io_drain( this );
    300                                         readyThread = pop_fast( this->cltr );
    301                                         if(readyThread) { ready_schedule_unlock(); break SEARCH; }
    302                                 }
    303 
    304                                 // no luck, try stealing a few times
    305                                 for(5) {
    306                                         if( __maybe_io_drain( this ) ) {
    307                                                 readyThread = pop_fast( this->cltr );
    308                                         } else {
    309                                                 readyThread = pop_slow( this->cltr );
    310                                         }
    311                                         if(readyThread) { ready_schedule_unlock(); break SEARCH; }
    312                                 }
    313 
    314                                 // still no luck, search for a thread
    315                                 readyThread = pop_search( this->cltr );
    316                                 if(readyThread) { ready_schedule_unlock(); break SEARCH; }
    317 
    318                                 // Don't block if we are done
    319                                 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;
    320 
    321                                 __STATS( __tls_stats()->ready.sleep.halts++; )
    322 
    323                                 // Push self to idle stack
    324                                 ready_schedule_unlock();
    325                                 mark_idle(this->cltr->procs, * this);
    326                                 ready_schedule_lock();
    327 
    328                                 // Confirm the ready-queue is empty
    329                                 __maybe_io_drain( this );
    330                                 readyThread = pop_search( this->cltr );
    331                                 ready_schedule_unlock();
    332 
    333                                 if( readyThread ) {
    334                                         // A thread was found, cancel the halt
    335                                         mark_awake(this->cltr->procs, * this);
    336 
    337                                         __STATS( __tls_stats()->ready.sleep.cancels++; )
    338 
    339                                         // continue the main loop
    340                                         break SEARCH;
    341                                 }
    342 
    343                                 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )
    344                                 __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle);
    345 
    346                                 {
    347                                         eventfd_t val;
    348                                         ssize_t ret = read( this->idle, &val, sizeof(val) );
    349                                         if(ret < 0) {
    350                                                 switch((int)errno) {
    351                                                 case EAGAIN:
    352                                                 #if EAGAIN != EWOULDBLOCK
    353                                                         case EWOULDBLOCK:
    354                                                 #endif
    355                                                 case EINTR:
    356                                                         // No need to do anything special here, just assume it's a legitimate wake-up
    357                                                         break;
    358                                                 default:
    359                                                         abort( "KERNEL : internal error, read failure on idle eventfd, error(%d) %s.", (int)errno, strerror( (int)errno ) );
    360                                                 }
    361                                         }
    362                                 }
    363 
    364                                         __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl()); )
    365 
    366                                 // We were woken up, remove self from idle
    367                                 mark_awake(this->cltr->procs, * this);
    368 
    369                                 // DON'T just proceed, start looking again
    370                                 continue MAIN_LOOP;
    371                         }
    372 
    373357                RUN_THREAD:
    374358                        /* paranoid */ verify( ! __preemption_enabled() );
     
    385369
    386370                        if(this->io.pending && !this->io.dirty) {
    387                                 __cfa_io_flush( this );
     371                                __cfa_io_flush( this, false );
    388372                        }
    389373
     
    758742
    759743        // Check if there is a sleeping processor
    760         processor * p;
    761         unsigned idle;
    762         unsigned total;
    763         [idle, total, p] = query_idles(this->procs);
     744        int fd = __atomic_load_n(&this->procs.fd, __ATOMIC_SEQ_CST);
    764745
    765746        // If no one is sleeping, we are done
    766         if( idle == 0 ) return;
     747        if( fd == 0 ) return;
    767748
    768749        // We found a processor, wake it up
    769750        eventfd_t val;
    770751        val = 1;
    771         eventfd_write( p->idle, val );
     752        eventfd_write( fd, val );
    772753
    773754        #if !defined(__CFA_NO_STATISTICS__)
     
    794775                eventfd_t val;
    795776                val = 1;
    796                 eventfd_write( this->idle, val );
     777                eventfd_write( this->idle_fd, val );
    797778        __enable_interrupts_checked();
    798779}
    799780
    800 static void mark_idle(__cluster_proc_list & this, processor & proc) {
    801         /* paranoid */ verify( ! __preemption_enabled() );
    802         lock( this );
     781static void idle_sleep(processor * this, io_future_t & future, char buf[]) {
     782        #if !defined(IO_URING_IDLE) || !defined(CFA_HAVE_LINUX_IO_URING_H)
     783                #if !defined(__CFA_NO_STATISTICS__)
     784                        if(this->print_halts) {
     785                                __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl());
     786                        }
     787                #endif
     788
     789                __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle_fd);
     790
     791                {
     792                        eventfd_t val;
     793                        ssize_t ret = read( this->idle_fd, &val, sizeof(val) );
     794                        if(ret < 0) {
     795                                switch((int)errno) {
     796                                case EAGAIN:
     797                                #if EAGAIN != EWOULDBLOCK
     798                                        case EWOULDBLOCK:
     799                                #endif
     800                                case EINTR:
     801                                        // No need to do anything special here, just assume it's a legitimate wake-up
     802                                        break;
     803                                default:
     804                                        abort( "KERNEL : internal error, read failure on idle eventfd, error(%d) %s.", (int)errno, strerror( (int)errno ) );
     805                                }
     806                        }
     807                }
     808
     809                #if !defined(__CFA_NO_STATISTICS__)
     810                        if(this->print_halts) {
     811                                __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl());
     812                        }
     813                #endif
     814        #else
     815                #if !defined(CFA_HAVE_IORING_OP_READ)
     816                        #error this is only implemented if the read is present
     817                #endif
     818                // Do we already have a pending read
     819                if(available(future)) {
     820                        // There is no pending read, we need to add one
     821                        reset(future);
     822
     823                        __kernel_read(this, future, buf, this->idle_fd );
     824                }
     825
     826                __cfa_io_flush( this, true );
     827        #endif
     828}
     829
     830static bool mark_idle(__cluster_proc_list & this, processor & proc) {
     831        /* paranoid */ verify( ! __preemption_enabled() );
     832        if(!try_lock( this )) return false;
    803833                this.idle++;
    804834                /* paranoid */ verify( this.idle <= this.total );
    805835                remove(proc);
    806836                insert_first(this.idles, proc);
     837
     838                __atomic_store_n(&this.fd, proc.idle_fd, __ATOMIC_SEQ_CST);
    807839        unlock( this );
    808840        /* paranoid */ verify( ! __preemption_enabled() );
     841
     842        return true;
    809843}
    810844
     
    816850                remove(proc);
    817851                insert_last(this.actives, proc);
     852
     853                {
     854                        int fd = 0;
     855                        if(!this.idles`isEmpty) fd = this.idles`first.idle_fd;
     856                        __atomic_store_n(&this.fd, fd, __ATOMIC_SEQ_CST);
     857                }
     858
    818859        unlock( this );
    819         /* paranoid */ verify( ! __preemption_enabled() );
    820 }
    821 
    822 static [unsigned idle, unsigned total, * processor] query_idles( & __cluster_proc_list this ) {
    823         /* paranoid */ verify( ! __preemption_enabled() );
    824         /* paranoid */ verify( ready_schedule_islocked() );
    825 
    826         for() {
    827                 uint64_t l = __atomic_load_n(&this.lock, __ATOMIC_SEQ_CST);
    828                 if( 1 == (l % 2) ) { Pause(); continue; }
    829                 unsigned idle    = this.idle;
    830                 unsigned total   = this.total;
    831                 processor * proc = &this.idles`first;
    832                 // Compiler fence is unnecessary, but gcc-8 and older incorrectly reorder code without it
    833                 asm volatile("": : :"memory");
    834                 if(l != __atomic_load_n(&this.lock, __ATOMIC_SEQ_CST)) { Pause(); continue; }
    835                 return [idle, total, proc];
    836         }
    837 
    838         /* paranoid */ verify( ready_schedule_islocked() );
    839860        /* paranoid */ verify( ! __preemption_enabled() );
    840861}
     
    898919                if(head == tail) return false;
    899920                #if OLD_MAIN
    900                 ready_schedule_lock();
    901                 ret = __cfa_io_drain( proc );
    902                 ready_schedule_unlock();
     921                        ready_schedule_lock();
     922                        ret = __cfa_io_drain( proc );
     923                        ready_schedule_unlock();
    903924                #else
    904925                        ret = __cfa_io_drain( proc );
    905         #endif
     926                #endif
    906927        #endif
    907928        return ret;
     
    939960                        /* paranoid */ verifyf( it, "Unexpected null iterator, at index %u of %u\n", i, count);
    940961                        /* paranoid */ verify( it->local_data->this_stats );
     962                        // __print_stats( it->local_data->this_stats, cltr->print_stats, "Processor", it->name, (void*)it );
    941963                        __tally_stats( cltr->stats, it->local_data->this_stats );
    942964                        it = &(*it)`next;
     
    948970                // this doesn't solve all problems but does solve many
    949971                // so it's probably good enough
     972                disable_interrupts();
    950973                uint_fast32_t last_size = ready_mutate_lock();
    951974
     
    955978                // Unlock the RWlock
    956979                ready_mutate_unlock( last_size );
     980                enable_interrupts();
    957981        }
    958982
Note: See TracChangeset for help on using the changeset viewer.