65

I am working on a Linux module for IA64. My current problem is that the driver uses the PAGE_SIZE and PAGE_SHIFT macros for dma page allocation. The problem I am having is that the machine compiling the driver is not the ones that needed to run the driver. So, if the PAGE_SIZE on the compiling machine is 2^14K and the destination machine is 2^16K then the driver fails.

I don't want to turn this question into a 'best practice' issue about compiling modules on machines which are not the ones running the modules. I understand the issues about that. What I found is that people mostly uses getpagesize() or sysconf(_SC_PAGE_SIZE). These two options are out of the ia64 kernel headers so I can't use them. Is there another way that I could get the runtime PAGE_SIZE?

Options I am looking at:

  • Reading some file in /proc?
  • syscall?
  • Other function that let me calculate the PAGE_SIZE by inference (e.g ORDER, getpageshift, etc)?
  • Other?
2
  • Are you saying the PAGE_SIZE is configurable for the IA64 architecture, and not fixed? I thought PAGE_SIZE was fixed for a given architecture (e.g. always 4096 for x86). Commented Apr 10, 2013 at 0:54
  • IA64 does indeed support multiple page sizes: informit.com/articles/article.aspx?p=29961&seqNum=3 Commented Mar 24, 2015 at 1:59

8 Answers 8

85

Try using the getconf utility, which will allow you to retrieve the page size, in bytes, easily.

getconf PAGESIZE

Example output, in bytes:

4096
Sign up to request clarification or add additional context in comments.

3 Comments

I don't think this answers the OP's question, but it's helpful info since sysconf(_SC_PAGESIZE) appears to return 4K on linuxia64 (while mprotect fails on a page not aligned on a 16K boundary).
my x86-64 only returns 4k, but it has 4k, 2M, and 1G pages.
Works on my Ubuntu 18.04 machine, but not in an embedded Linux board I have. The embedded Linux board says: bash: getconf: command not found. Note that the embedded Linux board uses Busybox. This answer works on my embedded Linux board instead.
23

One approximate method is to read /proc/meminfo and check Mapped size ( on mine its 52544 kB as of now ) and then check nr_mapped in /proc/vmstat ( on mine its 131136 as of now ). Finally PAGE_SIZE = Mapped/nr_mapped. Sometimes this gives you an accurate value ( as in the current example I've quoted ) and sometimes its approximate but very close. Hope this helps!

4 Comments

I did not find Mapped/nr_mapped even close to what was provided by getconf PAGESIZE
@MCP On my machine the Mapped/nr_mapped = 379512KB/94878 = 4KB. And getconf PAGESIZE gives 4096. They do match. Could you share what you saw?
Assuming kB: echo "$(( $(grep Mapped: /proc/meminfo | awk '{ print $2 }') * 1024 / $(grep nr_mapped /proc/vmstat | awk '{ print $2 }') ))"
or you can just : grep -ir pagesize /proc/self/smaps
16

One way to find the page size is to obtain it from smaps for a process.

For example:

cd /proc/1
grep -i pagesize smaps

Output:

KernelPageSize:        4 kB
MMUPageSize:           4 kB

2 Comments

Curiously using your trick I see 4 kB on an ARM, but inside arch/arm/include/asm/page.h of the running kernel it's #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) and evidently it is 1024 Bytes when checking from my driver, as already suggested by the define page.h. What gives?
This does work on my embedded Linux board as well, unlike getconf PAGESIZE which does not. It also works on Ubuntu, but note that for the grep part of the command you must use sudo grep.
7

If you are trying to build a kernel module, you will need to have at least the kernel headers that are configured for the kernel the module will run on. Those will define the page size macros you need. If you don't have the correctly configured headers, the kernel will refuse to load your module.

And there is nothing wrong with compiling a module on one machine to run on another, even if it's a different architecture. You just need to build against the correct kernel source.

3 Comments

It is not an issue about kernel sources. It is about loading a module compiled on a machine, to run into another machine (Both have the same kernel version but configured differently). I don't want to re-compile against the new configuration because PAGE_SIZE changed. I am looking to get that parameter from the kernel as an API, not as a MACRO (which is solved at compile time).
<<And there is nothing wrong with compiling a module on one machine to run on another, even if it's a different architecture. You just need to build against the correct kernel source. ...........EXACTLY THE POINT
PAGE_SIZE is very fundamental and required for a module while building the module itself..
6

This is what I finally did:

  • Re-work my current module to take a new module parameter called page_shift and used that to calculate the PAGE_SIZE (PAGE_SIZE = 1 << PAGE_SHIFT)
  • Created a module loader wrapper which gets the current system PAGE_SHIFT using getconf API from libc. This wrapper gets the current system page shift and pass it as a module parameter.

Right now the module is being loaded on different architectures with different PAGE_SIZE without any problems.

1 Comment

Noob question. Could you explain a little more about wrapper which uses getconf API? I mean did you called getconf command inside popen() function or something. A little more light over it will be super appreciated.
0

I fear that it is impossible to do as page size is something defined as part of the kernel. page size knowledge is required in case of toolchain also which you use to compile the kernel module.

So atleast with current kernel architecture, it is impossible to do so.

Comments

0

You could just run a test, just mmap a file with different offsets and see which fail. Might be annoying in a kernel module though, but maybe there is some other test like that you could use.

Comments

0

In order to have the proper page size within the Linux kernel module code (if getconf PAGESIZE not wanted... ?) what you should do is to find the closest n^2 exponent from the meminfo and vmstat.

There's a proper way to achieve this, by using the logarithm from the bc command as follow:

  • retrieve the 'Mapped' kB value from /proc/meminfo:
  • find the base 2 exponent with the logarithm function from bc
  • round the returned value to the closest unit
_x=$(grep Mapped /proc/meminfo | head -n 1 | tr -s ' ' | cut -d ' ' -f 2);
_x=$(echo "l($_x)/l(2)" | bc -l | cut  -d '.' -f 1);
_x=$(printf "%.0f" "$_x");
echo "$_x";
18

Do the same thing for the nr_mapped value from /proc/vmstat:

_y=$(grep nr_mapped /proc/vmstat | cut -d ' ' -f 2);
_y=$(echo "l($_y)/l(2)" | bc -l | cut  -d '.' -f 1);
_y=$(printf "%.0f" "$_y");
echo "$_y";
16

At the end since you have both exponent for kB based values you can divide their end result and multiply by 1024 in order to have page size in bytes:

echo $((2**$_x/2**$_y*1024));
4096

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.