list.h/macro(continue

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



 hi all,
while googling i found similar question but in detail
the same problem i had faced i'm cut and pasting this
,sorry i can't get the answer while googling
so let me know?
      
 In list.h, we have:

#define list_entry(ptr, type, member) \
	((type *)((char *)(ptr)-(unsigned long)(&((type
*)0)->member)))

>From what I understand, this is supposed to be a hack
that gives C++
inheritence support in C.  

The idea is that the compiler thinks that ptr is a
pointer to type list_head
but in reality it is a pointer to a larger struct that
shares the same first
bytes as list_head.  Like this:

struct list_head {
	struct list_head *next, *prev;
};

typedef struct page {
	struct list_head list;
	struct address_space *mapping;
	unsigned long index;
	struct page *next_hash;
	atomic_t count;
	unsigned long flags;	/* atomic flags, some possibly
updated asynchronously */
	struct list_head lru;
	wait_queue_head_t wait;
	struct page **pprev_hash;
	struct buffer_head * buffers;
	unsigned long virtual; /* nonzero if kmapped */
	struct zone_struct *zone;
} mem_map_t;

Notice how list_head is the first element in
mem_map_t.  

What I don't understand about list_entry is why there
is a minus symbol instead
of a plus symbol.  Assume to call list_entry with
these parameters:

	struct list_head *curr;
	struct page *page;

	page = memlist_entry(curr, struct page, list);

According to the macro, we treat curr as a char
pointer.  "(unsigned
long)(&((type *)0)->member)" is equal to the offset
within "struct page" of
member "list", which happens to be zero.  so "(char
*)(ptr) - 0) is just (char
*)(ptr), which is then typecast to (struct page *).

I think the minus is a bug.  It should be plus.  Let's
take a case where the
third parameter is NOT "list":

struct pci_driver {
	struct list_head node;
	char *name;
	const struct pci_device_id *id_table;	/* NULL if
wants all devices */
	int (*probe)(struct pci_dev *dev, const struct
pci_device_id *id);	/* New
device inserted */
	void (*remove)(struct pci_dev *dev);	/* Device
removed (NULL if not a hot-plug
capable driver) */
	void (*suspend)(struct pci_dev *dev);	/* Device
suspended */
	void (*resume)(struct pci_dev *dev);	/* Device woken
up */
};

	struct list_head *ln;
	struct pci_driver *drv = list_entry(ln, struct
pci_driver, node);


"(unsigned long)(&((type *)0)->member)" gives you the
offset of name, which is
basically sizeof(struct list_head), or 8.  In this
case, list_entry is equal to
the address of ln MINUS 8, which points outside of the
structure!  I know the
kernel would crash instantly if this macro didn't
work, but I can't see how it
could!






__________________________________
Do you Yahoo!?
Yahoo! Finance: Get your refund fast by filing online.
http://taxes.yahoo.com/filing.html

--
Kernelnewbies: Help each other learn about the Linux kernel.
Archive:       http://mail.nl.linux.org/kernelnewbies/
FAQ:           http://kernelnewbies.org/faq/


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux