diff options
-rw-r--r-- | ext2fs/pager.c | 43 |
1 files changed, 27 insertions, 16 deletions
diff --git a/ext2fs/pager.c b/ext2fs/pager.c index 558c87e4..8869c652 100644 --- a/ext2fs/pager.c +++ b/ext2fs/pager.c @@ -598,7 +598,8 @@ pager_clear_user_data (struct user_pager_info *upi) if (upi->type == FILE_DATA) { spin_lock (&node_to_page_lock); - upi->node->dn->fileinfo = 0; + if (upi->node->dn->fileinfo == upi) + upi->node->dn->fileinfo = 0; spin_unlock (&node_to_page_lock); diskfs_nrele_light (upi->node); @@ -662,21 +663,31 @@ diskfs_get_filemap (struct node *node) || (S_ISLNK (node->dn_stat.st_mode))); spin_lock (&node_to_page_lock); - if (!node->dn->fileinfo) - { - upi = malloc (sizeof (struct user_pager_info)); - upi->type = FILE_DATA; - upi->node = node; - diskfs_nref_light (node); - upi->p = - pager_create (upi, pager_bucket, MAY_CACHE, MEMORY_OBJECT_COPY_DELAY); - node->dn->fileinfo = upi; - right = pager_get_port (node->dn->fileinfo->p); - ports_port_deref (node->dn->fileinfo->p); - } - else - /* XXX race; see ufs/pager.c here. */ - right = pager_get_port (node->dn->fileinfo->p); + do + if (!node->dn->fileinfo) + { + upi = malloc (sizeof (struct user_pager_info)); + upi->type = FILE_DATA; + upi->node = node; + diskfs_nref_light (node); + upi->p = + pager_create (upi, pager_bucket, MAY_CACHE, + MEMORY_OBJECT_COPY_DELAY); + node->dn->fileinfo = upi; + right = pager_get_port (node->dn->fileinfo->p); + ports_port_deref (node->dn->fileinfo->p); + } + else + { + /* Because NP->dn->fileinfo->p is not a real reference, + this might be nearly deallocated. If that's so, then + the port right will be null. In that case, clear here + and loop. The deallocation will complete separately. */ + right = pager_get_port (node->dn->fileinfo->p); + if (right == MACH_PORT_NULL) + node->dn->fileinfo = 0; + } + while (right == MACH_PORT_NULL); spin_unlock (&node_to_page_lock); mach_port_insert_right (mach_task_self (), right, right, |