/* * user_dir.c - kernel DIR related operations implementation. * * Authors Alain Greiner (2016,2017,2018) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include ///////////////////////////////////////////// xptr_t user_dir_from_ident( intptr_t ident ) { // get pointer on local process_descriptor process_t * process = CURRENT_THREAD->process; // get pointers on reference process xptr_t ref_xp = process->ref_xp; cxy_t ref_cxy = GET_CXY( ref_xp ); process_t * ref_ptr = GET_PTR( ref_xp ); // get extended pointers on open directories list and lock xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->dir_root ); xptr_t lock_xp = XPTR( ref_cxy , &ref_ptr->dir_lock ); // get lock protecting open directories list remote_queuelock_acquire( lock_xp ); // scan reference process dir list xptr_t iter_xp; xptr_t dir_xp; cxy_t dir_cxy; user_dir_t * dir_ptr; intptr_t current; bool_t found = false; XLIST_FOREACH( root_xp , iter_xp ) { dir_xp = XLIST_ELEMENT( iter_xp , user_dir_t , list ); dir_cxy = GET_CXY( dir_xp ); dir_ptr = GET_PTR( dir_xp ); current = (intptr_t)hal_remote_lpt( XPTR( dir_cxy , &dir_ptr->ident ) ); if( ident == current ) { found = true; break; } } // relese lock protecting open directories list remote_queuelock_release( lock_xp ); if( found == false ) return XPTR_NULL; else return dir_xp; } // end user_dir_from_ident() ////////////////////////////////////////////////// user_dir_t * user_dir_create( vfs_inode_t * inode, xptr_t ref_xp ) { user_dir_t * dir; // local pointer on created user_dir_t vseg_t * vseg; // local pointer on dirent array vseg uint32_t vseg_size; // size of vseg in bytes process_t * ref_ptr; // local pointer on reference process cxy_t ref_cxy; // reference process cluster identifier pid_t ref_pid; // reference process PID xptr_t gpt_xp; // extended pointer on reference process GPT uint32_t gpt_attributes; // attributes for all mapped gpt entries uint32_t dirents_per_page; // number of dirent descriptors per page xptr_t page_xp; // extended pointer on page descriptor page_t * page; // local pointer on page descriptor xptr_t base_xp; // extended pointer on physical page base struct dirent * base; // local pointer on physical page base uint32_t total_dirents; // total number of dirents in dirent array uint32_t total_pages; // total number of pages for dirent array vpn_t vpn; // first page in dirent array vseg ppn_t ppn; // ppn of currently allocated physical page uint32_t entries; // number of dirent actually comied in one page uint32_t first_entry; // index of first dentry to copy in dirent array bool_t done; // last entry found and copied when true list_entry_t root; // root of temporary list of allocated pages uint32_t page_id; // page index in list of physical pages kmem_req_t req; // kmem request descriptor error_t error; // get cluster, local pointer, and pid of reference user process ref_cxy = GET_CXY( ref_xp ); ref_ptr = GET_PTR( ref_xp ); ref_pid = hal_remote_l32( XPTR( ref_cxy , &ref_ptr->pid ) ); #if DEBUG_USER_DIR uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( cycle > DEBUG_USER_DIR ) printk("\n[%s] thread[%x,%x] enter for inode (%x,%x) and process %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, local_cxy, inode, ref_pid, cycle ); #endif // check dirent size assert( ( sizeof(struct dirent) == 64), "sizeof(dirent) != 64\n"); // compute number of dirent per page dirents_per_page = CONFIG_PPM_PAGE_SIZE >> 6; // initialise temporary list of pages list_root_init( &root ); // allocate memory for a local user_dir descriptor req.type = KMEM_DIR; req.flags = AF_ZERO; dir = kmem_alloc( &req ); if( dir == NULL ) { printk("\n[ERROR] in %s : cannot allocate user_dir_t in cluster %x\n", __FUNCTION__, local_cxy ); return NULL; } // Build an initialize the dirent array as a list of physical pages. // For each iteration in this while loop: // - allocate one physical 4 Kbytes (64 dirent slots) // - call the relevant FS specific function to scan the directory mapper, // and copy up to 64 entries in the page. // - register the page in a temporary list using the embedded page list_entry // - exit when the last entry has been found (done == true). // initialize loops variables done = false; total_dirents = 0; total_pages = 0; first_entry = 0; while( done == false ) // loop on physical pages { // allocate one physical page req.type = KMEM_PAGE; req.size = 0; req.flags = AF_ZERO; page = kmem_alloc( &req ); if( page == NULL ) { printk("\n[ERROR] in %s : cannot allocate page in cluster %x\n", __FUNCTION__, ref_cxy ); goto user_dir_create_failure; } // get pointer on page base (array of dirents) page_xp = XPTR( local_cxy , page ); base_xp = ppm_page2base( page_xp ); base = GET_PTR( base_xp ); // call the relevant FS specific function to copy up to 64 dirents in page error = vfs_fs_get_user_dir( inode, base, dirents_per_page, first_entry, false, // don't create missing inodes &entries, &done ); if( error ) { printk("\n[ERROR] in %s : cannot initialise dirent array in cluster %x\n", __FUNCTION__, ref_cxy ); goto user_dir_create_failure; } // increment number of written dirents total_dirents += entries; // register page in temporary list list_add_last( &root , &page->list ); total_pages++; // set first_entry for next iteration first_entry = total_dirents; } // end while #if DEBUG_USER_DIR if( cycle > DEBUG_USER_DIR ) printk("\n[%s] thread[%x,%x] initialised dirent array / %d entries\n", __FUNCTION__, this->process->pid, this->trdid, total_dirents, cycle ); #endif // compute required vseg size for a 64 bytes dirent vseg_size = total_dirents << 6; // create an ANON vseg and register it in reference process VSL if( local_cxy == ref_cxy ) { vseg = vmm_create_vseg( ref_ptr, VSEG_TYPE_ANON, 0, // vseg base (unused) vseg_size, 0, // file offset (unused) 0, // file_size (unused) XPTR_NULL, // mapper (unused) local_cxy ); } else { rpc_vmm_create_vseg_client( ref_cxy, ref_ptr, VSEG_TYPE_ANON, 0, // vseg base (unused) vseg_size, 0, // file offset (unused) 0, // file size (unused) XPTR_NULL, // mapper (unused) local_cxy, &vseg ); } if( vseg == NULL ) { printk("\n[ERROR] in %s : cannot create vseg for user_dir in cluster %x\n", __FUNCTION__, ref_cxy); goto user_dir_create_failure; } #if DEBUG_USER_DIR if( cycle > DEBUG_USER_DIR ) printk("\n[%s] thread[%x,%x] allocated vseg ANON / base %x / size %x\n", __FUNCTION__, this->process->pid, this->trdid, vseg->min, vseg->max - vseg->min ); #endif // check vseg size assert( (total_pages == hal_remote_l32( XPTR( ref_cxy , &vseg->vpn_size ) ) ), "unconsistent vseg size for dirent array" ); // build extended pointer on reference process GPT, PTE attributes and ppn gpt_xp = XPTR( ref_cxy , &ref_ptr->vmm.gpt ); gpt_attributes = GPT_MAPPED | GPT_SMALL | GPT_READABLE | GPT_CACHABLE | GPT_USER ; // get first vpn from vseg descriptor vpn = hal_remote_l32( XPTR( ref_cxy , &vseg->vpn_base ) ); // scan the list of allocated physical pages to map // all physical pages in the in the reference process GPT page_id = 0; while( list_is_empty( &root ) == false ) { // get pointer on first page descriptor page = LIST_FIRST( &root , page_t , list ); // compute ppn ppn = ppm_page2ppn( XPTR( local_cxy , page ) ); error = hal_gpt_set_pte( gpt_xp, vpn + page_id, gpt_attributes, ppn ); if( error ) { printk("\n[ERROR] in %s : cannot map vpn %x in GPT\n", __FUNCTION__, (vpn + page_id) ); // delete the vseg if( ref_cxy == local_cxy) vmm_delete_vseg( ref_pid, vpn< DEBUG_USER_DIR ) printk("\n[%s] thread[%x,%x] mapped vpn %x to ppn %x\n", __FUNCTION__, this->process->pid, this->trdid, vpn + page_id, ppn ); #endif // remove the page from temporary list list_unlink( &page->list ); page_id++; } // end map loop // check number of pages assert( (page_id == total_pages) , "unconsistent pages number\n" ); // initialise user_dir_t structure dir->current = 0; dir->entries = total_dirents; dir->ident = (intptr_t)(vpn << CONFIG_PPM_PAGE_SHIFT); // build extended pointers on root and lock of user_dir xlist in ref process xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->dir_root ); xptr_t lock_xp = XPTR( ref_cxy , &ref_ptr->dir_lock ); // build extended pointer on list field in user_dir structure xptr_t entry_xp = XPTR( local_cxy , &dir->list ); // get lock protecting open directories list remote_queuelock_acquire( lock_xp ); // register user_dir_t in reference process xlist_add_first( root_xp , entry_xp ); // release lock protecting open directorie list remote_queuelock_release( lock_xp ); #if DEBUG_USER_DIR cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_USER_DIR ) printk("\n[%s] thread[%x,%x] created user_dir (%x,%x) / %d entries / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, local_cxy, dir, total_dirents, cycle ); #endif return dir; user_dir_create_failure: // release local user_dir_t structure req.type = KMEM_DIR; req.ptr = dir; kmem_free( &req ); // release local physical pages while( list_is_empty( &root ) == false ) { page = LIST_FIRST( &root , page_t , list ); req.type = KMEM_PAGE; req.ptr = page; kmem_free( &req ); } return NULL; } // end user_dir_create() //////////////////////////////////////// void user_dir_destroy( user_dir_t * dir, xptr_t ref_xp ) { thread_t * this; // local pointer on calling thread process_t * process; // local pointer on calling process cluster_t * cluster; // local pointer on local cluster intptr_t ident; // user pointer on dirent array xptr_t ref_pid; // reference process PID cxy_t ref_cxy; // reference process cluster identifier process_t * ref_ptr; // local pointer on reference process xptr_t root_xp; // root of xlist xptr_t lock_xp; // extended pointer on lock protecting xlist xptr_t iter_xp; // iteratot in xlist reg_t save_sr; // for critical section cxy_t owner_cxy; // owner process cluster lpid_t lpid; // process local index rpc_desc_t rpc; // rpc descriptor uint32_t responses; // response counter // get pointers on calling process & thread this = CURRENT_THREAD; process = this->process; cluster = LOCAL_CLUSTER; // get cluster, local pointer, and PID of reference user process ref_cxy = GET_CXY( ref_xp ); ref_ptr = GET_PTR( ref_xp ); ref_pid = hal_remote_l32( XPTR( ref_cxy , &ref_ptr->pid ) ); #if DEBUG_USER_DIR uint32_t cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_USER_DIR ) printk("\n[%s] thread[%x,%x] enter for user_dir (%x,%x) and process %x / cycle %d\n", __FUNCTION__, process->pid, this->trdid, local_cxy, dir, ref_pid, cycle ); #endif // get user pointer on dirent array ident = dir->ident; // build extended pointer on lock protecting open directories list lock_xp = XPTR( ref_cxy , &ref_ptr->dir_lock ); // get lock protecting open directories list remote_queuelock_acquire( lock_xp ); // remove dir from reference process xlist xlist_unlink( XPTR( local_cxy , &dir->list ) ); // release lock protecting open directories list remote_queuelock_release( lock_xp ); // To delete all copies of the vseg containing the dirent array, the client thread // send parallel RPCs to all clusters containing a client process copy (including // the local cluster). It blocks and deschedules when all RPCs have been sent, // to wait all RPC responses, and will be unblocked by the last RPC server thread. // It allocates a - shared - RPC descriptor in the stack, because all parallel // server threads use the same input arguments, and the same response field. // get owner cluster identifier and process lpid owner_cxy = CXY_FROM_PID( ref_pid ); lpid = LPID_FROM_PID( ref_pid ); // get root of list of copies and lock from owner cluster root_xp = XPTR( owner_cxy , &cluster->pmgr.copies_root[lpid] ); lock_xp = XPTR( owner_cxy , &cluster->pmgr.copies_lock[lpid] ); // mask IRQs hal_disable_irq( &save_sr); // client thread blocks itself thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_RPC ); // initialize responses counter responses = 0; // initialize a shared RPC descriptor // can be shared, because no out arguments rpc.rsp = &responses; rpc.blocking = false; rpc.index = RPC_VMM_DELETE_VSEG; rpc.thread = this; rpc.lid = this->core->lid; rpc.args[0] = ref_pid; rpc.args[1] = ident; // take the lock protecting process copies remote_queuelock_acquire( lock_xp ); // scan list of process copies XLIST_FOREACH( root_xp , iter_xp ) { // get extended pointer and cluster of process xptr_t process_xp = XLIST_ELEMENT( iter_xp , process_t , copies_list ); cxy_t process_cxy = GET_CXY( process_xp ); // atomically increment responses counter hal_atomic_add( &responses , 1 ); // send RPC to target cluster rpc_send( process_cxy , &rpc ); } // release the lock protecting process copies remote_queuelock_release( lock_xp ); // client thread deschedule sched_yield("blocked on rpc_vmm_delete_vseg"); // restore IRQs hal_restore_irq( save_sr); // release local user_dir_t structure kmem_req_t req; req.type = KMEM_DIR; req.ptr = dir; kmem_free( &req ); #if DEBUG_USER_DIR cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_USER_DIR ) printk("\n[%s] thread[%x,%x] deleted user_dir (%x,%x) / cycle %d\n", __FUNCTION__, process->pid, this->trdid, local_cxy, dir, cycle ); #endif } // end user_dir_destroy()