[API-NEXT,PATCHv2,1/2] linux-gen: _ishmphy: adding function for physical address query

Message ID 1478875829-35000-2-git-send-email-christophe.milard@linaro.org
State New
Headers show

Commit Message

Christophe Milard Nov. 11, 2016, 2:50 p.m.
The function _odp_ishmphy_virt_to_phys() is added to query for physical
addresses (given a virtual address)
This function is meant to be used to populate the physical address
of packet segments which are to be used by drivers (without iommu).
The function _odp_ishmphy_can_virt_to_phys(), also added, return true if
_odp_ishmphy_virt_to_phys() is able to works (as it requires specific
permission)

Signed-off-by: Christophe Milard <christophe.milard@linaro.org>

---
 platform/linux-generic/_ishmphy.c                  | 82 ++++++++++++++++++++++
 platform/linux-generic/include/_ishmphy_internal.h | 14 ++++
 2 files changed, 96 insertions(+)

-- 
2.7.4

Comments

Yi He Nov. 14, 2016, 5 a.m. | #1
One comment inline:

On 11 November 2016 at 22:50, Christophe Milard <
christophe.milard@linaro.org> wrote:

> The function _odp_ishmphy_virt_to_phys() is added to query for physical

> addresses (given a virtual address)

> This function is meant to be used to populate the physical address

> of packet segments which are to be used by drivers (without iommu).

> The function _odp_ishmphy_can_virt_to_phys(), also added, return true if

> _odp_ishmphy_virt_to_phys() is able to works (as it requires specific

> permission)

>

> Signed-off-by: Christophe Milard <christophe.milard@linaro.org>

> ---

>  platform/linux-generic/_ishmphy.c                  | 82

> ++++++++++++++++++++++

>  platform/linux-generic/include/_ishmphy_internal.h | 14 ++++

>  2 files changed, 96 insertions(+)

>

> diff --git a/platform/linux-generic/_ishmphy.c b/platform/linux-generic/_

> ishmphy.c

> index 2b2d100..8c0f46e 100644

> --- a/platform/linux-generic/_ishmphy.c

> +++ b/platform/linux-generic/_ishmphy.c

> @@ -29,6 +29,7 @@

>  #include <fcntl.h>

>  #include <sys/types.h>

>  #include <sys/wait.h>

> +#include <inttypes.h>

>  #include <_ishmphy_internal.h>

>

>  static void *common_va_address;

> @@ -38,6 +39,8 @@ static uint64_t common_va_len;

>  #define MAP_ANONYMOUS MAP_ANON

>  #endif

>

> +#define PAGEMAP_FILE "/proc/self/pagemap"

> +

>  /* Book some virtual address space

>   * This function is called at odp_init_global() time to pre-book some

>   * virtual address space inherited by all odpthreads (i.e. descendant

> @@ -183,3 +186,82 @@ int _odp_ishmphy_unmap(void *start, uint64_t len, int

> flags)

>                 ODP_ERR("_ishmphy_free failure: %s\n", strerror(errno));

>         return ret;

>  }

> +

> +/*

> + * Get physical address from virtual address addr.

> + */

>


I saw DPDK API commented that "The page must be locked", and provides an
API rte_mem_lock_page to do so, in this API is the locking guarenteed by
like _odp_ishm_reserve calls already?

+phys_addr_t _odp_ishmphy_virt_to_phys(const void *addr)
> +{

> +       int page_sz;

> +       int fd;

> +       off_t offset;

> +       int  read_bytes;

> +       uint64_t page;

> +       phys_addr_t phys_addr;

> +

> +       /* get normal page sizes: */

> +       page_sz = odp_sys_page_size();

> +

> +       /* read 8 bytes (uint64_t) at position N*8, where N is

> addr/page_sz */

> +       fd = open(PAGEMAP_FILE, O_RDONLY);

> +       if (fd < 0) {

> +               ODP_ERR("cannot open " PAGEMAP_FILE ": %s\n",

> +                       strerror(errno));

> +               return PHYS_ADDR_INVALID;

> +       }

> +

> +       offset = ((unsigned long)addr / page_sz) * sizeof(uint64_t);

> +       if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {

> +               ODP_ERR("cannot seek " PAGEMAP_FILE ": %s\n",

> +                       strerror(errno));

> +               close(fd);

> +               return PHYS_ADDR_INVALID;

> +       }

> +

> +       read_bytes = read(fd, &page, sizeof(uint64_t));

> +       close(fd);

> +       if (read_bytes < 0) {

> +               ODP_ERR("cannot read " PAGEMAP_FILE ": %s\n",

> +                       strerror(errno));

> +               return PHYS_ADDR_INVALID;

> +       } else if (read_bytes != sizeof(uint64_t)) {

> +               ODP_ERR("read %d bytes from " PAGEMAP_FILE " "

> +                       "but expected %d:\n",

> +                       read_bytes, sizeof(uint64_t));

> +               return PHYS_ADDR_INVALID;

> +       }

> +

> +       /* some kernel return PFN zero when permission is denied: */

> +       if (!(page & 0x7fffffffffffffULL))

> +               return PHYS_ADDR_INVALID;

> +

> +       /*

> +        * the pfn (page frame number) are bits 0-54 (see

> +        * pagemap.txt in linux Documentation)

> +        */

> +       phys_addr = ((page & 0x7fffffffffffffULL) * page_sz)

> +               + ((unsigned long)addr % page_sz);

> +

> +       return phys_addr;

> +}

> +

> +/*

> + * check if physical address are readable

> + * return true if physical addresses can be retrieved.

> + * Just do a test to see if it works

> + */

> +int _odp_ishmphy_can_virt_to_phys(void)

> +{

> +       int block_index;

> +       phys_addr_t phy;

> +

> +       /* allocate a block, locked in memory and try to grab its phy

> address */

> +       block_index = _odp_ishm_reserve(NULL, 10, -1, 0, _ODP_ISHM_LOCK,

> 0);

> +       phy = _odp_ishmphy_virt_to_phys((void *)_odp_ishm_address(block_

> index));

> +       _odp_ishm_free_by_index(block_index);

> +

> +       if (phy == PHYS_ADDR_INVALID)

> +               return 0;

> +

> +       return 1;

> +}

> diff --git a/platform/linux-generic/include/_ishmphy_internal.h

> b/platform/linux-generic/include/_ishmphy_internal.h

> index 4fe560f..2022590 100644

> --- a/platform/linux-generic/include/_ishmphy_internal.h

> +++ b/platform/linux-generic/include/_ishmphy_internal.h

> @@ -13,11 +13,25 @@ extern "C" {

>

>  #include <stdint.h>

>

> +typedef uint64_t phys_addr_t; /* Physical address definition. */

> +#define PHYS_ADDR_INVALID ((phys_addr_t)-1)

> +

>  void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align);

>  int   _odp_ishmphy_unbook_va(void);

>  void *_odp_ishmphy_map(int fd, void *start, uint64_t size, int flags);

>  int   _odp_ishmphy_unmap(void *start, uint64_t len, int flags);

>

> +/*

> + * check if physical address are readable

> + * return true if physical addresses can be retrieved.

> + */

> +int _odp_ishmphy_can_virt_to_phys(void);

> +

> +/*

> + * Get physical address from virtual address addr.

> + */

> +phys_addr_t _odp_ishmphy_virt_to_phys(const void *addr);

> +

>  #ifdef __cplusplus

>  }

>  #endif

> --

> 2.7.4

>

>
Christophe Milard Nov. 14, 2016, 7:26 a.m. | #2
Th page must be locked, indeed: if the page is swapped out its
physical address does not exist...

_ishm (the internal ODP memory allocator has a flag to lock memory:
_ODP_ISHM_LOCK

On the north API (odp_*), any memory reserved is locked anyway. This
was a requirement from Petri: that is any  odp_shm_reserve() will get
locked page. Assuming we are using the _ishm memory allocator on the
north interface (this patch serie is still waiting for Petri's
blessing)

On the south interface (odpdrv_*) both the driver still has the
choice, through a flag: ODPDRV_SHM_LOCK
ODPDRV_SHM_LOCK maps to the _ishm _ODP_ISHM_LOCK flag.

Christophe

On 14 November 2016 at 06:00, Yi He <yi.he@linaro.org> wrote:
> One comment inline:

>

> On 11 November 2016 at 22:50, Christophe Milard

> <christophe.milard@linaro.org> wrote:

>>

>> The function _odp_ishmphy_virt_to_phys() is added to query for physical

>> addresses (given a virtual address)

>> This function is meant to be used to populate the physical address

>> of packet segments which are to be used by drivers (without iommu).

>> The function _odp_ishmphy_can_virt_to_phys(), also added, return true if

>> _odp_ishmphy_virt_to_phys() is able to works (as it requires specific

>> permission)

>>

>> Signed-off-by: Christophe Milard <christophe.milard@linaro.org>

>> ---

>>  platform/linux-generic/_ishmphy.c                  | 82

>> ++++++++++++++++++++++

>>  platform/linux-generic/include/_ishmphy_internal.h | 14 ++++

>>  2 files changed, 96 insertions(+)

>>

>> diff --git a/platform/linux-generic/_ishmphy.c

>> b/platform/linux-generic/_ishmphy.c

>> index 2b2d100..8c0f46e 100644

>> --- a/platform/linux-generic/_ishmphy.c

>> +++ b/platform/linux-generic/_ishmphy.c

>> @@ -29,6 +29,7 @@

>>  #include <fcntl.h>

>>  #include <sys/types.h>

>>  #include <sys/wait.h>

>> +#include <inttypes.h>

>>  #include <_ishmphy_internal.h>

>>

>>  static void *common_va_address;

>> @@ -38,6 +39,8 @@ static uint64_t common_va_len;

>>  #define MAP_ANONYMOUS MAP_ANON

>>  #endif

>>

>> +#define PAGEMAP_FILE "/proc/self/pagemap"

>> +

>>  /* Book some virtual address space

>>   * This function is called at odp_init_global() time to pre-book some

>>   * virtual address space inherited by all odpthreads (i.e. descendant

>> @@ -183,3 +186,82 @@ int _odp_ishmphy_unmap(void *start, uint64_t len, int

>> flags)

>>                 ODP_ERR("_ishmphy_free failure: %s\n", strerror(errno));

>>         return ret;

>>  }

>> +

>> +/*

>> + * Get physical address from virtual address addr.

>> + */

>

>

> I saw DPDK API commented that "The page must be locked", and provides an API

> rte_mem_lock_page to do so, in this API is the locking guarenteed by like

> _odp_ishm_reserve calls already?

>

>> +phys_addr_t _odp_ishmphy_virt_to_phys(const void *addr)

>> +{

>> +       int page_sz;

>> +       int fd;

>> +       off_t offset;

>> +       int  read_bytes;

>> +       uint64_t page;

>> +       phys_addr_t phys_addr;

>> +

>> +       /* get normal page sizes: */

>> +       page_sz = odp_sys_page_size();

>> +

>> +       /* read 8 bytes (uint64_t) at position N*8, where N is

>> addr/page_sz */

>> +       fd = open(PAGEMAP_FILE, O_RDONLY);

>> +       if (fd < 0) {

>> +               ODP_ERR("cannot open " PAGEMAP_FILE ": %s\n",

>> +                       strerror(errno));

>> +               return PHYS_ADDR_INVALID;

>> +       }

>> +

>> +       offset = ((unsigned long)addr / page_sz) * sizeof(uint64_t);

>> +       if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {

>> +               ODP_ERR("cannot seek " PAGEMAP_FILE ": %s\n",

>> +                       strerror(errno));

>> +               close(fd);

>> +               return PHYS_ADDR_INVALID;

>> +       }

>> +

>> +       read_bytes = read(fd, &page, sizeof(uint64_t));

>> +       close(fd);

>> +       if (read_bytes < 0) {

>> +               ODP_ERR("cannot read " PAGEMAP_FILE ": %s\n",

>> +                       strerror(errno));

>> +               return PHYS_ADDR_INVALID;

>> +       } else if (read_bytes != sizeof(uint64_t)) {

>> +               ODP_ERR("read %d bytes from " PAGEMAP_FILE " "

>> +                       "but expected %d:\n",

>> +                       read_bytes, sizeof(uint64_t));

>> +               return PHYS_ADDR_INVALID;

>> +       }

>> +

>> +       /* some kernel return PFN zero when permission is denied: */

>> +       if (!(page & 0x7fffffffffffffULL))

>> +               return PHYS_ADDR_INVALID;

>> +

>> +       /*

>> +        * the pfn (page frame number) are bits 0-54 (see

>> +        * pagemap.txt in linux Documentation)

>> +        */

>> +       phys_addr = ((page & 0x7fffffffffffffULL) * page_sz)

>> +               + ((unsigned long)addr % page_sz);

>> +

>> +       return phys_addr;

>> +}

>> +

>> +/*

>> + * check if physical address are readable

>> + * return true if physical addresses can be retrieved.

>> + * Just do a test to see if it works

>> + */

>> +int _odp_ishmphy_can_virt_to_phys(void)

>> +{

>> +       int block_index;

>> +       phys_addr_t phy;

>> +

>> +       /* allocate a block, locked in memory and try to grab its phy

>> address */

>> +       block_index = _odp_ishm_reserve(NULL, 10, -1, 0, _ODP_ISHM_LOCK,

>> 0);

>> +       phy = _odp_ishmphy_virt_to_phys((void

>> *)_odp_ishm_address(block_index));

>> +       _odp_ishm_free_by_index(block_index);

>> +

>> +       if (phy == PHYS_ADDR_INVALID)

>> +               return 0;

>> +

>> +       return 1;

>> +}

>> diff --git a/platform/linux-generic/include/_ishmphy_internal.h

>> b/platform/linux-generic/include/_ishmphy_internal.h

>> index 4fe560f..2022590 100644

>> --- a/platform/linux-generic/include/_ishmphy_internal.h

>> +++ b/platform/linux-generic/include/_ishmphy_internal.h

>> @@ -13,11 +13,25 @@ extern "C" {

>>

>>  #include <stdint.h>

>>

>> +typedef uint64_t phys_addr_t; /* Physical address definition. */

>> +#define PHYS_ADDR_INVALID ((phys_addr_t)-1)

>> +

>>  void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align);

>>  int   _odp_ishmphy_unbook_va(void);

>>  void *_odp_ishmphy_map(int fd, void *start, uint64_t size, int flags);

>>  int   _odp_ishmphy_unmap(void *start, uint64_t len, int flags);

>>

>> +/*

>> + * check if physical address are readable

>> + * return true if physical addresses can be retrieved.

>> + */

>> +int _odp_ishmphy_can_virt_to_phys(void);

>> +

>> +/*

>> + * Get physical address from virtual address addr.

>> + */

>> +phys_addr_t _odp_ishmphy_virt_to_phys(const void *addr);

>> +

>>  #ifdef __cplusplus

>>  }

>>  #endif

>> --

>> 2.7.4

>>

>

Patch

diff --git a/platform/linux-generic/_ishmphy.c b/platform/linux-generic/_ishmphy.c
index 2b2d100..8c0f46e 100644
--- a/platform/linux-generic/_ishmphy.c
+++ b/platform/linux-generic/_ishmphy.c
@@ -29,6 +29,7 @@ 
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <inttypes.h>
 #include <_ishmphy_internal.h>
 
 static void *common_va_address;
@@ -38,6 +39,8 @@  static uint64_t common_va_len;
 #define MAP_ANONYMOUS MAP_ANON
 #endif
 
+#define PAGEMAP_FILE "/proc/self/pagemap"
+
 /* Book some virtual address space
  * This function is called at odp_init_global() time to pre-book some
  * virtual address space inherited by all odpthreads (i.e. descendant
@@ -183,3 +186,82 @@  int _odp_ishmphy_unmap(void *start, uint64_t len, int flags)
 		ODP_ERR("_ishmphy_free failure: %s\n", strerror(errno));
 	return ret;
 }
+
+/*
+ * Get physical address from virtual address addr.
+ */
+phys_addr_t _odp_ishmphy_virt_to_phys(const void *addr)
+{
+	int page_sz;
+	int fd;
+	off_t offset;
+	int  read_bytes;
+	uint64_t page;
+	phys_addr_t phys_addr;
+
+	/* get normal page sizes: */
+	page_sz = odp_sys_page_size();
+
+	/* read 8 bytes (uint64_t) at position N*8, where N is addr/page_sz */
+	fd = open(PAGEMAP_FILE, O_RDONLY);
+	if (fd < 0) {
+		ODP_ERR("cannot open " PAGEMAP_FILE ": %s\n",
+			strerror(errno));
+		return PHYS_ADDR_INVALID;
+	}
+
+	offset = ((unsigned long)addr / page_sz) * sizeof(uint64_t);
+	if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
+		ODP_ERR("cannot seek " PAGEMAP_FILE ": %s\n",
+			strerror(errno));
+		close(fd);
+		return PHYS_ADDR_INVALID;
+	}
+
+	read_bytes = read(fd, &page, sizeof(uint64_t));
+	close(fd);
+	if (read_bytes < 0) {
+		ODP_ERR("cannot read " PAGEMAP_FILE ": %s\n",
+			strerror(errno));
+		return PHYS_ADDR_INVALID;
+	} else if (read_bytes != sizeof(uint64_t)) {
+		ODP_ERR("read %d bytes from " PAGEMAP_FILE " "
+			"but expected %d:\n",
+			read_bytes, sizeof(uint64_t));
+		return PHYS_ADDR_INVALID;
+	}
+
+	/* some kernel return PFN zero when permission is denied: */
+	if (!(page & 0x7fffffffffffffULL))
+		return PHYS_ADDR_INVALID;
+
+	/*
+	 * the pfn (page frame number) are bits 0-54 (see
+	 * pagemap.txt in linux Documentation)
+	 */
+	phys_addr = ((page & 0x7fffffffffffffULL) * page_sz)
+		+ ((unsigned long)addr % page_sz);
+
+	return phys_addr;
+}
+
+/*
+ * check if physical address are readable
+ * return true if physical addresses can be retrieved.
+ * Just do a test to see if it works
+ */
+int _odp_ishmphy_can_virt_to_phys(void)
+{
+	int block_index;
+	phys_addr_t phy;
+
+	/* allocate a block, locked in memory and try to grab its phy address */
+	block_index = _odp_ishm_reserve(NULL, 10, -1, 0, _ODP_ISHM_LOCK, 0);
+	phy = _odp_ishmphy_virt_to_phys((void *)_odp_ishm_address(block_index));
+	_odp_ishm_free_by_index(block_index);
+
+	if (phy == PHYS_ADDR_INVALID)
+		return 0;
+
+	return 1;
+}
diff --git a/platform/linux-generic/include/_ishmphy_internal.h b/platform/linux-generic/include/_ishmphy_internal.h
index 4fe560f..2022590 100644
--- a/platform/linux-generic/include/_ishmphy_internal.h
+++ b/platform/linux-generic/include/_ishmphy_internal.h
@@ -13,11 +13,25 @@  extern "C" {
 
 #include <stdint.h>
 
+typedef uint64_t phys_addr_t; /* Physical address definition. */
+#define PHYS_ADDR_INVALID ((phys_addr_t)-1)
+
 void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align);
 int   _odp_ishmphy_unbook_va(void);
 void *_odp_ishmphy_map(int fd, void *start, uint64_t size, int flags);
 int   _odp_ishmphy_unmap(void *start, uint64_t len, int flags);
 
+/*
+ * check if physical address are readable
+ * return true if physical addresses can be retrieved.
+ */
+int _odp_ishmphy_can_virt_to_phys(void);
+
+/*
+ * Get physical address from virtual address addr.
+ */
+phys_addr_t _odp_ishmphy_virt_to_phys(const void *addr);
+
 #ifdef __cplusplus
 }
 #endif