Skip to contentSkip to conversation

zsmalloc: correct fragile [kmap|kunmap]_atomic use

PATCH In review Minchan Kim 5 participants 7 messages Nov 14, 2014 – Nov 18, 2014

Subsystems (2)
lore
Share & tools
lore

Minchan Kim

author maintainer
#0 lore raw

PATCH 1/1 zsmalloc: correct fragile [kmap|kunmap]_atomic use

The kunmap_atomic should use virtual address getting by kmap_atomic. However, some pieces of code in zsmalloc uses modified address, not the one got by kmap_atomic for kunmap_atomic.

It's okay for working because zsmalloc modifies the address inner PAGE_SIZE bounday so it works with current kmap_atomic's implementation. But it's still fragile with potential changing of kmap_atomic so let's correct it.

---
 mm/zsmalloc.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)
mm/zsmalloc.c +12 -9
index b3b57ef85830..85e14f584048 100644
@@ -629,6 +629,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 		struct page *next_page;
 		struct link_free *link;
 		unsigned int i = 1;
+		void *vaddr;
 
 		/*
 		 * page->index stores offset of first object starting
@@ -639,8 +640,8 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 		if (page != first_page)
 			page->index = off;
 
-		link = (struct link_free *)kmap_atomic(page) +
-						off / sizeof(*link);
+		vaddr = kmap_atomic(page);
+		link = (struct link_free *)vaddr + off / sizeof(*link);
 
 		while ((off += class->size) < PAGE_SIZE) {
 			link->next = obj_location_to_handle(page, i++);
@@ -654,7 +655,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 		 */
 		next_page = get_next_page(page);
 		link->next = obj_location_to_handle(next_page, 0);
-		kunmap_atomic(link);
+		kunmap_atomic(vaddr);
 		page = next_page;
 		off %= PAGE_SIZE;
 	}
@@ -1055,6 +1056,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
 	unsigned long obj;
 	struct link_free *link;
 	struct size_class *class;
+	void *vaddr;
 
 	struct page *first_page, *m_page;
 	unsigned long m_objidx, m_offset;
@@ -1083,11 +1085,11 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
 	obj_handle_to_location(obj, &m_page, &m_objidx);
 	m_offset = obj_idx_to_offset(m_page, m_objidx, class->size);
 
-	link = (struct link_free *)kmap_atomic(m_page) +
-					m_offset / sizeof(*link);
+	vaddr = kmap_atomic(m_page);
+	link = (struct link_free *)vaddr + m_offset / sizeof(*link);
 	first_page->freelist = link->next;
 	memset(link, POISON_INUSE, sizeof(*link));
-	kunmap_atomic(link);
+	kunmap_atomic(vaddr);
 
 	first_page->inuse++;
 	/* Now move the zspage to another fullness group, if required */
@@ -1103,6 +1105,7 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 	struct link_free *link;
 	struct page *first_page, *f_page;
 	unsigned long f_objidx, f_offset;
+	void *vaddr;
 
 	int class_idx;
 	struct size_class *class;
@@ -1121,10 +1124,10 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 	spin_lock(&class->lock);
 
 	/* Insert this object in containing zspage's freelist */
-	link = (struct link_free *)((unsigned char *)kmap_atomic(f_page)
-							+ f_offset);
+	vaddr = kmap_atomic(f_page);
+	link = (struct link_free *)(vaddr + f_offset);
 	link->next = first_page->freelist;
-	kunmap_atomic(link);
+	kunmap_atomic(vaddr);
 	first_page->freelist = (void *)obj;
 
 	first_page->inuse--;
Signature · 1 line
2.0.0
Reviewed-by on v1 1/1
#1 lore raw
Quoted 8 lines · Minchan Kim

The kunmap_atomic should use virtual address getting by kmap_atomic. However, some pieces of code in zsmalloc uses modified address, not the one got by kmap_atomic for kunmap_atomic.

It's okay for working because zsmalloc modifies the address inner PAGE_SIZE bounday so it works with current kmap_atomic's implementation. But it's still fragile with potential changing of kmap_atomic so let's correct it.

Seems like you could just use PAGE_MASK to get the base page address from link like this:

---

mm/zsmalloc.c +3 -3
index b3b57ef..d6ca05a 100644
@@ -654,7 +654,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
                 */
                next_page = get_next_page(page);
                link->next = obj_location_to_handle(next_page, 0);
-               kunmap_atomic(link);
+               kunmap_atomic((void *)((unsigned long)link & PAGE_MASK));
                page = next_page;
                off %= PAGE_SIZE;
        }
@@ -1087,7 +1087,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
                                        m_offset / sizeof(*link);
        first_page->freelist = link->next;
        memset(link, POISON_INUSE, sizeof(*link));
-       kunmap_atomic(link);
+       kunmap_atomic((void *)((unsigned long)link & PAGE_MASK));
 
        first_page->inuse++;
        /* Now move the zspage to another fullness group, if required */
@@ -1124,7 +1124,7 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
        link = (struct link_free *)((unsigned char *)kmap_atomic(f_page)
                                                        + f_offset);
        link->next = first_page->freelist;
-       kunmap_atomic(link);
+       kunmap_atomic((void *)((unsigned long)link & PAGE_MASK));
        first_page->freelist = (void *)obj;
 
        first_page->inuse--;

---

This seems cleaner, but, at the same time, it isn't obvious that we are passing the same value to kunmap_atomic() that we got from kmap_atomic(). Just a thought.

Either way:

Quoted 91 lines
---
 mm/zsmalloc.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)
mm/zsmalloc.c +12 -9
index b3b57ef85830..85e14f584048 100644
@@ -629,6 +629,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 		struct page *next_page;
 		struct link_free *link;
 		unsigned int i = 1;
+		void *vaddr;
 
 		/*
 		 * page->index stores offset of first object starting
@@ -639,8 +640,8 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 		if (page != first_page)
 			page->index = off;
 
-		link = (struct link_free *)kmap_atomic(page) +
-						off / sizeof(*link);
+		vaddr = kmap_atomic(page);
+		link = (struct link_free *)vaddr + off / sizeof(*link);
 
 		while ((off += class->size) < PAGE_SIZE) {
 			link->next = obj_location_to_handle(page, i++);
@@ -654,7 +655,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 		 */
 		next_page = get_next_page(page);
 		link->next = obj_location_to_handle(next_page, 0);
-		kunmap_atomic(link);
+		kunmap_atomic(vaddr);
 		page = next_page;
 		off %= PAGE_SIZE;
 	}
@@ -1055,6 +1056,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
 	unsigned long obj;
 	struct link_free *link;
 	struct size_class *class;
+	void *vaddr;
 
 	struct page *first_page, *m_page;
 	unsigned long m_objidx, m_offset;
@@ -1083,11 +1085,11 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
 	obj_handle_to_location(obj, &m_page, &m_objidx);
 	m_offset = obj_idx_to_offset(m_page, m_objidx, class->size);
 
-	link = (struct link_free *)kmap_atomic(m_page) +
-					m_offset / sizeof(*link);
+	vaddr = kmap_atomic(m_page);
+	link = (struct link_free *)vaddr + m_offset / sizeof(*link);
 	first_page->freelist = link->next;
 	memset(link, POISON_INUSE, sizeof(*link));
-	kunmap_atomic(link);
+	kunmap_atomic(vaddr);
 
 	first_page->inuse++;
 	/* Now move the zspage to another fullness group, if required */
@@ -1103,6 +1105,7 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 	struct link_free *link;
 	struct page *first_page, *f_page;
 	unsigned long f_objidx, f_offset;
+	void *vaddr;
 
 	int class_idx;
 	struct size_class *class;
@@ -1121,10 +1124,10 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 	spin_lock(&class->lock);
 
 	/* Insert this object in containing zspage's freelist */
-	link = (struct link_free *)((unsigned char *)kmap_atomic(f_page)
-							+ f_offset);
+	vaddr = kmap_atomic(f_page);
+	link = (struct link_free *)(vaddr + f_offset);
 	link->next = first_page->freelist;
-	kunmap_atomic(link);
+	kunmap_atomic(vaddr);
 	first_page->freelist = (void *)obj;
 
 	first_page->inuse--;
Signature · 7 lines
2.0.0

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#2 lore raw
Quoted 5 lines · Seth Jennings
Quoted 4 lines · Minchan Kim

The kunmap_atomic should use virtual address getting by kmap_atomic. However, some pieces of code in zsmalloc uses modified address, not the one got by kmap_atomic for kunmap_atomic.

Quoted 8 lines
Quoted 4 lines

It's okay for working because zsmalloc modifies the address inner PAGE_SIZE bounday so it works with current kmap_atomic's implementation. But it's still fragile with potential changing of kmap_atomic so let's correct it.

Seems like you could just use PAGE_MASK to get the base page address from link like this:

both solutions look good to me:

	-ss
Quoted 128 lines

---

mm/zsmalloc.c +3 -4
index b3b57ef..d6ca05a 100644
@@ -654,7 +654,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
                 */
                next_page = get_next_page(page);
                link->next = obj_location_to_handle(next_page, 0);
-               kunmap_atomic(link);
+               kunmap_atomic((void *)((unsigned long)link & PAGE_MASK));
                page = next_page;
                off %= PAGE_SIZE;
        }
@@ -1087,7 +1087,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
                                        m_offset / sizeof(*link);
        first_page->freelist = link->next;
        memset(link, POISON_INUSE, sizeof(*link));
-       kunmap_atomic(link);
+       kunmap_atomic((void *)((unsigned long)link & PAGE_MASK));
 
        first_page->inuse++;
        /* Now move the zspage to another fullness group, if required */
@@ -1124,7 +1124,7 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
        link = (struct link_free *)((unsigned char *)kmap_atomic(f_page)
                                                        + f_offset);
        link->next = first_page->freelist;
-       kunmap_atomic(link);
+       kunmap_atomic((void *)((unsigned long)link & PAGE_MASK));
        first_page->freelist = (void *)obj;
 
        first_page->inuse--;
---

This seems cleaner, but, at the same time, it isn't obvious that we are passing the same value to kunmap_atomic() that we got from kmap_atomic(). Just a thought.

Either way:

Quoted 86 lines
---
 mm/zsmalloc.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)
mm/zsmalloc.c +12 -9
index b3b57ef85830..85e14f584048 100644
@@ -629,6 +629,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 		struct page *next_page;
 		struct link_free *link;
 		unsigned int i = 1;
+		void *vaddr;
 
 		/*
 		 * page->index stores offset of first object starting
@@ -639,8 +640,8 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 		if (page != first_page)
 			page->index = off;
 
-		link = (struct link_free *)kmap_atomic(page) +
-						off / sizeof(*link);
+		vaddr = kmap_atomic(page);
+		link = (struct link_free *)vaddr + off / sizeof(*link);
 
 		while ((off += class->size) < PAGE_SIZE) {
 			link->next = obj_location_to_handle(page, i++);
@@ -654,7 +655,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 		 */
 		next_page = get_next_page(page);
 		link->next = obj_location_to_handle(next_page, 0);
-		kunmap_atomic(link);
+		kunmap_atomic(vaddr);
 		page = next_page;
 		off %= PAGE_SIZE;
 	}
@@ -1055,6 +1056,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
 	unsigned long obj;
 	struct link_free *link;
 	struct size_class *class;
+	void *vaddr;
 
 	struct page *first_page, *m_page;
 	unsigned long m_objidx, m_offset;
@@ -1083,11 +1085,11 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
 	obj_handle_to_location(obj, &m_page, &m_objidx);
 	m_offset = obj_idx_to_offset(m_page, m_objidx, class->size);
 
-	link = (struct link_free *)kmap_atomic(m_page) +
-					m_offset / sizeof(*link);
+	vaddr = kmap_atomic(m_page);
+	link = (struct link_free *)vaddr + m_offset / sizeof(*link);
 	first_page->freelist = link->next;
 	memset(link, POISON_INUSE, sizeof(*link));
-	kunmap_atomic(link);
+	kunmap_atomic(vaddr);
 
 	first_page->inuse++;
 	/* Now move the zspage to another fullness group, if required */
@@ -1103,6 +1105,7 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 	struct link_free *link;
 	struct page *first_page, *f_page;
 	unsigned long f_objidx, f_offset;
+	void *vaddr;
 
 	int class_idx;
 	struct size_class *class;
@@ -1121,10 +1124,10 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 	spin_lock(&class->lock);
 
 	/* Insert this object in containing zspage's freelist */
-	link = (struct link_free *)((unsigned char *)kmap_atomic(f_page)
-							+ f_offset);
+	vaddr = kmap_atomic(f_page);
+	link = (struct link_free *)(vaddr + f_offset);
 	link->next = first_page->freelist;
-	kunmap_atomic(link);
+	kunmap_atomic(vaddr);
 	first_page->freelist = (void *)obj;
 
 	first_page->inuse--;
Signature · 1 line
2.0.0
Quoted 6 lines
Quoted 5 lines

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#3 lore raw
In reply to Seth Jennings
Quoted 9 lines · On Fri, 14 Nov 2014 09:07:32 -0600 Seth Jennings <[email protected]>…
Quoted 8 lines · Minchan Kim

The kunmap_atomic should use virtual address getting by kmap_atomic. However, some pieces of code in zsmalloc uses modified address, not the one got by kmap_atomic for kunmap_atomic.

It's okay for working because zsmalloc modifies the address inner PAGE_SIZE bounday so it works with current kmap_atomic's implementation. But it's still fragile with potential changing of kmap_atomic so let's correct it.

It is a bit alarming, but I've seen code elsewhere in which a modified pointer is passed to kunmap_atomic(). So the kunmap_atomic() interface is "kvaddr should point somewhere into the page" and that won't be changing without a big effort.

Quoted 2 lines

Seems like you could just use PAGE_MASK to get the base page address from link like this:

I think Minchan's approach is better: it explicitly retains the kmap_atomic() return value for passing to kunmap_atomic(). That's nicer than modifying it and then setting it back again.

I mean, a cleaner way of implementing your suggestion would be

void kunmap_atomic_unaligned(void *p)

{
	kunmap_atomic(void *)((unsigned long)p & PAGE_MASK);
}

but then one looks at

void __kunmap_atomic(void *kvaddr)

{
	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;

and asks "what the heck".

So I dunno. We could leave the code as-is. I have no strong feelings either way. Minchan's patch has no effect on zsmalloc.o section sizes with my compiler.

author maintainer
#4 lore raw

Hello Andrew,

Quoted 37 lines · Andrew Morton
Quoted 9 lines · On Fri, 14 Nov 2014 09:07:32 -0600 Seth Jennings <[email protected]>…
Quoted 8 lines · Minchan Kim

The kunmap_atomic should use virtual address getting by kmap_atomic. However, some pieces of code in zsmalloc uses modified address, not the one got by kmap_atomic for kunmap_atomic.

It's okay for working because zsmalloc modifies the address inner PAGE_SIZE bounday so it works with current kmap_atomic's implementation. But it's still fragile with potential changing of kmap_atomic so let's correct it.

It is a bit alarming, but I've seen code elsewhere in which a modified pointer is passed to kunmap_atomic(). So the kunmap_atomic() interface is "kvaddr should point somewhere into the page" and that won't be changing without a big effort.

Quoted 2 lines

Seems like you could just use PAGE_MASK to get the base page address from link like this:

I think Minchan's approach is better: it explicitly retains the kmap_atomic() return value for passing to kunmap_atomic(). That's nicer than modifying it and then setting it back again.

I mean, a cleaner way of implementing your suggestion would be

void kunmap_atomic_unaligned(void *p)

{
	kunmap_atomic(void *)((unsigned long)p & PAGE_MASK);
}

but then one looks at

void __kunmap_atomic(void *kvaddr)

{
	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
Quoted 6 lines

and asks "what the heck".

So I dunno. We could leave the code as-is. I have no strong feelings either way. Minchan's patch has no effect on zsmalloc.o section sizes with my compiler.

I hope to merge my patch.

Main reason I sent the patch is I got a subtle bug when I implement new feature of zsmalloc(ie, compaction) due to link's mishandling (ie, link was over page boundary by my fault).
Although it was totally my mistake, it took time for a while to find a root cause because unpredictable kmapped address should be unmapped so it's almost random crash.

IOW, it's fragile to depend on kunmap_atomic's internal and at least, I wanted to make zram code more robust.

Thanks.

Quoted 6 lines

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

Signature · 8 lines
Kind regards,
Minchan Kim

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#5 lore raw
Quoted 6 lines · On Wed, 19 Nov 2014 08:21:39 +0900 Minchan Kim <[email protected]> wrote:

Main reason I sent the patch is I got a subtle bug when I implement new feature of zsmalloc(ie, compaction) due to link's mishandling (ie, link was over page boundary by my fault).
Although it was totally my mistake, it took time for a while to find a root cause because unpredictable kmapped address should be unmapped so it's almost random crash.

Fair enough.

That's pretty rude behaviour from kunmap_atomic(). Unfortunately it just doesn't have anything with which to check the address - we'd need to create a special per-cpu array[KM_TYPE_NR] just for the purpose.

#6 lore raw
In reply to Minchan Kim

Hello

2014-11-14 9:11 GMT+08:00 Minchan Kim <[email protected]>:

Quoted 27 lines

The kunmap_atomic should use virtual address getting by kmap_atomic. However, some pieces of code in zsmalloc uses modified address, not the one got by kmap_atomic for kunmap_atomic.

It's okay for working because zsmalloc modifies the address inner PAGE_SIZE bounday so it works with current kmap_atomic's implementation. But it's still fragile with potential changing of kmap_atomic so let's correct it.

---
 mm/zsmalloc.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)
mm/zsmalloc.c +1 -0
index b3b57ef85830..85e14f584048 100644
@@ -629,6 +629,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
                struct page *next_page;
                struct link_free *link;
                unsigned int i = 1;
+               void *vaddr;
 
                /*
                 * page->index stores offset of first object starting

mm/zsmalloc.c static void init_zspage(struct page *first_page, struct size_class *class) · line 644

Quoted 7 lines
mm/zsmalloc.c +2 -2
@@ -639,8 +640,8 @@ static void init_zspage(struct page *first_page, struct size_class *class)
                if (page != first_page)
                        page->index = off;
 
-               link = (struct link_free *)kmap_atomic(page) +
-                                               off / sizeof(*link);
+               vaddr = kmap_atomic(page);
+               link = (struct link_free *)vaddr + off / sizeof(*link);

Just a nitpick:
I think

                    link = (struct link_free *)(vaddr + off);
is better.
To use the same method as in zs_free()
                   link = (struct link_free *)(vaddr + f_offset);
Quoted 21 lines
mm/zsmalloc.c +2 -1
                while ((off += class->size) < PAGE_SIZE) {
                        link->next = obj_location_to_handle(page, i++);
@@ -654,7 +655,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
                 */
                next_page = get_next_page(page);
                link->next = obj_location_to_handle(next_page, 0);
-               kunmap_atomic(link);
+               kunmap_atomic(vaddr);
                page = next_page;
                off %= PAGE_SIZE;
        }
@@ -1055,6 +1056,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
        unsigned long obj;
        struct link_free *link;
        struct size_class *class;
+       void *vaddr;
 
        struct page *first_page, *m_page;
        unsigned long m_objidx, m_offset;

mm/zsmalloc.c unsigned long zs_malloc(struct zs_pool *pool, size_t size) · line 1089

Quoted 7 lines
mm/zsmalloc.c +2 -2
@@ -1083,11 +1085,11 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
        obj_handle_to_location(obj, &m_page, &m_objidx);
        m_offset = obj_idx_to_offset(m_page, m_objidx, class->size);
 
-       link = (struct link_free *)kmap_atomic(m_page) +
-                                       m_offset / sizeof(*link);
+       vaddr = kmap_atomic(m_page);
+       link = (struct link_free *)vaddr + m_offset / sizeof(*link);

link = (struct link_free *)(vaddr + m_offset)

Quoted 16 lines
mm/zsmalloc.c +2 -1
        first_page->freelist = link->next;
        memset(link, POISON_INUSE, sizeof(*link));
-       kunmap_atomic(link);
+       kunmap_atomic(vaddr);
 
        first_page->inuse++;
        /* Now move the zspage to another fullness group, if required */
@@ -1103,6 +1105,7 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
        struct link_free *link;
        struct page *first_page, *f_page;
        unsigned long f_objidx, f_offset;
+       void *vaddr;
 
        int class_idx;
        struct size_class *class;

mm/zsmalloc.c void zs_free(struct zs_pool *pool, unsigned long obj) · line 1128

Quoted 7 lines
mm/zsmalloc.c +2 -2
@@ -1121,10 +1124,10 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
        spin_lock(&class->lock);
 
        /* Insert this object in containing zspage's freelist */
-       link = (struct link_free *)((unsigned char *)kmap_atomic(f_page)
-                                                       + f_offset);
+       vaddr = kmap_atomic(f_page);
+       link = (struct link_free *)(vaddr + f_offset);

Yes, I think this method to calc *link* is better. (:

Thanks

Quoted 9 lines
mm/zsmalloc.c +1 -2
        link->next = first_page->freelist;
-       kunmap_atomic(link);
+       kunmap_atomic(vaddr);
        first_page->freelist = (void *)obj;
 
        first_page->inuse--;
--

2.0.0

Quoted 5 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>