77
Notice how the is_pmem flag on line 96 is used just like it would be for calls to pmem_
persist(), since the pmem_memcpy_persist() function includes the flush to persistence.
The interface pmem_memcpy_persist() includes the flush to persistent because it
may determine that the copy is more optimally performed by using non-temporal stores,
which bypass the CPU cache and do not require subsequent cache flush instructions for
persistence. By providing this API, which both copies and flushes, libpmem is free to use
the most optimal way to perform both steps.
Separating the Flush Steps
Flushing to persistence involves two steps:
1. Flush the CPU caches or bypass them entirely as explained in the
previous example.
2. Wait for any hardware buffers to drain, to ensure writes have
reached the media.
These steps are performed together when pmem_persist() is called, or they can be
called individually by calling pmem_flush() for the first step and pmem_drain() for the
second. Note that either of these steps may be unnecessary on a given platform, and
the library knows how to check for that and do what is correct. For example, on Intel
platforms, pmem_drain is an empty function.
When does it make sense to break flushing into steps? The example in Listing 6-5
illustrates one reason you might want to do this. Since the example copies data using
multiple calls to memcpy(), it uses the version of libpmem copy (pmem_memcpy_nodrain())
that only performs the flush, postponing the final drain step to the end. This works
because, unlike the flush step, the drain step does not take an address range; it is a
system-wide drain operation so can happen at the end of the loop that copies individual
blocks of data.
Listing 6-5. full_copy.c: Separating the flush steps
58 / *
59 * do_copy_to_pmem
60 * /
61 static void
62 do_copy_to_pmem(char * pmemaddr, int srcfd, off_t len)
Chapter 6 libpmem: low-level persistent memory support
Notice how the is_pmem flag on line 96 is used just like it would be for calls to pmem_
persist(), since the pmem_memcpy_persist() function includes the flush to persistence.
The interface pmem_memcpy_persist() includes the flush to persistent because it
may determine that the copy is more optimally performed by using non-temporal stores,
which bypass the CPU cache and do not require subsequent cache flush instructions for
persistence. By providing this API, which both copies and flushes, libpmem is free to use
the most optimal way to perform both steps.
Separating the Flush Steps
Flushing to persistence involves two steps:
1. Flush the CPU caches or bypass them entirely as explained in the
previous example.
2. Wait for any hardware buffers to drain, to ensure writes have
reached the media.
These steps are performed together when pmem_persist() is called, or they can be
called individually by calling pmem_flush() for the first step and pmem_drain() for the
second. Note that either of these steps may be unnecessary on a given platform, and
the library knows how to check for that and do what is correct. For example, on Intel
platforms, pmem_drain is an empty function.
When does it make sense to break flushing into steps? The example in Listing 6-5
illustrates one reason you might want to do this. Since the example copies data using
multiple calls to memcpy(), it uses the version of libpmem copy (pmem_memcpy_nodrain())
that only performs the flush, postponing the final drain step to the end. This works
because, unlike the flush step, the drain step does not take an address range; it is a
system-wide drain operation so can happen at the end of the loop that copies individual
blocks of data.
Listing 6-5. full_copy.c: Separating the flush steps
58 / *
59 * do_copy_to_pmem
60 * /
61 static void
62 do_copy_to_pmem(char * pmemaddr, int srcfd, off_t len)
Chapter 6 libpmem: low-level persistent memory support
