tech-net archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: RFC: ALTQ caller refactor
Date: Mon, 7 Mar 2016 17:39:15 +0900
From: Kengo NAKAHARA <k-nakahara%iij.ad.jp@localhost>
On 2016/03/04 12:23, Taylor R Campbell wrote:
> One of the questions from the thread you cited is whether you can put
> it in the mbuf packet header instead of using an m_tag. I have no
> opinion on that, but it would be nice to address that question.
Hmm, I feel it is excessive to put mbuf packet. My main intention is
not increasing ALTQ performance but just removing pattr argument from
IFQ_ENQUEUE. Hmm..., maybe I don't have a sense...
Fair enough!
> One comment on the patch after a quick glance:
>
> + altq_pattr_cache = pool_cache_init(sizeof(struct altq_pktattr)
> + + sizeof(struct m_tag),
> + 0, 0, 0, "altqmtag", NULL, IPL_NET, NULL, NULL, NULL);
>
> The addition of sizes here may not necessarily work. In particular,
> the pointer returned from pool_cache_get will be aligned, but adding
> sizeof(struct altq_pktattr) to that pointer does not necessarily give
> an aligned result:
>
> p = pool_cache_get(altq_pattr_cache, ...);
> pktattr = p;
> tag = (char *)p + sizeof(struct altq_pktattr);
Hmm..., sorry, I am confused. Just to confirm, m_tag needs metadata
(struct m_tag) in the front of m_tag data(e.g. in this case struct
altq_pktqttr), so both struct m_tag and sturct altq_pktattr are
necessary, I think. It is similar to malloc(len + sizeof(struct m_tag))
in m_tag_get().
Could you tell me about the reason which the addition of sizes may not
necessarily work in detail?
Suppose we had this definition of struct m_tag:
struct m_tag {
char foo;
};
with sizeof(struct m_tag) == 1. Suppose we ask for
m_tag_get(sizeof(uint64_t)), which is essentially
struct m_tag *p = malloc(sizeof(struct m_tag) + sizeof(uint64_t)).
Now p will be aligned for any kind of object. Suppose it has 8-byte
alignment. If we compute
uint64_t *q = (uint64_t *)((char *)p + sizeof(struct m_tag)),
then, because p is 8-byte-aligned and sizeof(struct m_tag) == 1, we
will get (char *)p + 1, which is an unaligned pointer to uint64_t --
slow on x86 and forbidden altogether on sparc64.
If, on the other hand, we did this:
struct mtag64 {
struct m_tag t;
uint64_t u64;
};
p = m_tag_get(sizeof(struct mtag64) - sizeof(struct m_tag));
/* i.e., p = malloc(sizeof(struct mtag64)); */
q = &((struct mtag64 *)p)->u64;
/* or, better: q = container_of(p, mtag64, t)->u64; */
then the compiler would insert padding between t and u64 to make q an
aligned pointer to uint64_t.
It happens to be the case that our definition of struct m_tag includes
a pointer, and usually that's sufficient to cause the compiler to
insert enough padding to make the idiom
uint64_t *q = (uint64_t *)((char *)p + sizeof(struct m_tag))
work. The same goes for struct altq_pktattr.
Presumably that is why this has not been a problem in practice. But
it is not hard to imagine an architecture where uint64_t requires
8-byte alignment even though pointers require only 4-byte alignment.
E.g., it wouldn't surprise me if MIPS N32 or similar required this --
pointers are 32-bit and need only 4-byte alignment but the native
64-bit words need 8-byte alignment.
In any case, whether or not you change anything about the existing
m_tag_get code or users, I suggest that you use the struct and
container_of idiom in any new code.
Home |
Main Index |
Thread Index |
Old Index