diff mbox

[V8,1/6] LIBIO: Introduce a generic PIO mapping method

Message ID 1490887619-61732-2-git-send-email-yuanzhichang@hisilicon.com
State Not Applicable
Headers show

Commit Message

Zhichang Yuan March 30, 2017, 3:26 p.m. UTC
In commit 41f8bba7f55(of/pci: Add pci_register_io_range() and
pci_pio_to_address()), a new I/O space management was supported. With that
driver, the I/O ranges configured for PCI/PCIE hosts on some architectures can
be mapped to logical PIO, converted easily between CPU address and the
corresponding logicial PIO. Based on this, PCI I/O devices can be accessed in a
memory read/write way through the unified in/out accessors.

But on some archs/platforms, there are bus hosts which access I/O peripherals
with host-local I/O port addresses rather than memory addresses after
memory-mapped.
To support those devices, a more generic I/O mapping method is introduced here.
Through this patch, both the CPU addresses and the host-local port can be
mapped into the logical PIO space with different logical/fake PIOs. After this,
all the I/O accesses to either PCI MMIO devices or host-local I/O peripherals
can be unified into the existing I/O accessors defined in asm-generic/io.h and
be redirected to the right device-specific hooks based on the input logical PIO.

Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
---
 include/asm-generic/io.h  |  50 ++++++
 include/linux/logic_pio.h | 174 +++++++++++++++++++
 lib/Kconfig               |  26 +++
 lib/Makefile              |   2 +
 lib/logic_pio.c           | 413 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 665 insertions(+)
 create mode 100644 include/linux/logic_pio.h
 create mode 100644 lib/logic_pio.c

Comments

kernel test robot April 1, 2017, 5:58 a.m. UTC | #1
Hi zhichang.yuan,

[auto build test ERROR on linus/master]
[also build test ERROR on v4.11-rc4 next-20170331]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/zhichang-yuan/LIBIO-Introduce-a-generic-PIO-mapping-method/20170401-104801
config: alpha-allyesconfig (attached as .config)
compiler: alpha-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=alpha 

All error/warnings (new ones prefixed by >>):

>> lib/logic_pio.c:32:50: error: 'PIO_MAX_SECT' undeclared here (not in a function)
    static struct logic_pio_root logic_pio_root_list[PIO_MAX_SECT] = {
                                                     ^~~~~~~~~~~~
>> lib/logic_pio.c:39:3: error: 'PIO_CPU_MMIO' undeclared here (not in a function)
     [PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
      ^~~~~~~~~~~~
>> lib/logic_pio.c:39:20: error: 'PIO_INDIRECT' undeclared here (not in a function)
     [PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
                       ^~~~~~~~~~~~
>> lib/logic_pio.c:39:3: error: array index in initializer not of integer type
     [PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
      ^~~~~~~~~~~~
   lib/logic_pio.c:39:3: note: (near initialization for 'logic_pio_root_list')
>> lib/logic_pio.c:40:3: error: field name not in record or union initializer
      .sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
      ^
   lib/logic_pio.c:40:3: note: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:41:3: error: field name not in record or union initializer
      .sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
      ^
   lib/logic_pio.c:41:3: note: (near initialization for 'logic_pio_root_list')
>> lib/logic_pio.c:41:14: error: implicit declaration of function 'PIO_SECT_MIN' [-Werror=implicit-function-declaration]
      .sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
                 ^~~~~~~~~~~~
   lib/logic_pio.c:42:3: error: field name not in record or union initializer
      .sec_max = PIO_SECT_MAX(PIO_INDIRECT - 1),
      ^
   lib/logic_pio.c:42:3: note: (near initialization for 'logic_pio_root_list')
>> lib/logic_pio.c:42:14: error: implicit declaration of function 'PIO_SECT_MAX' [-Werror=implicit-function-declaration]
      .sec_max = PIO_SECT_MAX(PIO_INDIRECT - 1),
                 ^~~~~~~~~~~~
   lib/logic_pio.c:46:3: error: array index in initializer not of integer type
     [PIO_INDIRECT] = {
      ^~~~~~~~~~~~
   lib/logic_pio.c:46:3: note: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:47:3: error: field name not in record or union initializer
      .sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_INDIRECT].sec_head),
      ^
   lib/logic_pio.c:47:3: note: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:48:3: error: field name not in record or union initializer
      .sec_min = PIO_SECT_MIN(PIO_INDIRECT),
      ^
   lib/logic_pio.c:48:3: note: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:49:3: error: field name not in record or union initializer
      .sec_max = PIO_SECT_MAX(PIO_INDIRECT),
      ^
   lib/logic_pio.c:49:3: note: (near initialization for 'logic_pio_root_list')
   In file included from include/linux/list.h:8:0,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
   lib/logic_pio.c: In function 'logic_pio_find_range_byaddr':
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type 'struct logic_pio_hwaddr'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                     
   include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                     ^~~~
>> include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^~~~~~~~~~~~~~
>> lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^~~~~~~~~~~~
>> include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^~~~~~~~~~~~~~
>> lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^~~~~~~~~~~~~~~~~~~~~~~
>> include/linux/kernel.h:852:48: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^~~~~~~~~~~~
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^~~~~~~~~~~~~~
>> lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^~~~~~~~~~~~~~~~~~~~~~~
   lib/logic_pio.c: In function 'logic_pio_alloc_range':
>> lib/logic_pio.c:109:19: error: dereferencing pointer to incomplete type 'struct logic_pio_root'
     idle_start = root->sec_min;
                      ^~
   In file included from include/linux/list.h:8:0,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type 'struct logic_pio_sect'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                     
   include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                     ^~~~
>> include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^~~~~~~~~~~~~~
   lib/logic_pio.c:111:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(entry, &root->sec_head, list) {
     ^~~~~~~~~~~~~~~~~~~~~~~
>> include/linux/kernel.h:852:48: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^

vim +/PIO_MAX_SECT +32 lib/logic_pio.c

    12	 * GNU General Public License for more details.
    13	 *
    14	 * You should have received a copy of the GNU General Public License
    15	 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16	 */
    17	
  > 18	#include <linux/of.h>
    19	#include <linux/io.h>
    20	#include <linux/mm.h>
    21	#include <linux/rculist.h>
    22	#include <linux/sizes.h>
    23	#include <linux/slab.h>
    24	
    25	/* The unique hardware address list. */
    26	static LIST_HEAD(io_range_list);
    27	static DEFINE_MUTEX(io_range_mutex);
    28	
    29	/*
    30	 * These are the lists for PIO. The highest PIO_SECT_BITS of PIO is the index.
    31	 */
  > 32	static struct logic_pio_root logic_pio_root_list[PIO_MAX_SECT] = {
    33	#ifdef CONFIG_INDIRECT_PIO
    34		/*
    35		 * At this moment, assign all the other logic PIO space to MMIO.
    36		 * If more elements added, please adjust the ending index and .sec_max;
    37		 * Please keep MMIO element started from index ZERO.
    38		 */
  > 39		[PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
  > 40			.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
  > 41			.sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
  > 42			.sec_max = PIO_SECT_MAX(PIO_INDIRECT - 1),
    43		},
    44	
    45		/* The last element */
    46		[PIO_INDIRECT] = {
    47			.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_INDIRECT].sec_head),
    48			.sec_min = PIO_SECT_MIN(PIO_INDIRECT),
    49			.sec_max = PIO_SECT_MAX(PIO_INDIRECT),
    50		},
    51	#else
    52		[PIO_CPU_MMIO] = {
    53			.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
    54			.sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
    55			.sec_max = PIO_SECT_MAX(PIO_CPU_MMIO),
    56		},
    57	
    58	#endif
    59	};
    60	
    61	/*
    62	 * Search a io_range registered which match the fwnode and addr.
    63	 *
    64	 * @fwnode: the host fwnode which must be valid;
    65	 * @start: the start hardware address of this search;
    66	 * @end: the end hardware address of this search. can be equal to @start;
    67	 *
    68	 * return NULL when there is no matched node; IS_ERR() means ERROR;
    69	 * valid virtual address represent a matched node was found.
    70	 */
    71	static struct logic_pio_hwaddr *
    72	logic_pio_find_range_byaddr(struct fwnode_handle *fwnode,
    73				resource_size_t start, resource_size_t end)
    74	{
    75		struct logic_pio_hwaddr *range;
    76	
  > 77		list_for_each_entry_rcu(range, &io_range_list, list) {
    78			if (!range->pio_peer) {
    79				pr_warn("Invalid cpu addr node(%pa) in list!\n",
    80					&range->hw_start);
    81				continue;
    82			}
    83			if (range->fwnode != fwnode)
    84				continue;
    85			/* without any overlap with current range */
    86			if (start >= range->hw_start + range->size ||
    87				end < range->hw_start)
    88				continue;
    89			/* overlap is not supported now. */
    90			if (start < range->hw_start ||
    91				end >= range->hw_start + range->size)
    92				return ERR_PTR(-EBUSY);
    93			/* had been registered. */
    94			return range;
    95		}
    96	
    97		return NULL;
    98	}
    99	
   100	
   101	static int logic_pio_alloc_range(struct logic_pio_root *root,
   102			resource_size_t size, unsigned long align,
   103			struct list_head **prev, resource_size_t *pio_alloc)
   104	{
   105		struct logic_pio_sect *entry;
   106		resource_size_t tmp_start;
   107		resource_size_t idle_start, idle_end;
   108	
 > 109		idle_start = root->sec_min;
   110		*prev = &root->sec_head;
   111		list_for_each_entry_rcu(entry, &root->sec_head, list) {
   112			if (!entry->hwpeer ||
   113				idle_start > entry->io_start) {
   114				WARN(1, "skip an invalid io range during traversal!\n");
   115				goto nextentry;
   116			}
   117			/* set the end edge. */
   118			if (idle_start == entry->io_start) {
   119				struct logic_pio_sect *next;
   120	
   121				idle_start = entry->io_start + entry->hwpeer->size;
 > 122				next = list_next_or_null_rcu(&root->sec_head,
 > 123					&entry->list, struct logic_pio_sect, list);
   124				if (next) {
   125					entry = next;
   126				} else {
   127					*prev = &entry->list;
   128					break;
   129				}
   130			}
   131			idle_end = entry->io_start - 1;
   132	
   133			/* contiguous range... */
   134			if (idle_start > idle_end)
   135				goto nextentry;
   136	
   137			tmp_start = idle_start;
   138			idle_start = ALIGN(idle_start, align);
   139			if (idle_start >= tmp_start &&
   140				idle_start + size <= idle_end) {
   141				*prev = &entry->list;
   142				*pio_alloc = idle_start;
   143				return 0;
   144			}
   145	
   146	nextentry:
   147			idle_start = entry->io_start + entry->hwpeer->size;
   148			*prev = &entry->list;
   149		}
   150		/* check the last free gap... */
   151		idle_end = root->sec_max;
   152	
   153		tmp_start = idle_start;
   154		idle_start = ALIGN(idle_start, align);
   155		if (idle_start >= tmp_start &&
   156			idle_start + size <= idle_end) {
   157			*pio_alloc = idle_start;
   158			return 0;
   159		}
   160	
   161		return -EBUSY;
   162	}
   163	
   164	/*
   165	 * register a io range node in the io range list.
   166	 *
   167	 * @newrange: pointer to the io range to be registered.
   168	 *
   169	 * return 'newrange' when success, ERR_VALUE() is for failures.
   170	 * specially, return a valid pointer which is not equal to 'newrange' when
   171	 * the io range had been registered before.
   172	 */
   173	struct logic_pio_hwaddr
   174	*logic_pio_register_range(struct logic_pio_hwaddr *newrange,
   175			unsigned long align)
   176	{
   177		struct logic_pio_hwaddr *range;
   178		struct logic_pio_sect *newsect;
   179		resource_size_t pio_alloc;
   180		struct list_head *prev, *hwprev;
   181		unsigned long sect_id;
   182		int err;
   183	
   184		if (!newrange || !newrange->fwnode || !newrange->size)
   185			return ERR_PTR(-EINVAL);
   186	
   187		sect_id = newrange->flags;
   188		if (sect_id >= PIO_MAX_SECT)
   189			return ERR_PTR(-EINVAL);
   190	
   191		mutex_lock(&io_range_mutex);
   192		range = logic_pio_find_range_byaddr(newrange->fwnode,
   193				newrange->hw_start,
   194				newrange->hw_start + newrange->size - 1);
   195		if (range) {
   196			if (!IS_ERR(range))
   197				pr_info("the request IO range had been registered!\n");
   198			else
   199				pr_err("registering IO[%pa - sz%pa) got failed!\n",
   200					&newrange->hw_start, &newrange->size);
   201			mutex_unlock(&io_range_mutex);
   202			return range;
   203		}
   204	
   205		err = logic_pio_alloc_range(&logic_pio_root_list[sect_id],
   206				newrange->size, align, &prev, &pio_alloc);
   207		if (err) {
   208			pr_err("can't find free %pa logical IO range!\n",
   209				&newrange->size);
   210			goto exitproc;
   211		}
   212	
   213		if (prev == &logic_pio_root_list[sect_id].sec_head) {
   214			hwprev = &io_range_list;
   215		} else {
 > 216			newsect = to_pio_sect(prev);
 > 217			hwprev = &newsect->hwpeer->list;
   218		}
   219	
   220		newsect = kzalloc(sizeof(*newsect), GFP_KERNEL);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot April 1, 2017, 6:31 a.m. UTC | #2
Hi zhichang.yuan,

[auto build test ERROR on linus/master]
[also build test ERROR on v4.11-rc4 next-20170331]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/zhichang-yuan/LIBIO-Introduce-a-generic-PIO-mapping-method/20170401-104801
config: m68k-m5475evb_defconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 4.9.0
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=m68k 

All error/warnings (new ones prefixed by >>):

   lib/logic_pio.c:32:50: error: 'PIO_MAX_SECT' undeclared here (not in a function)
    static struct logic_pio_root logic_pio_root_list[PIO_MAX_SECT] = {
                                                     ^
   lib/logic_pio.c:52:3: error: 'PIO_CPU_MMIO' undeclared here (not in a function)
     [PIO_CPU_MMIO] = {
      ^
   lib/logic_pio.c:52:2: error: array index in initializer not of integer type
     [PIO_CPU_MMIO] = {
     ^
   lib/logic_pio.c:52:2: error: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:53:3: error: field name not in record or union initializer
      .sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
      ^
   lib/logic_pio.c:53:3: error: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:54:3: error: field name not in record or union initializer
      .sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
      ^
   lib/logic_pio.c:54:3: error: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:54:3: error: implicit declaration of function 'PIO_SECT_MIN' [-Werror=implicit-function-declaration]
   lib/logic_pio.c:55:3: error: field name not in record or union initializer
      .sec_max = PIO_SECT_MAX(PIO_CPU_MMIO),
      ^
   lib/logic_pio.c:55:3: error: (near initialization for 'logic_pio_root_list')
   lib/logic_pio.c:55:3: error: implicit declaration of function 'PIO_SECT_MAX' [-Werror=implicit-function-declaration]
   In file included from include/linux/list.h:8:0,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
   lib/logic_pio.c: In function 'logic_pio_find_range_byaddr':
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                    ^
   include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                     ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/kernel.h:852:48: warning: initialization from incompatible pointer type
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                    ^
   include/linux/kernel.h:853:3: note: in definition of macro 'container_of'
     (type *)( (char *)__mptr - offsetof(type,member) );})
      ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   In file included from include/linux/compiler.h:62:0,
                    from include/uapi/linux/stddef.h:1,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/of.h:18,
                    from lib/logic_pio.c:18:
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                    ^
   include/linux/compiler-gcc.h:161:21: note: in definition of macro '__compiler_offsetof'
     __builtin_offsetof(a, b)
                        ^
   include/linux/kernel.h:853:29: note: in expansion of macro 'offsetof'
     (type *)( (char *)__mptr - offsetof(type,member) );})
                                ^
   include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   In file included from include/linux/pid.h:4:0,
                    from include/linux/sched.h:13,
                    from arch/m68k/include/asm/pgtable_mm.h:10,
                    from arch/m68k/include/asm/pgtable.h:4,
                    from include/linux/mm.h:68,
                    from lib/logic_pio.c:20:
   include/linux/rculist.h:352:7: error: dereferencing pointer to incomplete type
      &pos->member != (head); \
          ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   In file included from include/linux/list.h:8:0,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
   include/linux/rculist.h:353:49: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                                                    ^
   include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                     ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:27: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                              ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/kernel.h:852:48: warning: initialization makes pointer from integer without a cast
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   include/linux/rculist.h:353:49: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                                                    ^
   include/linux/kernel.h:853:3: note: in definition of macro 'container_of'
     (type *)( (char *)__mptr - offsetof(type,member) );})
      ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
   In file included from include/linux/compiler.h:62:0,
                    from include/uapi/linux/stddef.h:1,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/of.h:18,
                    from lib/logic_pio.c:18:
   include/linux/rculist.h:353:49: error: dereferencing pointer to incomplete type
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
                                                    ^
   include/linux/compiler-gcc.h:161:21: note: in definition of macro '__compiler_offsetof'
     __builtin_offsetof(a, b)
                        ^
   include/linux/kernel.h:853:29: note: in expansion of macro 'offsetof'
     (type *)( (char *)__mptr - offsetof(type,member) );})
                                ^
   include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
     container_of(lockless_dereference(ptr), type, member)
     ^
   include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
      pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
            ^
   lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(range, &io_range_list, list) {
     ^
>> lib/logic_pio.c:78:13: error: dereferencing pointer to incomplete type
      if (!range->pio_peer) {
                ^
   In file included from include/linux/kernel.h:13:0,
                    from include/linux/list.h:8,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
   lib/logic_pio.c:80:11: error: dereferencing pointer to incomplete type
        &range->hw_start);
              ^
   include/linux/printk.h:303:37: note: in definition of macro 'pr_warning'
     printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
                                        ^
>> lib/logic_pio.c:79:4: note: in expansion of macro 'pr_warn'
       pr_warn("Invalid cpu addr node(%pa) in list!\n",
       ^
   lib/logic_pio.c:83:12: error: dereferencing pointer to incomplete type
      if (range->fwnode != fwnode)
               ^
   lib/logic_pio.c:86:21: error: dereferencing pointer to incomplete type
      if (start >= range->hw_start + range->size ||
                        ^
   lib/logic_pio.c:86:39: error: dereferencing pointer to incomplete type
      if (start >= range->hw_start + range->size ||
                                          ^
   lib/logic_pio.c:87:15: error: dereferencing pointer to incomplete type
       end < range->hw_start)
                  ^
   lib/logic_pio.c:90:20: error: dereferencing pointer to incomplete type
      if (start < range->hw_start ||
                       ^
   lib/logic_pio.c:91:16: error: dereferencing pointer to incomplete type
       end >= range->hw_start + range->size)
                   ^
   lib/logic_pio.c:91:34: error: dereferencing pointer to incomplete type
       end >= range->hw_start + range->size)
                                     ^
   lib/logic_pio.c: In function 'logic_pio_alloc_range':
   lib/logic_pio.c:109:19: error: dereferencing pointer to incomplete type
     idle_start = root->sec_min;
                      ^
   lib/logic_pio.c:110:15: error: dereferencing pointer to incomplete type
     *prev = &root->sec_head;
                  ^
   In file included from include/linux/list.h:8:0,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:21,
                    from lib/logic_pio.c:18:
>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                                                    ^
   include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                     ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:111:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(entry, &root->sec_head, list) {
     ^
   lib/logic_pio.c:111:38: error: dereferencing pointer to incomplete type
     list_for_each_entry_rcu(entry, &root->sec_head, list) {
                                         ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
>> include/linux/rculist.h:277:15: note: in expansion of macro 'lockless_dereference'
     container_of(lockless_dereference(ptr), type, member)
                  ^
   include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
                ^
   lib/logic_pio.c:111:2: note: in expansion of macro 'list_for_each_entry_rcu'
     list_for_each_entry_rcu(entry, &root->sec_head, list) {
     ^
   lib/logic_pio.c:111:38: error: dereferencing pointer to incomplete type
     list_for_each_entry_rcu(entry, &root->sec_head, list) {
                                         ^
   include/linux/kernel.h:852:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^
   include/linux/compiler.h:323:22: note: in expansion of macro '__READ_ONCE'
    #define READ_ONCE(x) __READ_ONCE(x, 1)
                         ^
>> include/linux/compiler.h:574:26: note: in expansion of macro 'READ_ONCE'
     typeof(p) _________p1 = READ_ONCE(p); \
                             ^

vim +351 include/linux/rculist.h

3943f42c Andrey Utkin         2014-11-14  271   * @member:     the name of the list_head within the struct.
72c6a987 Jiri Pirko           2009-04-14  272   *
72c6a987 Jiri Pirko           2009-04-14  273   * This primitive may safely run concurrently with the _rcu list-mutation
72c6a987 Jiri Pirko           2009-04-14  274   * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
72c6a987 Jiri Pirko           2009-04-14  275   */
72c6a987 Jiri Pirko           2009-04-14  276  #define list_entry_rcu(ptr, type, member) \
8db70b13 Patrick Marlier      2015-09-11 @277  	container_of(lockless_dereference(ptr), type, member)
72c6a987 Jiri Pirko           2009-04-14  278  
72c6a987 Jiri Pirko           2009-04-14  279  /**
f88022a4 Michel Machado       2012-04-10  280   * Where are list_empty_rcu() and list_first_entry_rcu()?
f88022a4 Michel Machado       2012-04-10  281   *
f88022a4 Michel Machado       2012-04-10  282   * Implementing those functions following their counterparts list_empty() and
f88022a4 Michel Machado       2012-04-10  283   * list_first_entry() is not advisable because they lead to subtle race
f88022a4 Michel Machado       2012-04-10  284   * conditions as the following snippet shows:
f88022a4 Michel Machado       2012-04-10  285   *
f88022a4 Michel Machado       2012-04-10  286   * if (!list_empty_rcu(mylist)) {
f88022a4 Michel Machado       2012-04-10  287   *	struct foo *bar = list_first_entry_rcu(mylist, struct foo, list_member);
f88022a4 Michel Machado       2012-04-10  288   *	do_something(bar);
f88022a4 Michel Machado       2012-04-10  289   * }
f88022a4 Michel Machado       2012-04-10  290   *
f88022a4 Michel Machado       2012-04-10  291   * The list may not be empty when list_empty_rcu checks it, but it may be when
f88022a4 Michel Machado       2012-04-10  292   * list_first_entry_rcu rereads the ->next pointer.
f88022a4 Michel Machado       2012-04-10  293   *
f88022a4 Michel Machado       2012-04-10  294   * Rereading the ->next pointer is not a problem for list_empty() and
f88022a4 Michel Machado       2012-04-10  295   * list_first_entry() because they would be protected by a lock that blocks
f88022a4 Michel Machado       2012-04-10  296   * writers.
f88022a4 Michel Machado       2012-04-10  297   *
f88022a4 Michel Machado       2012-04-10  298   * See list_first_or_null_rcu for an alternative.
f88022a4 Michel Machado       2012-04-10  299   */
f88022a4 Michel Machado       2012-04-10  300  
f88022a4 Michel Machado       2012-04-10  301  /**
f88022a4 Michel Machado       2012-04-10  302   * list_first_or_null_rcu - get the first element from a list
72c6a987 Jiri Pirko           2009-04-14  303   * @ptr:        the list head to take the element from.
72c6a987 Jiri Pirko           2009-04-14  304   * @type:       the type of the struct this is embedded in.
3943f42c Andrey Utkin         2014-11-14  305   * @member:     the name of the list_head within the struct.
72c6a987 Jiri Pirko           2009-04-14  306   *
f88022a4 Michel Machado       2012-04-10  307   * Note that if the list is empty, it returns NULL.
72c6a987 Jiri Pirko           2009-04-14  308   *
72c6a987 Jiri Pirko           2009-04-14  309   * This primitive may safely run concurrently with the _rcu list-mutation
72c6a987 Jiri Pirko           2009-04-14  310   * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
72c6a987 Jiri Pirko           2009-04-14  311   */
f88022a4 Michel Machado       2012-04-10  312  #define list_first_or_null_rcu(ptr, type, member) \
0adab9b9 Joe Perches          2013-12-05  313  ({ \
0adab9b9 Joe Perches          2013-12-05  314  	struct list_head *__ptr = (ptr); \
7d0ae808 Paul E. McKenney     2015-03-03  315  	struct list_head *__next = READ_ONCE(__ptr->next); \
0adab9b9 Joe Perches          2013-12-05  316  	likely(__ptr != __next) ? list_entry_rcu(__next, type, member) : NULL; \
f88022a4 Michel Machado       2012-04-10  317  })
72c6a987 Jiri Pirko           2009-04-14  318  
82524746 Franck Bui-Huu       2008-05-12  319  /**
ff3c44e6 Tom Herbert          2016-03-07  320   * list_next_or_null_rcu - get the first element from a list
ff3c44e6 Tom Herbert          2016-03-07  321   * @head:	the head for the list.
ff3c44e6 Tom Herbert          2016-03-07  322   * @ptr:        the list head to take the next element from.
ff3c44e6 Tom Herbert          2016-03-07  323   * @type:       the type of the struct this is embedded in.
ff3c44e6 Tom Herbert          2016-03-07  324   * @member:     the name of the list_head within the struct.
ff3c44e6 Tom Herbert          2016-03-07  325   *
ff3c44e6 Tom Herbert          2016-03-07  326   * Note that if the ptr is at the end of the list, NULL is returned.
ff3c44e6 Tom Herbert          2016-03-07  327   *
ff3c44e6 Tom Herbert          2016-03-07  328   * This primitive may safely run concurrently with the _rcu list-mutation
ff3c44e6 Tom Herbert          2016-03-07  329   * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
ff3c44e6 Tom Herbert          2016-03-07  330   */
ff3c44e6 Tom Herbert          2016-03-07  331  #define list_next_or_null_rcu(head, ptr, type, member) \
ff3c44e6 Tom Herbert          2016-03-07  332  ({ \
ff3c44e6 Tom Herbert          2016-03-07  333  	struct list_head *__head = (head); \
ff3c44e6 Tom Herbert          2016-03-07  334  	struct list_head *__ptr = (ptr); \
ff3c44e6 Tom Herbert          2016-03-07  335  	struct list_head *__next = READ_ONCE(__ptr->next); \
ff3c44e6 Tom Herbert          2016-03-07  336  	likely(__next != __head) ? list_entry_rcu(__next, type, \
ff3c44e6 Tom Herbert          2016-03-07  337  						  member) : NULL; \
ff3c44e6 Tom Herbert          2016-03-07  338  })
ff3c44e6 Tom Herbert          2016-03-07  339  
ff3c44e6 Tom Herbert          2016-03-07  340  /**
82524746 Franck Bui-Huu       2008-05-12  341   * list_for_each_entry_rcu	-	iterate over rcu list of given type
82524746 Franck Bui-Huu       2008-05-12  342   * @pos:	the type * to use as a loop cursor.
82524746 Franck Bui-Huu       2008-05-12  343   * @head:	the head for your list.
3943f42c Andrey Utkin         2014-11-14  344   * @member:	the name of the list_head within the struct.
82524746 Franck Bui-Huu       2008-05-12  345   *
82524746 Franck Bui-Huu       2008-05-12  346   * This list-traversal primitive may safely run concurrently with
82524746 Franck Bui-Huu       2008-05-12  347   * the _rcu list-mutation primitives such as list_add_rcu()
82524746 Franck Bui-Huu       2008-05-12  348   * as long as the traversal is guarded by rcu_read_lock().
82524746 Franck Bui-Huu       2008-05-12  349   */
82524746 Franck Bui-Huu       2008-05-12  350  #define list_for_each_entry_rcu(pos, head, member) \
72c6a987 Jiri Pirko           2009-04-14 @351  	for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
e66eed65 Linus Torvalds       2011-05-19  352  		&pos->member != (head); \
72c6a987 Jiri Pirko           2009-04-14 @353  		pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
82524746 Franck Bui-Huu       2008-05-12  354  
82524746 Franck Bui-Huu       2008-05-12  355  /**
69b90729 Alexey Kardashevskiy 2015-12-05  356   * list_entry_lockless - get the struct for this entry

:::::: The code at line 351 was first introduced by commit
:::::: 72c6a9870f901045f2464c3dc6ee8914bfdc07aa rculist.h: introduce list_entry_rcu() and list_first_entry_rcu()

:::::: TO: Jiri Pirko <jpirko@redhat.com>
:::::: CC: Ingo Molnar <mingo@elte.hu>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
zhichang April 5, 2017, 12:18 p.m. UTC | #3
Hi,

Thanks for your report!

I am sorry for that!

This issue was caused by missing the '#include <linux/logic_pio.h>' in
logic_pio.c for some architectures where the 'asm-generic/io.h' wasn't been
included.

Will be fixed in the next V9.

Apologized for this!

-Zhichang


On 04/01/2017 01:58 PM, kbuild test robot wrote:
> Hi zhichang.yuan,
> 
> [auto build test ERROR on linus/master]
> [also build test ERROR on v4.11-rc4 next-20170331]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
> 
> url:    https://github.com/0day-ci/linux/commits/zhichang-yuan/LIBIO-Introduce-a-generic-PIO-mapping-method/20170401-104801
> config: alpha-allyesconfig (attached as .config)
> compiler: alpha-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
> reproduce:
>         wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>         chmod +x ~/bin/make.cross
>         # save the attached .config to linux build tree
>         make.cross ARCH=alpha 
> 
> All error/warnings (new ones prefixed by >>):
> 
>>> lib/logic_pio.c:32:50: error: 'PIO_MAX_SECT' undeclared here (not in a function)
>     static struct logic_pio_root logic_pio_root_list[PIO_MAX_SECT] = {
>                                                      ^~~~~~~~~~~~
>>> lib/logic_pio.c:39:3: error: 'PIO_CPU_MMIO' undeclared here (not in a function)
>      [PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
>       ^~~~~~~~~~~~
>>> lib/logic_pio.c:39:20: error: 'PIO_INDIRECT' undeclared here (not in a function)
>      [PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
>                        ^~~~~~~~~~~~
>>> lib/logic_pio.c:39:3: error: array index in initializer not of integer type
>      [PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
>       ^~~~~~~~~~~~
>    lib/logic_pio.c:39:3: note: (near initialization for 'logic_pio_root_list')
>>> lib/logic_pio.c:40:3: error: field name not in record or union initializer
>       .sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
>       ^
>    lib/logic_pio.c:40:3: note: (near initialization for 'logic_pio_root_list')
>    lib/logic_pio.c:41:3: error: field name not in record or union initializer
>       .sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
>       ^
>    lib/logic_pio.c:41:3: note: (near initialization for 'logic_pio_root_list')
>>> lib/logic_pio.c:41:14: error: implicit declaration of function 'PIO_SECT_MIN' [-Werror=implicit-function-declaration]
>       .sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
>                  ^~~~~~~~~~~~
>    lib/logic_pio.c:42:3: error: field name not in record or union initializer
>       .sec_max = PIO_SECT_MAX(PIO_INDIRECT - 1),
>       ^
>    lib/logic_pio.c:42:3: note: (near initialization for 'logic_pio_root_list')
>>> lib/logic_pio.c:42:14: error: implicit declaration of function 'PIO_SECT_MAX' [-Werror=implicit-function-declaration]
>       .sec_max = PIO_SECT_MAX(PIO_INDIRECT - 1),
>                  ^~~~~~~~~~~~
>    lib/logic_pio.c:46:3: error: array index in initializer not of integer type
>      [PIO_INDIRECT] = {
>       ^~~~~~~~~~~~
>    lib/logic_pio.c:46:3: note: (near initialization for 'logic_pio_root_list')
>    lib/logic_pio.c:47:3: error: field name not in record or union initializer
>       .sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_INDIRECT].sec_head),
>       ^
>    lib/logic_pio.c:47:3: note: (near initialization for 'logic_pio_root_list')
>    lib/logic_pio.c:48:3: error: field name not in record or union initializer
>       .sec_min = PIO_SECT_MIN(PIO_INDIRECT),
>       ^
>    lib/logic_pio.c:48:3: note: (near initialization for 'logic_pio_root_list')
>    lib/logic_pio.c:49:3: error: field name not in record or union initializer
>       .sec_max = PIO_SECT_MAX(PIO_INDIRECT),
>       ^
>    lib/logic_pio.c:49:3: note: (near initialization for 'logic_pio_root_list')
>    In file included from include/linux/list.h:8:0,
>                     from include/linux/kobject.h:20,
>                     from include/linux/of.h:21,
>                     from lib/logic_pio.c:18:
>    lib/logic_pio.c: In function 'logic_pio_find_range_byaddr':
>>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type 'struct logic_pio_hwaddr'
>      for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
>                                                      
>    include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
>      const typeof( ((type *)0)->member ) *__mptr = (ptr); \
>                      ^~~~
>>> include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
>      for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
>                 ^~~~~~~~~~~~~~
>>> lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
>      list_for_each_entry_rcu(range, &io_range_list, list) {
>      ^~~~~~~~~~~~~~~~~~~~~~~
>    include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
>      const typeof( ((type *)0)->member ) *__mptr = (ptr); \
>                                                    ^
>>> include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
>      container_of(lockless_dereference(ptr), type, member)
>      ^~~~~~~~~~~~
>>> include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
>      for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
>                 ^~~~~~~~~~~~~~
>>> lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
>      list_for_each_entry_rcu(range, &io_range_list, list) {
>      ^~~~~~~~~~~~~~~~~~~~~~~
>>> include/linux/kernel.h:852:48: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
>      const typeof( ((type *)0)->member ) *__mptr = (ptr); \
>                                                    ^
>>> include/linux/rculist.h:277:2: note: in expansion of macro 'container_of'
>      container_of(lockless_dereference(ptr), type, member)
>      ^~~~~~~~~~~~
>    include/linux/rculist.h:353:9: note: in expansion of macro 'list_entry_rcu'
>       pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
>             ^~~~~~~~~~~~~~
>>> lib/logic_pio.c:77:2: note: in expansion of macro 'list_for_each_entry_rcu'
>      list_for_each_entry_rcu(range, &io_range_list, list) {
>      ^~~~~~~~~~~~~~~~~~~~~~~
>    lib/logic_pio.c: In function 'logic_pio_alloc_range':
>>> lib/logic_pio.c:109:19: error: dereferencing pointer to incomplete type 'struct logic_pio_root'
>      idle_start = root->sec_min;
>                       ^~
>    In file included from include/linux/list.h:8:0,
>                     from include/linux/kobject.h:20,
>                     from include/linux/of.h:21,
>                     from lib/logic_pio.c:18:
>>> include/linux/rculist.h:351:49: error: dereferencing pointer to incomplete type 'struct logic_pio_sect'
>      for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
>                                                      
>    include/linux/kernel.h:852:18: note: in definition of macro 'container_of'
>      const typeof( ((type *)0)->member ) *__mptr = (ptr); \
>                      ^~~~
>>> include/linux/rculist.h:351:13: note: in expansion of macro 'list_entry_rcu'
>      for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
>                 ^~~~~~~~~~~~~~
>    lib/logic_pio.c:111:2: note: in expansion of macro 'list_for_each_entry_rcu'
>      list_for_each_entry_rcu(entry, &root->sec_head, list) {
>      ^~~~~~~~~~~~~~~~~~~~~~~
>>> include/linux/kernel.h:852:48: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
>      const typeof( ((type *)0)->member ) *__mptr = (ptr); \
>                                                    ^
> 
> vim +/PIO_MAX_SECT +32 lib/logic_pio.c
> 
>     12	 * GNU General Public License for more details.
>     13	 *
>     14	 * You should have received a copy of the GNU General Public License
>     15	 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
>     16	 */
>     17	
>   > 18	#include <linux/of.h>
>     19	#include <linux/io.h>
>     20	#include <linux/mm.h>
>     21	#include <linux/rculist.h>
>     22	#include <linux/sizes.h>
>     23	#include <linux/slab.h>
>     24	
>     25	/* The unique hardware address list. */
>     26	static LIST_HEAD(io_range_list);
>     27	static DEFINE_MUTEX(io_range_mutex);
>     28	
>     29	/*
>     30	 * These are the lists for PIO. The highest PIO_SECT_BITS of PIO is the index.
>     31	 */
>   > 32	static struct logic_pio_root logic_pio_root_list[PIO_MAX_SECT] = {
>     33	#ifdef CONFIG_INDIRECT_PIO
>     34		/*
>     35		 * At this moment, assign all the other logic PIO space to MMIO.
>     36		 * If more elements added, please adjust the ending index and .sec_max;
>     37		 * Please keep MMIO element started from index ZERO.
>     38		 */
>   > 39		[PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
>   > 40			.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
>   > 41			.sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
>   > 42			.sec_max = PIO_SECT_MAX(PIO_INDIRECT - 1),
>     43		},
>     44	
>     45		/* The last element */
>     46		[PIO_INDIRECT] = {
>     47			.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_INDIRECT].sec_head),
>     48			.sec_min = PIO_SECT_MIN(PIO_INDIRECT),
>     49			.sec_max = PIO_SECT_MAX(PIO_INDIRECT),
>     50		},
>     51	#else
>     52		[PIO_CPU_MMIO] = {
>     53			.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
>     54			.sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
>     55			.sec_max = PIO_SECT_MAX(PIO_CPU_MMIO),
>     56		},
>     57	
>     58	#endif
>     59	};
>     60	
>     61	/*
>     62	 * Search a io_range registered which match the fwnode and addr.
>     63	 *
>     64	 * @fwnode: the host fwnode which must be valid;
>     65	 * @start: the start hardware address of this search;
>     66	 * @end: the end hardware address of this search. can be equal to @start;
>     67	 *
>     68	 * return NULL when there is no matched node; IS_ERR() means ERROR;
>     69	 * valid virtual address represent a matched node was found.
>     70	 */
>     71	static struct logic_pio_hwaddr *
>     72	logic_pio_find_range_byaddr(struct fwnode_handle *fwnode,
>     73				resource_size_t start, resource_size_t end)
>     74	{
>     75		struct logic_pio_hwaddr *range;
>     76	
>   > 77		list_for_each_entry_rcu(range, &io_range_list, list) {
>     78			if (!range->pio_peer) {
>     79				pr_warn("Invalid cpu addr node(%pa) in list!\n",
>     80					&range->hw_start);
>     81				continue;
>     82			}
>     83			if (range->fwnode != fwnode)
>     84				continue;
>     85			/* without any overlap with current range */
>     86			if (start >= range->hw_start + range->size ||
>     87				end < range->hw_start)
>     88				continue;
>     89			/* overlap is not supported now. */
>     90			if (start < range->hw_start ||
>     91				end >= range->hw_start + range->size)
>     92				return ERR_PTR(-EBUSY);
>     93			/* had been registered. */
>     94			return range;
>     95		}
>     96	
>     97		return NULL;
>     98	}
>     99	
>    100	
>    101	static int logic_pio_alloc_range(struct logic_pio_root *root,
>    102			resource_size_t size, unsigned long align,
>    103			struct list_head **prev, resource_size_t *pio_alloc)
>    104	{
>    105		struct logic_pio_sect *entry;
>    106		resource_size_t tmp_start;
>    107		resource_size_t idle_start, idle_end;
>    108	
>  > 109		idle_start = root->sec_min;
>    110		*prev = &root->sec_head;
>    111		list_for_each_entry_rcu(entry, &root->sec_head, list) {
>    112			if (!entry->hwpeer ||
>    113				idle_start > entry->io_start) {
>    114				WARN(1, "skip an invalid io range during traversal!\n");
>    115				goto nextentry;
>    116			}
>    117			/* set the end edge. */
>    118			if (idle_start == entry->io_start) {
>    119				struct logic_pio_sect *next;
>    120	
>    121				idle_start = entry->io_start + entry->hwpeer->size;
>  > 122				next = list_next_or_null_rcu(&root->sec_head,
>  > 123					&entry->list, struct logic_pio_sect, list);
>    124				if (next) {
>    125					entry = next;
>    126				} else {
>    127					*prev = &entry->list;
>    128					break;
>    129				}
>    130			}
>    131			idle_end = entry->io_start - 1;
>    132	
>    133			/* contiguous range... */
>    134			if (idle_start > idle_end)
>    135				goto nextentry;
>    136	
>    137			tmp_start = idle_start;
>    138			idle_start = ALIGN(idle_start, align);
>    139			if (idle_start >= tmp_start &&
>    140				idle_start + size <= idle_end) {
>    141				*prev = &entry->list;
>    142				*pio_alloc = idle_start;
>    143				return 0;
>    144			}
>    145	
>    146	nextentry:
>    147			idle_start = entry->io_start + entry->hwpeer->size;
>    148			*prev = &entry->list;
>    149		}
>    150		/* check the last free gap... */
>    151		idle_end = root->sec_max;
>    152	
>    153		tmp_start = idle_start;
>    154		idle_start = ALIGN(idle_start, align);
>    155		if (idle_start >= tmp_start &&
>    156			idle_start + size <= idle_end) {
>    157			*pio_alloc = idle_start;
>    158			return 0;
>    159		}
>    160	
>    161		return -EBUSY;
>    162	}
>    163	
>    164	/*
>    165	 * register a io range node in the io range list.
>    166	 *
>    167	 * @newrange: pointer to the io range to be registered.
>    168	 *
>    169	 * return 'newrange' when success, ERR_VALUE() is for failures.
>    170	 * specially, return a valid pointer which is not equal to 'newrange' when
>    171	 * the io range had been registered before.
>    172	 */
>    173	struct logic_pio_hwaddr
>    174	*logic_pio_register_range(struct logic_pio_hwaddr *newrange,
>    175			unsigned long align)
>    176	{
>    177		struct logic_pio_hwaddr *range;
>    178		struct logic_pio_sect *newsect;
>    179		resource_size_t pio_alloc;
>    180		struct list_head *prev, *hwprev;
>    181		unsigned long sect_id;
>    182		int err;
>    183	
>    184		if (!newrange || !newrange->fwnode || !newrange->size)
>    185			return ERR_PTR(-EINVAL);
>    186	
>    187		sect_id = newrange->flags;
>    188		if (sect_id >= PIO_MAX_SECT)
>    189			return ERR_PTR(-EINVAL);
>    190	
>    191		mutex_lock(&io_range_mutex);
>    192		range = logic_pio_find_range_byaddr(newrange->fwnode,
>    193				newrange->hw_start,
>    194				newrange->hw_start + newrange->size - 1);
>    195		if (range) {
>    196			if (!IS_ERR(range))
>    197				pr_info("the request IO range had been registered!\n");
>    198			else
>    199				pr_err("registering IO[%pa - sz%pa) got failed!\n",
>    200					&newrange->hw_start, &newrange->size);
>    201			mutex_unlock(&io_range_mutex);
>    202			return range;
>    203		}
>    204	
>    205		err = logic_pio_alloc_range(&logic_pio_root_list[sect_id],
>    206				newrange->size, align, &prev, &pio_alloc);
>    207		if (err) {
>    208			pr_err("can't find free %pa logical IO range!\n",
>    209				&newrange->size);
>    210			goto exitproc;
>    211		}
>    212	
>    213		if (prev == &logic_pio_root_list[sect_id].sec_head) {
>    214			hwprev = &io_range_list;
>    215		} else {
>  > 216			newsect = to_pio_sect(prev);
>  > 217			hwprev = &newsect->hwpeer->list;
>    218		}
>    219	
>    220		newsect = kzalloc(sizeof(*newsect), GFP_KERNEL);
> 
> ---
> 0-DAY kernel test infrastructure                Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
>
diff mbox

Patch

diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index 7ef015e..f7fbec3 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -351,6 +351,8 @@  static inline void writesq(volatile void __iomem *addr, const void *buffer,
 #define IO_SPACE_LIMIT 0xffff
 #endif
 
+#include <linux/logic_pio.h>
+
 /*
  * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be
  * implemented on hardware that needs an additional delay for I/O accesses to
@@ -358,51 +360,75 @@  static inline void writesq(volatile void __iomem *addr, const void *buffer,
  */
 
 #ifndef inb
+#ifdef CONFIG_INDIRECT_PIO
+#define inb logic_inb
+#else
 #define inb inb
 static inline u8 inb(unsigned long addr)
 {
 	return readb(PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef inw
+#ifdef CONFIG_INDIRECT_PIO
+#define inw logic_inw
+#else
 #define inw inw
 static inline u16 inw(unsigned long addr)
 {
 	return readw(PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef inl
+#ifdef CONFIG_INDIRECT_PIO
+#define inl logic_inl
+#else
 #define inl inl
 static inline u32 inl(unsigned long addr)
 {
 	return readl(PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef outb
+#ifdef CONFIG_INDIRECT_PIO
+#define outb logic_outb
+#else
 #define outb outb
 static inline void outb(u8 value, unsigned long addr)
 {
 	writeb(value, PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef outw
+#ifdef CONFIG_INDIRECT_PIO
+#define outw logic_outw
+#else
 #define outw outw
 static inline void outw(u16 value, unsigned long addr)
 {
 	writew(value, PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef outl
+#ifdef CONFIG_INDIRECT_PIO
+#define outl logic_outl
+#else
 #define outl outl
 static inline void outl(u32 value, unsigned long addr)
 {
 	writel(value, PCI_IOBASE + addr);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef inb_p
@@ -459,54 +485,78 @@  static inline void outl_p(u32 value, unsigned long addr)
  */
 
 #ifndef insb
+#ifdef CONFIG_INDIRECT_PIO
+#define insb logic_insb
+#else
 #define insb insb
 static inline void insb(unsigned long addr, void *buffer, unsigned int count)
 {
 	readsb(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef insw
+#ifdef CONFIG_INDIRECT_PIO
+#define insw logic_insw
+#else
 #define insw insw
 static inline void insw(unsigned long addr, void *buffer, unsigned int count)
 {
 	readsw(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef insl
+#ifdef CONFIG_INDIRECT_PIO
+#define insl logic_insl
+#else
 #define insl insl
 static inline void insl(unsigned long addr, void *buffer, unsigned int count)
 {
 	readsl(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef outsb
+#ifdef CONFIG_INDIRECT_PIO
+#define outsb logic_outsb
+#else
 #define outsb outsb
 static inline void outsb(unsigned long addr, const void *buffer,
 			 unsigned int count)
 {
 	writesb(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef outsw
+#ifdef CONFIG_INDIRECT_PIO
+#define outsw logic_outsw
+#else
 #define outsw outsw
 static inline void outsw(unsigned long addr, const void *buffer,
 			 unsigned int count)
 {
 	writesw(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef outsl
+#ifdef CONFIG_INDIRECT_PIO
+#define outsl logic_outsl
+#else
 #define outsl outsl
 static inline void outsl(unsigned long addr, const void *buffer,
 			 unsigned int count)
 {
 	writesl(PCI_IOBASE + addr, buffer, count);
 }
+#endif /* CONFIG_INDIRECT_PIO */
 #endif
 
 #ifndef insb_p
diff --git a/include/linux/logic_pio.h b/include/linux/logic_pio.h
new file mode 100644
index 0000000..e9f5644
--- /dev/null
+++ b/include/linux/logic_pio.h
@@ -0,0 +1,174 @@ 
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_LIBIO_H
+#define __LINUX_LIBIO_H
+
+#ifdef __KERNEL__
+
+#include <linux/fwnode.h>
+
+/*
+ *		Total IO space is 0 to IO_SPACE_LIMIT
+ *
+ *    section			pio
+ * |________|________________________________________|
+ *
+ * In this division, the benefits are:
+ * 1) The MMIO PIO space is consecutive, then ioport_map() still works well
+ * for MMIO;
+ * 2) The search happened in inX/outX with input PIO will have better
+ * performance for indirect_IO. For MMIO, the performance is nearly same
+ * even when CONFIG_INDIRECT_PIO is enabled;
+ *
+ * Some notes:
+ * 1) Don't increase the IO_SPACE_LIMIT to avoid modification on so many
+ * architectural files;
+ * 2) To reduce the impact on the original I/O space to a minimum, we only
+ * apply this IO space division when CONFIG_INDIRECT_PIO is enabled; And
+ * only allocate the last section to INDIRECT_PIO, all the other PIO space are
+ * for MMIO;
+ * 3) For better efficiency, one more I/O segment can be separated from 'pio'
+ * bit section. But it will make the IO space size decreased. Won't apply at
+ * this moment;
+ */
+#ifdef CONFIG_INDIRECT_PIO
+#define PIO_SECT_BITS		2
+#else
+#define PIO_SECT_BITS		0
+#endif
+#define PIO_MAX_SECT		(0x01UL << PIO_SECT_BITS)
+#define PIO_SECT_MASK		(PIO_MAX_SECT - 1)
+
+/* The last section. */
+#define PIO_INDIRECT		(PIO_MAX_SECT - 1)
+/* This one is for MMIO(PCI) to keep compatibility */
+#define PIO_CPU_MMIO		0x00UL
+
+struct logic_pio_root {
+	struct list_head sec_head;
+	resource_size_t sec_min;
+	resource_size_t sec_max;
+};
+
+#if ((IO_SPACE_LIMIT + 1) & IO_SPACE_LIMIT)
+#error "(IO_SPACE_LIMIT + 1) must be power of 2!"
+#endif
+
+#define PIO_VAL_MASK		(IO_SPACE_LIMIT >> PIO_SECT_BITS)
+#define PIO_VAL_BIT_LEN		(ilog2(PIO_VAL_MASK) + 1)
+
+#define PIO_SECT_MIN(sec_id)	((sec_id) << PIO_VAL_BIT_LEN)
+#define PIO_SECT_MAX(sec_id)	(PIO_SECT_MIN(sec_id) | PIO_VAL_MASK)
+
+#define PIO_SECT_ID(pio)	((pio >> PIO_VAL_BIT_LEN) & PIO_SECT_MASK)
+
+struct logic_pio_sect {
+	struct list_head list;
+	resource_size_t io_start;
+
+	struct logic_pio_hwaddr *hwpeer;
+};
+#define to_pio_sect(node) container_of(node, struct logic_pio_sect, list)
+
+struct logic_pio_hwaddr {
+	struct list_head list;
+	struct fwnode_handle *fwnode;
+	resource_size_t hw_start;
+	resource_size_t size; /* range size populated */
+	unsigned long flags;
+
+	struct logic_pio_sect *pio_peer;
+
+	void *devpara;	/* private parameter of the host device */
+	struct hostio_ops *ops;	/* ops operating on this node */
+};
+#define to_pio_hwaddr(node) container_of(node, struct logic_pio_hwaddr, list)
+
+struct hostio_ops {
+	u32 (*pfin)(void *devobj, unsigned long ptaddr,	size_t dlen);
+	void (*pfout)(void *devobj, unsigned long ptaddr, u32 outval,
+			size_t dlen);
+	u32 (*pfins)(void *devobj, unsigned long ptaddr, void *inbuf,
+			size_t dlen, unsigned int count);
+	void (*pfouts)(void *devobj, unsigned long ptaddr,
+			const void *outbuf, size_t dlen, unsigned int count);
+};
+
+#ifdef CONFIG_INDIRECT_PIO
+#define LPC_MIN_BUS_RANGE	0x0
+
+/*
+ * The default maximal IO size for Hip06/Hip07 LPC bus.
+ * Defining the I/O range size as 0x400 here should be sufficient for
+ * all peripherals under the bus.
+ */
+#define LPC_BUS_IO_SIZE		0x400
+#endif
+
+extern u8 logic_inb(unsigned long addr);
+extern void logic_outb(u8 value, unsigned long addr);
+extern void logic_outw(u16 value, unsigned long addr);
+extern void logic_outl(u32 value, unsigned long addr);
+extern u16 logic_inw(unsigned long addr);
+extern u32 logic_inl(unsigned long addr);
+extern void logic_outb(u8 value, unsigned long addr);
+extern void logic_outw(u16 value, unsigned long addr);
+extern void logic_outl(u32 value, unsigned long addr);
+extern void logic_insb(unsigned long addr, void *buffer, unsigned int count);
+extern void logic_insl(unsigned long addr, void *buffer, unsigned int count);
+extern void logic_insw(unsigned long addr, void *buffer, unsigned int count);
+extern void logic_outsb(unsigned long addr, const void *buffer,
+			unsigned int count);
+extern void logic_outsw(unsigned long addr, const void *buffer,
+			unsigned int count);
+extern void logic_outsl(unsigned long addr, const void *buffer,
+			unsigned int count);
+#ifdef CONFIG_LOGIC_PIO
+extern struct logic_pio_hwaddr
+*find_io_range_by_fwnode(struct fwnode_handle *fwnode);
+
+extern unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode,
+			resource_size_t hw_addr);
+#else
+static inline struct logic_pio_hwaddr
+*find_io_range_by_fwnode(struct fwnode_handle *fwnode)
+{
+	return NULL;
+}
+
+static inline unsigned long
+logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, resource_size_t hw_addr)
+{
+	return -1;
+}
+#endif
+
+/*
+ * These are used by pci. As LOGIC_PIO is bound with PCI, no need to add dummy
+ * functions for them.
+ */
+extern struct logic_pio_hwaddr
+*logic_pio_register_range(struct logic_pio_hwaddr *newrange,
+	unsigned long align);
+
+extern resource_size_t logic_pio_to_hwaddr(unsigned long pio);
+
+extern unsigned long logic_pio_trans_cpuaddr(resource_size_t hw_addr);
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_LIBIO_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 0c8b78a..503c2e0 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -59,6 +59,32 @@  config ARCH_USE_CMPXCHG_LOCKREF
 config ARCH_HAS_FAST_MULTIPLIER
 	bool
 
+config LOGIC_PIO
+	bool "Generic logical I/O management"
+	def_bool y if PCI && !X86 && !IA64 && !POWERPC
+	help
+	  For some architectures, there are no IO space. To support the
+	  accesses to legacy I/O devices on those architectures, kernel
+	  implemented the memory mapped I/O mechanism based on bridge bus
+	  supports. But for some buses which do not support MMIO, the
+	  peripherals there should be accessed with device-specific way.
+	  To abstract those different I/O accesses into unified I/O accessors,
+	  this option provide a generic I/O space management way after mapping
+	  the device I/O to system logical/fake I/O and help to hide all the
+	  hardware detail.
+
+config INDIRECT_PIO
+	bool "Access I/O in non-MMIO mode" if LOGIC_PIO
+	help
+	  On some platforms where no separate I/O space exist, there are I/O
+	  hosts which can not be accessed in MMIO mode. Based on LOGIC_PIO
+	  mechanism, the host-local I/O resource can be mapped into system
+	  logic PIO space shared with MMIO hosts, such as PCI/PCIE, then system
+	  can access the I/O devices with the mapped logic PIO through I/O
+	  accessors.
+	  This way has a little I/O performance cost. Please make sure your
+	  devices really need this configure item enabled.
+
 config CRC_CCITT
 	tristate "CRC-CCITT functions"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index 320ac46a..26dcec0 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -77,6 +77,8 @@  obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o
 obj-$(CONFIG_CHECK_SIGNATURE) += check_signature.o
 obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o
 
+obj-$(CONFIG_LOGIC_PIO) += logic_pio.o
+
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
 
 obj-$(CONFIG_BTREE) += btree.o
diff --git a/lib/logic_pio.c b/lib/logic_pio.c
new file mode 100644
index 0000000..ca247e3
--- /dev/null
+++ b/lib/logic_pio.c
@@ -0,0 +1,413 @@ 
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/rculist.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+/* The unique hardware address list. */
+static LIST_HEAD(io_range_list);
+static DEFINE_MUTEX(io_range_mutex);
+
+/*
+ * These are the lists for PIO. The highest PIO_SECT_BITS of PIO is the index.
+ */
+static struct logic_pio_root logic_pio_root_list[PIO_MAX_SECT] = {
+#ifdef CONFIG_INDIRECT_PIO
+	/*
+	 * At this moment, assign all the other logic PIO space to MMIO.
+	 * If more elements added, please adjust the ending index and .sec_max;
+	 * Please keep MMIO element started from index ZERO.
+	 */
+	[PIO_CPU_MMIO ... PIO_INDIRECT - 1] = {
+		.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
+		.sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
+		.sec_max = PIO_SECT_MAX(PIO_INDIRECT - 1),
+	},
+
+	/* The last element */
+	[PIO_INDIRECT] = {
+		.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_INDIRECT].sec_head),
+		.sec_min = PIO_SECT_MIN(PIO_INDIRECT),
+		.sec_max = PIO_SECT_MAX(PIO_INDIRECT),
+	},
+#else
+	[PIO_CPU_MMIO] = {
+		.sec_head = LIST_HEAD_INIT(logic_pio_root_list[PIO_CPU_MMIO].sec_head),
+		.sec_min = PIO_SECT_MIN(PIO_CPU_MMIO),
+		.sec_max = PIO_SECT_MAX(PIO_CPU_MMIO),
+	},
+
+#endif
+};
+
+/*
+ * Search a io_range registered which match the fwnode and addr.
+ *
+ * @fwnode: the host fwnode which must be valid;
+ * @start: the start hardware address of this search;
+ * @end: the end hardware address of this search. can be equal to @start;
+ *
+ * return NULL when there is no matched node; IS_ERR() means ERROR;
+ * valid virtual address represent a matched node was found.
+ */
+static struct logic_pio_hwaddr *
+logic_pio_find_range_byaddr(struct fwnode_handle *fwnode,
+			resource_size_t start, resource_size_t end)
+{
+	struct logic_pio_hwaddr *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (!range->pio_peer) {
+			pr_warn("Invalid cpu addr node(%pa) in list!\n",
+				&range->hw_start);
+			continue;
+		}
+		if (range->fwnode != fwnode)
+			continue;
+		/* without any overlap with current range */
+		if (start >= range->hw_start + range->size ||
+			end < range->hw_start)
+			continue;
+		/* overlap is not supported now. */
+		if (start < range->hw_start ||
+			end >= range->hw_start + range->size)
+			return ERR_PTR(-EBUSY);
+		/* had been registered. */
+		return range;
+	}
+
+	return NULL;
+}
+
+
+static int logic_pio_alloc_range(struct logic_pio_root *root,
+		resource_size_t size, unsigned long align,
+		struct list_head **prev, resource_size_t *pio_alloc)
+{
+	struct logic_pio_sect *entry;
+	resource_size_t tmp_start;
+	resource_size_t idle_start, idle_end;
+
+	idle_start = root->sec_min;
+	*prev = &root->sec_head;
+	list_for_each_entry_rcu(entry, &root->sec_head, list) {
+		if (!entry->hwpeer ||
+			idle_start > entry->io_start) {
+			WARN(1, "skip an invalid io range during traversal!\n");
+			goto nextentry;
+		}
+		/* set the end edge. */
+		if (idle_start == entry->io_start) {
+			struct logic_pio_sect *next;
+
+			idle_start = entry->io_start + entry->hwpeer->size;
+			next = list_next_or_null_rcu(&root->sec_head,
+				&entry->list, struct logic_pio_sect, list);
+			if (next) {
+				entry = next;
+			} else {
+				*prev = &entry->list;
+				break;
+			}
+		}
+		idle_end = entry->io_start - 1;
+
+		/* contiguous range... */
+		if (idle_start > idle_end)
+			goto nextentry;
+
+		tmp_start = idle_start;
+		idle_start = ALIGN(idle_start, align);
+		if (idle_start >= tmp_start &&
+			idle_start + size <= idle_end) {
+			*prev = &entry->list;
+			*pio_alloc = idle_start;
+			return 0;
+		}
+
+nextentry:
+		idle_start = entry->io_start + entry->hwpeer->size;
+		*prev = &entry->list;
+	}
+	/* check the last free gap... */
+	idle_end = root->sec_max;
+
+	tmp_start = idle_start;
+	idle_start = ALIGN(idle_start, align);
+	if (idle_start >= tmp_start &&
+		idle_start + size <= idle_end) {
+		*pio_alloc = idle_start;
+		return 0;
+	}
+
+	return -EBUSY;
+}
+
+/*
+ * register a io range node in the io range list.
+ *
+ * @newrange: pointer to the io range to be registered.
+ *
+ * return 'newrange' when success, ERR_VALUE() is for failures.
+ * specially, return a valid pointer which is not equal to 'newrange' when
+ * the io range had been registered before.
+ */
+struct logic_pio_hwaddr
+*logic_pio_register_range(struct logic_pio_hwaddr *newrange,
+		unsigned long align)
+{
+	struct logic_pio_hwaddr *range;
+	struct logic_pio_sect *newsect;
+	resource_size_t pio_alloc;
+	struct list_head *prev, *hwprev;
+	unsigned long sect_id;
+	int err;
+
+	if (!newrange || !newrange->fwnode || !newrange->size)
+		return ERR_PTR(-EINVAL);
+
+	sect_id = newrange->flags;
+	if (sect_id >= PIO_MAX_SECT)
+		return ERR_PTR(-EINVAL);
+
+	mutex_lock(&io_range_mutex);
+	range = logic_pio_find_range_byaddr(newrange->fwnode,
+			newrange->hw_start,
+			newrange->hw_start + newrange->size - 1);
+	if (range) {
+		if (!IS_ERR(range))
+			pr_info("the request IO range had been registered!\n");
+		else
+			pr_err("registering IO[%pa - sz%pa) got failed!\n",
+				&newrange->hw_start, &newrange->size);
+		mutex_unlock(&io_range_mutex);
+		return range;
+	}
+
+	err = logic_pio_alloc_range(&logic_pio_root_list[sect_id],
+			newrange->size, align, &prev, &pio_alloc);
+	if (err) {
+		pr_err("can't find free %pa logical IO range!\n",
+			&newrange->size);
+		goto exitproc;
+	}
+
+	if (prev == &logic_pio_root_list[sect_id].sec_head) {
+		hwprev = &io_range_list;
+	} else {
+		newsect = to_pio_sect(prev);
+		hwprev = &newsect->hwpeer->list;
+	}
+
+	newsect = kzalloc(sizeof(*newsect), GFP_KERNEL);
+	if (!newsect) {
+		err = -ENOMEM;
+		goto exitproc;
+	}
+	newsect->io_start = pio_alloc;
+	newsect->hwpeer = newrange;
+	list_add_rcu(&newsect->list, prev);
+
+	newrange->pio_peer = newsect;
+	list_add_rcu(&newrange->list, hwprev);
+
+exitproc:
+	mutex_unlock(&io_range_mutex);
+	return err ? ERR_PTR(err) : newrange;
+}
+
+/*
+ * traverse the io_range_list to find the registered node whose device node
+ * and/or physical IO address match to.
+ */
+struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
+{
+	struct logic_pio_hwaddr *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (range->fwnode == fwnode)
+			return range;
+	}
+	return NULL;
+}
+
+/*
+ * Translate the input logical pio to the corresponding hardware address.
+ * The input pio should be unique in the whole logical PIO space.
+ */
+resource_size_t logic_pio_to_hwaddr(unsigned long pio)
+{
+	struct logic_pio_sect *entry;
+	struct logic_pio_root *root;
+
+	/* The caller should check the section id is valid. */
+	root = &logic_pio_root_list[PIO_SECT_ID(pio)];
+	list_for_each_entry_rcu(entry, &root->sec_head, list) {
+		if (!entry->hwpeer) {
+			pr_warn("Invalid PIO entry(%pa) in list!\n",
+				&entry->io_start);
+			continue;
+		}
+		if (pio < entry->io_start)
+			break;
+
+		if (pio < entry->io_start + entry->hwpeer->size)
+			return pio - entry->io_start + entry->hwpeer->hw_start;
+	}
+
+	return -1;
+}
+
+/*
+ * This function is generic for translating a hardware address to logical PIO.
+ * @hw_addr: the hardware address of host, can be CPU address or host-local
+ *		address;
+ */
+unsigned long
+logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, resource_size_t addr)
+{
+	struct logic_pio_hwaddr *range;
+
+	range = logic_pio_find_range_byaddr(fwnode, addr, addr);
+	if (!range)
+		return -1;
+
+	return addr - range->hw_start + range->pio_peer->io_start;
+}
+
+unsigned long
+logic_pio_trans_cpuaddr(resource_size_t addr)
+{
+	struct logic_pio_hwaddr *range;
+
+	list_for_each_entry_rcu(range, &io_range_list, list) {
+		if (!range->pio_peer) {
+			pr_warn("Invalid cpu addr node(%pa) in list!\n",
+				&range->hw_start);
+			continue;
+		}
+		if (range->flags != PIO_CPU_MMIO)
+			continue;
+		if (addr >= range->hw_start &&
+			addr < range->hw_start + range->size)
+			return addr - range->hw_start +
+				range->pio_peer->io_start;
+	}
+	return -1;
+}
+
+#if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE)
+static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
+{
+	struct logic_pio_sect *entry;
+	struct logic_pio_root *root;
+
+	root = &logic_pio_root_list[PIO_SECT_ID(pio)];
+	if (pio < root->sec_min || pio > root->sec_max)
+		return NULL;
+	/*
+	 * non indirectIO section, no need to convert the addr. Jump to mmio ops
+	 * directly.
+	 */
+	if (&root->sec_head == &logic_pio_root_list[PIO_CPU_MMIO].sec_head)
+		return NULL;
+	list_for_each_entry_rcu(entry, &root->sec_head, list) {
+		if (!entry->hwpeer) {
+			pr_warn("Invalid PIO entry(%pa) in list!\n",
+				&entry->io_start);
+			continue;
+		}
+		if (pio < entry->io_start)
+			break;
+
+		if (pio < entry->io_start + entry->hwpeer->size)
+			return entry->hwpeer;
+	}
+
+	return NULL;
+}
+
+#define BUILD_LOGIC_IO(bw, type)					\
+type logic_in##bw(unsigned long addr)					\
+{									\
+	struct logic_pio_hwaddr *entry = find_io_range(addr);		\
+									\
+	if (entry && entry->ops)					\
+		return entry->ops->pfin(entry->devpara,			\
+					addr, sizeof(type));		\
+	return read##bw(PCI_IOBASE + addr);				\
+}									\
+									\
+void logic_out##bw(type value, unsigned long addr)			\
+{									\
+	struct logic_pio_hwaddr *entry = find_io_range(addr);		\
+									\
+	if (entry && entry->ops)					\
+		entry->ops->pfout(entry->devpara,			\
+					addr, value, sizeof(type));	\
+	else								\
+		write##bw(value, PCI_IOBASE + addr);			\
+}									\
+									\
+void logic_ins##bw(unsigned long addr, void *buffer, unsigned int count)\
+{									\
+	struct logic_pio_hwaddr *entry = find_io_range(addr);		\
+									\
+	if (entry && entry->ops)					\
+		entry->ops->pfins(entry->devpara,			\
+				addr, buffer, sizeof(type), count);	\
+	else								\
+		reads##bw(PCI_IOBASE + addr, buffer, count);		\
+}									\
+									\
+void logic_outs##bw(unsigned long addr, const void *buffer,		\
+		    unsigned int count)					\
+{									\
+	struct logic_pio_hwaddr *entry = find_io_range(addr);		\
+									\
+	if (entry && entry->ops)					\
+		entry->ops->pfouts(entry->devpara,			\
+				addr, buffer, sizeof(type), count);	\
+	else								\
+		writes##bw(PCI_IOBASE + addr, buffer, count);	\
+}
+
+BUILD_LOGIC_IO(b, u8)
+
+EXPORT_SYMBOL(logic_inb);
+EXPORT_SYMBOL(logic_outb);
+EXPORT_SYMBOL(logic_insb);
+EXPORT_SYMBOL(logic_outsb);
+
+BUILD_LOGIC_IO(w, u16)
+
+EXPORT_SYMBOL(logic_inw);
+EXPORT_SYMBOL(logic_outw);
+EXPORT_SYMBOL(logic_insw);
+EXPORT_SYMBOL(logic_outsw);
+
+BUILD_LOGIC_IO(l, u32)
+
+EXPORT_SYMBOL(logic_inl);
+EXPORT_SYMBOL(logic_outl);
+EXPORT_SYMBOL(logic_insl);
+EXPORT_SYMBOL(logic_outsl);
+#endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */