Skip to content
  • Ross Zwisler's avatar
    x86/asm: Add support for the pcommit instruction · 719d359d
    Ross Zwisler authored
    Add support for the new pcommit (persistent commit) instruction.
    This instruction was announced in the document "Intel
    Architecture Instruction Set Extensions Programming Reference"
    with reference number 319433-022:
    
      https://software.intel.com/sites/default/files/managed/0d/53/319433-022.pdf
    
    
    
    The pcommit instruction ensures that data that has been flushed
    from the processor's cache hierarchy with clwb, clflushopt or
    clflush is accepted to memory and is durable on the DIMM.  The
    primary use case for this is persistent memory.
    
    This function shows how to properly use clwb/clflushopt/clflush
    and pcommit with appropriate fencing:
    
    void flush_and_commit_buffer(void *vaddr, unsigned int size)
    {
    	void *vend = vaddr + size - 1;
    
    	for (; vaddr < vend; vaddr += boot_cpu_data.x86_clflush_size)
    		clwb(vaddr);
    
    	/* Flush any possible final partial cacheline */
    	clwb(vend);
    
    	/*
    	 * sfence to order clwb/clflushopt/clflush cache flushes
    	 * mfence via mb() also works
    	 */
    	wmb();
    
    	/* pcommit and the required sfence for ordering */
    	pcommit_sfence();
    }
    
    After this function completes the data pointed to by vaddr is
    has been accepted to memory and will be durable if the vaddr
    points to persistent memory.
    
    Pcommit must always be ordered by an mfence or sfence, so to
    help simplify things we include both the pcommit and the
    required sfence in the alternatives generated by
    pcommit_sfence().  The other option is to keep them separated,
    but on platforms that don't support pcommit this would then turn
    into:
    
    void flush_and_commit_buffer(void *vaddr, unsigned int size)
    {
            void *vend = vaddr + size - 1;
    
            for (; vaddr < vend; vaddr += boot_cpu_data.x86_clflush_size)
                    clwb(vaddr);
    
            /* Flush any possible final partial cacheline */
            clwb(vend);
    
            /*
             * sfence to order clwb/clflushopt/clflush cache flushes
             * mfence via mb() also works
             */
            wmb();
    
            nop(); /* from pcommit(), via alternatives */
    
            /*
             * sfence to order pcommit
             * mfence via mb() also works
             */
            wmb();
    }
    
    This is still correct, but now you've got two fences separated
    by only a nop.  With the commit and the fence together in
    pcommit_sfence() you avoid the final unneeded fence.
    
    Signed-off-by: default avatarRoss Zwisler <ross.zwisler@linux.intel.com>
    Acked-by: default avatarBorislav Petkov <bp@suse.de>
    Acked-by: default avatarH. Peter Anvin <hpa@linux.intel.com>
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Link: http://lkml.kernel.org/r/1424367448-24254-1-git-send-email-ross.zwisler@linux.intel.com
    
    
    Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
    719d359d