static int _pipeisclosed(struct Fd *fd, struct Pipe *p) { int res; uint32_t runs1, runs2; // Your code here. // // Check pageref(fd) and pageref(p), // returning 1 if they're the same, 0 otherwise. // // The logic here is that pageref(p) is the total // number of readers *and* writers, whereas pageref(fd) // is the number of file descriptors like fd (readers if fd is // a reader, writers if fd is a writer). // // If the number of file descriptors like fd is equal // to the total number of readers and writers, then // everybody left is what fd is. So the other end of // the pipe is closed. for (;;) { runs1 = env->env_runs; res = pageref(fd) == pageref(p); runs2 = env->env_runs; if (runs1 == runs2) return res; if ((runs1 != runs2) && res) cprintf("pipe race avoided\n"); } }
static int _pipeisclosed(struct Fd *fd, struct Pipe *p) { // Your code here. // // Check pageref(fd) and pageref(p), // returning 1 if they're the same, 0 otherwise. // // The logic here is that pageref(p) is the total // number of readers *and* writers, whereas pageref(fd) // is the number of file descriptors like fd (readers if fd is // a reader, writers if fd is a writer). // // If the number of file descriptors like fd is equal // to the total number of readers and writers, then // everybody left is what fd is. So the other end of // the pipe is closed. int runs, r; do { runs = env->env_runs; r = 0; if (pageref(fd) == pageref(p)) r = 1; } while (runs != env->env_runs); return r; }
static int _pipeisclosed(struct Fd *fd, struct Pipe *p) { // Check pageref(fd) and pageref(p), // returning 1 if they're the same, 0 otherwise. // // The logic here is that pageref(p) is the total // number of readers *and* writers, whereas pageref(fd) // is the number of file descriptors like fd (readers if fd is // a reader, writers if fd is a writer). // // If the number of file descriptors like fd is equal // to the total number of readers and writers, then // everybody left is what fd is. So the other end of // the pipe is closed. // // LAB 5: Your code here. while (true) { uint32_t before = thisenv->env_runs; bool test = (pageref(fd) == pageref(p)); asm volatile("" : : : "memory"); if (before == thisenv->env_runs) return test; if (test) cprintf("pipe race avoided\n"); } }
// Look up an open file for envid. int openfile_lookup(envid_t envid, uint32_t fileid, struct OpenFile **po) { struct OpenFile *o; o = &opentab[fileid % MAXOPEN]; if (pageref(o->o_fd) == 1 || o->o_fileid != fileid) { cprintf("pageref(o->o_fd) is %d\n", pageref(o->o_fd)); return -E_INVAL; } *po = o; return 0; }
static int _pipeisclosed(struct Fd *fd, struct Pipe *p) { int n, nn, ret; while (1) { n = thisenv->env_runs; ret = pageref(fd) == pageref(p); nn = thisenv->env_runs; if (n == nn) return ret; if (n != nn && ret == 1) cprintf("pipe race avoided\n", n, thisenv->env_runs, ret); } }
// Allocate an open file. int open_alloc(struct Open **o) { int i, r; // Find an available open-file table entry for (i = 0; i < MAXOPEN; i++) { switch (pageref(opentab[i].o_ff)) { case 0: //writef("^^^^^^^^^^^^^^^^ (u_int)opentab[i].o_ff: %x\n",(u_int)opentab[i].o_ff); if ((r = syscall_mem_alloc(0, (u_int)opentab[i].o_ff, PTE_V | PTE_R | PTE_LIBRARY)) < 0) { return r; } case 1: opentab[i].o_fileid += MAXOPEN; *o = &opentab[i]; user_bzero((void *)opentab[i].o_ff, BY2PG); return (*o)->o_fileid; } } return -E_MAX_OPEN; }
// Look up an open file for envid. int openfile_lookup(envid_t envid, uint32_t fileid, struct OpenFile **po) { struct OpenFile *o; o = &opentab[fileid % MAXOPEN]; if (pageref(o->o_fd) <= 1 || o->o_fileid != fileid) return -E_INVAL; *po = o; return 0; }
// Look up an open file for envid. int open_lookup(u_int envid, u_int fileid, struct Open **po) { struct Open *o; o = &opentab[fileid%MAXOPEN]; if (pageref(o->o_ff) == 1 || o->o_fileid != fileid) return -E_INVAL; *po = o; return 0; }
static int _pipeisclosed(struct Fd *fd, struct Pipe *p) { // Your code here. // // Check pageref(fd) and pageref(p), // returning 1 if they're the same, 0 otherwise. // // The logic here is that pageref(p) is the total // number of readers *and* writers, whereas pageref(fd) // is the number of file descriptors like fd (readers if fd is // a reader, writers if fd is a writer). // // If the number of file descriptors like fd is equal // to the total number of readers and writers, then // everybody left is what fd is. So the other end of // the pipe is closed. int runs; bool isclosed; tryagain: runs = env->env_runs; //critical section isclosed = (pageref(fd)==pageref(p)); //end of critical section if(runs != env->env_runs) { if(isclosed) cprintf("pipe race avoided\n"); goto tryagain; } if (isclosed) return 1; return 0; }
// Check that fdnum is in range and mapped. // If it is, set *fd_store to the fd page virtual address. // // Returns 0 on success (the page is in range and mapped), < 0 on error. // Errors are: // -E_INVAL: fdnum was either not in range or not mapped. int fd_lookup(int fdnum, struct Fd **fd_store) { struct Fd *fd; if (fdnum >= 0 && fdnum < MAXFD) { fd = INDEX2FD(fdnum); if (pageref(fd) > 0) { *fd_store = fd; return 0; } } *fd_store = 0; return -E_INVAL; }
// Finds the smallest i from 0 to MAXFD-1 that doesn't have // its fd page mapped. // Sets *fd_store to the corresponding fd page virtual address. // // fd_alloc does NOT actually allocate an fd page. // It is up to the caller to allocate the page somehow. // This means that if someone calls fd_alloc twice in a row // without allocating the first page we return, we'll return the same // page the second time. // // Hint: Use INDEX2FD. // // Returns 0 on success, < 0 on error. Errors are: // -E_MAX_FD: no more file descriptors // On error, *fd_store is set to 0. int fd_alloc(struct Fd **fd_store) { struct Fd *fd; int i; for (i = 0; i < MAXFD; i++) { fd = INDEX2FD(i); if (pageref(fd) == 0) { *fd_store = fd; return 0; } } *fd_store = 0; return -E_MAX_OPEN; }
// Close the file descriptor. // static int devfile_close(struct Fd *fd) { // If this call will close the last reference to the file descriptor, // then you must account for the outstanding open file reference // in the inode. // But the file descriptor might still be open elsewhere // (because of fork and dup). Use pageref to check. // // LAB 5: Your code here. //cprintf("%d\n", pageref(fd)); if (pageref(fd) > 1) return 0; struct Inode *ino; int r; if ((r = inode_open(fd->fd_file.inum, &ino)) < 0) return r; ino->i_opencount--; return inode_close(ino); }
// Allocate an open file. int openfile_alloc(struct OpenFile **o) { int i, r; // Find an available open-file table entry for (i = 0; i < MAXOPEN; i++) { switch (pageref(opentab[i].o_fd)) { case 0: if ((r = sys_page_alloc(0, opentab[i].o_fd, PTE_P|PTE_U|PTE_W)) < 0) return r; /* fall through */ case 1: opentab[i].o_fileid += MAXOPEN; *o = &opentab[i]; memset(opentab[i].o_fd, 0, PGSIZE); return (*o)->o_fileid; } } return -E_MAX_OPEN; }
// Allocate an open file. int open_alloc(struct Open **o) { int i, r; // Find an available open-file table entry for (i = 0; i < MAXOPEN; i++) { switch (pageref(opentab[i].o_ff)) { case 0: if ((r = sys_mem_alloc(0, (u_int)opentab[i].o_ff, PTE_P|PTE_U|PTE_W)) < 0) return r; /* fall through */ case 1: opentab[i].o_fileid += MAXOPEN; *o = &opentab[i]; memset((void*)opentab[i].o_ff, 0, BY2PG); return (*o)->o_fileid; } } return -E_MAX_OPEN; }
void umain(void) { int p[2], r, pid, i, max; void *va; struct Fd *fd; volatile struct Env *kid; cprintf("testing for dup race...\n"); if ((r = pipe(p)) < 0) panic("pipe: %e", r); max = 200; if ((r = fork()) < 0) panic("fork: %e", r); if (r == 0) { close(p[1]); // // Now the ref count for p[0] will toggle between 2 and 3 // as the parent dups and closes it (there's a close implicit in dup). // // The ref count for p[1] is 1. // Thus the ref count for the underlying pipe structure // will toggle between 3 and 4. // // If a clock interrupt catches close between unmapping // the pipe structure and unmapping the fd, we'll have // a ref count for p[0] of 3, a ref count for p[1] of 1, // and a ref count for the pipe structure of 3, which is // a no-no. // // If a clock interrupt catches dup between mapping the // fd and mapping the pipe structure, we'll have the same // ref counts, still a no-no. // for (i=0; i<max; i++) { if(pipeisclosed(p[0])){ cprintf("RACE: pipe appears closed\n"); exit(); } sys_yield(); } // do something to be not runnable besides exiting ipc_recv(0,0,0); } pid = r; cprintf("pid is %d\n", pid); va = 0; kid = &envs[ENVX(pid)]; cprintf("kid is %d\n", kid-envs); dup(p[0], 10); while (kid->env_status == ENV_RUNNABLE) dup(p[0], 10); cprintf("child done with loop\n"); if (pipeisclosed(p[0])) panic("somehow the other end of p[0] got closed!"); if ((r = fd_lookup(p[0], &fd)) < 0) panic("cannot look up p[0]: %e", r); va = fd2data(fd); if (pageref(va) != 3+1) cprintf("\nchild detected race\n"); else cprintf("\nrace didn't happen\n", max); }