diff options
author | Richard Braun <rbraun@sceen.net> | 2013-05-03 19:56:50 +0200 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2013-09-16 01:22:20 +0200 |
commit | 0e847864cef404f555387d7fcc97f7dbe769e1e9 (patch) | |
tree | dbafbabcd1491b4840914d60358cf153beed9af8 | |
parent | b2e27fcee4cec98ffc39273ecfaa73aace9da2c3 (diff) |
Handle notification on page eviction
If requested by the user, make libpager call pager_notify_evict when a page
is flushed out by the kernel. Based on work by Ognyan Kulev.
* console/pager.c (pager_notify_evict): New function.
(user_pager_create): Update call to pager_create.
* ext2fs/pager.c: (pager_notify_evict): New function.
(create_disk_pager): Update call to diskfs_start_disk_pager.
(diskfs_get_filemap): Update call to pager_create.
* fatfs/pager.c: (pager_notify_evict): New function.
(create_fat_pager): Update call to diskfs_start_disk_pager.
(diskfs_get_filemap): Update call to pager_create.
* isofs/pager.c: (pager_notify_evict): New function.
(create_disk_pager): Update call to diskfs_start_disk_pager.
(diskfs_get_filemap): Update call to pager_create.
* libdiskfs/disk-pager.c (diskfs_start_disk_pager): Update definition and call
to pager_create.
* libdiskfs/diskfs-pager.h (diskfs_start_disk_pager): Update declaration.
* libpager/data-request.c (_pager_seqnos_memory_object_data_request): Take
pager's `notify_on_evict' member into account when calling
memory_object_data_supply.
* libpager/data-return.c (_pager_do_write_request): Handle user notification
on page flush.
* libpager/pager-create.c (pager_create): Update definition and set pager's
`notify_on_evict' member.
* libpager/pager.h (pager_create): Update declaration.
(pager_notify_evict): New declaration.
* libpager/priv.h (struct pager): New `notify_on_evict' member.
* storeio/pager.c: (pager_notify_evict): New function.
(dev_get_memory_object): Update call to pager_create.
* tmpfs/pager-stubs.c (pager_notify_evict): New function.
* ufs/pager.c (pager_notify_evict): New function.
(create_disk_pager): Update call to diskfs_start_disk_pager.
(diskfs_get_filemap): Update call to pager_create.
-rw-r--r-- | console/pager.c | 10 | ||||
-rw-r--r-- | ext2fs/pager.c | 14 | ||||
-rw-r--r-- | fatfs/pager.c | 11 | ||||
-rw-r--r-- | isofs/pager.c | 12 | ||||
-rw-r--r-- | libdiskfs/disk-pager.c | 6 | ||||
-rw-r--r-- | libdiskfs/diskfs-pager.h | 3 | ||||
-rw-r--r-- | libpager/data-request.c | 3 | ||||
-rw-r--r-- | libpager/data-return.c | 79 | ||||
-rw-r--r-- | libpager/pager-create.c | 4 | ||||
-rw-r--r-- | libpager/pager.h | 23 | ||||
-rw-r--r-- | libpager/priv.h | 1 | ||||
-rw-r--r-- | storeio/pager.c | 9 | ||||
-rw-r--r-- | tmpfs/pager-stubs.c | 8 | ||||
-rw-r--r-- | ufs/pager.c | 11 |
14 files changed, 158 insertions, 36 deletions
diff --git a/console/pager.c b/console/pager.c index 781ba35e..4d0c5cdb 100644 --- a/console/pager.c +++ b/console/pager.c @@ -95,6 +95,14 @@ pager_unlock_page (struct user_pager_info *pager, } +void +pager_notify_evict (struct user_pager_info *pager, + vm_offset_t page) +{ + assert (!"unrequested notification on eviction"); +} + + /* Tell how big the file is. */ error_t pager_report_extent (struct user_pager_info *upi, @@ -170,7 +178,7 @@ user_pager_create (struct user_pager *user_pager, unsigned int npages, /* XXX Are the values 1 and MEMORY_OBJECT_COPY_DELAY correct? */ user_pager->pager = pager_create (upi, pager_bucket, - 1, MEMORY_OBJECT_COPY_DELAY); + 1, MEMORY_OBJECT_COPY_DELAY, 0); if (!user_pager->pager) { free (upi); diff --git a/ext2fs/pager.c b/ext2fs/pager.c index f740434a..92137112 100644 --- a/ext2fs/pager.c +++ b/ext2fs/pager.c @@ -511,6 +511,13 @@ pager_write_page (struct user_pager_info *pager, vm_offset_t page, else return file_pager_write_page (pager->node, page, (void *)buf); } + +void +pager_notify_evict (struct user_pager_info *pager, vm_offset_t page) +{ + assert (!"unrequested notification on eviction"); +} + /* Make page PAGE writable, at least up to ALLOCSIZE. This function and diskfs_grow are the only places that blocks are actually added to the @@ -776,8 +783,9 @@ create_disk_pager (void) ext2_panic ("can't create disk pager: %s", strerror (errno)); upi->type = DISK; pager_bucket = ports_create_bucket (); - diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, store->size, - &disk_image); + diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, 0, + store->size, &disk_image); + } /* Call this to create a FILE_DATA pager and return a send right. @@ -817,7 +825,7 @@ diskfs_get_filemap (struct node *node, vm_prot_t prot) diskfs_nref_light (node); node->dn->pager = pager_create (upi, pager_bucket, MAY_CACHE, - MEMORY_OBJECT_COPY_DELAY); + MEMORY_OBJECT_COPY_DELAY, 0); if (node->dn->pager == 0) { diskfs_nrele_light (node); diff --git a/fatfs/pager.c b/fatfs/pager.c index f892c88f..8146e648 100644 --- a/fatfs/pager.c +++ b/fatfs/pager.c @@ -596,6 +596,13 @@ pager_unlock_page (struct user_pager_info *pager, return 0; } +void +pager_notify_evict (struct user_pager_info *pager, + vm_offset_t page) +{ + assert (!"unrequested notification on eviction"); +} + /* Grow the disk allocated to locked node NODE to be at least SIZE bytes, and set NODE->allocsize to the actual allocated size. (If the allocated size is already SIZE bytes, do nothing.) CRED @@ -752,7 +759,7 @@ create_fat_pager (void) struct user_pager_info *upi = malloc (sizeof (struct user_pager_info)); upi->type = FAT; pager_bucket = ports_create_bucket (); - diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, + diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, 0, bytes_per_sector * sectors_per_fat, &fat_image); } @@ -794,7 +801,7 @@ diskfs_get_filemap (struct node *node, vm_prot_t prot) diskfs_nref_light (node); node->dn->pager = pager_create (upi, pager_bucket, MAY_CACHE, - MEMORY_OBJECT_COPY_DELAY); + MEMORY_OBJECT_COPY_DELAY, 0); if (node->dn->pager == 0) { diskfs_nrele_light (node); diff --git a/isofs/pager.c b/isofs/pager.c index f93e0c82..7a38ba33 100644 --- a/isofs/pager.c +++ b/isofs/pager.c @@ -94,6 +94,13 @@ pager_unlock_page (struct user_pager_info *pager, return EROFS; } +void +pager_notify_evict (struct user_pager_info *pager, + vm_offset_t page) +{ + assert (!"unrequested notification on eviction"); +} + /* Tell how big the file is. */ error_t pager_report_extent (struct user_pager_info *pager, @@ -137,7 +144,7 @@ create_disk_pager (void) upi->type = DISK; upi->np = 0; pager_bucket = ports_create_bucket (); - diskfs_start_disk_pager (upi, pager_bucket, 1, store->size, &disk_image); + diskfs_start_disk_pager (upi, pager_bucket, 1, 0, store->size, &disk_image); upi->p = diskfs_disk_pager; } @@ -168,7 +175,8 @@ diskfs_get_filemap (struct node *np, vm_prot_t prot) upi->type = FILE_DATA; upi->np = np; diskfs_nref_light (np); - upi->p = pager_create (upi, pager_bucket, 1, MEMORY_OBJECT_COPY_DELAY); + upi->p = pager_create (upi, pager_bucket, 1, + MEMORY_OBJECT_COPY_DELAY, 0); if (upi->p == 0) { diskfs_nrele_light (np); diff --git a/libdiskfs/disk-pager.c b/libdiskfs/disk-pager.c index 5795a281..8fe8f806 100644 --- a/libdiskfs/disk-pager.c +++ b/libdiskfs/disk-pager.c @@ -49,7 +49,8 @@ service_paging_requests (void *arg) void diskfs_start_disk_pager (struct user_pager_info *upi, - struct port_bucket *pager_bucket, int may_cache, + struct port_bucket *pager_bucket, + int may_cache, int notify_on_evict, size_t size, void **image) { pthread_t thread; @@ -68,7 +69,8 @@ diskfs_start_disk_pager (struct user_pager_info *upi, /* Create the pager. */ diskfs_disk_pager = pager_create (upi, pager_bucket, - may_cache, MEMORY_OBJECT_COPY_NONE); + may_cache, MEMORY_OBJECT_COPY_NONE, + notify_on_evict); assert (diskfs_disk_pager); /* Get a port to the disk pager. */ diff --git a/libdiskfs/diskfs-pager.h b/libdiskfs/diskfs-pager.h index bd0a050c..a253069b 100644 --- a/libdiskfs/diskfs-pager.h +++ b/libdiskfs/diskfs-pager.h @@ -35,7 +35,8 @@ extern __thread struct disk_image_user *diskfs_exception_diu; mapped is returned in IMAGE. INFO, PAGER_BUCKET, & MAY_CACHE are passed to `pager_create'. */ extern void diskfs_start_disk_pager (struct user_pager_info *info, - struct port_bucket *pager_bucket, int may_cache, + struct port_bucket *pager_bucket, + int may_cache, int notify_on_evict, size_t size, void **image); extern struct pager *diskfs_disk_pager; diff --git a/libpager/data-request.c b/libpager/data-request.c index 4454facb..34b8b438 100644 --- a/libpager/data-request.c +++ b/libpager/data-request.c @@ -119,7 +119,8 @@ _pager_seqnos_memory_object_data_request (mach_port_t object, goto error_read; memory_object_data_supply (p->memobjcntl, offset, page, length, 1, - write_lock ? VM_PROT_WRITE : VM_PROT_NONE, 0, + write_lock ? VM_PROT_WRITE : VM_PROT_NONE, + p->notify_on_evict ? 1 : 0, MACH_PORT_NULL); pthread_mutex_lock (&p->interlock); _pager_mark_object_error (p, offset, length, 0); diff --git a/libpager/data-return.c b/libpager/data-return.c index c70f0e8d..6a3b9037 100644 --- a/libpager/data-return.c +++ b/libpager/data-return.c @@ -39,6 +39,7 @@ _pager_do_write_request (mach_port_t object, struct pager *p; short *pm_entries; int npages, i; + char *notified; error_t *pagerrs; struct lock_request *lr; struct lock_list {struct lock_request *lr; @@ -71,9 +72,6 @@ _pager_do_write_request (mach_port_t object, goto release_out; } - if (! dirty) - goto release_out; - if (p->pager_state != NORMAL) { printf ("pager in wrong state for write\n"); @@ -83,6 +81,11 @@ _pager_do_write_request (mach_port_t object, npages = length / __vm_page_size; pagerrs = alloca (npages * sizeof (error_t)); + notified = alloca (npages * (sizeof *notified)); +#ifndef NDEBUG + memset (notified, -1, npages * (sizeof *notified)); +#endif + _pager_block_termination (p); /* until we are done with the pagemap when the write completes. */ @@ -90,6 +93,24 @@ _pager_do_write_request (mach_port_t object, pm_entries = &p->pagemap[offset / __vm_page_size]; + if (! dirty) + { + munmap ((void *) data, length); + if (!kcopy) { + /* Prepare notified array. */ + for (i = 0; i < npages; i++) + notified[i] = (p->notify_on_evict + && ! (pm_entries[i] & PM_PAGEINWAIT)); + + _pager_release_seqno (p, seqno); + goto notify; + } + else { + _pager_allow_termination (p); + goto release_out; + } + } + /* Make sure there are no other in-progress writes for any of these pages before we begin. This imposes a little more serialization than we really have to require (because *all* future writes on @@ -120,10 +141,6 @@ _pager_do_write_request (mach_port_t object, for (i = 0; i < npages; i++) pm_entries[i] |= PM_PAGINGOUT | PM_INIT; - if (!kcopy) - for (i = 0; i < npages; i++) - pm_entries[i] &= ~PM_INCORE; - /* If this write occurs while a lock is pending, record it. We have to keep this list because a lock request might come in while we do the I/O; in that case there @@ -163,7 +180,10 @@ _pager_do_write_request (mach_port_t object, for (i = 0; i < npages; i++) { if (omitdata & (1 << i)) - continue; + { + notified[i] = 0; + continue; + } if (pm_entries[i] & PM_WRITEWAIT) wakeup = 1; @@ -179,14 +199,22 @@ _pager_do_write_request (mach_port_t object, pm_entries[i] |= PM_INVALID; if (pm_entries[i] & PM_PAGEINWAIT) - memory_object_data_supply (p->memobjcntl, - offset + (vm_page_size * i), - data + (vm_page_size * i), - vm_page_size, 1, - VM_PROT_NONE, 0, MACH_PORT_NULL); + { + memory_object_data_supply (p->memobjcntl, + offset + (vm_page_size * i), + data + (vm_page_size * i), + vm_page_size, 1, + VM_PROT_NONE, 0, MACH_PORT_NULL); + notified[i] = 0; + } else - munmap ((caddr_t) (data + (vm_page_size * i)), - vm_page_size); + { + munmap ((void *) (data + (vm_page_size * i)), + vm_page_size); + notified[i] = (! kcopy && p->notify_on_evict); + if (! kcopy) + pm_entries[i] &= ~PM_INCORE; + } pm_entries[i] &= ~(PM_PAGINGOUT | PM_PAGEINWAIT | PM_WRITEWAIT); } @@ -198,10 +226,29 @@ _pager_do_write_request (mach_port_t object, if (wakeup) pthread_cond_broadcast (&p->wakeup); + notify: _pager_allow_termination (p); - pthread_mutex_unlock (&p->interlock); + for (i = 0; i < npages; i++) + { + assert (notified[i] == 0 || notified[i] == 1); + if (notified[i]) + { + short *pm_entry = &pm_entries[i]; + + /* Do notify user. */ + pager_notify_evict (p->upi, offset + (i * vm_page_size)); + + /* Clear any error that is left. Notification on eviction + is used only to change association of page, so any + error may no longer be valid. */ + pthread_mutex_lock (&p->interlock); + *pm_entry = SET_PM_ERROR (SET_PM_NEXTERROR (*pm_entry, 0), 0); + pthread_mutex_unlock (&p->interlock); + } + } + ports_port_deref (p); return 0; diff --git a/libpager/pager-create.c b/libpager/pager-create.c index 318c9f15..1aea6e96 100644 --- a/libpager/pager-create.c +++ b/libpager/pager-create.c @@ -22,7 +22,8 @@ struct pager * pager_create (struct user_pager_info *upi, struct port_bucket *bucket, boolean_t may_cache, - memory_object_copy_strategy_t copy_strategy) + memory_object_copy_strategy_t copy_strategy, + boolean_t notify_on_evict) { struct pager *p; @@ -38,6 +39,7 @@ pager_create (struct user_pager_info *upi, p->attribute_requests = 0; p->may_cache = may_cache; p->copy_strategy = copy_strategy; + p->notify_on_evict = notify_on_evict; p->memobjcntl = MACH_PORT_NULL; p->memobjname = MACH_PORT_NULL; p->seqno = -1; diff --git a/libpager/pager.h b/libpager/pager.h index 99fb3845..75ff108a 100644 --- a/libpager/pager.h +++ b/libpager/pager.h @@ -36,14 +36,17 @@ int pager_demuxer (mach_msg_header_t *inp, to receive requests. U_PAGER will be provided to later calls to pager_find_address. The pager will have one user reference created. MAY_CACHE and COPY_STRATEGY are the original values of - those attributes as for memory_object_ready. Users may create - references to pagers by use of the relevant ports library - functions. On errors, return null and set errno. */ + those attributes as for memory_object_ready. If NOTIFY_ON_EVICT is + non-zero, pager_notify_evict user callback will be called when page + is evicted. Users may create references to pagers by use of the + relevant ports library functions. On errors, return null and set + errno. */ struct pager * pager_create (struct user_pager_info *u_pager, struct port_bucket *bucket, boolean_t may_cache, - memory_object_copy_strategy_t copy_strategy); + memory_object_copy_strategy_t copy_strategy, + boolean_t notify_on_evict); /* Return the user_pager_info struct associated with a pager. */ struct user_pager_info * @@ -172,6 +175,18 @@ error_t pager_unlock_page (struct user_pager_info *pager, vm_offset_t address); +/* The user must define this function. It is used when you want be + able to change association of pages to backing store. To use it, + pass non-zero value in NOTIFY_ON_EVICT when pager is created with + pager_create. You can change association of page only when + pager_notify_evict has been called and you haven't touched page + content after that. Note there is a possibility that a page is + evicted, but user is not notified about that. The user should be + able to handle this case. */ +void +pager_notify_evict (struct user_pager_info *pager, + vm_offset_t page); + /* The user must define this function. It should report back (in *OFFSET and *SIZE the minimum valid address the pager will accept and the size of the object. */ diff --git a/libpager/priv.h b/libpager/priv.h index e6b2546b..7aa0fb44 100644 --- a/libpager/priv.h +++ b/libpager/priv.h @@ -49,6 +49,7 @@ struct pager boolean_t may_cache; memory_object_copy_strategy_t copy_strategy; + boolean_t notify_on_evict; /* Interface ports */ memory_object_control_t memobjcntl; diff --git a/storeio/pager.c b/storeio/pager.c index cbae7eb6..7d787110 100644 --- a/storeio/pager.c +++ b/storeio/pager.c @@ -110,6 +110,13 @@ pager_unlock_page (struct user_pager_info *upi, vm_offset_t address) return 0; } +void +pager_notify_evict (struct user_pager_info *pager, + vm_offset_t page) +{ + assert (!"unrequested notification on eviction"); +} + /* The user must define this function. It should report back (in *OFFSET and *SIZE the minimum valid address the pager will accept and the size of the object. */ @@ -246,7 +253,7 @@ dev_get_memory_object (struct dev *dev, vm_prot_t prot, memory_object_t *memobj) { dev->pager = pager_create ((struct user_pager_info *)dev, pager_port_bucket, - 1, MEMORY_OBJECT_COPY_DELAY); + 1, MEMORY_OBJECT_COPY_DELAY, 0); if (dev->pager == NULL) { pthread_mutex_unlock (&dev->pager_lock); diff --git a/tmpfs/pager-stubs.c b/tmpfs/pager-stubs.c index 25d70fe2..3cb264bb 100644 --- a/tmpfs/pager-stubs.c +++ b/tmpfs/pager-stubs.c @@ -57,6 +57,14 @@ pager_unlock_page (struct user_pager_info *pager, return EIEIO; } +void +pager_notify_evict (struct user_pager_info *pager, + vm_offset_t page) +{ + abort(); +} + + /* The user must define this function. It should report back (in *OFFSET and *SIZE the minimum valid address the pager will accept and the size of the object. */ diff --git a/ufs/pager.c b/ufs/pager.c index 1e3d140c..5d3e44ab 100644 --- a/ufs/pager.c +++ b/ufs/pager.c @@ -450,6 +450,13 @@ pager_unlock_page (struct user_pager_info *pager, return err; } +void +pager_notify_evict (struct user_pager_info *pager, + vm_offset_t page) +{ + assert (!"unrequested notification on eviction"); +} + /* Implement the pager_report_extent callback from the pager library. See <hurd/pager.h> for the interface description. */ inline error_t @@ -502,7 +509,7 @@ create_disk_pager (void) upi->type = DISK; upi->np = 0; pager_bucket = ports_create_bucket (); - diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, store->size, + diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, 0, store->size, &disk_image); upi->p = diskfs_disk_pager; } @@ -595,7 +602,7 @@ diskfs_get_filemap (struct node *np, vm_prot_t prot) upi->unlocked_pagein_length = 0; diskfs_nref_light (np); upi->p = pager_create (upi, pager_bucket, - MAY_CACHE, MEMORY_OBJECT_COPY_DELAY); + MAY_CACHE, MEMORY_OBJECT_COPY_DELAY, 0); if (upi->p == 0) { diskfs_nrele_light (np); |