Changeset View
Changeset View
Standalone View
Standalone View
head/sys/vm/vm_page.c
Show First 20 Lines • Show All 3,456 Lines • ▼ Show 20 Lines | |||||
* Move the page from one queue to another, or to the tail of its | * Move the page from one queue to another, or to the tail of its | ||||
* current queue, in the face of a possible concurrent call to | * current queue, in the face of a possible concurrent call to | ||||
* vm_page_dequeue_deferred_free(). | * vm_page_dequeue_deferred_free(). | ||||
*/ | */ | ||||
void | void | ||||
vm_page_swapqueue(vm_page_t m, uint8_t oldq, uint8_t newq) | vm_page_swapqueue(vm_page_t m, uint8_t oldq, uint8_t newq) | ||||
{ | { | ||||
struct vm_pagequeue *pq; | struct vm_pagequeue *pq; | ||||
vm_page_t next; | |||||
bool queued; | |||||
KASSERT(oldq < PQ_COUNT && newq < PQ_COUNT && oldq != newq, | KASSERT(oldq < PQ_COUNT && newq < PQ_COUNT && oldq != newq, | ||||
("vm_page_swapqueue: invalid queues (%d, %d)", oldq, newq)); | ("vm_page_swapqueue: invalid queues (%d, %d)", oldq, newq)); | ||||
KASSERT((m->oflags & VPO_UNMANAGED) == 0, | |||||
("vm_page_swapqueue: page %p is unmanaged", m)); | |||||
vm_page_assert_locked(m); | vm_page_assert_locked(m); | ||||
/* | |||||
* Atomically update the queue field and set PGA_REQUEUE while | |||||
* ensuring that PGA_DEQUEUE has not been set. | |||||
*/ | |||||
pq = &vm_pagequeue_domain(m)->vmd_pagequeues[oldq]; | pq = &vm_pagequeue_domain(m)->vmd_pagequeues[oldq]; | ||||
vm_pagequeue_lock(pq); | vm_pagequeue_lock(pq); | ||||
if (!vm_page_pqstate_cmpset(m, oldq, newq, PGA_DEQUEUE, PGA_REQUEUE)) { | |||||
/* | |||||
* The physical queue state might change at any point before the page | |||||
* queue lock is acquired, so we must verify that we hold the correct | |||||
* lock before proceeding. | |||||
*/ | |||||
if (__predict_false(m->queue != oldq)) { | |||||
vm_pagequeue_unlock(pq); | vm_pagequeue_unlock(pq); | ||||
return; | return; | ||||
} | } | ||||
if ((m->aflags & PGA_ENQUEUED) != 0) { | |||||
vm_pagequeue_remove(pq, m); | /* | ||||
* Once the queue index of the page changes, there is nothing | |||||
* synchronizing with further updates to the physical queue state. | |||||
* Therefore we must remove the page from the queue now in anticipation | |||||
* of a successful commit, and be prepared to roll back. | |||||
*/ | |||||
if (__predict_true((m->aflags & PGA_ENQUEUED) != 0)) { | |||||
alc: You could shorten/simplify this by writing:
```
queued = (m->aflags & PGA_ENQUEUED) !=… | |||||
markjAuthorUnsubmitted Done Inline ActionsI have a larger patch which generalizes the logic in this function. Basically, all queue state updates will use the new vm_page_pqstate_commit(), which wraps the cmpset and handles removal of the page from its queue. It also eliminates the extra atomic operations we currently have here: right now we are clearing PGA_ENQUEUED and setting m->queue in separate atomic operations. I implemented your suggestion in my patch. If it's ok with you, I will simply leave this function as-is for now to avoid an extra commit. markj: I have a larger patch which generalizes the logic in this function. Basically, all queue state… | |||||
alcUnsubmitted Not Done Inline ActionsSounds good. alc: Sounds good. | |||||
next = TAILQ_NEXT(m, plinks.q); | |||||
TAILQ_REMOVE(&pq->pq_pl, m, plinks.q); | |||||
vm_page_aflag_clear(m, PGA_ENQUEUED); | vm_page_aflag_clear(m, PGA_ENQUEUED); | ||||
queued = true; | |||||
} else { | |||||
queued = false; | |||||
} | } | ||||
/* | |||||
* Atomically update the queue field and set PGA_REQUEUE while | |||||
* ensuring that PGA_DEQUEUE has not been set. | |||||
*/ | |||||
if (__predict_false(!vm_page_pqstate_cmpset(m, oldq, newq, PGA_DEQUEUE, | |||||
PGA_REQUEUE))) { | |||||
if (queued) { | |||||
vm_page_aflag_set(m, PGA_ENQUEUED); | |||||
if (next != NULL) | |||||
TAILQ_INSERT_BEFORE(next, m, plinks.q); | |||||
else | |||||
TAILQ_INSERT_TAIL(&pq->pq_pl, m, plinks.q); | |||||
} | |||||
vm_pagequeue_unlock(pq); | |||||
return; | |||||
} | |||||
vm_pagequeue_cnt_dec(pq); | |||||
vm_pagequeue_unlock(pq); | vm_pagequeue_unlock(pq); | ||||
vm_page_pqbatch_submit(m, newq); | vm_page_pqbatch_submit(m, newq); | ||||
} | } | ||||
/* | /* | ||||
* vm_page_free_prep: | * vm_page_free_prep: | ||||
* | * | ||||
* Prepares the given page to be put on the free list, | * Prepares the given page to be put on the free list, | ||||
▲ Show 20 Lines • Show All 1,405 Lines • Show Last 20 Lines |
You could shorten/simplify this by writing: