Index: npl/kernel/linux_src/4200_fbcondecor.patch
===================================================================
--- npl/kernel/linux_src/4200_fbcondecor.patch	(revision f6630dda9bbc206e69f0475bfb8672e20d5beaa1)
+++ npl/kernel/linux_src/4200_fbcondecor.patch	(revision e8565120de16c582dc1bd83033f7d7370bacd011)
@@ -1,4 +1,4 @@
 diff --git a/Documentation/fb/00-INDEX b/Documentation/fb/00-INDEX
-index fe85e7c..2230930 100644
+index fe85e7c5907a..22309308ba56 100644
 --- a/Documentation/fb/00-INDEX
 +++ b/Documentation/fb/00-INDEX
@@ -14,5 +14,5 @@
 diff --git a/Documentation/fb/fbcondecor.txt b/Documentation/fb/fbcondecor.txt
 new file mode 100644
-index 0000000..637209e
+index 000000000000..637209e11ccd
 --- /dev/null
 +++ b/Documentation/fb/fbcondecor.txt
@@ -226,9 +226,9 @@
 +
 diff --git a/drivers/Makefile b/drivers/Makefile
-index 53abb4a..1721aee 100644
+index 1d034b680431..9f41f2ea0c8b 100644
 --- a/drivers/Makefile
 +++ b/drivers/Makefile
-@@ -17,6 +17,10 @@ obj-y				+= pwm/
- obj-$(CONFIG_PCI)		+= pci/
+@@ -23,6 +23,10 @@ obj-y				+= pci/dwc/
+ 
  obj-$(CONFIG_PARISC)		+= parisc/
  obj-$(CONFIG_RAPIDIO)		+= rapidio/
@@ -240,5 +240,5 @@
  obj-y				+= idle/
  
-@@ -45,11 +49,6 @@ obj-$(CONFIG_REGULATOR)		+= regulator/
+@@ -53,11 +57,6 @@ obj-$(CONFIG_REGULATOR)		+= regulator/
  # reset controllers early, since gpu drivers might rely on them to initialize
  obj-$(CONFIG_RESET_CONTROLLER)	+= reset/
@@ -253,8 +253,8 @@
  
 diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
-index 38da6e2..fe58152 100644
+index 7f1f1fbcef9e..8439b618dfc0 100644
 --- a/drivers/video/console/Kconfig
 +++ b/drivers/video/console/Kconfig
-@@ -130,6 +130,19 @@ config FRAMEBUFFER_CONSOLE_ROTATION
+@@ -151,6 +151,19 @@ config FRAMEBUFFER_CONSOLE_ROTATION
           such that other users of the framebuffer will remain normally
           oriented.
@@ -277,62 +277,16 @@
          depends on PARISC
 diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
-index 43bfa48..cc104b6 100644
+index db07b784bd2c..3e369bd120b8 100644
 --- a/drivers/video/console/Makefile
 +++ b/drivers/video/console/Makefile
-@@ -16,4 +16,5 @@ obj-$(CONFIG_FRAMEBUFFER_CONSOLE)     += fbcon_rotate.o fbcon_cw.o fbcon_ud.o \
-                                          fbcon_ccw.o
- endif
+@@ -9,4 +9,5 @@ obj-$(CONFIG_STI_CONSOLE)         += sticon.o sticore.o
+ obj-$(CONFIG_VGA_CONSOLE)         += vgacon.o
+ obj-$(CONFIG_MDA_CONSOLE)         += mdacon.o
  
 +obj-$(CONFIG_FB_CON_DECOR)     	  += fbcondecor.o cfbcondecor.o
  obj-$(CONFIG_FB_STI)              += sticore.o
-diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
-index dbfe4ee..14da307 100644
---- a/drivers/video/console/bitblit.c
-+++ b/drivers/video/console/bitblit.c
-@@ -18,6 +18,7 @@
- #include <linux/console.h>
- #include <asm/types.h>
- #include "fbcon.h"
-+#include "fbcondecor.h"
- 
- /*
-  * Accelerated handlers.
-@@ -55,6 +56,13 @@ static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy,
- 	area.height = height * vc->vc_font.height;
- 	area.width = width * vc->vc_font.width;
- 
-+	if (fbcon_decor_active(info, vc)) {
-+		area.sx += vc->vc_decor.tx;
-+		area.sy += vc->vc_decor.ty;
-+		area.dx += vc->vc_decor.tx;
-+		area.dy += vc->vc_decor.ty;
-+	}
-+
- 	info->fbops->fb_copyarea(info, &area);
- }
- 
-@@ -379,11 +387,15 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode,
- 	cursor.image.depth = 1;
- 	cursor.rop = ROP_XOR;
- 
--	if (info->fbops->fb_cursor)
--		err = info->fbops->fb_cursor(info, &cursor);
-+	if (fbcon_decor_active(info, vc)) {
-+		fbcon_decor_cursor(info, &cursor);
-+	} else {
-+		if (info->fbops->fb_cursor)
-+			err = info->fbops->fb_cursor(info, &cursor);
- 
--	if (err)
--		soft_cursor(info, &cursor);
-+		if (err)
-+			soft_cursor(info, &cursor);
-+	}
- 
- 	ops->cursor_reset = 0;
- }
 diff --git a/drivers/video/console/cfbcondecor.c b/drivers/video/console/cfbcondecor.c
 new file mode 100644
-index 0000000..c262540
+index 000000000000..b00960803edc
 --- /dev/null
 +++ b/drivers/video/console/cfbcondecor.c
@@ -362,5 +316,5 @@
 +#include <asm/irq.h>
 +
-+#include "fbcon.h"
++#include "../fbdev/core/fbcon.h"
 +#include "fbcondecor.h"
 +
@@ -811,355 +765,7 @@
 +}
 +
-diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
-index b87f5cf..ce44538 100644
---- a/drivers/video/console/fbcon.c
-+++ b/drivers/video/console/fbcon.c
-@@ -79,6 +79,7 @@
- #include <asm/irq.h>
- 
- #include "fbcon.h"
-+#include "../console/fbcondecor.h"
- 
- #ifdef FBCONDEBUG
- #  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
-@@ -94,7 +95,7 @@ enum {
- 
- static struct display fb_display[MAX_NR_CONSOLES];
- 
--static signed char con2fb_map[MAX_NR_CONSOLES];
-+signed char con2fb_map[MAX_NR_CONSOLES];
- static signed char con2fb_map_boot[MAX_NR_CONSOLES];
- 
- static int logo_lines;
-@@ -282,7 +283,7 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
- 		!vt_force_oops_output(vc);
- }
- 
--static int get_color(struct vc_data *vc, struct fb_info *info,
-+int get_color(struct vc_data *vc, struct fb_info *info,
- 	      u16 c, int is_fg)
- {
- 	int depth = fb_get_color_depth(&info->var, &info->fix);
-@@ -546,6 +547,9 @@ static int do_fbcon_takeover(int show_logo)
- 		info_idx = -1;
- 	} else {
- 		fbcon_has_console_bind = 1;
-+#ifdef CONFIG_FB_CON_DECOR
-+		fbcon_decor_init();
-+#endif
- 	}
- 
- 	return err;
-@@ -1005,6 +1009,12 @@ static const char *fbcon_startup(void)
- 	rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
- 	cols /= vc->vc_font.width;
- 	rows /= vc->vc_font.height;
-+
-+	if (fbcon_decor_active(info, vc)) {
-+		cols = vc->vc_decor.twidth / vc->vc_font.width;
-+		rows = vc->vc_decor.theight / vc->vc_font.height;
-+	}
-+
- 	vc_resize(vc, cols, rows);
- 
- 	DPRINTK("mode:   %s\n", info->fix.id);
-@@ -1034,7 +1044,7 @@ static void fbcon_init(struct vc_data *vc, int init)
- 	cap = info->flags;
- 
- 	if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
--	    (info->fix.type == FB_TYPE_TEXT))
-+	    (info->fix.type == FB_TYPE_TEXT) || fbcon_decor_active(info, vc))
- 		logo = 0;
- 
- 	if (var_to_display(p, &info->var, info))
-@@ -1259,6 +1269,11 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
- 		fbcon_clear_margins(vc, 0);
- 	}
- 
-+	if (fbcon_decor_active(info, vc)) {
-+		fbcon_decor_clear(vc, info, sy, sx, height, width);
-+		return;
-+	}
-+
- 	/* Split blits that cross physical y_wrap boundary */
- 
- 	y_break = p->vrows - p->yscroll;
-@@ -1278,10 +1293,15 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
- 	struct display *p = &fb_display[vc->vc_num];
- 	struct fbcon_ops *ops = info->fbcon_par;
- 
--	if (!fbcon_is_inactive(vc, info))
--		ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
--			   get_color(vc, info, scr_readw(s), 1),
--			   get_color(vc, info, scr_readw(s), 0));
-+	if (!fbcon_is_inactive(vc, info)) {
-+
-+		if (fbcon_decor_active(info, vc))
-+			fbcon_decor_putcs(vc, info, s, count, ypos, xpos);
-+		else
-+			ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
-+				   get_color(vc, info, scr_readw(s), 1),
-+				   get_color(vc, info, scr_readw(s), 0));
-+	}
- }
- 
- static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
-@@ -1297,8 +1317,12 @@ static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
- 	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
- 	struct fbcon_ops *ops = info->fbcon_par;
- 
--	if (!fbcon_is_inactive(vc, info))
--		ops->clear_margins(vc, info, bottom_only);
-+	if (!fbcon_is_inactive(vc, info)) {
-+		if (fbcon_decor_active(info, vc))
-+			fbcon_decor_clear_margins(vc, info, bottom_only);
-+		else
-+			ops->clear_margins(vc, info, bottom_only);
-+	}
- }
- 
- static void fbcon_cursor(struct vc_data *vc, int mode)
-@@ -1819,7 +1843,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
- 			count = vc->vc_rows;
- 		if (softback_top)
- 			fbcon_softback_note(vc, t, count);
--		if (logo_shown >= 0)
-+		if (logo_shown >= 0 || fbcon_decor_active(info, vc))
- 			goto redraw_up;
- 		switch (p->scrollmode) {
- 		case SCROLL_MOVE:
-@@ -1912,6 +1936,8 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
- 			count = vc->vc_rows;
- 		if (logo_shown >= 0)
- 			goto redraw_down;
-+		if (fbcon_decor_active(info, vc))
-+			goto redraw_down;
- 		switch (p->scrollmode) {
- 		case SCROLL_MOVE:
- 			fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
-@@ -2060,6 +2086,13 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s
- 		}
- 		return;
- 	}
-+
-+	if (fbcon_decor_active(info, vc) && sy == dy && height == 1) {
-+		/* must use slower redraw bmove to keep background pic intact */
-+		fbcon_decor_bmove_redraw(vc, info, sy, sx, dx, width);
-+		return;
-+	}
-+
- 	ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
- 		   height, width);
- }
-@@ -2130,8 +2163,8 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
- 	var.yres = virt_h * virt_fh;
- 	x_diff = info->var.xres - var.xres;
- 	y_diff = info->var.yres - var.yres;
--	if (x_diff < 0 || x_diff > virt_fw ||
--	    y_diff < 0 || y_diff > virt_fh) {
-+	if ((x_diff < 0 || x_diff > virt_fw ||
-+		y_diff < 0 || y_diff > virt_fh) && !vc->vc_decor.state) {
- 		const struct fb_videomode *mode;
- 
- 		DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
-@@ -2167,6 +2200,22 @@ static int fbcon_switch(struct vc_data *vc)
- 
- 	info = registered_fb[con2fb_map[vc->vc_num]];
- 	ops = info->fbcon_par;
-+	prev_console = ops->currcon;
-+	if (prev_console != -1)
-+		old_info = registered_fb[con2fb_map[prev_console]];
-+
-+#ifdef CONFIG_FB_CON_DECOR
-+	if (!fbcon_decor_active_vc(vc) && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
-+		struct vc_data *vc_curr = vc_cons[prev_console].d;
-+
-+		if (vc_curr && fbcon_decor_active_vc(vc_curr)) {
-+			// Clear the screen to avoid displaying funky colors
-+			// during palette updates.
-+			memset((u8 *)info->screen_base + info->fix.line_length * info->var.yoffset,
-+			       0, info->var.yres * info->fix.line_length);
-+		}
-+	}
-+#endif
- 
- 	if (softback_top) {
- 		if (softback_lines)
-@@ -2185,9 +2234,6 @@ static int fbcon_switch(struct vc_data *vc)
- 		logo_shown = FBCON_LOGO_CANSHOW;
- 	}
- 
--	prev_console = ops->currcon;
--	if (prev_console != -1)
--		old_info = registered_fb[con2fb_map[prev_console]];
- 	/*
- 	 * FIXME: If we have multiple fbdev's loaded, we need to
- 	 * update all info->currcon.  Perhaps, we can place this
-@@ -2231,6 +2277,18 @@ static int fbcon_switch(struct vc_data *vc)
- 			fbcon_del_cursor_timer(old_info);
- 	}
- 
-+	if (fbcon_decor_active_vc(vc)) {
-+		struct vc_data *vc_curr = vc_cons[prev_console].d;
-+
-+		if (!vc_curr->vc_decor.theme ||
-+			strcmp(vc->vc_decor.theme, vc_curr->vc_decor.theme) ||
-+			(fbcon_decor_active_nores(info, vc_curr) &&
-+			 !fbcon_decor_active(info, vc_curr))) {
-+			fbcon_decor_disable(vc, 0);
-+			fbcon_decor_call_helper("modechange", vc->vc_num);
-+		}
-+	}
-+
- 	if (fbcon_is_inactive(vc, info) ||
- 	    ops->blank_state != FB_BLANK_UNBLANK)
- 		fbcon_del_cursor_timer(info);
-@@ -2339,15 +2397,20 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
- 		}
- 	}
- 
-- 	if (!fbcon_is_inactive(vc, info)) {
-+	if (!fbcon_is_inactive(vc, info)) {
- 		if (ops->blank_state != blank) {
- 			ops->blank_state = blank;
- 			fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
- 			ops->cursor_flash = (!blank);
- 
--			if (!(info->flags & FBINFO_MISC_USEREVENT))
--				if (fb_blank(info, blank))
--					fbcon_generic_blank(vc, info, blank);
-+			if (!(info->flags & FBINFO_MISC_USEREVENT)) {
-+				if (fb_blank(info, blank)) {
-+					if (fbcon_decor_active(info, vc))
-+						fbcon_decor_blank(vc, info, blank);
-+					else
-+						fbcon_generic_blank(vc, info, blank);
-+				}
-+			}
- 		}
- 
- 		if (!blank)
-@@ -2522,13 +2585,22 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
- 	}
- 
- 	if (resize) {
-+		/* reset wrap/pan */
- 		int cols, rows;
- 
- 		cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
- 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
-+
-+		if (fbcon_decor_active(info, vc)) {
-+			info->var.xoffset = info->var.yoffset = p->yscroll = 0;
-+			cols = vc->vc_decor.twidth;
-+			rows = vc->vc_decor.theight;
-+		}
- 		cols /= w;
- 		rows /= h;
-+
- 		vc_resize(vc, cols, rows);
-+
- 		if (con_is_visible(vc) && softback_buf)
- 			fbcon_update_softback(vc);
- 	} else if (con_is_visible(vc)
-@@ -2657,7 +2729,11 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
- 	int i, j, k, depth;
- 	u8 val;
- 
--	if (fbcon_is_inactive(vc, info))
-+	if (fbcon_is_inactive(vc, info)
-+#ifdef CONFIG_FB_CON_DECOR
-+			|| vc->vc_num != fg_console
-+#endif
-+		)
- 		return;
- 
- 	if (!con_is_visible(vc))
-@@ -2683,7 +2759,47 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
- 	} else
- 		fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
- 
--	fb_set_cmap(&palette_cmap, info);
-+	if (fbcon_decor_active(info, vc_cons[fg_console].d) &&
-+	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
-+
-+		u16 *red, *green, *blue;
-+		int minlen = min(min(info->var.red.length, info->var.green.length),
-+				     info->var.blue.length);
-+
-+		struct fb_cmap cmap = {
-+			.start = 0,
-+			.len = (1 << minlen),
-+			.red = NULL,
-+			.green = NULL,
-+			.blue = NULL,
-+			.transp = NULL
-+		};
-+
-+		red = kmalloc(256 * sizeof(u16) * 3, GFP_KERNEL);
-+
-+		if (!red)
-+			goto out;
-+
-+		green = red + 256;
-+		blue = green + 256;
-+		cmap.red = red;
-+		cmap.green = green;
-+		cmap.blue = blue;
-+
-+		for (i = 0; i < cmap.len; i++)
-+			red[i] = green[i] = blue[i] = (0xffff * i)/(cmap.len-1);
-+
-+		fb_set_cmap(&cmap, info);
-+		fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d);
-+		kfree(red);
-+
-+		return;
-+
-+	} else if (fbcon_decor_active(info, vc_cons[fg_console].d) &&
-+		   info->var.bits_per_pixel == 8 && info->bgdecor.cmap.red != NULL)
-+		fb_set_cmap(&info->bgdecor.cmap, info);
-+
-+out:	fb_set_cmap(&palette_cmap, info);
- }
- 
- static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
-@@ -2908,7 +3024,14 @@ static void fbcon_modechanged(struct fb_info *info)
- 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
- 		cols /= vc->vc_font.width;
- 		rows /= vc->vc_font.height;
--		vc_resize(vc, cols, rows);
-+
-+		if (!fbcon_decor_active_nores(info, vc)) {
-+			vc_resize(vc, cols, rows);
-+		} else {
-+			fbcon_decor_disable(vc, 0);
-+			fbcon_decor_call_helper("modechange", vc->vc_num);
-+		}
-+
- 		updatescrollmode(p, info, vc);
- 		scrollback_max = 0;
- 		scrollback_current = 0;
-@@ -2953,7 +3076,8 @@ static void fbcon_set_all_vcs(struct fb_info *info)
- 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
- 		cols /= vc->vc_font.width;
- 		rows /= vc->vc_font.height;
--		vc_resize(vc, cols, rows);
-+		if (!fbcon_decor_active_nores(info, vc))
-+			vc_resize(vc, cols, rows);
- 	}
- 
- 	if (fg != -1)
-@@ -3594,6 +3718,7 @@ static void fbcon_exit(void)
- 		}
- 	}
- 
-+	fbcon_decor_exit();
- 	fbcon_has_exited = 1;
- }
- 
 diff --git a/drivers/video/console/fbcondecor.c b/drivers/video/console/fbcondecor.c
 new file mode 100644
-index 0000000..65cc0d3
+index 000000000000..78288a497a60
 --- /dev/null
 +++ b/drivers/video/console/fbcondecor.c
@@ -1202,9 +808,9 @@
 +#include <linux/compat.h>
 +#include <linux/console.h>
-+
++#include <linux/binfmts.h>
 +#include <linux/uaccess.h>
 +#include <asm/irq.h>
 +
-+#include "fbcon.h"
++#include "../fbdev/core/fbcon.h"
 +#include "fbcondecor.h"
 +
@@ -1337,5 +943,5 @@
 +		return -EINVAL;
 +
-+	len = strlen_user(cfg->theme);
++	len = strnlen_user(cfg->theme, MAX_ARG_STRLEN);
 +	if (!len || len > FBCON_DECOR_THEME_LEN)
 +		return -EINVAL;
@@ -1716,5 +1322,5 @@
 diff --git a/drivers/video/console/fbcondecor.h b/drivers/video/console/fbcondecor.h
 new file mode 100644
-index 0000000..c49386c
+index 000000000000..c49386c16695
 --- /dev/null
 +++ b/drivers/video/console/fbcondecor.h
@@ -1798,8 +1404,8 @@
 +#endif /* __FBCON_DECOR_H */
 diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
-index 88b008f..c84113d 100644
+index 5e58f5ec0a28..1daa8c2cb2d8 100644
 --- a/drivers/video/fbdev/Kconfig
 +++ b/drivers/video/fbdev/Kconfig
-@@ -1216,7 +1216,6 @@ config FB_MATROX
+@@ -1226,7 +1226,6 @@ config FB_MATROX
  	select FB_CFB_FILLRECT
  	select FB_CFB_COPYAREA
@@ -1809,6 +1415,52 @@
  	---help---
  	  Say Y here if you have a Matrox Millennium, Matrox Millennium II,
+diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c
+index 790900d646c0..3f940c93752c 100644
+--- a/drivers/video/fbdev/core/bitblit.c
++++ b/drivers/video/fbdev/core/bitblit.c
+@@ -18,6 +18,7 @@
+ #include <linux/console.h>
+ #include <asm/types.h>
+ #include "fbcon.h"
++#include "../../console/fbcondecor.h"
+ 
+ /*
+  * Accelerated handlers.
+@@ -55,6 +56,13 @@ static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy,
+ 	area.height = height * vc->vc_font.height;
+ 	area.width = width * vc->vc_font.width;
+ 
++	if (fbcon_decor_active(info, vc)) {
++		area.sx += vc->vc_decor.tx;
++		area.sy += vc->vc_decor.ty;
++		area.dx += vc->vc_decor.tx;
++		area.dy += vc->vc_decor.ty;
++	}
++
+ 	info->fbops->fb_copyarea(info, &area);
+ }
+ 
+@@ -379,11 +387,15 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode,
+ 	cursor.image.depth = 1;
+ 	cursor.rop = ROP_XOR;
+ 
+-	if (info->fbops->fb_cursor)
+-		err = info->fbops->fb_cursor(info, &cursor);
++	if (fbcon_decor_active(info, vc)) {
++		fbcon_decor_cursor(info, &cursor);
++	} else {
++		if (info->fbops->fb_cursor)
++			err = info->fbops->fb_cursor(info, &cursor);
+ 
+-	if (err)
+-		soft_cursor(info, &cursor);
++		if (err)
++			soft_cursor(info, &cursor);
++	}
+ 
+ 	ops->cursor_reset = 0;
+ }
 diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c
-index f89245b..c2c12ce 100644
+index 68a113594808..21f977cb59d2 100644
 --- a/drivers/video/fbdev/core/fbcmap.c
 +++ b/drivers/video/fbdev/core/fbcmap.c
@@ -1822,5 +1474,5 @@
      0x0000, 0xaaaa
  };
-@@ -254,9 +256,12 @@ int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info)
+@@ -256,9 +258,12 @@ int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info)
  				break;
  		}
@@ -1837,9 +1489,357 @@
  }
  
+diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
+index 04612f938bab..95c349200078 100644
+--- a/drivers/video/fbdev/core/fbcon.c
++++ b/drivers/video/fbdev/core/fbcon.c
+@@ -80,6 +80,7 @@
+ #include <asm/irq.h>
+ 
+ #include "fbcon.h"
++#include "../../console/fbcondecor.h"
+ 
+ #ifdef FBCONDEBUG
+ #  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
+@@ -95,7 +96,7 @@ enum {
+ 
+ static struct display fb_display[MAX_NR_CONSOLES];
+ 
+-static signed char con2fb_map[MAX_NR_CONSOLES];
++signed char con2fb_map[MAX_NR_CONSOLES];
+ static signed char con2fb_map_boot[MAX_NR_CONSOLES];
+ 
+ static int logo_lines;
+@@ -282,7 +283,7 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
+ 		!vt_force_oops_output(vc);
+ }
+ 
+-static int get_color(struct vc_data *vc, struct fb_info *info,
++int get_color(struct vc_data *vc, struct fb_info *info,
+ 	      u16 c, int is_fg)
+ {
+ 	int depth = fb_get_color_depth(&info->var, &info->fix);
+@@ -551,6 +552,9 @@ static int do_fbcon_takeover(int show_logo)
+ 		info_idx = -1;
+ 	} else {
+ 		fbcon_has_console_bind = 1;
++#ifdef CONFIG_FB_CON_DECOR
++		fbcon_decor_init();
++#endif
+ 	}
+ 
+ 	return err;
+@@ -1013,6 +1017,12 @@ static const char *fbcon_startup(void)
+ 	rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
+ 	cols /= vc->vc_font.width;
+ 	rows /= vc->vc_font.height;
++
++	if (fbcon_decor_active(info, vc)) {
++		cols = vc->vc_decor.twidth / vc->vc_font.width;
++		rows = vc->vc_decor.theight / vc->vc_font.height;
++	}
++
+ 	vc_resize(vc, cols, rows);
+ 
+ 	DPRINTK("mode:   %s\n", info->fix.id);
+@@ -1042,7 +1052,7 @@ static void fbcon_init(struct vc_data *vc, int init)
+ 	cap = info->flags;
+ 
+ 	if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
+-	    (info->fix.type == FB_TYPE_TEXT))
++	    (info->fix.type == FB_TYPE_TEXT) || fbcon_decor_active(info, vc))
+ 		logo = 0;
+ 
+ 	if (var_to_display(p, &info->var, info))
+@@ -1275,6 +1285,11 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
+ 		fbcon_clear_margins(vc, 0);
+ 	}
+ 
++	if (fbcon_decor_active(info, vc)) {
++		fbcon_decor_clear(vc, info, sy, sx, height, width);
++		return;
++	}
++
+ 	/* Split blits that cross physical y_wrap boundary */
+ 
+ 	y_break = p->vrows - p->yscroll;
+@@ -1294,10 +1309,15 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
+ 	struct display *p = &fb_display[vc->vc_num];
+ 	struct fbcon_ops *ops = info->fbcon_par;
+ 
+-	if (!fbcon_is_inactive(vc, info))
+-		ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
+-			   get_color(vc, info, scr_readw(s), 1),
+-			   get_color(vc, info, scr_readw(s), 0));
++	if (!fbcon_is_inactive(vc, info)) {
++
++		if (fbcon_decor_active(info, vc))
++			fbcon_decor_putcs(vc, info, s, count, ypos, xpos);
++		else
++			ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
++				   get_color(vc, info, scr_readw(s), 1),
++				   get_color(vc, info, scr_readw(s), 0));
++	}
+ }
+ 
+ static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
+@@ -1313,8 +1333,12 @@ static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
+ 	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
+ 	struct fbcon_ops *ops = info->fbcon_par;
+ 
+-	if (!fbcon_is_inactive(vc, info))
+-		ops->clear_margins(vc, info, margin_color, bottom_only);
++	if (!fbcon_is_inactive(vc, info)) {
++		if (fbcon_decor_active(info, vc))
++			fbcon_decor_clear_margins(vc, info, bottom_only);
++		else
++			ops->clear_margins(vc, info, margin_color, bottom_only);
++	}
+ }
+ 
+ static void fbcon_cursor(struct vc_data *vc, int mode)
+@@ -1835,7 +1859,7 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
+ 			count = vc->vc_rows;
+ 		if (softback_top)
+ 			fbcon_softback_note(vc, t, count);
+-		if (logo_shown >= 0)
++		if (logo_shown >= 0 || fbcon_decor_active(info, vc))
+ 			goto redraw_up;
+ 		switch (p->scrollmode) {
+ 		case SCROLL_MOVE:
+@@ -1928,6 +1952,8 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
+ 			count = vc->vc_rows;
+ 		if (logo_shown >= 0)
+ 			goto redraw_down;
++		if (fbcon_decor_active(info, vc))
++			goto redraw_down;
+ 		switch (p->scrollmode) {
+ 		case SCROLL_MOVE:
+ 			fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
+@@ -2076,6 +2102,13 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s
+ 		}
+ 		return;
+ 	}
++
++	if (fbcon_decor_active(info, vc) && sy == dy && height == 1) {
++		/* must use slower redraw bmove to keep background pic intact */
++		fbcon_decor_bmove_redraw(vc, info, sy, sx, dx, width);
++		return;
++	}
++
+ 	ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
+ 		   height, width);
+ }
+@@ -2146,8 +2179,8 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
+ 	var.yres = virt_h * virt_fh;
+ 	x_diff = info->var.xres - var.xres;
+ 	y_diff = info->var.yres - var.yres;
+-	if (x_diff < 0 || x_diff > virt_fw ||
+-	    y_diff < 0 || y_diff > virt_fh) {
++	if ((x_diff < 0 || x_diff > virt_fw ||
++		y_diff < 0 || y_diff > virt_fh) && !vc->vc_decor.state) {
+ 		const struct fb_videomode *mode;
+ 
+ 		DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
+@@ -2183,6 +2216,22 @@ static int fbcon_switch(struct vc_data *vc)
+ 
+ 	info = registered_fb[con2fb_map[vc->vc_num]];
+ 	ops = info->fbcon_par;
++	prev_console = ops->currcon;
++	if (prev_console != -1)
++		old_info = registered_fb[con2fb_map[prev_console]];
++
++#ifdef CONFIG_FB_CON_DECOR
++	if (!fbcon_decor_active_vc(vc) && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
++		struct vc_data *vc_curr = vc_cons[prev_console].d;
++
++		if (vc_curr && fbcon_decor_active_vc(vc_curr)) {
++			// Clear the screen to avoid displaying funky colors
++			// during palette updates.
++			memset((u8 *)info->screen_base + info->fix.line_length * info->var.yoffset,
++			       0, info->var.yres * info->fix.line_length);
++		}
++	}
++#endif
+ 
+ 	if (softback_top) {
+ 		if (softback_lines)
+@@ -2201,9 +2250,6 @@ static int fbcon_switch(struct vc_data *vc)
+ 		logo_shown = FBCON_LOGO_CANSHOW;
+ 	}
+ 
+-	prev_console = ops->currcon;
+-	if (prev_console != -1)
+-		old_info = registered_fb[con2fb_map[prev_console]];
+ 	/*
+ 	 * FIXME: If we have multiple fbdev's loaded, we need to
+ 	 * update all info->currcon.  Perhaps, we can place this
+@@ -2247,6 +2293,18 @@ static int fbcon_switch(struct vc_data *vc)
+ 			fbcon_del_cursor_timer(old_info);
+ 	}
+ 
++	if (fbcon_decor_active_vc(vc)) {
++		struct vc_data *vc_curr = vc_cons[prev_console].d;
++
++		if (!vc_curr->vc_decor.theme ||
++			strcmp(vc->vc_decor.theme, vc_curr->vc_decor.theme) ||
++			(fbcon_decor_active_nores(info, vc_curr) &&
++			 !fbcon_decor_active(info, vc_curr))) {
++			fbcon_decor_disable(vc, 0);
++			fbcon_decor_call_helper("modechange", vc->vc_num);
++		}
++	}
++
+ 	if (fbcon_is_inactive(vc, info) ||
+ 	    ops->blank_state != FB_BLANK_UNBLANK)
+ 		fbcon_del_cursor_timer(info);
+@@ -2355,15 +2413,20 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
+ 		}
+ 	}
+ 
+- 	if (!fbcon_is_inactive(vc, info)) {
++	if (!fbcon_is_inactive(vc, info)) {
+ 		if (ops->blank_state != blank) {
+ 			ops->blank_state = blank;
+ 			fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
+ 			ops->cursor_flash = (!blank);
+ 
+-			if (!(info->flags & FBINFO_MISC_USEREVENT))
+-				if (fb_blank(info, blank))
+-					fbcon_generic_blank(vc, info, blank);
++			if (!(info->flags & FBINFO_MISC_USEREVENT)) {
++				if (fb_blank(info, blank)) {
++					if (fbcon_decor_active(info, vc))
++						fbcon_decor_blank(vc, info, blank);
++					else
++						fbcon_generic_blank(vc, info, blank);
++				}
++			}
+ 		}
+ 
+ 		if (!blank)
+@@ -2546,13 +2609,22 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
+ 		set_vc_hi_font(vc, true);
+ 
+ 	if (resize) {
++		/* reset wrap/pan */
+ 		int cols, rows;
+ 
+ 		cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
+ 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
++
++		if (fbcon_decor_active(info, vc)) {
++			info->var.xoffset = info->var.yoffset = p->yscroll = 0;
++			cols = vc->vc_decor.twidth;
++			rows = vc->vc_decor.theight;
++		}
+ 		cols /= w;
+ 		rows /= h;
++
+ 		vc_resize(vc, cols, rows);
++
+ 		if (con_is_visible(vc) && softback_buf)
+ 			fbcon_update_softback(vc);
+ 	} else if (con_is_visible(vc)
+@@ -2681,7 +2753,11 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
+ 	int i, j, k, depth;
+ 	u8 val;
+ 
+-	if (fbcon_is_inactive(vc, info))
++	if (fbcon_is_inactive(vc, info)
++#ifdef CONFIG_FB_CON_DECOR
++			|| vc->vc_num != fg_console
++#endif
++		)
+ 		return;
+ 
+ 	if (!con_is_visible(vc))
+@@ -2707,7 +2783,47 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
+ 	} else
+ 		fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
+ 
+-	fb_set_cmap(&palette_cmap, info);
++	if (fbcon_decor_active(info, vc_cons[fg_console].d) &&
++	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
++
++		u16 *red, *green, *blue;
++		int minlen = min(min(info->var.red.length, info->var.green.length),
++				     info->var.blue.length);
++
++		struct fb_cmap cmap = {
++			.start = 0,
++			.len = (1 << minlen),
++			.red = NULL,
++			.green = NULL,
++			.blue = NULL,
++			.transp = NULL
++		};
++
++		red = kmalloc(256 * sizeof(u16) * 3, GFP_KERNEL);
++
++		if (!red)
++			goto out;
++
++		green = red + 256;
++		blue = green + 256;
++		cmap.red = red;
++		cmap.green = green;
++		cmap.blue = blue;
++
++		for (i = 0; i < cmap.len; i++)
++			red[i] = green[i] = blue[i] = (0xffff * i)/(cmap.len-1);
++
++		fb_set_cmap(&cmap, info);
++		fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d);
++		kfree(red);
++
++		return;
++
++	} else if (fbcon_decor_active(info, vc_cons[fg_console].d) &&
++		   info->var.bits_per_pixel == 8 && info->bgdecor.cmap.red != NULL)
++		fb_set_cmap(&info->bgdecor.cmap, info);
++
++out:	fb_set_cmap(&palette_cmap, info);
+ }
+ 
+ static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
+@@ -2932,7 +3048,14 @@ static void fbcon_modechanged(struct fb_info *info)
+ 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
+ 		cols /= vc->vc_font.width;
+ 		rows /= vc->vc_font.height;
+-		vc_resize(vc, cols, rows);
++
++		if (!fbcon_decor_active_nores(info, vc)) {
++			vc_resize(vc, cols, rows);
++		} else {
++			fbcon_decor_disable(vc, 0);
++			fbcon_decor_call_helper("modechange", vc->vc_num);
++		}
++
+ 		updatescrollmode(p, info, vc);
+ 		scrollback_max = 0;
+ 		scrollback_current = 0;
+@@ -2977,7 +3100,8 @@ static void fbcon_set_all_vcs(struct fb_info *info)
+ 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
+ 		cols /= vc->vc_font.width;
+ 		rows /= vc->vc_font.height;
+-		vc_resize(vc, cols, rows);
++		if (!fbcon_decor_active_nores(info, vc))
++			vc_resize(vc, cols, rows);
+ 	}
+ 
+ 	if (fg != -1)
+@@ -3618,6 +3742,7 @@ static void fbcon_exit(void)
+ 		}
+ 	}
+ 
++	fbcon_decor_exit();
+ 	fbcon_has_exited = 1;
+ }
+ 
 diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
-index 76c1ad9..fafc0af 100644
+index f741ba8df01b..b0141433d249 100644
 --- a/drivers/video/fbdev/core/fbmem.c
 +++ b/drivers/video/fbdev/core/fbmem.c
-@@ -1251,15 +1251,6 @@ struct fb_fix_screeninfo32 {
+@@ -1253,15 +1253,6 @@ struct fb_fix_screeninfo32 {
  	u16			reserved[3];
  };
@@ -1859,5 +1859,5 @@
 diff --git a/include/linux/console_decor.h b/include/linux/console_decor.h
 new file mode 100644
-index 0000000..1514355
+index 000000000000..15143556c2aa
 --- /dev/null
 +++ b/include/linux/console_decor.h
@@ -1910,8 +1910,8 @@
 +#endif
 diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h
-index 6fd3c90..c649555 100644
+index c0ec478ea5bf..8bfed6b21fc9 100644
 --- a/include/linux/console_struct.h
 +++ b/include/linux/console_struct.h
-@@ -20,6 +20,7 @@ struct vt_struct;
+@@ -21,6 +21,7 @@ struct vt_struct;
  struct uni_pagedir;
  
@@ -1921,5 +1921,5 @@
  /*
   * Example: vc_data of a console that was scrolled 3 lines down.
-@@ -140,6 +141,8 @@ struct vc_data {
+@@ -141,6 +142,8 @@ struct vc_data {
  	struct uni_pagedir *vc_uni_pagedir;
  	struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */
@@ -1931,8 +1931,8 @@
  
 diff --git a/include/linux/fb.h b/include/linux/fb.h
-index a964d07..672cc64 100644
+index bc24e48e396d..ad7d182c7545 100644
 --- a/include/linux/fb.h
 +++ b/include/linux/fb.h
-@@ -238,6 +238,34 @@ struct fb_deferred_io {
+@@ -239,6 +239,34 @@ struct fb_deferred_io {
  };
  #endif
@@ -1969,5 +1969,5 @@
   * Frame buffer operations
   *
-@@ -508,6 +536,9 @@ struct fb_info {
+@@ -509,6 +537,9 @@ struct fb_info {
  #define FBINFO_STATE_SUSPENDED	1
  	u32 state;			/* Hardware state i.e suspend */
@@ -1980,8 +1980,8 @@
  	/* we need the PCI or similar aperture base/size not
 diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h
-index fb795c3..4b57c67 100644
+index 6cd9b198b7c6..a228440649fa 100644
 --- a/include/uapi/linux/fb.h
 +++ b/include/uapi/linux/fb.h
-@@ -8,6 +8,23 @@
+@@ -9,6 +9,23 @@
  
  #define FB_MAX			32	/* sufficient for now */
@@ -2007,5 +2007,5 @@
     0x46 is 'F'								*/
  #define FBIOGET_VSCREENINFO	0x4600
-@@ -35,6 +52,25 @@
+@@ -36,6 +53,25 @@
  #define FBIOGET_DISPINFO        0x4618
  #define FBIO_WAITFORVSYNC	_IOW('F', 0x20, __u32)
@@ -2033,5 +2033,5 @@
  #define FB_TYPE_PLANES			1	/* Non interleaved planes */
  #define FB_TYPE_INTERLEAVED_PLANES	2	/* Interleaved planes	*/
-@@ -277,6 +313,29 @@ struct fb_var_screeninfo {
+@@ -278,6 +314,29 @@ struct fb_var_screeninfo {
  	__u32 reserved[4];		/* Reserved for future compatibility */
  };
@@ -2064,8 +2064,8 @@
  	__u32 len;			/* Number of entries */
 diff --git a/kernel/sysctl.c b/kernel/sysctl.c
-index 6ee416e..d2c2425 100644
+index d9c31bc2eaea..e33ac56cc32a 100644
 --- a/kernel/sysctl.c
 +++ b/kernel/sysctl.c
-@@ -149,6 +149,10 @@ static const int cap_last_cap = CAP_LAST_CAP;
+@@ -150,6 +150,10 @@ static const int cap_last_cap = CAP_LAST_CAP;
  static unsigned long hung_task_timeout_max = (LONG_MAX/HZ);
  #endif
@@ -2078,5 +2078,5 @@
  #include <linux/inotify.h>
  #endif
-@@ -266,6 +270,15 @@ static struct ctl_table sysctl_base_table[] = {
+@@ -283,6 +287,15 @@ static struct ctl_table sysctl_base_table[] = {
  		.mode		= 0555,
  		.child		= dev_table,
Index: npl/kernel/linux_src/config
===================================================================
--- npl/kernel/linux_src/config	(revision f6630dda9bbc206e69f0475bfb8672e20d5beaa1)
+++ npl/kernel/linux_src/config	(revision e8565120de16c582dc1bd83033f7d7370bacd011)
@@ -1,5 +1,5 @@
 #
 # Automatically generated file; DO NOT EDIT.
-# Linux/x86 4.9.215 Kernel Configuration
+# Linux/x86 4.14.172 Kernel Configuration
 #
 # CONFIG_64BIT is not set
@@ -40,5 +40,4 @@
 CONFIG_ARCH_SUPPORTS_UPROBES=y
 CONFIG_FIX_EARLYCON_MEM=y
-CONFIG_DEBUG_RODATA=y
 CONFIG_PGTABLE_LEVELS=3
 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
@@ -84,5 +83,7 @@
 CONFIG_GENERIC_IRQ_PROBE=y
 CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
 CONFIG_GENERIC_PENDING_IRQ=y
+CONFIG_GENERIC_IRQ_MIGRATION=y
 CONFIG_IRQ_DOMAIN=y
 CONFIG_IRQ_DOMAIN_HIERARCHY=y
@@ -128,8 +129,8 @@
 # CONFIG_RCU_EXPERT is not set
 CONFIG_SRCU=y
+CONFIG_TREE_SRCU=y
 # CONFIG_TASKS_RCU is not set
 CONFIG_RCU_STALL_COMMON=y
-# CONFIG_TREE_RCU_TRACE is not set
-# CONFIG_RCU_EXPEDITE_BOOT is not set
+CONFIG_RCU_NEED_SEGCBLIST=y
 CONFIG_BUILD_BIN2C=y
 CONFIG_IKCONFIG=y
@@ -137,5 +138,5 @@
 CONFIG_LOG_BUF_SHIFT=17
 CONFIG_LOG_CPU_MAX_BUF_SHIFT=12
-CONFIG_NMI_LOG_BUF_SHIFT=13
+CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13
 CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y
 CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y
@@ -148,4 +149,5 @@
 CONFIG_RT_GROUP_SCHED=y
 # CONFIG_CGROUP_PIDS is not set
+# CONFIG_CGROUP_RDMA is not set
 # CONFIG_CGROUP_FREEZER is not set
 # CONFIG_CGROUP_HUGETLB is not set
@@ -155,5 +157,5 @@
 CONFIG_CGROUP_CPUACCT=y
 # CONFIG_CGROUP_PERF is not set
-# CONFIG_CGROUP_DEBUG is not set
+CONFIG_SOCK_CGROUP_DATA=y
 # CONFIG_CHECKPOINT_RESTORE is not set
 CONFIG_NAMESPACES=y
@@ -188,4 +190,5 @@
 CONFIG_SYSFS_SYSCALL=y
 # CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_POSIX_TIMERS=y
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ABSOLUTE_PERCPU is not set
@@ -198,4 +201,5 @@
 CONFIG_BASE_FULL=y
 CONFIG_FUTEX=y
+CONFIG_FUTEX_PI=y
 CONFIG_EPOLL=y
 CONFIG_SIGNALFD=y
@@ -211,4 +215,5 @@
 # CONFIG_EMBEDDED is not set
 CONFIG_HAVE_PERF_EVENTS=y
+# CONFIG_PC104 is not set
 
 #
@@ -220,7 +225,9 @@
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
+CONFIG_SLAB_MERGE_DEFAULT=y
 # CONFIG_SLAB_FREELIST_RANDOM is not set
 # CONFIG_SYSTEM_DATA_VERIFICATION is not set
 # CONFIG_PROFILING is not set
+CONFIG_CRASH_CORE=y
 CONFIG_KEXEC_CORE=y
 CONFIG_HOTPLUG_SMT=y
@@ -243,4 +250,6 @@
 CONFIG_HAVE_DMA_CONTIGUOUS=y
 CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_ARCH_HAS_FORTIFY_SOURCE=y
+CONFIG_ARCH_HAS_SET_MEMORY=y
 CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y
 CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
@@ -251,7 +260,10 @@
 CONFIG_HAVE_USER_RETURN_NOTIFIER=y
 CONFIG_HAVE_PERF_EVENTS_NMI=y
+CONFIG_HAVE_HARDLOCKUP_DETECTOR_PERF=y
 CONFIG_HAVE_PERF_REGS=y
 CONFIG_HAVE_PERF_USER_STACK_DUMP=y
 CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_RCU_TABLE_FREE=y
+CONFIG_HAVE_RCU_TABLE_INVALIDATE=y
 CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
 CONFIG_HAVE_CMPXCHG_LOCAL=y
@@ -267,8 +279,10 @@
 # CONFIG_CC_STACKPROTECTOR_REGULAR is not set
 # CONFIG_CC_STACKPROTECTOR_STRONG is not set
+CONFIG_THIN_ARCHIVES=y
 CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES=y
 CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
 CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
 CONFIG_HAVE_ARCH_HUGE_VMAP=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
 CONFIG_MODULES_USE_ELF_REL=y
 CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
@@ -284,4 +298,12 @@
 # CONFIG_CPU_NO_EFFICIENT_FFS is not set
 # CONFIG_HAVE_ARCH_VMAP_STACK is not set
+# CONFIG_ARCH_OPTIONAL_KERNEL_RWX is not set
+# CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT is not set
+CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y
+CONFIG_STRICT_KERNEL_RWX=y
+CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y
+CONFIG_STRICT_MODULE_RWX=y
+CONFIG_ARCH_HAS_REFCOUNT=y
+# CONFIG_REFCOUNT_FULL is not set
 
 #
@@ -305,8 +327,14 @@
 CONFIG_BLOCK=y
 CONFIG_LBDAF=y
+CONFIG_BLK_SCSI_REQUEST=y
 CONFIG_BLK_DEV_BSG=y
 CONFIG_BLK_DEV_BSGLIB=y
 CONFIG_BLK_DEV_INTEGRITY=y
+# CONFIG_BLK_DEV_ZONED is not set
 # CONFIG_BLK_CMDLINE_PARSER is not set
+CONFIG_BLK_WBT=y
+CONFIG_BLK_WBT_SQ=y
+CONFIG_BLK_WBT_MQ=y
+# CONFIG_BLK_SED_OPAL is not set
 
 #
@@ -334,4 +362,5 @@
 # CONFIG_CMDLINE_PARTITION is not set
 CONFIG_BLK_MQ_PCI=y
+CONFIG_BLK_MQ_VIRTIO=y
 
 #
@@ -345,5 +374,9 @@
 # CONFIG_DEFAULT_NOOP is not set
 CONFIG_DEFAULT_IOSCHED="cfq"
+CONFIG_MQ_IOSCHED_DEADLINE=y
+CONFIG_MQ_IOSCHED_KYBER=y
+# CONFIG_IOSCHED_BFQ is not set
 CONFIG_PREEMPT_NOTIFIERS=y
+CONFIG_PADATA=y
 CONFIG_ASN1=m
 CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
@@ -372,5 +405,6 @@
 CONFIG_X86_BIGSMP=y
 # CONFIG_GOLDFISH is not set
-CONFIG_RETPOLINE=y
+# CONFIG_RETPOLINE is not set
+# CONFIG_INTEL_RDT is not set
 # CONFIG_X86_EXTENDED_PLATFORM is not set
 CONFIG_X86_INTEL_LPSS=y
@@ -385,5 +419,4 @@
 # CONFIG_XEN is not set
 CONFIG_KVM_GUEST=y
-CONFIG_LGUEST_GUEST=y
 # CONFIG_PARAVIRT_TIME_ACCOUNTING is not set
 CONFIG_PARAVIRT_CLOCK=y
@@ -435,4 +468,5 @@
 CONFIG_SCHED_SMT=y
 CONFIG_SCHED_MC=y
+CONFIG_SCHED_MC_PRIO=y
 CONFIG_PREEMPT_NONE=y
 # CONFIG_PREEMPT_VOLUNTARY is not set
@@ -442,9 +476,9 @@
 CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y
 CONFIG_X86_MCE=y
+# CONFIG_X86_MCELOG_LEGACY is not set
 CONFIG_X86_MCE_INTEL=y
 CONFIG_X86_MCE_AMD=y
 CONFIG_X86_ANCIENT_MCE=y
 CONFIG_X86_MCE_THRESHOLD=y
-# CONFIG_X86_MCE_INJECT is not set
 CONFIG_X86_THERMAL_VECTOR=y
 
@@ -477,4 +511,5 @@
 CONFIG_ARCH_PHYS_ADDR_T_64BIT=y
 CONFIG_ARCH_DMA_ADDR_T_64BIT=y
+CONFIG_ARCH_HAS_MEM_ENCRYPT=y
 # CONFIG_NUMA is not set
 CONFIG_ARCH_FLATMEM_ENABLE=y
@@ -490,4 +525,5 @@
 CONFIG_HAVE_MEMBLOCK=y
 CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
+CONFIG_HAVE_GENERIC_GUP=y
 CONFIG_ARCH_DISCARD_MEMBLOCK=y
 CONFIG_MEMORY_ISOLATION=y
@@ -510,4 +546,5 @@
 CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
 # CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set
+# CONFIG_ARCH_WANTS_THP_SWAP is not set
 CONFIG_TRANSPARENT_HUGE_PAGECACHE=y
 CONFIG_CLEANCACHE=y
@@ -524,4 +561,5 @@
 CONFIG_ARCH_SUPPORTS_DEFERRED_STRUCT_PAGE_INIT=y
 # CONFIG_IDLE_PAGE_TRACKING is not set
+# CONFIG_PERCPU_STATS is not set
 # CONFIG_X86_PMEM_LEGACY is not set
 CONFIG_HIGHPTE=y
@@ -536,5 +574,4 @@
 CONFIG_ARCH_RANDOM=y
 CONFIG_X86_SMAP=y
-# CONFIG_X86_INTEL_MPX is not set
 CONFIG_X86_INTEL_TSX_MODE_OFF=y
 # CONFIG_X86_INTEL_TSX_MODE_ON is not set
@@ -574,4 +611,5 @@
 CONFIG_PM=y
 # CONFIG_PM_DEBUG is not set
+CONFIG_PM_OPP=y
 CONFIG_PM_CLK=y
 # CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set
@@ -610,5 +648,4 @@
 # CONFIG_ACPI_BGRT is not set
 # CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set
-# CONFIG_ACPI_NFIT is not set
 CONFIG_HAVE_ACPI_APEI=y
 CONFIG_HAVE_ACPI_APEI_NMI=y
@@ -699,4 +736,5 @@
 CONFIG_PCIEASPM_DEFAULT=y
 # CONFIG_PCIEASPM_POWERSAVE is not set
+# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set
 # CONFIG_PCIEASPM_PERFORMANCE is not set
 CONFIG_PCIE_PME=y
@@ -710,4 +748,5 @@
 CONFIG_HT_IRQ=y
 CONFIG_PCI_ATS=y
+CONFIG_PCI_LOCKLESS_CONFIG=y
 # CONFIG_PCI_IOV is not set
 # CONFIG_PCI_PRI is not set
@@ -717,7 +756,21 @@
 
 #
+# DesignWare PCI Core Support
+#
+# CONFIG_PCIE_DW_PLAT is not set
+
+#
 # PCI host controller drivers
 #
-# CONFIG_PCIE_DW_PLAT is not set
+
+#
+# PCI Endpoint
+#
+# CONFIG_PCI_ENDPOINT is not set
+
+#
+# PCI switch controller drivers
+#
+# CONFIG_PCI_SW_SWITCHTEC is not set
 CONFIG_ISA_DMA_API=y
 # CONFIG_ISA is not set
@@ -742,6 +795,6 @@
 CONFIG_BINFMT_MISC=y
 CONFIG_COREDUMP=y
+CONFIG_COMPAT_32=y
 CONFIG_HAVE_ATOMIC_IOMAP=y
-CONFIG_PMC_ATOM=y
 CONFIG_NET=y
 CONFIG_NET_INGRESS=y
@@ -755,4 +808,5 @@
 CONFIG_UNIX=y
 CONFIG_UNIX_DIAG=m
+# CONFIG_TLS is not set
 CONFIG_XFRM=y
 CONFIG_XFRM_ALGO=m
@@ -789,4 +843,5 @@
 CONFIG_INET_AH=m
 CONFIG_INET_ESP=m
+# CONFIG_INET_ESP_OFFLOAD is not set
 CONFIG_INET_IPCOMP=m
 CONFIG_INET_XFRM_TUNNEL=m
@@ -798,4 +853,5 @@
 CONFIG_INET_TCP_DIAG=y
 # CONFIG_INET_UDP_DIAG is not set
+# CONFIG_INET_RAW_DIAG is not set
 # CONFIG_INET_DIAG_DESTROY is not set
 CONFIG_TCP_CONG_ADVANCED=y
@@ -832,4 +888,5 @@
 CONFIG_INET6_AH=m
 CONFIG_INET6_ESP=m
+# CONFIG_INET6_ESP_OFFLOAD is not set
 CONFIG_INET6_IPCOMP=m
 CONFIG_IPV6_MIP6=m
@@ -854,9 +911,10 @@
 # CONFIG_IPV6_MROUTE_MULTIPLE_TABLES is not set
 CONFIG_IPV6_PIMSM_V2=y
+# CONFIG_IPV6_SEG6_LWTUNNEL is not set
+# CONFIG_IPV6_SEG6_HMAC is not set
 CONFIG_NETWORK_SECMARK=y
 CONFIG_NET_PTP_CLASSIFY=y
 # CONFIG_NETWORK_PHY_TIMESTAMPING is not set
 CONFIG_NETFILTER=y
-# CONFIG_NETFILTER_DEBUG is not set
 CONFIG_NETFILTER_ADVANCED=y
 CONFIG_BRIDGE_NETFILTER=m
@@ -872,4 +930,5 @@
 CONFIG_NF_CONNTRACK=m
 CONFIG_NF_LOG_COMMON=m
+# CONFIG_NF_LOG_NETDEV is not set
 CONFIG_NF_CONNTRACK_MARK=y
 CONFIG_NF_CONNTRACK_SECMARK=y
@@ -879,8 +938,8 @@
 # CONFIG_NF_CONNTRACK_TIMEOUT is not set
 # CONFIG_NF_CONNTRACK_TIMESTAMP is not set
-CONFIG_NF_CT_PROTO_DCCP=m
+CONFIG_NF_CT_PROTO_DCCP=y
 CONFIG_NF_CT_PROTO_GRE=m
-CONFIG_NF_CT_PROTO_SCTP=m
-CONFIG_NF_CT_PROTO_UDPLITE=m
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
 CONFIG_NF_CONNTRACK_AMANDA=m
 CONFIG_NF_CONNTRACK_FTP=m
@@ -899,7 +958,7 @@
 CONFIG_NF_NAT=m
 CONFIG_NF_NAT_NEEDED=y
-CONFIG_NF_NAT_PROTO_DCCP=m
-CONFIG_NF_NAT_PROTO_UDPLITE=m
-CONFIG_NF_NAT_PROTO_SCTP=m
+CONFIG_NF_NAT_PROTO_DCCP=y
+CONFIG_NF_NAT_PROTO_UDPLITE=y
+CONFIG_NF_NAT_PROTO_SCTP=y
 CONFIG_NF_NAT_AMANDA=m
 CONFIG_NF_NAT_FTP=m
@@ -989,5 +1048,4 @@
 CONFIG_NETFILTER_XT_MATCH_RECENT=m
 CONFIG_NETFILTER_XT_MATCH_SCTP=m
-CONFIG_NETFILTER_XT_MATCH_SOCKET=m
 CONFIG_NETFILTER_XT_MATCH_STATE=m
 CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
@@ -1006,4 +1064,5 @@
 CONFIG_IP_SET_HASH_IPPORTIP=m
 CONFIG_IP_SET_HASH_IPPORTNET=m
+# CONFIG_IP_SET_HASH_IPMAC is not set
 CONFIG_IP_SET_HASH_MAC=m
 CONFIG_IP_SET_HASH_NETPORTNET=m
@@ -1020,4 +1079,5 @@
 CONFIG_NF_DEFRAG_IPV4=m
 CONFIG_NF_CONNTRACK_IPV4=m
+# CONFIG_NF_SOCKET_IPV4 is not set
 CONFIG_NF_DUP_IPV4=m
 # CONFIG_NF_LOG_ARP is not set
@@ -1055,4 +1115,5 @@
 CONFIG_NF_DEFRAG_IPV6=m
 CONFIG_NF_CONNTRACK_IPV6=m
+# CONFIG_NF_SOCKET_IPV6 is not set
 CONFIG_NF_DUP_IPV6=m
 CONFIG_NF_REJECT_IPV6=m
@@ -1156,4 +1217,5 @@
 CONFIG_NET_SCH_INGRESS=m
 CONFIG_NET_SCH_PLUG=m
+# CONFIG_NET_SCH_DEFAULT is not set
 
 #
@@ -1188,4 +1250,5 @@
 CONFIG_GACT_PROB=y
 CONFIG_NET_ACT_MIRRED=m
+# CONFIG_NET_ACT_SAMPLE is not set
 CONFIG_NET_ACT_IPT=m
 # CONFIG_NET_ACT_NAT is not set
@@ -1211,8 +1274,10 @@
 CONFIG_VMWARE_VMCI_VSOCKETS=m
 # CONFIG_VIRTIO_VSOCKETS is not set
+# CONFIG_HYPERV_VSOCKETS is not set
 # CONFIG_NETLINK_DIAG is not set
 CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
 # CONFIG_MPLS_ROUTING is not set
+# CONFIG_NET_NSH is not set
 CONFIG_HSR=m
 # CONFIG_NET_SWITCHDEV is not set
@@ -1222,5 +1287,4 @@
 CONFIG_RFS_ACCEL=y
 CONFIG_XPS=y
-CONFIG_SOCK_CGROUP_DATA=y
 # CONFIG_CGROUP_NET_PRIO is not set
 CONFIG_CGROUP_NET_CLASSID=y
@@ -1235,5 +1299,4 @@
 # CONFIG_HAMRADIO is not set
 # CONFIG_CAN is not set
-# CONFIG_IRDA is not set
 # CONFIG_BT is not set
 # CONFIG_AF_RXRPC is not set
@@ -1255,6 +1318,9 @@
 # CONFIG_CEPH_LIB is not set
 # CONFIG_NFC is not set
+# CONFIG_PSAMPLE is not set
+# CONFIG_NET_IFE is not set
 # CONFIG_LWTUNNEL is not set
 CONFIG_DST_CACHE=y
+CONFIG_GRO_CELLS=y
 # CONFIG_NET_DEVLINK is not set
 CONFIG_MAY_USE_DEVLINK=y
@@ -1278,4 +1344,5 @@
 # CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set
 CONFIG_ALLOW_DEV_COREDUMP=y
+# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set
 # CONFIG_SYS_HYPERVISOR is not set
 # CONFIG_GENERIC_CPU_DEVICES is not set
@@ -1299,4 +1366,8 @@
 # CONFIG_MTD_CMDLINE_PARTS is not set
 CONFIG_MTD_AR7_PARTS=m
+
+#
+# Partition parsers
+#
 
 #
@@ -1383,5 +1454,4 @@
 # CONFIG_MTD_NAND_GPIO is not set
 # CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set
-CONFIG_MTD_NAND_IDS=m
 # CONFIG_MTD_NAND_RICOH is not set
 # CONFIG_MTD_NAND_DISKONCHIP is not set
@@ -1391,6 +1461,4 @@
 # CONFIG_MTD_NAND_NANDSIM is not set
 # CONFIG_MTD_NAND_PLATFORM is not set
-# CONFIG_MTD_NAND_HISI504 is not set
-# CONFIG_MTD_NAND_MTK is not set
 CONFIG_MTD_ONENAND=m
 # CONFIG_MTD_ONENAND_VERIFY_WRITE is not set
@@ -1428,6 +1496,5 @@
 CONFIG_BLK_DEV_PCIESSD_MTIP32XX=m
 CONFIG_ZRAM=m
-CONFIG_BLK_CPQ_CISS_DA=m
-CONFIG_CISS_SCSI_TAPE=y
+# CONFIG_ZRAM_WRITEBACK is not set
 CONFIG_BLK_DEV_DAC960=m
 CONFIG_BLK_DEV_UMEM=m
@@ -1445,10 +1512,10 @@
 CONFIG_ATA_OVER_ETH=m
 CONFIG_VIRTIO_BLK=m
-# CONFIG_BLK_DEV_HD is not set
+# CONFIG_VIRTIO_BLK_SCSI is not set
 # CONFIG_BLK_DEV_RBD is not set
 CONFIG_BLK_DEV_RSXX=m
 CONFIG_NVME_CORE=m
 CONFIG_BLK_DEV_NVME=m
-# CONFIG_BLK_DEV_NVME_SCSI is not set
+# CONFIG_NVME_FC is not set
 # CONFIG_NVME_TARGET is not set
 
@@ -1478,4 +1545,5 @@
 # CONFIG_USB_SWITCH_FSA9480 is not set
 # CONFIG_SRAM is not set
+# CONFIG_PCI_ENDPOINT_TEST is not set
 # CONFIG_C2PORT is not set
 
@@ -1487,4 +1555,5 @@
 # CONFIG_EEPROM_MAX6875 is not set
 # CONFIG_EEPROM_93CX6 is not set
+# CONFIG_EEPROM_IDT_89HPESX is not set
 # CONFIG_CB710_CORE is not set
 
@@ -1538,4 +1607,5 @@
 # CONFIG_CXL_BASE is not set
 # CONFIG_CXL_AFU_DRIVER_OPS is not set
+# CONFIG_CXL_LIB is not set
 CONFIG_HAVE_IDE=y
 # CONFIG_IDE is not set
@@ -1661,4 +1731,6 @@
 CONFIG_SCSI_QLA_FC=m
 CONFIG_SCSI_QLA_ISCSI=m
+# CONFIG_QEDI is not set
+# CONFIG_QEDF is not set
 CONFIG_SCSI_LPFC=m
 CONFIG_SCSI_DC395x=m
@@ -1791,5 +1863,5 @@
 # CONFIG_DM_DEBUG is not set
 CONFIG_DM_BUFIO=m
-# CONFIG_DM_DEBUG_BLOCK_STACK_TRACING is not set
+# CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING is not set
 CONFIG_DM_BIO_PRISON=m
 CONFIG_DM_PERSISTENT_DATA=m
@@ -1799,5 +1871,4 @@
 CONFIG_DM_CACHE=m
 CONFIG_DM_CACHE_SMQ=m
-CONFIG_DM_CACHE_CLEANER=m
 # CONFIG_DM_ERA is not set
 CONFIG_DM_MIRROR=m
@@ -1814,4 +1885,5 @@
 CONFIG_DM_SWITCH=m
 # CONFIG_DM_LOG_WRITES is not set
+# CONFIG_DM_INTEGRITY is not set
 # CONFIG_TARGET_CORE is not set
 CONFIG_FUSION=y
@@ -1878,4 +1950,6 @@
 CONFIG_NET_VENDOR_AGERE=y
 CONFIG_ET131X=m
+CONFIG_NET_VENDOR_ALACRITECH=y
+# CONFIG_SLICOSS is not set
 CONFIG_NET_VENDOR_ALTEON=y
 CONFIG_ACENIC=m
@@ -1887,4 +1961,7 @@
 CONFIG_AMD8111_ETH=m
 CONFIG_PCNET32=m
+# CONFIG_AMD_XGBE is not set
+# CONFIG_AMD_XGBE_HAVE_ECC is not set
+CONFIG_NET_VENDOR_AQUANTIA=y
 CONFIG_NET_VENDOR_ARC=y
 CONFIG_NET_VENDOR_ATHEROS=y
@@ -1898,4 +1975,6 @@
 CONFIG_NET_CADENCE=y
 CONFIG_MACB=m
+CONFIG_MACB_USE_HWSTAMP=y
+# CONFIG_MACB_PCI is not set
 CONFIG_NET_VENDOR_BROADCOM=y
 CONFIG_B44=m
@@ -1903,10 +1982,11 @@
 CONFIG_B44_PCICORE_AUTOSELECT=y
 CONFIG_B44_PCI=y
-CONFIG_BCMGENET=m
 CONFIG_BNX2=m
 CONFIG_CNIC=m
 CONFIG_TIGON3=m
+CONFIG_TIGON3_HWMON=y
 CONFIG_BNX2X=m
 CONFIG_BNXT=m
+CONFIG_BNXT_FLOWER_OFFLOAD=y
 CONFIG_NET_VENDOR_BROCADE=y
 CONFIG_BNA=m
@@ -1949,4 +2029,6 @@
 CONFIG_NET_VENDOR_HP=y
 CONFIG_HP100=m
+CONFIG_NET_VENDOR_HUAWEI=y
+# CONFIG_HINIC is not set
 CONFIG_NET_VENDOR_INTEL=y
 CONFIG_E100=m
@@ -1977,8 +2059,13 @@
 CONFIG_MLX4_DEBUG=y
 CONFIG_MLX5_CORE=m
+# CONFIG_MLX5_FPGA is not set
 # CONFIG_MLX5_CORE_EN is not set
 CONFIG_MLXSW_CORE=m
 CONFIG_MLXSW_CORE_HWMON=y
+CONFIG_MLXSW_CORE_THERMAL=y
 CONFIG_MLXSW_PCI=m
+CONFIG_MLXSW_I2C=m
+CONFIG_MLXSW_MINIMAL=m
+# CONFIG_MLXFW is not set
 CONFIG_NET_VENDOR_MICREL=y
 CONFIG_KS8842=m
@@ -1992,5 +2079,5 @@
 CONFIG_NS83820=m
 CONFIG_NET_VENDOR_NETRONOME=y
-# CONFIG_NFP_NETVF is not set
+# CONFIG_NFP is not set
 CONFIG_NET_VENDOR_8390=y
 CONFIG_NE2K_PCI=m
@@ -2013,4 +2100,5 @@
 CONFIG_NET_VENDOR_QUALCOMM=y
 # CONFIG_QCOM_EMAC is not set
+# CONFIG_RMNET is not set
 CONFIG_NET_VENDOR_REALTEK=y
 CONFIG_8139CP=m
@@ -2033,8 +2121,10 @@
 CONFIG_SIS900=m
 CONFIG_SIS190=m
+CONFIG_NET_VENDOR_SOLARFLARE=y
 CONFIG_SFC=m
 CONFIG_SFC_MTD=y
 CONFIG_SFC_MCDI_MON=y
 CONFIG_SFC_MCDI_LOGGING=y
+# CONFIG_SFC_FALCON is not set
 CONFIG_NET_VENDOR_SMSC=y
 CONFIG_EPIC100=m
@@ -2052,5 +2142,4 @@
 CONFIG_CASSINI=m
 CONFIG_NIU=m
-CONFIG_NET_VENDOR_SYNOPSYS=y
 CONFIG_NET_VENDOR_TEHUTI=y
 CONFIG_TEHUTI=m
@@ -2068,16 +2157,15 @@
 # CONFIG_WIZNET_BUS_INDIRECT is not set
 CONFIG_WIZNET_BUS_ANY=y
+CONFIG_NET_VENDOR_SYNOPSYS=y
+# CONFIG_DWC_XLGMAC is not set
 # CONFIG_FDDI is not set
 # CONFIG_HIPPI is not set
 # CONFIG_NET_SB1000 is not set
+CONFIG_MDIO_DEVICE=m
+CONFIG_MDIO_BUS=m
+CONFIG_MDIO_BITBANG=m
+CONFIG_MDIO_GPIO=m
 CONFIG_PHYLIB=m
 CONFIG_SWPHY=y
-
-#
-# MDIO bus device drivers
-#
-# CONFIG_MDIO_BCM_UNIMAC is not set
-CONFIG_MDIO_BITBANG=m
-CONFIG_MDIO_GPIO=m
 
 #
@@ -2092,4 +2180,5 @@
 CONFIG_BROADCOM_PHY=m
 CONFIG_CICADA_PHY=m
+# CONFIG_CORTINA_PHY is not set
 CONFIG_DAVICOM_PHY=m
 # CONFIG_DP83848_PHY is not set
@@ -2101,4 +2190,5 @@
 CONFIG_LXT_PHY=m
 CONFIG_MARVELL_PHY=m
+# CONFIG_MARVELL_10G_PHY is not set
 CONFIG_MICREL_PHY=m
 # CONFIG_MICROCHIP_PHY is not set
@@ -2107,4 +2197,5 @@
 CONFIG_QSEMI_PHY=m
 CONFIG_REALTEK_PHY=m
+# CONFIG_ROCKCHIP_PHY is not set
 CONFIG_SMSC_PHY=m
 CONFIG_STE10XP=m
@@ -2172,4 +2263,5 @@
 # CONFIG_KEYBOARD_QT1070 is not set
 # CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_DLINK_DIR685 is not set
 # CONFIG_KEYBOARD_LKKBD is not set
 CONFIG_KEYBOARD_GPIO=m
@@ -2188,4 +2280,5 @@
 # CONFIG_KEYBOARD_STOWAWAY is not set
 # CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
 # CONFIG_KEYBOARD_XTKBD is not set
 CONFIG_INPUT_MOUSE=y
@@ -2221,5 +2314,4 @@
 CONFIG_INPUT_PCSPKR=y
 # CONFIG_INPUT_MMA8450 is not set
-# CONFIG_INPUT_MPU3050 is not set
 # CONFIG_INPUT_APANEL is not set
 # CONFIG_INPUT_GP2A is not set
@@ -2263,4 +2355,5 @@
 # CONFIG_SERIO_ARC_PS2 is not set
 CONFIG_HYPERV_KEYBOARD=m
+# CONFIG_SERIO_GPIO_PS2 is not set
 # CONFIG_USERIO is not set
 # CONFIG_GAMEPORT is not set
@@ -2298,4 +2391,5 @@
 CONFIG_SERIAL_8250_DMA=y
 CONFIG_SERIAL_8250_PCI=y
+CONFIG_SERIAL_8250_EXAR=y
 CONFIG_SERIAL_8250_NR_UARTS=2
 CONFIG_SERIAL_8250_RUNTIME_UARTS=2
@@ -2329,7 +2423,9 @@
 CONFIG_SERIAL_RP2_NR_UARTS=32
 # CONFIG_SERIAL_FSL_LPUART is not set
+# CONFIG_SERIAL_DEV_BUS is not set
 CONFIG_HVC_DRIVER=y
-CONFIG_VIRTIO_CONSOLE=y
+CONFIG_VIRTIO_CONSOLE=m
 CONFIG_IPMI_HANDLER=m
+CONFIG_IPMI_DMI_DECODE=y
 CONFIG_IPMI_PANIC_EVENT=y
 CONFIG_IPMI_PANIC_STRING=y
@@ -2411,4 +2507,5 @@
 CONFIG_I2C_DESIGNWARE_CORE=m
 CONFIG_I2C_DESIGNWARE_PLATFORM=m
+# CONFIG_I2C_DESIGNWARE_SLAVE is not set
 CONFIG_I2C_DESIGNWARE_PCI=m
 # CONFIG_I2C_DESIGNWARE_BAYTRAIL is not set
@@ -2444,8 +2541,4 @@
 # CONFIG_SPMI is not set
 # CONFIG_HSI is not set
-
-#
-# PPS support
-#
 CONFIG_PPS=m
 # CONFIG_PPS_DEBUG is not set
@@ -2471,4 +2564,5 @@
 #
 CONFIG_PTP_1588_CLOCK_PCH=m
+CONFIG_PTP_1588_CLOCK_KVM=m
 CONFIG_PINCTRL=y
 
@@ -2477,7 +2571,12 @@
 #
 # CONFIG_PINCTRL_AMD is not set
+# CONFIG_PINCTRL_MCP23S08 is not set
 # CONFIG_PINCTRL_BAYTRAIL is not set
 # CONFIG_PINCTRL_CHERRYVIEW is not set
 # CONFIG_PINCTRL_BROXTON is not set
+# CONFIG_PINCTRL_CANNONLAKE is not set
+# CONFIG_PINCTRL_DENVERTON is not set
+# CONFIG_PINCTRL_GEMINILAKE is not set
+# CONFIG_PINCTRL_LEWISBURG is not set
 # CONFIG_PINCTRL_SUNRISEPOINT is not set
 CONFIG_GPIOLIB=y
@@ -2490,4 +2589,5 @@
 # CONFIG_GPIO_AMDPT is not set
 # CONFIG_GPIO_DWAPB is not set
+# CONFIG_GPIO_EXAR is not set
 # CONFIG_GPIO_GENERIC_PLATFORM is not set
 # CONFIG_GPIO_ICH is not set
@@ -2495,5 +2595,4 @@
 # CONFIG_GPIO_MOCKUP is not set
 # CONFIG_GPIO_VX855 is not set
-# CONFIG_GPIO_ZX is not set
 
 #
@@ -2514,5 +2613,4 @@
 # CONFIG_GPIO_PCF857X is not set
 # CONFIG_GPIO_TPIC2810 is not set
-# CONFIG_GPIO_TS4900 is not set
 
 #
@@ -2527,9 +2625,6 @@
 # CONFIG_GPIO_ML_IOH is not set
 # CONFIG_GPIO_PCH is not set
+# CONFIG_GPIO_PCI_IDIO_16 is not set
 # CONFIG_GPIO_RDC321X is not set
-
-#
-# SPI or I2C GPIO expanders
-#
 
 #
@@ -2548,4 +2643,5 @@
 # CONFIG_BATTERY_DS2782 is not set
 # CONFIG_BATTERY_SBS is not set
+# CONFIG_CHARGER_SBS is not set
 # CONFIG_BATTERY_BQ27XXX is not set
 # CONFIG_BATTERY_MAX17040 is not set
@@ -2554,6 +2650,6 @@
 # CONFIG_CHARGER_LP8727 is not set
 # CONFIG_CHARGER_GPIO is not set
+# CONFIG_CHARGER_LTC3651 is not set
 # CONFIG_CHARGER_BQ2415X is not set
-# CONFIG_CHARGER_BQ24190 is not set
 # CONFIG_CHARGER_BQ24257 is not set
 # CONFIG_CHARGER_BQ24735 is not set
@@ -2591,4 +2687,5 @@
 CONFIG_SENSORS_APPLESMC=m
 CONFIG_SENSORS_ASB100=m
+# CONFIG_SENSORS_ASPEED is not set
 CONFIG_SENSORS_ATXP1=m
 CONFIG_SENSORS_DS620=m
@@ -2632,4 +2729,5 @@
 CONFIG_SENSORS_MAX31790=m
 CONFIG_SENSORS_MCP3021=m
+# CONFIG_SENSORS_TC654 is not set
 CONFIG_SENSORS_LM63=m
 CONFIG_SENSORS_LM73=m
@@ -2658,4 +2756,6 @@
 CONFIG_SENSORS_PMBUS=m
 CONFIG_SENSORS_ADM1275=m
+# CONFIG_SENSORS_IBM_CFFPS is not set
+# CONFIG_SENSORS_IR35221 is not set
 CONFIG_SENSORS_LM25066=m
 CONFIG_SENSORS_LTC2978=m
@@ -2666,4 +2766,5 @@
 CONFIG_SENSORS_MAX8688=m
 # CONFIG_SENSORS_TPS40422 is not set
+# CONFIG_SENSORS_TPS53679 is not set
 CONFIG_SENSORS_UCD9000=m
 CONFIG_SENSORS_UCD9200=m
@@ -2682,4 +2783,5 @@
 CONFIG_SENSORS_SMSC47B397=m
 # CONFIG_SENSORS_SCH56XX_COMMON is not set
+# CONFIG_SENSORS_STTS751 is not set
 CONFIG_SENSORS_SMM665=m
 CONFIG_SENSORS_ADC128D818=m
@@ -2694,4 +2796,5 @@
 CONFIG_SENSORS_TMP102=m
 CONFIG_SENSORS_TMP103=m
+# CONFIG_SENSORS_TMP108 is not set
 CONFIG_SENSORS_TMP401=m
 CONFIG_SENSORS_TMP421=m
@@ -2717,4 +2820,5 @@
 CONFIG_SENSORS_ATK0110=m
 CONFIG_THERMAL=y
+CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
 CONFIG_THERMAL_WRITABLE_TRIPS=y
 CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
@@ -2727,4 +2831,6 @@
 CONFIG_THERMAL_GOV_USER_SPACE=y
 # CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set
+# CONFIG_CLOCK_THERMAL is not set
+# CONFIG_DEVFREQ_THERMAL is not set
 # CONFIG_THERMAL_EMULATION is not set
 # CONFIG_INTEL_POWERCLAMP is not set
@@ -2753,8 +2859,4 @@
 # CONFIG_SSB_DRIVER_GPIO is not set
 CONFIG_BCMA_POSSIBLE=y
-
-#
-# Broadcom specific AMBA
-#
 # CONFIG_BCMA is not set
 
@@ -2765,4 +2867,5 @@
 # CONFIG_MFD_CS5535 is not set
 # CONFIG_MFD_BCM590XX is not set
+# CONFIG_MFD_BD9571MWV is not set
 # CONFIG_MFD_AXP20X_I2C is not set
 # CONFIG_MFD_CROS_EC is not set
@@ -2771,5 +2874,4 @@
 # CONFIG_MFD_DA9150 is not set
 # CONFIG_MFD_DLN2 is not set
-# CONFIG_MFD_EXYNOS_LPASS is not set
 # CONFIG_MFD_MC13XXX_I2C is not set
 # CONFIG_HTC_PASIC3 is not set
@@ -2802,4 +2904,5 @@
 # CONFIG_MFD_TI_AM335X_TSCADC is not set
 # CONFIG_MFD_LP3943 is not set
+# CONFIG_MFD_TI_LMU is not set
 # CONFIG_TPS6105X is not set
 # CONFIG_TPS65010 is not set
@@ -2818,4 +2921,18 @@
 # CONFIG_MFD_WM8994 is not set
 # CONFIG_REGULATOR is not set
+CONFIG_RC_CORE=y
+CONFIG_RC_MAP=y
+CONFIG_RC_DECODERS=y
+# CONFIG_LIRC is not set
+CONFIG_IR_NEC_DECODER=y
+CONFIG_IR_RC5_DECODER=y
+CONFIG_IR_RC6_DECODER=y
+CONFIG_IR_JVC_DECODER=y
+CONFIG_IR_SONY_DECODER=y
+CONFIG_IR_SANYO_DECODER=y
+CONFIG_IR_SHARP_DECODER=y
+CONFIG_IR_MCE_KBD_DECODER=y
+CONFIG_IR_XMP_DECODER=y
+# CONFIG_RC_DEVICES is not set
 # CONFIG_MEDIA_SUPPORT is not set
 
@@ -2832,4 +2949,5 @@
 # ACP (Audio CoProcessor) Configuration
 #
+# CONFIG_DRM_LIB_RANDOM is not set
 
 #
@@ -2849,4 +2967,5 @@
 # CONFIG_FB_SYS_COPYAREA is not set
 # CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA is not set
 # CONFIG_FB_FOREIGN_ENDIAN is not set
 # CONFIG_FB_SYS_FOPS is not set
@@ -2916,4 +3035,5 @@
 CONFIG_VGACON_SOFT_SCROLLBACK=y
 CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=16
+# CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT is not set
 CONFIG_DUMMY_CONSOLE=y
 CONFIG_DUMMY_CONSOLE_COLUMNS=80
@@ -2939,7 +3059,9 @@
 #
 # CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACCUTOUCH is not set
 # CONFIG_HID_ACRUX is not set
 # CONFIG_HID_APPLE is not set
 # CONFIG_HID_APPLEIR is not set
+# CONFIG_HID_ASUS is not set
 # CONFIG_HID_AUREAL is not set
 # CONFIG_HID_BELKIN is not set
@@ -2949,5 +3071,4 @@
 # CONFIG_HID_CORSAIR is not set
 # CONFIG_HID_CMEDIA is not set
-# CONFIG_HID_CP2112 is not set
 # CONFIG_HID_CYPRESS is not set
 # CONFIG_HID_DRAGONRISE is not set
@@ -2966,4 +3087,5 @@
 # CONFIG_HID_GYRATION is not set
 # CONFIG_HID_ICADE is not set
+CONFIG_HID_ITE=y
 # CONFIG_HID_TWINHAN is not set
 # CONFIG_HID_KENSINGTON is not set
@@ -2973,7 +3095,9 @@
 # CONFIG_HID_LOGITECH is not set
 # CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MAYFLASH is not set
 # CONFIG_HID_MICROSOFT is not set
 # CONFIG_HID_MONTEREY is not set
 # CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_NTI is not set
 # CONFIG_HID_NTRIG is not set
 # CONFIG_HID_ORTEK is not set
@@ -2984,4 +3108,5 @@
 # CONFIG_HID_PLANTRONICS is not set
 # CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_RETRODE is not set
 # CONFIG_HID_ROCCAT is not set
 # CONFIG_HID_SAITEK is not set
@@ -2999,4 +3124,5 @@
 # CONFIG_HID_THINGM is not set
 # CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_UDRAW_PS3 is not set
 # CONFIG_HID_WACOM is not set
 # CONFIG_HID_WIIMOTE is not set
@@ -3023,4 +3149,5 @@
 CONFIG_USB_ARCH_HAS_HCD=y
 CONFIG_USB=m
+CONFIG_USB_PCI=y
 CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
 
@@ -3128,4 +3255,5 @@
 CONFIG_USB_SERIAL_EDGEPORT_TI=m
 CONFIG_USB_SERIAL_F81232=m
+# CONFIG_USB_SERIAL_F8153X is not set
 CONFIG_USB_SERIAL_GARMIN=m
 CONFIG_USB_SERIAL_IPW=m
@@ -3173,4 +3301,5 @@
 CONFIG_USB_SERIAL_SSU100=m
 # CONFIG_USB_SERIAL_QT2 is not set
+# CONFIG_USB_SERIAL_UPD78F0730 is not set
 # CONFIG_USB_SERIAL_DEBUG is not set
 
@@ -3198,9 +3327,9 @@
 # CONFIG_USB_YUREX is not set
 CONFIG_USB_EZUSB_FX2=m
+# CONFIG_USB_HUB_USB251XB is not set
 # CONFIG_USB_HSIC_USB3503 is not set
 # CONFIG_USB_HSIC_USB4604 is not set
 # CONFIG_USB_LINK_LAYER_TEST is not set
 # CONFIG_USB_CHAOSKEY is not set
-# CONFIG_UCSI is not set
 
 #
@@ -3212,4 +3341,9 @@
 # CONFIG_USB_ISP1301 is not set
 # CONFIG_USB_GADGET is not set
+
+#
+# USB Power Delivery and Type-C drivers
+#
+# CONFIG_TYPEC_UCSI is not set
 # CONFIG_USB_ULPI_BUS is not set
 # CONFIG_UWB is not set
@@ -3219,4 +3353,5 @@
 CONFIG_LEDS_CLASS=m
 # CONFIG_LEDS_CLASS_FLASH is not set
+# CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set
 
 #
@@ -3249,4 +3384,6 @@
 #
 # CONFIG_LEDS_BLINKM is not set
+# CONFIG_LEDS_USER is not set
+# CONFIG_LEDS_NIC78BX is not set
 
 #
@@ -3262,5 +3399,4 @@
 # CONFIG_EDAC_DEBUG is not set
 CONFIG_EDAC_DECODE_MCE=y
-CONFIG_EDAC_MM_EDAC=m
 CONFIG_EDAC_AMD64=m
 # CONFIG_EDAC_AMD64_ERROR_INJECTION is not set
@@ -3289,4 +3425,5 @@
 CONFIG_RTC_SYSTOHC_DEVICE="rtc0"
 # CONFIG_RTC_DEBUG is not set
+CONFIG_RTC_NVMEM=y
 
 #
@@ -3360,4 +3497,5 @@
 # on-CPU RTC drivers
 #
+# CONFIG_RTC_DRV_FTRTC010 is not set
 
 #
@@ -3374,4 +3512,5 @@
 CONFIG_DMA_VIRTUAL_CHANNELS=y
 CONFIG_DMA_ACPI=y
+# CONFIG_ALTERA_MSGDMA is not set
 CONFIG_INTEL_IDMA64=m
 CONFIG_PCH_DMA=m
@@ -3404,8 +3543,9 @@
 # CONFIG_UIO_PRUSS is not set
 # CONFIG_UIO_MF624 is not set
+# CONFIG_UIO_HV_GENERIC is not set
 # CONFIG_VFIO is not set
 CONFIG_IRQ_BYPASS_MANAGER=m
 CONFIG_VIRT_DRIVERS=y
-CONFIG_VIRTIO=y
+CONFIG_VIRTIO=m
 
 #
@@ -3423,8 +3563,10 @@
 #
 CONFIG_HYPERV=m
+# CONFIG_HYPERV_TSCPAGE is not set
 CONFIG_HYPERV_UTILS=m
 CONFIG_HYPERV_BALLOON=m
 # CONFIG_STAGING is not set
 # CONFIG_X86_PLATFORM_DEVICES is not set
+CONFIG_PMC_ATOM=y
 # CONFIG_CHROME_PLATFORMS is not set
 CONFIG_CLKDEV_LOOKUP=y
@@ -3441,8 +3583,5 @@
 # CONFIG_COMMON_CLK_PXA is not set
 # CONFIG_COMMON_CLK_PIC32 is not set
-
-#
-# Hardware Spinlock drivers
-#
+# CONFIG_HWSPINLOCK is not set
 
 #
@@ -3475,5 +3614,5 @@
 # Remoteproc drivers
 #
-# CONFIG_STE_MODEM_RPROC is not set
+# CONFIG_REMOTEPROC is not set
 
 #
@@ -3486,5 +3625,17 @@
 
 #
+# Amlogic SoC drivers
+#
+
+#
 # Broadcom SoC drivers
+#
+
+#
+# i.MX SoC drivers
+#
+
+#
+# Qualcomm SoC drivers
 #
 # CONFIG_SUNXI_SRAM is not set
@@ -3516,4 +3667,6 @@
 # CONFIG_RESET_ATH79 is not set
 # CONFIG_RESET_BERLIN is not set
+# CONFIG_RESET_IMX7 is not set
+# CONFIG_RESET_LANTIQ is not set
 # CONFIG_RESET_LPC18XX is not set
 # CONFIG_RESET_MESON is not set
@@ -3522,6 +3675,7 @@
 # CONFIG_RESET_STM32 is not set
 # CONFIG_RESET_SUNXI is not set
-# CONFIG_TI_SYSCON_RESET is not set
+# CONFIG_RESET_TI_SYSCON is not set
 # CONFIG_RESET_ZYNQ is not set
+# CONFIG_RESET_TEGRA_BPMP is not set
 # CONFIG_FMC is not set
 
@@ -3530,7 +3684,7 @@
 #
 CONFIG_GENERIC_PHY=y
+# CONFIG_BCM_KONA_USB2_PHY is not set
 # CONFIG_PHY_PXA_28NM_HSIC is not set
 # CONFIG_PHY_PXA_28NM_USB2 is not set
-# CONFIG_BCM_KONA_USB2_PHY is not set
 # CONFIG_POWERCAP is not set
 # CONFIG_MCB is not set
@@ -3547,13 +3701,15 @@
 # CONFIG_ANDROID is not set
 # CONFIG_LIBNVDIMM is not set
+CONFIG_DAX=m
 # CONFIG_DEV_DAX is not set
-# CONFIG_NVMEM is not set
+CONFIG_NVMEM=y
 # CONFIG_STM is not set
 # CONFIG_INTEL_TH is not set
-
-#
-# FPGA Configuration Support
-#
 # CONFIG_FPGA is not set
+
+#
+# FSI support
+#
+# CONFIG_FSI is not set
 
 #
@@ -3579,6 +3735,12 @@
 # CONFIG_EFI_FAKE_MEMMAP is not set
 CONFIG_EFI_RUNTIME_WRAPPERS=y
-# CONFIG_EFI_CAPSULE_LOADER is not set
+CONFIG_EFI_CAPSULE_LOADER=y
+CONFIG_EFI_CAPSULE_QUIRK_QUARK_CSH=y
 # CONFIG_EFI_TEST is not set
+# CONFIG_EFI_DEV_PATH_PARSER is not set
+
+#
+# Tegra firmware driver
+#
 
 #
@@ -3693,5 +3855,4 @@
 # CONFIG_JFFS2_FS is not set
 # CONFIG_UBIFS_FS is not set
-# CONFIG_LOGFS is not set
 # CONFIG_CRAMFS is not set
 CONFIG_SQUASHFS=y
@@ -3706,4 +3867,5 @@
 # CONFIG_SQUASHFS_LZO is not set
 # CONFIG_SQUASHFS_XZ is not set
+# CONFIG_SQUASHFS_ZSTD is not set
 # CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set
 # CONFIG_SQUASHFS_EMBEDDED is not set
@@ -3759,4 +3921,5 @@
 CONFIG_CIFS=m
 # CONFIG_CIFS_STATS is not set
+CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y
 # CONFIG_CIFS_WEAK_PW_HASH is not set
 # CONFIG_CIFS_UPCALL is not set
@@ -3766,6 +3929,6 @@
 CONFIG_CIFS_DEBUG=y
 # CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_DEBUG_DUMP_KEYS is not set
 # CONFIG_CIFS_DFS_UPCALL is not set
-CONFIG_CIFS_SMB2=y
 # CONFIG_CIFS_SMB311 is not set
 CONFIG_CIFS_FSCACHE=y
@@ -3835,4 +3998,5 @@
 #
 CONFIG_PRINTK_TIME=y
+CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
 CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
 
@@ -3849,8 +4013,8 @@
 # CONFIG_DEBUG_SECTION_MISMATCH is not set
 CONFIG_SECTION_MISMATCH_WARN_ONLY=y
-CONFIG_ARCH_WANT_FRAME_POINTERS=y
 CONFIG_FRAME_POINTER=y
 CONFIG_MAGIC_SYSRQ=y
 CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
+CONFIG_MAGIC_SYSRQ_SERIAL=y
 # CONFIG_DEBUG_KERNEL is not set
 
@@ -3860,8 +4024,9 @@
 # CONFIG_PAGE_EXTENSION is not set
 # CONFIG_PAGE_POISONING is not set
+CONFIG_DEBUG_RODATA_TEST=y
 CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y
 CONFIG_DEBUG_MEMORY_INIT=y
 CONFIG_HAVE_DEBUG_STACKOVERFLOW=y
-CONFIG_HAVE_ARCH_KMEMCHECK=y
 
 #
@@ -3877,5 +4042,7 @@
 # Lock Debugging (spinlocks, mutexes, etc...)
 #
+# CONFIG_WW_MUTEX_SELFTEST is not set
 # CONFIG_STACKTRACE is not set
+# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set
 CONFIG_DEBUG_BUGVERBOSE=y
 
@@ -3884,5 +4051,4 @@
 #
 # CONFIG_PROVE_RCU is not set
-# CONFIG_SPARSE_RCU_POINTER is not set
 # CONFIG_TORTURE_TEST is not set
 CONFIG_RCU_CPU_STALL_TIMEOUT=60
@@ -3897,8 +4063,12 @@
 CONFIG_TRACING_SUPPORT=y
 # CONFIG_FTRACE is not set
+# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
+# CONFIG_DMA_API_DEBUG is not set
 
 #
 # Runtime Testing
 #
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_TEST_SORT is not set
 # CONFIG_ATOMIC64_SELFTEST is not set
 # CONFIG_ASYNC_RAID6_TEST is not set
@@ -3911,13 +4081,14 @@
 # CONFIG_TEST_RHASHTABLE is not set
 # CONFIG_TEST_HASH is not set
-# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
-# CONFIG_DMA_API_DEBUG is not set
 # CONFIG_TEST_LKM is not set
 # CONFIG_TEST_USER_COPY is not set
 # CONFIG_TEST_BPF is not set
 # CONFIG_TEST_FIRMWARE is not set
+# CONFIG_TEST_SYSCTL is not set
 # CONFIG_TEST_UDELAY is not set
+# CONFIG_TEST_STATIC_KEYS is not set
+# CONFIG_TEST_KMOD is not set
 CONFIG_MEMTEST=y
-# CONFIG_TEST_STATIC_KEYS is not set
+# CONFIG_BUG_ON_DATA_CORRUPTION is not set
 # CONFIG_SAMPLES is not set
 CONFIG_HAVE_ARCH_KGDB=y
@@ -3932,9 +4103,8 @@
 # CONFIG_EARLY_PRINTK_DBGP is not set
 # CONFIG_EARLY_PRINTK_EFI is not set
+# CONFIG_EARLY_PRINTK_USB_XDBC is not set
 # CONFIG_X86_PTDUMP_CORE is not set
 # CONFIG_EFI_PGT_DUMP is not set
-CONFIG_DEBUG_RODATA_TEST=y
 # CONFIG_DEBUG_WX is not set
-# CONFIG_DEBUG_SET_MODULE_RONX is not set
 CONFIG_DOUBLEFAULT=y
 # CONFIG_IOMMU_STRESS is not set
@@ -3951,4 +4121,5 @@
 # CONFIG_OPTIMIZE_INLINING is not set
 # CONFIG_PUNIT_ATOM_DEBUG is not set
+CONFIG_UNWINDER_FRAME_POINTER=y
 
 #
@@ -3965,6 +4136,7 @@
 # CONFIG_INTEL_TXT is not set
 CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y
-CONFIG_HAVE_ARCH_HARDENED_USERCOPY=y
 # CONFIG_HARDENED_USERCOPY is not set
+# CONFIG_FORTIFY_SOURCE is not set
+# CONFIG_STATIC_USERMODEHELPER is not set
 CONFIG_DEFAULT_SECURITY_DAC=y
 CONFIG_DEFAULT_SECURITY=""
@@ -3994,7 +4166,9 @@
 CONFIG_CRYPTO_AKCIPHER=m
 CONFIG_CRYPTO_KPP2=y
+CONFIG_CRYPTO_KPP=m
+CONFIG_CRYPTO_ACOMP2=y
 CONFIG_CRYPTO_RSA=m
-# CONFIG_CRYPTO_DH is not set
-# CONFIG_CRYPTO_ECDH is not set
+CONFIG_CRYPTO_DH=m
+CONFIG_CRYPTO_ECDH=m
 CONFIG_CRYPTO_MANAGER=y
 CONFIG_CRYPTO_MANAGER2=y
@@ -4004,12 +4178,14 @@
 CONFIG_CRYPTO_NULL=m
 CONFIG_CRYPTO_NULL2=y
-# CONFIG_CRYPTO_PCRYPT is not set
+CONFIG_CRYPTO_PCRYPT=m
 CONFIG_CRYPTO_WORKQUEUE=y
 CONFIG_CRYPTO_CRYPTD=m
-# CONFIG_CRYPTO_MCRYPTD is not set
+CONFIG_CRYPTO_MCRYPTD=m
 CONFIG_CRYPTO_AUTHENC=m
 # CONFIG_CRYPTO_TEST is not set
 CONFIG_CRYPTO_ABLK_HELPER=m
+CONFIG_CRYPTO_SIMD=m
 CONFIG_CRYPTO_GLUE_HELPER_X86=m
+CONFIG_CRYPTO_ENGINE=m
 
 #
@@ -4018,5 +4194,5 @@
 CONFIG_CRYPTO_CCM=m
 CONFIG_CRYPTO_GCM=m
-# CONFIG_CRYPTO_CHACHA20POLY1305 is not set
+CONFIG_CRYPTO_CHACHA20POLY1305=m
 CONFIG_CRYPTO_SEQIV=m
 CONFIG_CRYPTO_ECHAINIV=m
@@ -4032,5 +4208,5 @@
 CONFIG_CRYPTO_PCBC=m
 CONFIG_CRYPTO_XTS=m
-# CONFIG_CRYPTO_KEYWRAP is not set
+CONFIG_CRYPTO_KEYWRAP=m
 
 #
@@ -4051,5 +4227,5 @@
 CONFIG_CRYPTO_CRCT10DIF=y
 CONFIG_CRYPTO_GHASH=m
-# CONFIG_CRYPTO_POLY1305 is not set
+CONFIG_CRYPTO_POLY1305=m
 CONFIG_CRYPTO_MD4=m
 CONFIG_CRYPTO_MD5=m
@@ -4062,5 +4238,5 @@
 CONFIG_CRYPTO_SHA256=m
 CONFIG_CRYPTO_SHA512=m
-# CONFIG_CRYPTO_SHA3 is not set
+CONFIG_CRYPTO_SHA3=m
 CONFIG_CRYPTO_TGR192=m
 CONFIG_CRYPTO_WP512=m
@@ -4070,4 +4246,5 @@
 #
 CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_AES_TI=m
 CONFIG_CRYPTO_AES_586=m
 CONFIG_CRYPTO_AES_NI_INTEL=m
@@ -4084,6 +4261,5 @@
 CONFIG_CRYPTO_KHAZAD=m
 CONFIG_CRYPTO_SALSA20=m
-CONFIG_CRYPTO_SALSA20_586=m
-# CONFIG_CRYPTO_CHACHA20 is not set
+CONFIG_CRYPTO_CHACHA20=m
 CONFIG_CRYPTO_SEED=m
 CONFIG_CRYPTO_SERPENT=m
@@ -4099,5 +4275,5 @@
 CONFIG_CRYPTO_DEFLATE=y
 CONFIG_CRYPTO_LZO=m
-# CONFIG_CRYPTO_842 is not set
+CONFIG_CRYPTO_842=m
 CONFIG_CRYPTO_LZ4=m
 CONFIG_CRYPTO_LZ4HC=m
@@ -4116,6 +4292,6 @@
 CONFIG_CRYPTO_USER_API_HASH=m
 CONFIG_CRYPTO_USER_API_SKCIPHER=m
-# CONFIG_CRYPTO_USER_API_RNG is not set
-# CONFIG_CRYPTO_USER_API_AEAD is not set
+CONFIG_CRYPTO_USER_API_RNG=m
+CONFIG_CRYPTO_USER_API_AEAD=m
 CONFIG_CRYPTO_HW=y
 CONFIG_CRYPTO_DEV_PADLOCK=m
@@ -4123,6 +4299,8 @@
 CONFIG_CRYPTO_DEV_PADLOCK_SHA=m
 CONFIG_CRYPTO_DEV_GEODE=m
+# CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC is not set
 CONFIG_CRYPTO_DEV_CCP=y
 CONFIG_CRYPTO_DEV_CCP_DD=m
+CONFIG_CRYPTO_DEV_SP_CCP=y
 CONFIG_CRYPTO_DEV_CCP_CRYPTO=m
 # CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set
@@ -4133,4 +4311,5 @@
 # CONFIG_CRYPTO_DEV_QAT_C62XVF is not set
 # CONFIG_CRYPTO_DEV_CHELSIO is not set
+CONFIG_CRYPTO_DEV_VIRTIO=m
 # CONFIG_ASYMMETRIC_KEY_TYPE is not set
 
@@ -4138,4 +4317,5 @@
 # Certificates for signature checking
 #
+# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set
 CONFIG_HAVE_KVM=y
 CONFIG_HAVE_KVM_IRQCHIP=y
@@ -4154,10 +4334,8 @@
 CONFIG_KVM_INTEL=m
 CONFIG_KVM_AMD=m
-# CONFIG_KVM_DEVICE_ASSIGNMENT is not set
 CONFIG_VHOST_NET=m
 # CONFIG_VHOST_VSOCK is not set
 CONFIG_VHOST=m
 # CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set
-# CONFIG_LGUEST is not set
 # CONFIG_BINARY_PRINTF is not set
 
@@ -4187,4 +4365,5 @@
 # CONFIG_CRC32_SARWATE is not set
 # CONFIG_CRC32_BIT is not set
+# CONFIG_CRC4 is not set
 # CONFIG_CRC7 is not set
 CONFIG_LIBCRC32C=y
@@ -4192,8 +4371,10 @@
 # CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set
 # CONFIG_RANDOM32_SELFTEST is not set
+CONFIG_842_COMPRESS=m
+CONFIG_842_DECOMPRESS=m
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=y
-CONFIG_LZO_COMPRESS=y
-CONFIG_LZO_DECOMPRESS=y
+CONFIG_LZO_COMPRESS=m
+CONFIG_LZO_DECOMPRESS=m
 CONFIG_LZ4_COMPRESS=m
 CONFIG_LZ4HC_COMPRESS=m
@@ -4217,4 +4398,5 @@
 CONFIG_TEXTSEARCH_BM=m
 CONFIG_TEXTSEARCH_FSM=m
+CONFIG_BTREE=y
 CONFIG_RADIX_TREE_MULTIORDER=y
 CONFIG_ASSOCIATIVE_ARRAY=y
@@ -4222,4 +4404,7 @@
 CONFIG_HAS_IOPORT_MAP=y
 CONFIG_HAS_DMA=y
+# CONFIG_SGL_ALLOC is not set
+# CONFIG_DMA_NOOP_OPS is not set
+# CONFIG_DMA_VIRT_OPS is not set
 CONFIG_CHECK_SIGNATURE=y
 CONFIG_CPU_RMAP=y
@@ -4242,4 +4427,4 @@
 CONFIG_SG_POOL=y
 CONFIG_ARCH_HAS_SG_CHAIN=y
-CONFIG_ARCH_HAS_MMIO_FLUSH=y
 CONFIG_SBITMAP=y
+# CONFIG_STRING_SELFTEST is not set
Index: npl/kernel/linux_src/fbcondecor-3.19.patch
===================================================================
--- npl/kernel/linux_src/fbcondecor-3.19.patch	(revision f6630dda9bbc206e69f0475bfb8672e20d5beaa1)
+++ 	(revision )
@@ -1,2119 +1,0 @@
-diff --git a/Documentation/fb/00-INDEX b/Documentation/fb/00-INDEX
-index fe85e7c..2230930 100644
---- a/Documentation/fb/00-INDEX
-+++ b/Documentation/fb/00-INDEX
-@@ -23,6 +23,8 @@ ep93xx-fb.txt
- 	- info on the driver for EP93xx LCD controller.
- fbcon.txt
- 	- intro to and usage guide for the framebuffer console (fbcon).
-+fbcondecor.txt
-+	- info on the Framebuffer Console Decoration
- framebuffer.txt
- 	- introduction to frame buffer devices.
- gxfb.txt
-diff --git a/Documentation/fb/fbcondecor.txt b/Documentation/fb/fbcondecor.txt
-new file mode 100644
-index 0000000..3388c61
---- /dev/null
-+++ b/Documentation/fb/fbcondecor.txt
-@@ -0,0 +1,207 @@
-+What is it?
-+-----------
-+
-+The framebuffer decorations are a kernel feature which allows displaying a 
-+background picture on selected consoles.
-+
-+What do I need to get it to work?
-+---------------------------------
-+
-+To get fbcondecor up-and-running you will have to:
-+ 1) get a copy of splashutils [1] or a similar program
-+ 2) get some fbcondecor themes
-+ 3) build the kernel helper program
-+ 4) build your kernel with the FB_CON_DECOR option enabled.
-+
-+To get fbcondecor operational right after fbcon initialization is finished, you
-+will have to include a theme and the kernel helper into your initramfs image.
-+Please refer to splashutils documentation for instructions on how to do that.
-+
-+[1] The splashutils package can be downloaded from:
-+    http://github.com/alanhaggai/fbsplash
-+
-+The userspace helper
-+--------------------
-+
-+The userspace fbcondecor helper (by default: /sbin/fbcondecor_helper) is called by the
-+kernel whenever an important event occurs and the kernel needs some kind of
-+job to be carried out. Important events include console switches and video
-+mode switches (the kernel requests background images and configuration
-+parameters for the current console). The fbcondecor helper must be accessible at
-+all times. If it's not, fbcondecor will be switched off automatically.
-+
-+It's possible to set path to the fbcondecor helper by writing it to
-+/proc/sys/kernel/fbcondecor.
-+
-+*****************************************************************************
-+
-+The information below is mostly technical stuff. There's probably no need to
-+read it unless you plan to develop a userspace helper.
-+
-+The fbcondecor protocol
-+-----------------------
-+
-+The fbcondecor protocol defines a communication interface between the kernel and
-+the userspace fbcondecor helper.
-+
-+The kernel side is responsible for:
-+
-+ * rendering console text, using an image as a background (instead of a
-+   standard solid color fbcon uses),
-+ * accepting commands from the user via ioctls on the fbcondecor device,
-+ * calling the userspace helper to set things up as soon as the fb subsystem 
-+   is initialized.
-+
-+The userspace helper is responsible for everything else, including parsing
-+configuration files, decompressing the image files whenever the kernel needs
-+it, and communicating with the kernel if necessary.
-+
-+The fbcondecor protocol specifies how communication is done in both ways:
-+kernel->userspace and userspace->helper.
-+  
-+Kernel -> Userspace
-+-------------------
-+
-+The kernel communicates with the userspace helper by calling it and specifying
-+the task to be done in a series of arguments.
-+
-+The arguments follow the pattern:
-+<fbcondecor protocol version> <command> <parameters>
-+
-+All commands defined in fbcondecor protocol v2 have the following parameters:
-+ virtual console
-+ framebuffer number
-+ theme
-+
-+Fbcondecor protocol v1 specified an additional 'fbcondecor mode' after the
-+framebuffer number. Fbcondecor protocol v1 is deprecated and should not be used.
-+
-+Fbcondecor protocol v2 specifies the following commands:
-+
-+getpic
-+------
-+ The kernel issues this command to request image data. It's up to the 
-+ userspace  helper to find a background image appropriate for the specified 
-+ theme and the current resolution. The userspace helper should respond by 
-+ issuing the FBIOCONDECOR_SETPIC ioctl.
-+
-+init
-+----
-+ The kernel issues this command after the fbcondecor device is created and
-+ the fbcondecor interface is initialized. Upon receiving 'init', the userspace
-+ helper should parse the kernel command line (/proc/cmdline) or otherwise
-+ decide whether fbcondecor is to be activated.
-+
-+ To activate fbcondecor on the first console the helper should issue the
-+ FBIOCONDECOR_SETCFG, FBIOCONDECOR_SETPIC and FBIOCONDECOR_SETSTATE commands,
-+ in the above-mentioned order.
-+
-+ When the userspace helper is called in an early phase of the boot process
-+ (right after the initialization of fbcon), no filesystems will be mounted.
-+ The helper program should mount sysfs and then create the appropriate
-+ framebuffer, fbcondecor and tty0 devices (if they don't already exist) to get
-+ current display settings and to be able to communicate with the kernel side.
-+ It should probably also mount the procfs to be able to parse the kernel
-+ command line parameters.
-+
-+ Note that the console sem is not held when the kernel calls fbcondecor_helper
-+ with the 'init' command. The fbcondecor helper should perform all ioctls with
-+ origin set to FBCON_DECOR_IO_ORIG_USER.
-+
-+modechange
-+----------
-+ The kernel issues this command on a mode change. The helper's response should
-+ be similar to the response to the 'init' command. Note that this time the
-+ console sem is held and all ioctls must be performed with origin set to
-+ FBCON_DECOR_IO_ORIG_KERNEL.
-+
-+
-+Userspace -> Kernel
-+-------------------
-+
-+Userspace programs can communicate with fbcondecor via ioctls on the
-+fbcondecor device. These ioctls are to be used by both the userspace helper
-+(called only by the kernel) and userspace configuration tools (run by the users).
-+
-+The fbcondecor helper should set the origin field to FBCON_DECOR_IO_ORIG_KERNEL
-+when doing the appropriate ioctls. All userspace configuration tools should
-+use FBCON_DECOR_IO_ORIG_USER. Failure to set the appropriate value in the origin
-+field when performing ioctls from the kernel helper will most likely result
-+in a console deadlock.
-+
-+FBCON_DECOR_IO_ORIG_KERNEL instructs fbcondecor not to try to acquire the console
-+semaphore. Not surprisingly, FBCON_DECOR_IO_ORIG_USER instructs it to acquire
-+the console sem.
-+
-+The framebuffer console decoration provides the following ioctls (all defined in 
-+linux/fb.h):
-+
-+FBIOCONDECOR_SETPIC
-+description: loads a background picture for a virtual console
-+argument: struct fbcon_decor_iowrapper*; data: struct fb_image*
-+notes: 
-+If called for consoles other than the current foreground one, the picture data
-+will be ignored.
-+
-+If the current virtual console is running in a 8-bpp mode, the cmap substruct
-+of fb_image has to be filled appropriately: start should be set to 16 (first
-+16 colors are reserved for fbcon), len to a value <= 240 and red, green and
-+blue should point to valid cmap data. The transp field is ingored. The fields
-+dx, dy, bg_color, fg_color in fb_image are ignored as well.
-+
-+FBIOCONDECOR_SETCFG
-+description: sets the fbcondecor config for a virtual console
-+argument: struct fbcon_decor_iowrapper*; data: struct vc_decor*
-+notes: The structure has to be filled with valid data.
-+
-+FBIOCONDECOR_GETCFG
-+description: gets the fbcondecor config for a virtual console
-+argument: struct fbcon_decor_iowrapper*; data: struct vc_decor*
-+
-+FBIOCONDECOR_SETSTATE
-+description: sets the fbcondecor state for a virtual console
-+argument: struct fbcon_decor_iowrapper*; data: unsigned int*
-+          values: 0 = disabled, 1 = enabled.
-+
-+FBIOCONDECOR_GETSTATE
-+description: gets the fbcondecor state for a virtual console
-+argument: struct fbcon_decor_iowrapper*; data: unsigned int*
-+          values: as in FBIOCONDECOR_SETSTATE
-+
-+Info on used structures:
-+
-+Definition of struct vc_decor can be found in linux/console_decor.h. It's
-+heavily commented. Note that the 'theme' field should point to a string
-+no longer than FBCON_DECOR_THEME_LEN. When FBIOCONDECOR_GETCFG call is
-+performed, the theme field should point to a char buffer of length
-+FBCON_DECOR_THEME_LEN.
-+
-+Definition of struct fbcon_decor_iowrapper can be found in linux/fb.h.
-+The fields in this struct have the following meaning:
-+
-+vc: 
-+Virtual console number.
-+
-+origin: 
-+Specifies if the ioctl is performed as a response to a kernel request. The
-+fbcondecor helper should set this field to FBCON_DECOR_IO_ORIG_KERNEL, userspace
-+programs should set it to FBCON_DECOR_IO_ORIG_USER. This field is necessary to
-+avoid console semaphore deadlocks.
-+
-+data: 
-+Pointer to a data structure appropriate for the performed ioctl. Type of
-+the data struct is specified in the ioctls description.
-+
-+*****************************************************************************
-+
-+Credit
-+------
-+
-+Original 'bootsplash' project & implementation by:
-+  Volker Poplawski <volker@poplawski.de>, Stefan Reinauer <stepan@suse.de>,
-+  Steffen Winterfeldt <snwint@suse.de>, Michael Schroeder <mls@suse.de>,
-+  Ken Wimer <wimer@suse.de>.
-+
-+Fbcondecor, fbcondecor protocol design, current implementation & docs by:
-+  Michal Januszewski <michalj+fbcondecor@gmail.com>
-+
-diff --git a/drivers/Makefile b/drivers/Makefile
-index 7183b6a..d576148 100644
---- a/drivers/Makefile
-+++ b/drivers/Makefile
-@@ -17,6 +17,10 @@ obj-y				+= pwm/
- obj-$(CONFIG_PCI)		+= pci/
- obj-$(CONFIG_PARISC)		+= parisc/
- obj-$(CONFIG_RAPIDIO)		+= rapidio/
-+# tty/ comes before char/ so that the VT console is the boot-time
-+# default.
-+obj-y				+= tty/
-+obj-y				+= char/
- obj-y				+= video/
- obj-y				+= idle/
- 
-@@ -42,11 +46,6 @@ obj-$(CONFIG_REGULATOR)		+= regulator/
- # reset controllers early, since gpu drivers might rely on them to initialize
- obj-$(CONFIG_RESET_CONTROLLER)	+= reset/
- 
--# tty/ comes before char/ so that the VT console is the boot-time
--# default.
--obj-y				+= tty/
--obj-y				+= char/
--
- # iommu/ comes before gpu as gpu are using iommu controllers
- obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
-
-diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
-index fe1cd01..6d2e87a 100644
---- a/drivers/video/console/Kconfig
-+++ b/drivers/video/console/Kconfig
-@@ -126,6 +126,19 @@ config FRAMEBUFFER_CONSOLE_ROTATION
-          such that other users of the framebuffer will remain normally
-          oriented.
- 
-+config FB_CON_DECOR
-+	bool "Support for the Framebuffer Console Decorations"
-+	depends on FRAMEBUFFER_CONSOLE=y && !FB_TILEBLITTING
-+	default n
-+	---help---
-+	  This option enables support for framebuffer console decorations which
-+	  makes it possible to display images in the background of the system
-+	  consoles.  Note that userspace utilities are necessary in order to take 
-+	  advantage of these features. Refer to Documentation/fb/fbcondecor.txt 
-+	  for more information.
-+
-+	  If unsure, say N.
-+
- config STI_CONSOLE
-         bool "STI text console"
-         depends on PARISC
-diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
-index 43bfa48..cc104b6f 100644
---- a/drivers/video/console/Makefile
-+++ b/drivers/video/console/Makefile
-@@ -16,4 +16,5 @@ obj-$(CONFIG_FRAMEBUFFER_CONSOLE)     += fbcon_rotate.o fbcon_cw.o fbcon_ud.o \
-                                          fbcon_ccw.o
- endif
- 
-+obj-$(CONFIG_FB_CON_DECOR)     	  += fbcondecor.o cfbcondecor.o
- obj-$(CONFIG_FB_STI)              += sticore.o
-diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
-index 61b182b..984384b 100644
---- a/drivers/video/console/bitblit.c
-+++ b/drivers/video/console/bitblit.c
-@@ -18,6 +18,7 @@
- #include <linux/console.h>
- #include <asm/types.h>
- #include "fbcon.h"
-+#include "fbcondecor.h"
- 
- /*
-  * Accelerated handlers.
-@@ -55,6 +56,13 @@ static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy,
- 	area.height = height * vc->vc_font.height;
- 	area.width = width * vc->vc_font.width;
- 
-+	if (fbcon_decor_active(info, vc)) {
-+ 		area.sx += vc->vc_decor.tx;
-+ 		area.sy += vc->vc_decor.ty;
-+ 		area.dx += vc->vc_decor.tx;
-+ 		area.dy += vc->vc_decor.ty;
-+ 	}
-+
- 	info->fbops->fb_copyarea(info, &area);
- }
- 
-@@ -380,11 +388,15 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode,
- 	cursor.image.depth = 1;
- 	cursor.rop = ROP_XOR;
- 
--	if (info->fbops->fb_cursor)
--		err = info->fbops->fb_cursor(info, &cursor);
-+	if (fbcon_decor_active(info, vc)) {
-+		fbcon_decor_cursor(info, &cursor);
-+	} else {
-+		if (info->fbops->fb_cursor)
-+			err = info->fbops->fb_cursor(info, &cursor);
- 
--	if (err)
--		soft_cursor(info, &cursor);
-+		if (err)
-+			soft_cursor(info, &cursor);
-+	}
- 
- 	ops->cursor_reset = 0;
- }
-diff --git a/drivers/video/console/cfbcondecor.c b/drivers/video/console/cfbcondecor.c
-new file mode 100644
-index 0000000..a2b4497
---- /dev/null
-+++ b/drivers/video/console/cfbcondecor.c
-@@ -0,0 +1,471 @@
-+/*
-+ *  linux/drivers/video/cfbcon_decor.c -- Framebuffer decor render functions
-+ *
-+ *  Copyright (C) 2004 Michal Januszewski <michalj+fbcondecor@gmail.com>
-+ *
-+ *  Code based upon "Bootdecor" (C) 2001-2003
-+ *       Volker Poplawski <volker@poplawski.de>,
-+ *       Stefan Reinauer <stepan@suse.de>,
-+ *       Steffen Winterfeldt <snwint@suse.de>,
-+ *       Michael Schroeder <mls@suse.de>,
-+ *       Ken Wimer <wimer@suse.de>.
-+ *
-+ *  This file is subject to the terms and conditions of the GNU General Public
-+ *  License.  See the file COPYING in the main directory of this archive for
-+ *  more details.
-+ */
-+#include <linux/module.h>
-+#include <linux/types.h>
-+#include <linux/fb.h>
-+#include <linux/selection.h>
-+#include <linux/slab.h>
-+#include <linux/vt_kern.h>
-+#include <asm/irq.h>
-+
-+#include "fbcon.h"
-+#include "fbcondecor.h"
-+
-+#define parse_pixel(shift,bpp,type)						\
-+	do {									\
-+		if (d & (0x80 >> (shift)))					\
-+			dd2[(shift)] = fgx;					\
-+		else								\
-+			dd2[(shift)] = transparent ? *(type *)decor_src : bgx;	\
-+		decor_src += (bpp);						\
-+	} while (0)								\
-+
-+extern int get_color(struct vc_data *vc, struct fb_info *info,
-+		     u16 c, int is_fg);
-+
-+void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc)
-+{
-+	int i, j, k;
-+	int minlen = min(min(info->var.red.length, info->var.green.length),
-+			     info->var.blue.length);
-+	u32 col;
-+
-+	for (j = i = 0; i < 16; i++) {
-+		k = color_table[i];
-+
-+		col = ((vc->vc_palette[j++]  >> (8-minlen))
-+			<< info->var.red.offset);
-+		col |= ((vc->vc_palette[j++] >> (8-minlen))
-+			<< info->var.green.offset);
-+		col |= ((vc->vc_palette[j++] >> (8-minlen))
-+			<< info->var.blue.offset);
-+			((u32 *)info->pseudo_palette)[k] = col;
-+	}
-+}
-+
-+void fbcon_decor_renderc(struct fb_info *info, int ypos, int xpos, int height,
-+		      int width, u8* src, u32 fgx, u32 bgx, u8 transparent)
-+{
-+	unsigned int x, y;
-+	u32 dd;
-+	int bytespp = ((info->var.bits_per_pixel + 7) >> 3);
-+	unsigned int d = ypos * info->fix.line_length + xpos * bytespp;
-+	unsigned int ds = (ypos * info->var.xres + xpos) * bytespp;
-+	u16 dd2[4];
-+
-+	u8* decor_src = (u8 *)(info->bgdecor.data + ds);
-+	u8* dst = (u8 *)(info->screen_base + d);
-+
-+	if ((ypos + height) > info->var.yres || (xpos + width) > info->var.xres)
-+		return;
-+
-+	for (y = 0; y < height; y++) {
-+		switch (info->var.bits_per_pixel) {
-+
-+		case 32:
-+			for (x = 0; x < width; x++) {
-+
-+				if ((x & 7) == 0)
-+					d = *src++;
-+				if (d & 0x80)
-+					dd = fgx;
-+				else
-+					dd = transparent ?
-+					     *(u32 *)decor_src : bgx;
-+
-+				d <<= 1;
-+				decor_src += 4;
-+				fb_writel(dd, dst);
-+				dst += 4;
-+			}
-+			break;
-+		case 24:
-+			for (x = 0; x < width; x++) {
-+
-+				if ((x & 7) == 0)
-+					d = *src++;
-+				if (d & 0x80)
-+					dd = fgx;
-+				else
-+					dd = transparent ?
-+					     (*(u32 *)decor_src & 0xffffff) : bgx;
-+
-+				d <<= 1;
-+				decor_src += 3;
-+#ifdef __LITTLE_ENDIAN
-+				fb_writew(dd & 0xffff, dst);
-+				dst += 2;
-+				fb_writeb((dd >> 16), dst);
-+#else
-+				fb_writew(dd >> 8, dst);
-+				dst += 2;
-+				fb_writeb(dd & 0xff, dst);
-+#endif
-+				dst++;
-+			}
-+			break;
-+		case 16:
-+			for (x = 0; x < width; x += 2) {
-+				if ((x & 7) == 0)
-+					d = *src++;
-+
-+				parse_pixel(0, 2, u16);
-+				parse_pixel(1, 2, u16);
-+#ifdef __LITTLE_ENDIAN
-+				dd = dd2[0] | (dd2[1] << 16);
-+#else
-+				dd = dd2[1] | (dd2[0] << 16);
-+#endif
-+				d <<= 2;
-+				fb_writel(dd, dst);
-+				dst += 4;
-+			}
-+			break;
-+
-+		case 8:
-+			for (x = 0; x < width; x += 4) {
-+				if ((x & 7) == 0)
-+					d = *src++;
-+
-+				parse_pixel(0, 1, u8);
-+				parse_pixel(1, 1, u8);
-+				parse_pixel(2, 1, u8);
-+				parse_pixel(3, 1, u8);
-+
-+#ifdef __LITTLE_ENDIAN
-+				dd = dd2[0] | (dd2[1] << 8) | (dd2[2] << 16) | (dd2[3] << 24);
-+#else
-+				dd = dd2[3] | (dd2[2] << 8) | (dd2[1] << 16) | (dd2[0] << 24);
-+#endif
-+				d <<= 4;
-+				fb_writel(dd, dst);
-+				dst += 4;
-+			}
-+		}
-+
-+		dst += info->fix.line_length - width * bytespp;
-+		decor_src += (info->var.xres - width) * bytespp;
-+	}
-+}
-+
-+#define cc2cx(a) 						\
-+	((info->fix.visual == FB_VISUAL_TRUECOLOR || 		\
-+	  info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? 		\
-+	 ((u32*)info->pseudo_palette)[a] : a)
-+
-+void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info,
-+		   const unsigned short *s, int count, int yy, int xx)
-+{
-+	unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
-+	struct fbcon_ops *ops = info->fbcon_par;
-+	int fg_color, bg_color, transparent;
-+	u8 *src;
-+	u32 bgx, fgx;
-+	u16 c = scr_readw(s);
-+
-+	fg_color = get_color(vc, info, c, 1);
-+        bg_color = get_color(vc, info, c, 0);
-+
-+	/* Don't paint the background image if console is blanked */
-+	transparent = ops->blank_state ? 0 :
-+		(vc->vc_decor.bg_color == bg_color);
-+
-+	xx = xx * vc->vc_font.width + vc->vc_decor.tx;
-+	yy = yy * vc->vc_font.height + vc->vc_decor.ty;
-+
-+	fgx = cc2cx(fg_color);
-+	bgx = cc2cx(bg_color);
-+
-+	while (count--) {
-+		c = scr_readw(s++);
-+		src = vc->vc_font.data + (c & charmask) * vc->vc_font.height *
-+		      ((vc->vc_font.width + 7) >> 3);
-+
-+		fbcon_decor_renderc(info, yy, xx, vc->vc_font.height,
-+			       vc->vc_font.width, src, fgx, bgx, transparent);
-+		xx += vc->vc_font.width;
-+	}
-+}
-+
-+void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor)
-+{
-+	int i;
-+	unsigned int dsize, s_pitch;
-+	struct fbcon_ops *ops = info->fbcon_par;
-+	struct vc_data* vc;
-+	u8 *src;
-+
-+	/* we really don't need any cursors while the console is blanked */
-+	if (info->state != FBINFO_STATE_RUNNING || ops->blank_state)
-+		return;
-+
-+	vc = vc_cons[ops->currcon].d;
-+
-+	src = kmalloc(64 + sizeof(struct fb_image), GFP_ATOMIC);
-+	if (!src)
-+		return;
-+
-+	s_pitch = (cursor->image.width + 7) >> 3;
-+	dsize = s_pitch * cursor->image.height;
-+	if (cursor->enable) {
-+		switch (cursor->rop) {
-+		case ROP_XOR:
-+			for (i = 0; i < dsize; i++)
-+				src[i] = cursor->image.data[i] ^ cursor->mask[i];
-+                        break;
-+		case ROP_COPY:
-+		default:
-+			for (i = 0; i < dsize; i++)
-+				src[i] = cursor->image.data[i] & cursor->mask[i];
-+			break;
-+		}
-+	} else
-+		memcpy(src, cursor->image.data, dsize);
-+
-+	fbcon_decor_renderc(info,
-+			cursor->image.dy + vc->vc_decor.ty,
-+			cursor->image.dx + vc->vc_decor.tx,
-+			cursor->image.height,
-+			cursor->image.width,
-+			(u8*)src,
-+			cc2cx(cursor->image.fg_color),
-+			cc2cx(cursor->image.bg_color),
-+			cursor->image.bg_color == vc->vc_decor.bg_color);
-+
-+	kfree(src);
-+}
-+
-+static void decorset(u8 *dst, int height, int width, int dstbytes,
-+		        u32 bgx, int bpp)
-+{
-+	int i;
-+
-+	if (bpp == 8)
-+		bgx |= bgx << 8;
-+	if (bpp == 16 || bpp == 8)
-+		bgx |= bgx << 16;
-+
-+	while (height-- > 0) {
-+		u8 *p = dst;
-+
-+		switch (bpp) {
-+
-+		case 32:
-+			for (i=0; i < width; i++) {
-+				fb_writel(bgx, p); p += 4;
-+			}
-+			break;
-+		case 24:
-+			for (i=0; i < width; i++) {
-+#ifdef __LITTLE_ENDIAN
-+				fb_writew((bgx & 0xffff),(u16*)p); p += 2;
-+				fb_writeb((bgx >> 16),p++);
-+#else
-+				fb_writew((bgx >> 8),(u16*)p); p += 2;
-+				fb_writeb((bgx & 0xff),p++);
-+#endif
-+			}
-+		case 16:
-+			for (i=0; i < width/4; i++) {
-+				fb_writel(bgx,p); p += 4;
-+				fb_writel(bgx,p); p += 4;
-+			}
-+			if (width & 2) {
-+				fb_writel(bgx,p); p += 4;
-+			}
-+			if (width & 1)
-+				fb_writew(bgx,(u16*)p);
-+			break;
-+		case 8:
-+			for (i=0; i < width/4; i++) {
-+				fb_writel(bgx,p); p += 4;
-+			}
-+
-+			if (width & 2) {
-+				fb_writew(bgx,p); p += 2;
-+			}
-+			if (width & 1)
-+				fb_writeb(bgx,(u8*)p);
-+			break;
-+
-+		}
-+		dst += dstbytes;
-+	}
-+}
-+
-+void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes,
-+		   int srclinebytes, int bpp)
-+{
-+	int i;
-+
-+	while (height-- > 0) {
-+		u32 *p = (u32 *)dst;
-+		u32 *q = (u32 *)src;
-+
-+		switch (bpp) {
-+
-+		case 32:
-+			for (i=0; i < width; i++)
-+				fb_writel(*q++, p++);
-+			break;
-+		case 24:
-+			for (i=0; i < (width*3/4); i++)
-+				fb_writel(*q++, p++);
-+			if ((width*3) % 4) {
-+				if (width & 2) {
-+					fb_writeb(*(u8*)q, (u8*)p);
-+				} else if (width & 1) {
-+					fb_writew(*(u16*)q, (u16*)p);
-+					fb_writeb(*(u8*)((u16*)q+1),(u8*)((u16*)p+2));
-+				}
-+			}
-+			break;
-+		case 16:
-+			for (i=0; i < width/4; i++) {
-+				fb_writel(*q++, p++);
-+				fb_writel(*q++, p++);
-+			}
-+			if (width & 2)
-+				fb_writel(*q++, p++);
-+			if (width & 1)
-+				fb_writew(*(u16*)q, (u16*)p);
-+			break;
-+		case 8:
-+			for (i=0; i < width/4; i++)
-+				fb_writel(*q++, p++);
-+
-+			if (width & 2) {
-+				fb_writew(*(u16*)q, (u16*)p);
-+				q = (u32*) ((u16*)q + 1);
-+				p = (u32*) ((u16*)p + 1);
-+			}
-+			if (width & 1)
-+				fb_writeb(*(u8*)q, (u8*)p);
-+			break;
-+		}
-+
-+		dst += linebytes;
-+		src += srclinebytes;
-+	}
-+}
-+
-+static void decorfill(struct fb_info *info, int sy, int sx, int height,
-+		       int width)
-+{
-+	int bytespp = ((info->var.bits_per_pixel + 7) >> 3);
-+	int d  = sy * info->fix.line_length + sx * bytespp;
-+	int ds = (sy * info->var.xres + sx) * bytespp;
-+
-+	fbcon_decor_copy((u8 *)(info->screen_base + d), (u8 *)(info->bgdecor.data + ds),
-+		    height, width, info->fix.line_length, info->var.xres * bytespp,
-+		    info->var.bits_per_pixel);
-+}
-+
-+void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx,
-+		    int height, int width)
-+{
-+	int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
-+	struct fbcon_ops *ops = info->fbcon_par;
-+	u8 *dst;
-+	int transparent, bg_color = attr_bgcol_ec(bgshift, vc, info);
-+
-+	transparent = (vc->vc_decor.bg_color == bg_color);
-+	sy = sy * vc->vc_font.height + vc->vc_decor.ty;
-+	sx = sx * vc->vc_font.width + vc->vc_decor.tx;
-+	height *= vc->vc_font.height;
-+	width *= vc->vc_font.width;
-+
-+	/* Don't paint the background image if console is blanked */
-+	if (transparent && !ops->blank_state) {
-+		decorfill(info, sy, sx, height, width);
-+	} else {
-+		dst = (u8 *)(info->screen_base + sy * info->fix.line_length +
-+			     sx * ((info->var.bits_per_pixel + 7) >> 3));
-+		decorset(dst, height, width, info->fix.line_length, cc2cx(bg_color),
-+			  info->var.bits_per_pixel);
-+	}
-+}
-+
-+void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info,
-+			    int bottom_only)
-+{
-+	unsigned int tw = vc->vc_cols*vc->vc_font.width;
-+	unsigned int th = vc->vc_rows*vc->vc_font.height;
-+
-+	if (!bottom_only) {
-+		/* top margin */
-+		decorfill(info, 0, 0, vc->vc_decor.ty, info->var.xres);
-+		/* left margin */
-+		decorfill(info, vc->vc_decor.ty, 0, th, vc->vc_decor.tx);
-+		/* right margin */
-+		decorfill(info, vc->vc_decor.ty, vc->vc_decor.tx + tw, th, 
-+			   info->var.xres - vc->vc_decor.tx - tw);
-+	}
-+	decorfill(info, vc->vc_decor.ty + th, 0, 
-+		   info->var.yres - vc->vc_decor.ty - th, info->var.xres);
-+}
-+
-+void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, 
-+			   int sx, int dx, int width)
-+{
-+	u16 *d = (u16 *) (vc->vc_origin + vc->vc_size_row * y + dx * 2);
-+	u16 *s = d + (dx - sx);
-+	u16 *start = d;
-+	u16 *ls = d;
-+	u16 *le = d + width;
-+	u16 c;
-+	int x = dx;
-+	u16 attr = 1;
-+
-+	do {
-+		c = scr_readw(d);
-+		if (attr != (c & 0xff00)) {
-+			attr = c & 0xff00;
-+			if (d > start) {
-+				fbcon_decor_putcs(vc, info, start, d - start, y, x);
-+				x += d - start;
-+				start = d;
-+			}
-+		}
-+		if (s >= ls && s < le && c == scr_readw(s)) {
-+			if (d > start) {
-+				fbcon_decor_putcs(vc, info, start, d - start, y, x);
-+				x += d - start + 1;
-+				start = d + 1;
-+			} else {
-+				x++;
-+				start++;
-+			}
-+		}
-+		s++;
-+		d++;
-+	} while (d < le);
-+	if (d > start)
-+		fbcon_decor_putcs(vc, info, start, d - start, y, x);
-+}
-+
-+void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank)
-+{
-+	if (blank) {
-+		decorset((u8 *)info->screen_base, info->var.yres, info->var.xres,
-+			  info->fix.line_length, 0, info->var.bits_per_pixel);
-+	} else {
-+		update_screen(vc);
-+		fbcon_decor_clear_margins(vc, info, 0);
-+	}
-+}
-+
-diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
-index f447734..da50d61 100644
---- a/drivers/video/console/fbcon.c
-+++ b/drivers/video/console/fbcon.c
-@@ -79,6 +79,7 @@
- #include <asm/irq.h>
- 
- #include "fbcon.h"
-+#include "../console/fbcondecor.h"
- 
- #ifdef FBCONDEBUG
- #  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
-@@ -94,7 +95,7 @@ enum {
- 
- static struct display fb_display[MAX_NR_CONSOLES];
- 
--static signed char con2fb_map[MAX_NR_CONSOLES];
-+signed char con2fb_map[MAX_NR_CONSOLES];
- static signed char con2fb_map_boot[MAX_NR_CONSOLES];
- 
- static int logo_lines;
-@@ -286,7 +287,7 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
- 		!vt_force_oops_output(vc);
- }
- 
--static int get_color(struct vc_data *vc, struct fb_info *info,
-+int get_color(struct vc_data *vc, struct fb_info *info,
- 	      u16 c, int is_fg)
- {
- 	int depth = fb_get_color_depth(&info->var, &info->fix);
-@@ -551,6 +552,9 @@ static int do_fbcon_takeover(int show_logo)
- 		info_idx = -1;
- 	} else {
- 		fbcon_has_console_bind = 1;
-+#ifdef CONFIG_FB_CON_DECOR
-+		fbcon_decor_init();
-+#endif
- 	}
- 
- 	return err;
-@@ -1007,6 +1011,12 @@ static const char *fbcon_startup(void)
- 	rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
- 	cols /= vc->vc_font.width;
- 	rows /= vc->vc_font.height;
-+
-+	if (fbcon_decor_active(info, vc)) {
-+		cols = vc->vc_decor.twidth / vc->vc_font.width;
-+		rows = vc->vc_decor.theight / vc->vc_font.height;
-+	}
-+
- 	vc_resize(vc, cols, rows);
- 
- 	DPRINTK("mode:   %s\n", info->fix.id);
-@@ -1036,7 +1046,7 @@ static void fbcon_init(struct vc_data *vc, int init)
- 	cap = info->flags;
- 
- 	if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
--	    (info->fix.type == FB_TYPE_TEXT))
-+	    (info->fix.type == FB_TYPE_TEXT) || fbcon_decor_active(info, vc))
- 		logo = 0;
- 
- 	if (var_to_display(p, &info->var, info))
-@@ -1260,6 +1270,11 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
- 		fbcon_clear_margins(vc, 0);
- 	}
- 
-+ 	if (fbcon_decor_active(info, vc)) {
-+ 		fbcon_decor_clear(vc, info, sy, sx, height, width);
-+ 		return;
-+ 	}
-+
- 	/* Split blits that cross physical y_wrap boundary */
- 
- 	y_break = p->vrows - p->yscroll;
-@@ -1279,10 +1294,15 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
- 	struct display *p = &fb_display[vc->vc_num];
- 	struct fbcon_ops *ops = info->fbcon_par;
- 
--	if (!fbcon_is_inactive(vc, info))
--		ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
--			   get_color(vc, info, scr_readw(s), 1),
--			   get_color(vc, info, scr_readw(s), 0));
-+	if (!fbcon_is_inactive(vc, info)) {
-+
-+		if (fbcon_decor_active(info, vc))
-+			fbcon_decor_putcs(vc, info, s, count, ypos, xpos);
-+		else
-+			ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
-+				   get_color(vc, info, scr_readw(s), 1),
-+				   get_color(vc, info, scr_readw(s), 0));
-+	}
- }
- 
- static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
-@@ -1298,8 +1318,13 @@ static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
- 	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
- 	struct fbcon_ops *ops = info->fbcon_par;
- 
--	if (!fbcon_is_inactive(vc, info))
--		ops->clear_margins(vc, info, bottom_only);
-+	if (!fbcon_is_inactive(vc, info)) {
-+	 	if (fbcon_decor_active(info, vc)) {
-+	 		fbcon_decor_clear_margins(vc, info, bottom_only);
-+ 		} else {
-+			ops->clear_margins(vc, info, bottom_only);
-+		}
-+	}
- }
- 
- static void fbcon_cursor(struct vc_data *vc, int mode)
-@@ -1819,7 +1844,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
- 			count = vc->vc_rows;
- 		if (softback_top)
- 			fbcon_softback_note(vc, t, count);
--		if (logo_shown >= 0)
-+		if (logo_shown >= 0 || fbcon_decor_active(info, vc))
- 			goto redraw_up;
- 		switch (p->scrollmode) {
- 		case SCROLL_MOVE:
-@@ -1912,6 +1937,8 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
- 			count = vc->vc_rows;
- 		if (logo_shown >= 0)
- 			goto redraw_down;
-+		if (fbcon_decor_active(info, vc))
-+			goto redraw_down;
- 		switch (p->scrollmode) {
- 		case SCROLL_MOVE:
- 			fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
-@@ -2060,6 +2087,13 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s
- 		}
- 		return;
- 	}
-+
-+	if (fbcon_decor_active(info, vc) && sy == dy && height == 1) {
-+ 		/* must use slower redraw bmove to keep background pic intact */
-+ 		fbcon_decor_bmove_redraw(vc, info, sy, sx, dx, width);
-+ 		return;
-+ 	}
-+
- 	ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
- 		   height, width);
- }
-@@ -2130,8 +2164,8 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
- 	var.yres = virt_h * virt_fh;
- 	x_diff = info->var.xres - var.xres;
- 	y_diff = info->var.yres - var.yres;
--	if (x_diff < 0 || x_diff > virt_fw ||
--	    y_diff < 0 || y_diff > virt_fh) {
-+	if ((x_diff < 0 || x_diff > virt_fw ||
-+		y_diff < 0 || y_diff > virt_fh) && !vc->vc_decor.state) {
- 		const struct fb_videomode *mode;
- 
- 		DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
-@@ -2167,6 +2201,21 @@ static int fbcon_switch(struct vc_data *vc)
- 
- 	info = registered_fb[con2fb_map[vc->vc_num]];
- 	ops = info->fbcon_par;
-+	prev_console = ops->currcon;
-+	if (prev_console != -1)
-+		old_info = registered_fb[con2fb_map[prev_console]];
-+
-+#ifdef CONFIG_FB_CON_DECOR
-+	if (!fbcon_decor_active_vc(vc) && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
-+		struct vc_data *vc_curr = vc_cons[prev_console].d;
-+		if (vc_curr && fbcon_decor_active_vc(vc_curr)) {
-+			/* Clear the screen to avoid displaying funky colors during
-+			 * palette updates. */
-+			memset((u8*)info->screen_base + info->fix.line_length * info->var.yoffset,
-+			       0, info->var.yres * info->fix.line_length);
-+		}
-+	}
-+#endif
- 
- 	if (softback_top) {
- 		if (softback_lines)
-@@ -2185,9 +2234,6 @@ static int fbcon_switch(struct vc_data *vc)
- 		logo_shown = FBCON_LOGO_CANSHOW;
- 	}
- 
--	prev_console = ops->currcon;
--	if (prev_console != -1)
--		old_info = registered_fb[con2fb_map[prev_console]];
- 	/*
- 	 * FIXME: If we have multiple fbdev's loaded, we need to
- 	 * update all info->currcon.  Perhaps, we can place this
-@@ -2231,6 +2277,18 @@ static int fbcon_switch(struct vc_data *vc)
- 			fbcon_del_cursor_timer(old_info);
- 	}
- 
-+	if (fbcon_decor_active_vc(vc)) {
-+		struct vc_data *vc_curr = vc_cons[prev_console].d;
-+
-+		if (!vc_curr->vc_decor.theme ||
-+			strcmp(vc->vc_decor.theme, vc_curr->vc_decor.theme) ||
-+			(fbcon_decor_active_nores(info, vc_curr) &&
-+			 !fbcon_decor_active(info, vc_curr))) {
-+			fbcon_decor_disable(vc, 0);
-+			fbcon_decor_call_helper("modechange", vc->vc_num);
-+		}
-+	}
-+
- 	if (fbcon_is_inactive(vc, info) ||
- 	    ops->blank_state != FB_BLANK_UNBLANK)
- 		fbcon_del_cursor_timer(info);
-@@ -2339,15 +2397,20 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
- 		}
- 	}
- 
-- 	if (!fbcon_is_inactive(vc, info)) {
-+	if (!fbcon_is_inactive(vc, info)) {
- 		if (ops->blank_state != blank) {
- 			ops->blank_state = blank;
- 			fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
- 			ops->cursor_flash = (!blank);
- 
--			if (!(info->flags & FBINFO_MISC_USEREVENT))
--				if (fb_blank(info, blank))
--					fbcon_generic_blank(vc, info, blank);
-+			if (!(info->flags & FBINFO_MISC_USEREVENT)) {
-+				if (fb_blank(info, blank)) {
-+					if (fbcon_decor_active(info, vc))
-+						fbcon_decor_blank(vc, info, blank);
-+					else
-+						fbcon_generic_blank(vc, info, blank);
-+				}
-+			}
- 		}
- 
- 		if (!blank)
-@@ -2522,13 +2585,22 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
- 	}
- 
- 	if (resize) {
-+		/* reset wrap/pan */
- 		int cols, rows;
- 
- 		cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
- 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
-+
-+		if (fbcon_decor_active(info, vc)) {
-+			info->var.xoffset = info->var.yoffset = p->yscroll = 0;
-+			cols = vc->vc_decor.twidth;
-+			rows = vc->vc_decor.theight;
-+		}
- 		cols /= w;
- 		rows /= h;
-+
- 		vc_resize(vc, cols, rows);
-+
- 		if (CON_IS_VISIBLE(vc) && softback_buf)
- 			fbcon_update_softback(vc);
- 	} else if (CON_IS_VISIBLE(vc)
-@@ -2657,7 +2729,11 @@ static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
- 	int i, j, k, depth;
- 	u8 val;
- 
--	if (fbcon_is_inactive(vc, info))
-+	if (fbcon_is_inactive(vc, info)
-+#ifdef CONFIG_FB_CON_DECOR
-+			|| vc->vc_num != fg_console
-+#endif
-+		)
- 		return -EINVAL;
- 
- 	if (!CON_IS_VISIBLE(vc))
-@@ -2683,14 +2759,56 @@ static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
- 	} else
- 		fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
- 
--	return fb_set_cmap(&palette_cmap, info);
-+	if (fbcon_decor_active(info, vc_cons[fg_console].d) &&
-+	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
-+
-+		u16 *red, *green, *blue;
-+		int minlen = min(min(info->var.red.length, info->var.green.length),
-+				     info->var.blue.length);
-+		int h;
-+
-+		struct fb_cmap cmap = {
-+			.start = 0,
-+			.len = (1 << minlen),
-+			.red = NULL,
-+			.green = NULL,
-+			.blue = NULL,
-+			.transp = NULL
-+		};
-+
-+		red = kmalloc(256 * sizeof(u16) * 3, GFP_KERNEL);
-+
-+		if (!red)
-+			goto out;
-+
-+		green = red + 256;
-+		blue = green + 256;
-+		cmap.red = red;
-+		cmap.green = green;
-+		cmap.blue = blue;
-+
-+		for (i = 0; i < cmap.len; i++) {
-+			red[i] = green[i] = blue[i] = (0xffff * i)/(cmap.len-1);
-+		}
-+
-+		h = fb_set_cmap(&cmap, info);
-+		fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d);
-+		kfree(red);
-+
-+		return h;
-+
-+	} else if (fbcon_decor_active(info, vc_cons[fg_console].d) &&
-+		   info->var.bits_per_pixel == 8 && info->bgdecor.cmap.red != NULL)
-+		fb_set_cmap(&info->bgdecor.cmap, info);
-+
-+out:	return fb_set_cmap(&palette_cmap, info);
- }
- 
- static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
- {
- 	unsigned long p;
- 	int line;
--	
-+
- 	if (vc->vc_num != fg_console || !softback_lines)
- 		return (u16 *) (vc->vc_origin + offset);
- 	line = offset / vc->vc_size_row;
-@@ -2909,7 +3027,14 @@ static void fbcon_modechanged(struct fb_info *info)
- 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
- 		cols /= vc->vc_font.width;
- 		rows /= vc->vc_font.height;
--		vc_resize(vc, cols, rows);
-+
-+		if (!fbcon_decor_active_nores(info, vc)) {
-+			vc_resize(vc, cols, rows);
-+		} else {
-+			fbcon_decor_disable(vc, 0);
-+			fbcon_decor_call_helper("modechange", vc->vc_num);
-+		}
-+
- 		updatescrollmode(p, info, vc);
- 		scrollback_max = 0;
- 		scrollback_current = 0;
-@@ -2954,7 +3079,9 @@ static void fbcon_set_all_vcs(struct fb_info *info)
- 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
- 		cols /= vc->vc_font.width;
- 		rows /= vc->vc_font.height;
--		vc_resize(vc, cols, rows);
-+		if (!fbcon_decor_active_nores(info, vc)) {
-+			vc_resize(vc, cols, rows);
-+		}
- 	}
- 
- 	if (fg != -1)
-@@ -3596,6 +3723,7 @@ static void fbcon_exit(void)
- 		}
- 	}
- 
-+	fbcon_decor_exit();
- 	fbcon_has_exited = 1;
- }
- 
-diff --git a/drivers/video/console/fbcondecor.c b/drivers/video/console/fbcondecor.c
-new file mode 100644
-index 0000000..babc8c5
---- /dev/null
-+++ b/drivers/video/console/fbcondecor.c
-@@ -0,0 +1,555 @@
-+/*
-+ *  linux/drivers/video/console/fbcondecor.c -- Framebuffer console decorations
-+ *
-+ *  Copyright (C) 2004-2009 Michal Januszewski <michalj+fbcondecor@gmail.com>
-+ *
-+ *  Code based upon "Bootsplash" (C) 2001-2003
-+ *       Volker Poplawski <volker@poplawski.de>,
-+ *       Stefan Reinauer <stepan@suse.de>,
-+ *       Steffen Winterfeldt <snwint@suse.de>,
-+ *       Michael Schroeder <mls@suse.de>,
-+ *       Ken Wimer <wimer@suse.de>.
-+ *
-+ *  Compat ioctl support by Thorsten Klein <TK@Thorsten-Klein.de>.
-+ *
-+ *  This file is subject to the terms and conditions of the GNU General Public
-+ *  License.  See the file COPYING in the main directory of this archive for
-+ *  more details.
-+ *
-+ */
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/string.h>
-+#include <linux/types.h>
-+#include <linux/fb.h>
-+#include <linux/vt_kern.h>
-+#include <linux/vmalloc.h>
-+#include <linux/unistd.h>
-+#include <linux/syscalls.h>
-+#include <linux/init.h>
-+#include <linux/proc_fs.h>
-+#include <linux/workqueue.h>
-+#include <linux/kmod.h>
-+#include <linux/miscdevice.h>
-+#include <linux/device.h>
-+#include <linux/fs.h>
-+#include <linux/compat.h>
-+#include <linux/console.h>
-+
-+#include <asm/uaccess.h>
-+#include <asm/irq.h>
-+
-+#include "fbcon.h"
-+#include "fbcondecor.h"
-+
-+extern signed char con2fb_map[];
-+static int fbcon_decor_enable(struct vc_data *vc);
-+char fbcon_decor_path[KMOD_PATH_LEN] = "/sbin/fbcondecor_helper";
-+static int initialized = 0;
-+
-+int fbcon_decor_call_helper(char* cmd, unsigned short vc)
-+{
-+	char *envp[] = {
-+		"HOME=/",
-+		"PATH=/sbin:/bin",
-+		NULL
-+	};
-+
-+	char tfb[5];
-+	char tcons[5];
-+	unsigned char fb = (int) con2fb_map[vc];
-+
-+	char *argv[] = {
-+		fbcon_decor_path,
-+		"2",
-+		cmd,
-+		tcons,
-+		tfb,
-+		vc_cons[vc].d->vc_decor.theme,
-+		NULL
-+	};
-+
-+	snprintf(tfb,5,"%d",fb);
-+	snprintf(tcons,5,"%d",vc);
-+
-+	return call_usermodehelper(fbcon_decor_path, argv, envp, UMH_WAIT_EXEC);
-+}
-+
-+/* Disables fbcondecor on a virtual console; called with console sem held. */
-+int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw)
-+{
-+	struct fb_info* info;
-+
-+	if (!vc->vc_decor.state)
-+		return -EINVAL;
-+
-+	info = registered_fb[(int) con2fb_map[vc->vc_num]];
-+
-+	if (info == NULL)
-+		return -EINVAL;
-+
-+	vc->vc_decor.state = 0;
-+	vc_resize(vc, info->var.xres / vc->vc_font.width,
-+		  info->var.yres / vc->vc_font.height);
-+
-+	if (fg_console == vc->vc_num && redraw) {
-+		redraw_screen(vc, 0);
-+		update_region(vc, vc->vc_origin +
-+			      vc->vc_size_row * vc->vc_top,
-+			      vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2);
-+	}
-+
-+	printk(KERN_INFO "fbcondecor: switched decor state to 'off' on console %d\n",
-+			 vc->vc_num);
-+
-+	return 0;
-+}
-+
-+/* Enables fbcondecor on a virtual console; called with console sem held. */
-+static int fbcon_decor_enable(struct vc_data *vc)
-+{
-+	struct fb_info* info;
-+
-+	info = registered_fb[(int) con2fb_map[vc->vc_num]];
-+
-+	if (vc->vc_decor.twidth == 0 || vc->vc_decor.theight == 0 ||
-+	    info == NULL || vc->vc_decor.state || (!info->bgdecor.data &&
-+	    vc->vc_num == fg_console))
-+		return -EINVAL;
-+
-+	vc->vc_decor.state = 1;
-+	vc_resize(vc, vc->vc_decor.twidth / vc->vc_font.width,
-+		  vc->vc_decor.theight / vc->vc_font.height);
-+
-+	if (fg_console == vc->vc_num) {
-+		redraw_screen(vc, 0);
-+		update_region(vc, vc->vc_origin +
-+			      vc->vc_size_row * vc->vc_top,
-+			      vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2);
-+		fbcon_decor_clear_margins(vc, info, 0);
-+	}
-+
-+	printk(KERN_INFO "fbcondecor: switched decor state to 'on' on console %d\n",
-+			 vc->vc_num);
-+
-+	return 0;
-+}
-+
-+static inline int fbcon_decor_ioctl_dosetstate(struct vc_data *vc, unsigned int state, unsigned char origin)
-+{
-+	int ret;
-+
-+//	if (origin == FBCON_DECOR_IO_ORIG_USER)
-+		console_lock();
-+	if (!state)
-+		ret = fbcon_decor_disable(vc, 1);
-+	else
-+		ret = fbcon_decor_enable(vc);
-+//	if (origin == FBCON_DECOR_IO_ORIG_USER)
-+		console_unlock();
-+
-+	return ret;
-+}
-+
-+static inline void fbcon_decor_ioctl_dogetstate(struct vc_data *vc, unsigned int *state)
-+{
-+	*state = vc->vc_decor.state;
-+}
-+
-+static int fbcon_decor_ioctl_dosetcfg(struct vc_data *vc, struct vc_decor *cfg, unsigned char origin)
-+{
-+	struct fb_info *info;
-+	int len;
-+	char *tmp;
-+
-+	info = registered_fb[(int) con2fb_map[vc->vc_num]];
-+
-+	if (info == NULL || !cfg->twidth || !cfg->theight ||
-+	    cfg->tx + cfg->twidth  > info->var.xres ||
-+	    cfg->ty + cfg->theight > info->var.yres)
-+		return -EINVAL;
-+
-+	len = strlen_user(cfg->theme);
-+	if (!len || len > FBCON_DECOR_THEME_LEN)
-+		return -EINVAL;
-+	tmp = kmalloc(len, GFP_KERNEL);
-+	if (!tmp)
-+		return -ENOMEM;
-+	if (copy_from_user(tmp, (void __user *)cfg->theme, len))
-+		return -EFAULT;
-+	cfg->theme = tmp;
-+	cfg->state = 0;
-+
-+	/* If this ioctl is a response to a request from kernel, the console sem
-+	 * is already held; we also don't need to disable decor because either the
-+	 * new config and background picture will be successfully loaded, and the
-+	 * decor will stay on, or in case of a failure it'll be turned off in fbcon. */
-+//	if (origin == FBCON_DECOR_IO_ORIG_USER) {
-+		console_lock();
-+		if (vc->vc_decor.state)
-+			fbcon_decor_disable(vc, 1);
-+//	}
-+
-+	if (vc->vc_decor.theme)
-+		kfree(vc->vc_decor.theme);
-+
-+	vc->vc_decor = *cfg;
-+
-+//	if (origin == FBCON_DECOR_IO_ORIG_USER)
-+		console_unlock();
-+
-+	printk(KERN_INFO "fbcondecor: console %d using theme '%s'\n",
-+			 vc->vc_num, vc->vc_decor.theme);
-+	return 0;
-+}
-+
-+static int fbcon_decor_ioctl_dogetcfg(struct vc_data *vc, struct vc_decor *decor)
-+{
-+	char __user *tmp;
-+
-+	tmp = decor->theme;
-+	*decor = vc->vc_decor;
-+	decor->theme = tmp;
-+
-+	if (vc->vc_decor.theme) {
-+		if (copy_to_user(tmp, vc->vc_decor.theme, strlen(vc->vc_decor.theme) + 1))
-+			return -EFAULT;
-+	} else
-+		if (put_user(0, tmp))
-+			return -EFAULT;
-+
-+	return 0;
-+}
-+
-+static int fbcon_decor_ioctl_dosetpic(struct vc_data *vc, struct fb_image *img, unsigned char origin)
-+{
-+	struct fb_info *info;
-+	int len;
-+	u8 *tmp;
-+
-+	if (vc->vc_num != fg_console)
-+		return -EINVAL;
-+
-+	info = registered_fb[(int) con2fb_map[vc->vc_num]];
-+
-+	if (info == NULL)
-+		return -EINVAL;
-+
-+	if (img->width != info->var.xres || img->height != info->var.yres) {
-+		printk(KERN_ERR "fbcondecor: picture dimensions mismatch\n");
-+		printk(KERN_ERR "%dx%d vs %dx%d\n", img->width, img->height, info->var.xres, info->var.yres);
-+		return -EINVAL;
-+	}
-+
-+	if (img->depth != info->var.bits_per_pixel) {
-+		printk(KERN_ERR "fbcondecor: picture depth mismatch\n");
-+		return -EINVAL;
-+	}
-+
-+	if (img->depth == 8) {
-+		if (!img->cmap.len || !img->cmap.red || !img->cmap.green ||
-+		    !img->cmap.blue)
-+			return -EINVAL;
-+
-+		tmp = vmalloc(img->cmap.len * 3 * 2);
-+		if (!tmp)
-+			return -ENOMEM;
-+
-+		if (copy_from_user(tmp,
-+			    	   (void __user*)img->cmap.red, (img->cmap.len << 1)) ||
-+		    copy_from_user(tmp + (img->cmap.len << 1),
-+			    	   (void __user*)img->cmap.green, (img->cmap.len << 1)) ||
-+		    copy_from_user(tmp + (img->cmap.len << 2),
-+			    	   (void __user*)img->cmap.blue, (img->cmap.len << 1))) {
-+			vfree(tmp);
-+			return -EFAULT;
-+		}
-+
-+		img->cmap.transp = NULL;
-+		img->cmap.red = (u16*)tmp;
-+		img->cmap.green = img->cmap.red + img->cmap.len;
-+		img->cmap.blue = img->cmap.green + img->cmap.len;
-+	} else {
-+		img->cmap.red = NULL;
-+	}
-+
-+	len = ((img->depth + 7) >> 3) * img->width * img->height;
-+
-+	/*
-+	 * Allocate an additional byte so that we never go outside of the
-+	 * buffer boundaries in the rendering functions in a 24 bpp mode.
-+	 */
-+	tmp = vmalloc(len + 1);
-+
-+	if (!tmp)
-+		goto out;
-+
-+	if (copy_from_user(tmp, (void __user*)img->data, len))
-+		goto out;
-+
-+	img->data = tmp;
-+
-+	/* If this ioctl is a response to a request from kernel, the console sem
-+	 * is already held. */
-+//	if (origin == FBCON_DECOR_IO_ORIG_USER)
-+		console_lock();
-+
-+	if (info->bgdecor.data)
-+		vfree((u8*)info->bgdecor.data);
-+	if (info->bgdecor.cmap.red)
-+		vfree(info->bgdecor.cmap.red);
-+
-+	info->bgdecor = *img;
-+
-+	if (fbcon_decor_active_vc(vc) && fg_console == vc->vc_num) {
-+		redraw_screen(vc, 0);
-+		update_region(vc, vc->vc_origin +
-+			      vc->vc_size_row * vc->vc_top,
-+			      vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2);
-+		fbcon_decor_clear_margins(vc, info, 0);
-+	}
-+
-+//	if (origin == FBCON_DECOR_IO_ORIG_USER)
-+		console_unlock();
-+
-+	return 0;
-+
-+out:	if (img->cmap.red)
-+		vfree(img->cmap.red);
-+
-+	if (tmp)
-+		vfree(tmp);
-+	return -ENOMEM;
-+}
-+
-+static long fbcon_decor_ioctl(struct file *filp, u_int cmd, u_long arg)
-+{
-+	struct fbcon_decor_iowrapper __user *wrapper = (void __user*) arg;
-+	struct vc_data *vc = NULL;
-+	unsigned short vc_num = 0;
-+	unsigned char origin = 0;
-+	void __user *data = NULL;
-+
-+	if (!access_ok(VERIFY_READ, wrapper,
-+			sizeof(struct fbcon_decor_iowrapper)))
-+		return -EFAULT;
-+
-+	__get_user(vc_num, &wrapper->vc);
-+	__get_user(origin, &wrapper->origin);
-+	__get_user(data, &wrapper->data);
-+
-+	if (!vc_cons_allocated(vc_num))
-+		return -EINVAL;
-+
-+	vc = vc_cons[vc_num].d;
-+
-+	switch (cmd) {
-+	case FBIOCONDECOR_SETPIC:
-+	{
-+		struct fb_image img;
-+		if (copy_from_user(&img, (struct fb_image __user *)data, sizeof(struct fb_image)))
-+			return -EFAULT;
-+
-+		return fbcon_decor_ioctl_dosetpic(vc, &img, origin);
-+	}
-+	case FBIOCONDECOR_SETCFG:
-+	{
-+		struct vc_decor cfg;
-+		if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor)))
-+			return -EFAULT;
-+
-+		return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin);
-+	}
-+	case FBIOCONDECOR_GETCFG:
-+	{
-+		int rval;
-+		struct vc_decor cfg;
-+
-+		if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor)))
-+			return -EFAULT;
-+
-+		rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg);
-+
-+		if (copy_to_user(data, &cfg, sizeof(struct vc_decor)))
-+			return -EFAULT;
-+		return rval;
-+	}
-+	case FBIOCONDECOR_SETSTATE:
-+	{
-+		unsigned int state = 0;
-+		if (get_user(state, (unsigned int __user *)data))
-+			return -EFAULT;
-+		return fbcon_decor_ioctl_dosetstate(vc, state, origin);
-+	}
-+	case FBIOCONDECOR_GETSTATE:
-+	{
-+		unsigned int state = 0;
-+		fbcon_decor_ioctl_dogetstate(vc, &state);
-+		return put_user(state, (unsigned int __user *)data);
-+	}
-+
-+	default:
-+		return -ENOIOCTLCMD;
-+	}
-+}
-+
-+#ifdef CONFIG_COMPAT
-+
-+static long fbcon_decor_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
-+
-+	struct fbcon_decor_iowrapper32 __user *wrapper = (void __user *)arg;
-+	struct vc_data *vc = NULL;
-+	unsigned short vc_num = 0;
-+	unsigned char origin = 0;
-+	compat_uptr_t data_compat = 0;
-+	void __user *data = NULL;
-+
-+	if (!access_ok(VERIFY_READ, wrapper,
-+                       sizeof(struct fbcon_decor_iowrapper32)))
-+		return -EFAULT;
-+
-+	__get_user(vc_num, &wrapper->vc);
-+	__get_user(origin, &wrapper->origin);
-+	__get_user(data_compat, &wrapper->data);
-+	data = compat_ptr(data_compat);
-+
-+	if (!vc_cons_allocated(vc_num))
-+		return -EINVAL;
-+
-+	vc = vc_cons[vc_num].d;
-+
-+	switch (cmd) {
-+	case FBIOCONDECOR_SETPIC32:
-+	{
-+		struct fb_image32 img_compat;
-+		struct fb_image img;
-+
-+		if (copy_from_user(&img_compat, (struct fb_image32 __user *)data, sizeof(struct fb_image32)))
-+			return -EFAULT;
-+
-+		fb_image_from_compat(img, img_compat);
-+
-+		return fbcon_decor_ioctl_dosetpic(vc, &img, origin);
-+	}
-+
-+	case FBIOCONDECOR_SETCFG32:
-+	{
-+		struct vc_decor32 cfg_compat;
-+		struct vc_decor cfg;
-+
-+		if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32)))
-+			return -EFAULT;
-+
-+		vc_decor_from_compat(cfg, cfg_compat);
-+
-+		return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin);
-+	}
-+
-+	case FBIOCONDECOR_GETCFG32:
-+	{
-+		int rval;
-+		struct vc_decor32 cfg_compat;
-+		struct vc_decor cfg;
-+
-+		if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32)))
-+			return -EFAULT;
-+		cfg.theme = compat_ptr(cfg_compat.theme);
-+
-+		rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg);
-+
-+		vc_decor_to_compat(cfg_compat, cfg);
-+
-+		if (copy_to_user((struct vc_decor32 __user *)data, &cfg_compat, sizeof(struct vc_decor32)))
-+			return -EFAULT;
-+		return rval;
-+	}
-+
-+	case FBIOCONDECOR_SETSTATE32:
-+	{
-+		compat_uint_t state_compat = 0;
-+		unsigned int state = 0;
-+
-+		if (get_user(state_compat, (compat_uint_t __user *)data))
-+			return -EFAULT;
-+
-+		state = (unsigned int)state_compat;
-+
-+		return fbcon_decor_ioctl_dosetstate(vc, state, origin);
-+	}
-+
-+	case FBIOCONDECOR_GETSTATE32:
-+	{
-+		compat_uint_t state_compat = 0;
-+		unsigned int state = 0;
-+
-+		fbcon_decor_ioctl_dogetstate(vc, &state);
-+		state_compat = (compat_uint_t)state;
-+
-+		return put_user(state_compat, (compat_uint_t __user *)data);
-+	}
-+
-+	default:
-+		return -ENOIOCTLCMD;
-+	}
-+}
-+#else
-+  #define fbcon_decor_compat_ioctl NULL
-+#endif
-+
-+static struct file_operations fbcon_decor_ops = {
-+	.owner = THIS_MODULE,
-+	.unlocked_ioctl = fbcon_decor_ioctl,
-+	.compat_ioctl = fbcon_decor_compat_ioctl
-+};
-+
-+static struct miscdevice fbcon_decor_dev = {
-+	.minor = MISC_DYNAMIC_MINOR,
-+	.name = "fbcondecor",
-+	.fops = &fbcon_decor_ops
-+};
-+
-+void fbcon_decor_reset(void)
-+{
-+	int i;
-+
-+	for (i = 0; i < num_registered_fb; i++) {
-+		registered_fb[i]->bgdecor.data = NULL;
-+		registered_fb[i]->bgdecor.cmap.red = NULL;
-+	}
-+
-+	for (i = 0; i < MAX_NR_CONSOLES && vc_cons[i].d; i++) {
-+		vc_cons[i].d->vc_decor.state = vc_cons[i].d->vc_decor.twidth =
-+						vc_cons[i].d->vc_decor.theight = 0;
-+		vc_cons[i].d->vc_decor.theme = NULL;
-+	}
-+
-+	return;
-+}
-+
-+int fbcon_decor_init(void)
-+{
-+	int i;
-+
-+	fbcon_decor_reset();
-+
-+	if (initialized)
-+		return 0;
-+
-+	i = misc_register(&fbcon_decor_dev);
-+	if (i) {
-+		printk(KERN_ERR "fbcondecor: failed to register device\n");
-+		return i;
-+	}
-+
-+	fbcon_decor_call_helper("init", 0);
-+	initialized = 1;
-+	return 0;
-+}
-+
-+int fbcon_decor_exit(void)
-+{
-+	fbcon_decor_reset();
-+	return 0;
-+}
-+
-+EXPORT_SYMBOL(fbcon_decor_path);
-diff --git a/drivers/video/console/fbcondecor.h b/drivers/video/console/fbcondecor.h
-new file mode 100644
-index 0000000..3b3724b
---- /dev/null
-+++ b/drivers/video/console/fbcondecor.h
-@@ -0,0 +1,78 @@
-+/* 
-+ *  linux/drivers/video/console/fbcondecor.h -- Framebuffer Console Decoration headers
-+ *
-+ *  Copyright (C) 2004 Michal Januszewski <michalj+fbcondecor@gmail.com>
-+ *
-+ */
-+
-+#ifndef __FBCON_DECOR_H
-+#define __FBCON_DECOR_H
-+
-+#ifndef _LINUX_FB_H
-+#include <linux/fb.h>
-+#endif
-+
-+/* This is needed for vc_cons in fbcmap.c */
-+#include <linux/vt_kern.h>
-+
-+struct fb_cursor;
-+struct fb_info;
-+struct vc_data;
-+
-+#ifdef CONFIG_FB_CON_DECOR
-+/* fbcondecor.c */
-+int fbcon_decor_init(void);
-+int fbcon_decor_exit(void);
-+int fbcon_decor_call_helper(char* cmd, unsigned short cons);
-+int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw);
-+
-+/* cfbcondecor.c */
-+void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx);
-+void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor);
-+void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width);
-+void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only);
-+void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank);
-+void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width);
-+void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes, int srclinesbytes, int bpp);
-+void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc);
-+
-+/* vt.c */
-+void acquire_console_sem(void);
-+void release_console_sem(void);
-+void do_unblank_screen(int entering_gfx);
-+
-+/* struct vc_data *y */
-+#define fbcon_decor_active_vc(y) (y->vc_decor.state && y->vc_decor.theme) 
-+
-+/* struct fb_info *x, struct vc_data *y */
-+#define fbcon_decor_active_nores(x,y) (x->bgdecor.data && fbcon_decor_active_vc(y))
-+
-+/* struct fb_info *x, struct vc_data *y */
-+#define fbcon_decor_active(x,y) (fbcon_decor_active_nores(x,y) &&		\
-+			      x->bgdecor.width == x->var.xres && 	\
-+			      x->bgdecor.height == x->var.yres &&	\
-+			      x->bgdecor.depth == x->var.bits_per_pixel)
-+
-+
-+#else /* CONFIG_FB_CON_DECOR */
-+
-+static inline void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx) {}
-+static inline void fbcon_decor_putc(struct vc_data *vc, struct fb_info *info, int c, int ypos, int xpos) {}
-+static inline void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) {}
-+static inline void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) {}
-+static inline void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only) {}
-+static inline void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) {}
-+static inline void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) {}
-+static inline void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) {}
-+static inline int fbcon_decor_call_helper(char* cmd, unsigned short cons) { return 0; }
-+static inline int fbcon_decor_init(void) { return 0; }
-+static inline int fbcon_decor_exit(void) { return 0; }
-+static inline int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) { return 0; }
-+
-+#define fbcon_decor_active_vc(y) (0)
-+#define fbcon_decor_active_nores(x,y) (0)
-+#define fbcon_decor_active(x,y) (0)
-+
-+#endif /* CONFIG_FB_CON_DECOR */
-+
-+#endif /* __FBCON_DECOR_H */
-diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
-index e1f4727..2952e33 100644
---- a/drivers/video/fbdev/Kconfig
-+++ b/drivers/video/fbdev/Kconfig
-@@ -1204,7 +1204,6 @@ config FB_MATROX
- 	select FB_CFB_FILLRECT
- 	select FB_CFB_COPYAREA
- 	select FB_CFB_IMAGEBLIT
--	select FB_TILEBLITTING
- 	select FB_MACMODES if PPC_PMAC
- 	---help---
- 	  Say Y here if you have a Matrox Millennium, Matrox Millennium II,
-diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c
-index f89245b..05e036c 100644
---- a/drivers/video/fbdev/core/fbcmap.c
-+++ b/drivers/video/fbdev/core/fbcmap.c
-@@ -17,6 +17,8 @@
- #include <linux/slab.h>
- #include <linux/uaccess.h>
- 
-+#include "../../console/fbcondecor.h"
-+
- static u16 red2[] __read_mostly = {
-     0x0000, 0xaaaa
- };
-@@ -249,14 +251,17 @@ int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info)
- 			if (transp)
- 				htransp = *transp++;
- 			if (info->fbops->fb_setcolreg(start++,
--						      hred, hgreen, hblue,
-+						      hred, hgreen, hblue, 
- 						      htransp, info))
- 				break;
- 		}
- 	}
--	if (rc == 0)
-+	if (rc == 0) {
- 		fb_copy_cmap(cmap, &info->cmap);
--
-+		if (fbcon_decor_active(info, vc_cons[fg_console].d) &&
-+		    info->fix.visual == FB_VISUAL_DIRECTCOLOR)
-+			fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d);
-+	}
- 	return rc;
- }
- 
-diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
-index b6d5008..d6703f2 100644
---- a/drivers/video/fbdev/core/fbmem.c
-+++ b/drivers/video/fbdev/core/fbmem.c
-@@ -1250,15 +1250,6 @@ struct fb_fix_screeninfo32 {
- 	u16			reserved[3];
- };
- 
--struct fb_cmap32 {
--	u32			start;
--	u32			len;
--	compat_caddr_t	red;
--	compat_caddr_t	green;
--	compat_caddr_t	blue;
--	compat_caddr_t	transp;
--};
--
- static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
- 			  unsigned long arg)
- {
-diff --git a/include/linux/console_decor.h b/include/linux/console_decor.h
-new file mode 100644
-index 0000000..04b8d80
---- /dev/null
-+++ b/include/linux/console_decor.h
-@@ -0,0 +1,46 @@
-+#ifndef _LINUX_CONSOLE_DECOR_H_
-+#define _LINUX_CONSOLE_DECOR_H_ 1
-+
-+/* A structure used by the framebuffer console decorations (drivers/video/console/fbcondecor.c) */
-+struct vc_decor {
-+	__u8 bg_color;				/* The color that is to be treated as transparent */
-+	__u8 state;				/* Current decor state: 0 = off, 1 = on */
-+	__u16 tx, ty;				/* Top left corner coordinates of the text field */
-+	__u16 twidth, theight;			/* Width and height of the text field */
-+	char* theme;
-+};
-+
-+#ifdef __KERNEL__
-+#ifdef CONFIG_COMPAT
-+#include <linux/compat.h>
-+
-+struct vc_decor32 {
-+	__u8 bg_color;				/* The color that is to be treated as transparent */
-+	__u8 state;				/* Current decor state: 0 = off, 1 = on */
-+	__u16 tx, ty;				/* Top left corner coordinates of the text field */
-+	__u16 twidth, theight;			/* Width and height of the text field */
-+	compat_uptr_t theme;
-+};
-+
-+#define vc_decor_from_compat(to, from) \
-+	(to).bg_color = (from).bg_color; \
-+	(to).state    = (from).state; \
-+	(to).tx       = (from).tx; \
-+	(to).ty       = (from).ty; \
-+	(to).twidth   = (from).twidth; \
-+	(to).theight  = (from).theight; \
-+	(to).theme    = compat_ptr((from).theme)
-+
-+#define vc_decor_to_compat(to, from) \
-+	(to).bg_color = (from).bg_color; \
-+	(to).state    = (from).state; \
-+	(to).tx       = (from).tx; \
-+	(to).ty       = (from).ty; \
-+	(to).twidth   = (from).twidth; \
-+	(to).theight  = (from).theight; \
-+	(to).theme    = ptr_to_compat((from).theme)
-+
-+#endif /* CONFIG_COMPAT */
-+#endif /* __KERNEL__ */
-+
-+#endif
-diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h
-index 7f0c329..98f5d60 100644
---- a/include/linux/console_struct.h
-+++ b/include/linux/console_struct.h
-@@ -19,6 +19,7 @@
- struct vt_struct;
- 
- #define NPAR 16
-+#include <linux/console_decor.h>
- 
- struct vc_data {
- 	struct tty_port port;			/* Upper level data */
-@@ -107,6 +108,8 @@ struct vc_data {
- 	unsigned long	vc_uni_pagedir;
- 	unsigned long	*vc_uni_pagedir_loc;  /* [!] Location of uni_pagedir variable for this console */
- 	bool vc_panic_force_write; /* when oops/panic this VC can accept forced output/blanking */
-+
-+	struct vc_decor vc_decor;
- 	/* additional information is in vt_kern.h */
- };
- 
-diff --git a/include/linux/fb.h b/include/linux/fb.h
-index fe6ac95..1e36b03 100644
---- a/include/linux/fb.h
-+++ b/include/linux/fb.h
-@@ -219,6 +219,34 @@ struct fb_deferred_io {
- };
- #endif
- 
-+#ifdef __KERNEL__
-+#ifdef CONFIG_COMPAT
-+struct fb_image32 {
-+	__u32 dx;			/* Where to place image */
-+	__u32 dy;
-+	__u32 width;			/* Size of image */
-+	__u32 height;
-+	__u32 fg_color;			/* Only used when a mono bitmap */
-+	__u32 bg_color;
-+	__u8  depth;			/* Depth of the image */
-+	const compat_uptr_t data;	/* Pointer to image data */
-+	struct fb_cmap32 cmap;		/* color map info */
-+};
-+
-+#define fb_image_from_compat(to, from) \
-+	(to).dx       = (from).dx; \
-+	(to).dy       = (from).dy; \
-+	(to).width    = (from).width; \
-+	(to).height   = (from).height; \
-+	(to).fg_color = (from).fg_color; \
-+	(to).bg_color = (from).bg_color; \
-+	(to).depth    = (from).depth; \
-+	(to).data     = compat_ptr((from).data); \
-+	fb_cmap_from_compat((to).cmap, (from).cmap)
-+
-+#endif /* CONFIG_COMPAT */
-+#endif /* __KERNEL__ */
-+
- /*
-  * Frame buffer operations
-  *
-@@ -489,6 +517,9 @@ struct fb_info {
- #define FBINFO_STATE_SUSPENDED	1
- 	u32 state;			/* Hardware state i.e suspend */
- 	void *fbcon_par;                /* fbcon use-only private area */
-+
-+	struct fb_image bgdecor;
-+
- 	/* From here on everything is device dependent */
- 	void *par;
- 	/* we need the PCI or similar aperture base/size not
-diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h
-index fb795c3..dc77a03 100644
---- a/include/uapi/linux/fb.h
-+++ b/include/uapi/linux/fb.h
-@@ -8,6 +8,25 @@
- 
- #define FB_MAX			32	/* sufficient for now */
- 
-+struct fbcon_decor_iowrapper
-+{
-+	unsigned short vc;		/* Virtual console */
-+	unsigned char origin;		/* Point of origin of the request */
-+	void *data;
-+};
-+
-+#ifdef __KERNEL__
-+#ifdef CONFIG_COMPAT
-+#include <linux/compat.h>
-+struct fbcon_decor_iowrapper32
-+{
-+	unsigned short vc;		/* Virtual console */
-+	unsigned char origin;		/* Point of origin of the request */
-+	compat_uptr_t data;
-+};
-+#endif /* CONFIG_COMPAT */
-+#endif /* __KERNEL__ */
-+
- /* ioctls
-    0x46 is 'F'								*/
- #define FBIOGET_VSCREENINFO	0x4600
-@@ -35,6 +54,25 @@
- #define FBIOGET_DISPINFO        0x4618
- #define FBIO_WAITFORVSYNC	_IOW('F', 0x20, __u32)
- 
-+#define FBIOCONDECOR_SETCFG	_IOWR('F', 0x19, struct fbcon_decor_iowrapper)
-+#define FBIOCONDECOR_GETCFG	_IOR('F', 0x1A, struct fbcon_decor_iowrapper)
-+#define FBIOCONDECOR_SETSTATE	_IOWR('F', 0x1B, struct fbcon_decor_iowrapper)
-+#define FBIOCONDECOR_GETSTATE	_IOR('F', 0x1C, struct fbcon_decor_iowrapper)
-+#define FBIOCONDECOR_SETPIC 	_IOWR('F', 0x1D, struct fbcon_decor_iowrapper)
-+#ifdef __KERNEL__
-+#ifdef CONFIG_COMPAT
-+#define FBIOCONDECOR_SETCFG32	_IOWR('F', 0x19, struct fbcon_decor_iowrapper32)
-+#define FBIOCONDECOR_GETCFG32	_IOR('F', 0x1A, struct fbcon_decor_iowrapper32)
-+#define FBIOCONDECOR_SETSTATE32	_IOWR('F', 0x1B, struct fbcon_decor_iowrapper32)
-+#define FBIOCONDECOR_GETSTATE32	_IOR('F', 0x1C, struct fbcon_decor_iowrapper32)
-+#define FBIOCONDECOR_SETPIC32	_IOWR('F', 0x1D, struct fbcon_decor_iowrapper32)
-+#endif /* CONFIG_COMPAT */
-+#endif /* __KERNEL__ */
-+
-+#define FBCON_DECOR_THEME_LEN		128	/* Maximum lenght of a theme name */
-+#define FBCON_DECOR_IO_ORIG_KERNEL	0	/* Kernel ioctl origin */
-+#define FBCON_DECOR_IO_ORIG_USER	1	/* User ioctl origin */
-+ 
- #define FB_TYPE_PACKED_PIXELS		0	/* Packed Pixels	*/
- #define FB_TYPE_PLANES			1	/* Non interleaved planes */
- #define FB_TYPE_INTERLEAVED_PLANES	2	/* Interleaved planes	*/
-@@ -277,6 +315,29 @@ struct fb_var_screeninfo {
- 	__u32 reserved[4];		/* Reserved for future compatibility */
- };
- 
-+#ifdef __KERNEL__
-+#ifdef CONFIG_COMPAT
-+struct fb_cmap32 {
-+	__u32 start;
-+	__u32 len;			/* Number of entries */
-+	compat_uptr_t red;		/* Red values	*/
-+	compat_uptr_t green;
-+	compat_uptr_t blue;
-+	compat_uptr_t transp;		/* transparency, can be NULL */
-+};
-+
-+#define fb_cmap_from_compat(to, from) \
-+	(to).start  = (from).start; \
-+	(to).len    = (from).len; \
-+	(to).red    = compat_ptr((from).red); \
-+	(to).green  = compat_ptr((from).green); \
-+	(to).blue   = compat_ptr((from).blue); \
-+	(to).transp = compat_ptr((from).transp)
-+
-+#endif /* CONFIG_COMPAT */
-+#endif /* __KERNEL__ */
-+
-+
- struct fb_cmap {
- 	__u32 start;			/* First entry	*/
- 	__u32 len;			/* Number of entries */
-diff --git a/kernel/sysctl.c b/kernel/sysctl.c
-index 74f5b58..6386ab0 100644
---- a/kernel/sysctl.c
-+++ b/kernel/sysctl.c
-@@ -146,6 +146,10 @@ static const int cap_last_cap = CAP_LAST_CAP;
- static unsigned long hung_task_timeout_max = (LONG_MAX/HZ);
- #endif
- 
-+#ifdef CONFIG_FB_CON_DECOR
-+extern char fbcon_decor_path[];
-+#endif
-+
- #ifdef CONFIG_INOTIFY_USER
- #include <linux/inotify.h>
- #endif
-@@ -255,6 +259,15 @@ static struct ctl_table sysctl_base_table[] = {
- 		.mode		= 0555,
- 		.child		= dev_table,
- 	},
-+#ifdef CONFIG_FB_CON_DECOR
-+	{
-+		.procname	= "fbcondecor",
-+		.data		= &fbcon_decor_path,
-+		.maxlen		= KMOD_PATH_LEN,
-+		.mode		= 0644,
-+		.proc_handler	= &proc_dostring,
-+	},
-+#endif
- 	{ }
- };
- 
Index: npl/kernel/linux_src/linux-4.14-imq.diff
===================================================================
--- npl/kernel/linux_src/linux-4.14-imq.diff	(revision e8565120de16c582dc1bd83033f7d7370bacd011)
+++ npl/kernel/linux_src/linux-4.14-imq.diff	(revision e8565120de16c582dc1bd83033f7d7370bacd011)
@@ -0,0 +1,1759 @@
+diff -Naupr linux-4.14_orig/drivers/net/imq.c linux-4.14/drivers/net/imq.c
+--- linux-4.14_orig/drivers/net/imq.c	1970-01-01 07:00:00.000000000 +0700
++++ linux-4.14/drivers/net/imq.c	2017-11-13 11:46:45.844089945 +0700
+@@ -0,0 +1,962 @@
++/*
++ *             Pseudo-driver for the intermediate queue device.
++ *
++ *             This program is free software; you can redistribute it and/or
++ *             modify it under the terms of the GNU General Public License
++ *             as published by the Free Software Foundation; either version
++ *             2 of the License, or (at your option) any later version.
++ *
++ * Authors:    Patrick McHardy, <kaber@trash.net>
++ *
++ *            The first version was written by Martin Devera, <devik@cdi.cz>
++ *
++ *			   See Credits.txt
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/moduleparam.h>
++#include <linux/list.h>
++#include <linux/skbuff.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/rtnetlink.h>
++#include <linux/if_arp.h>
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4.h>
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++#include <linux/netfilter_ipv6.h>
++#endif
++#include <linux/imq.h>
++#include <net/pkt_sched.h>
++#include <net/netfilter/nf_queue.h>
++#include <net/sock.h>
++#include <linux/ip.h>
++#include <linux/ipv6.h>
++#include <linux/if_vlan.h>
++#include <linux/if_pppox.h>
++#include <net/ip.h>
++#include <net/ipv6.h>
++
++static int imq_nf_queue(struct nf_queue_entry *entry, unsigned queue_num);
++
++static nf_hookfn imq_nf_hook;
++
++static struct nf_hook_ops imq_ops[] = {
++	{
++	/* imq_ingress_ipv4 */
++		.hook		= imq_nf_hook,
++		.pf		= PF_INET,
++		.hooknum	= NF_INET_PRE_ROUTING,
++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
++		.priority	= NF_IP_PRI_MANGLE + 1,
++#else
++		.priority	= NF_IP_PRI_NAT_DST + 1,
++#endif
++	},
++	{
++	/* imq_egress_ipv4 */
++		.hook		= imq_nf_hook,
++		.pf		= PF_INET,
++		.hooknum	= NF_INET_POST_ROUTING,
++#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA)
++		.priority	= NF_IP_PRI_LAST,
++#else
++		.priority	= NF_IP_PRI_NAT_SRC - 1,
++#endif
++	},
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++	{
++	/* imq_ingress_ipv6 */
++		.hook		= imq_nf_hook,
++		.pf		= PF_INET6,
++		.hooknum	= NF_INET_PRE_ROUTING,
++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
++		.priority	= NF_IP6_PRI_MANGLE + 1,
++#else
++		.priority	= NF_IP6_PRI_NAT_DST + 1,
++#endif
++	},
++	{
++	/* imq_egress_ipv6 */
++		.hook		= imq_nf_hook,
++		.pf		= PF_INET6,
++		.hooknum	= NF_INET_POST_ROUTING,
++#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA)
++		.priority	= NF_IP6_PRI_LAST,
++#else
++		.priority	= NF_IP6_PRI_NAT_SRC - 1,
++#endif
++	},
++#endif
++};
++
++#if defined(CONFIG_IMQ_NUM_DEVS)
++static int numdevs = CONFIG_IMQ_NUM_DEVS;
++#else
++static int numdevs = IMQ_MAX_DEVS;
++#endif
++
++static struct net_device *imq_devs_cache[IMQ_MAX_DEVS];
++
++#define IMQ_MAX_QUEUES 32
++static int numqueues = 1;
++static u32 imq_hashrnd;
++static int imq_dev_accurate_stats = 1;
++
++static inline __be16 pppoe_proto(const struct sk_buff *skb)
++{
++	return *((__be16 *)(skb_mac_header(skb) + ETH_HLEN +
++			sizeof(struct pppoe_hdr)));
++}
++
++static u16 imq_hash(struct net_device *dev, struct sk_buff *skb)
++{
++	unsigned int pull_len;
++	u16 protocol = skb->protocol;
++	u32 addr1, addr2;
++	u32 hash, ihl = 0;
++	union {
++		u16 in16[2];
++		u32 in32;
++	} ports;
++	u8 ip_proto;
++
++	pull_len = 0;
++
++recheck:
++	switch (protocol) {
++	case htons(ETH_P_8021Q): {
++		if (unlikely(skb_pull(skb, VLAN_HLEN) == NULL))
++			goto other;
++
++		pull_len += VLAN_HLEN;
++		skb->network_header += VLAN_HLEN;
++
++		protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
++		goto recheck;
++	}
++
++	case htons(ETH_P_PPP_SES): {
++		if (unlikely(skb_pull(skb, PPPOE_SES_HLEN) == NULL))
++			goto other;
++
++		pull_len += PPPOE_SES_HLEN;
++		skb->network_header += PPPOE_SES_HLEN;
++
++		protocol = pppoe_proto(skb);
++		goto recheck;
++	}
++
++	case htons(ETH_P_IP): {
++		const struct iphdr *iph = ip_hdr(skb);
++
++		if (unlikely(!pskb_may_pull(skb, sizeof(struct iphdr))))
++			goto other;
++
++		addr1 = iph->daddr;
++		addr2 = iph->saddr;
++
++		ip_proto = !(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) ?
++				 iph->protocol : 0;
++		ihl = ip_hdrlen(skb);
++
++		break;
++	}
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++	case htons(ETH_P_IPV6): {
++		const struct ipv6hdr *iph = ipv6_hdr(skb);
++		__be16 fo = 0;
++
++		if (unlikely(!pskb_may_pull(skb, sizeof(struct ipv6hdr))))
++			goto other;
++
++		addr1 = iph->daddr.s6_addr32[3];
++		addr2 = iph->saddr.s6_addr32[3];
++		ihl = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &ip_proto,
++				       &fo);
++		if (unlikely(ihl < 0))
++			goto other;
++
++		break;
++	}
++#endif
++	default:
++other:
++		if (pull_len != 0) {
++			skb_push(skb, pull_len);
++			skb->network_header -= pull_len;
++		}
++
++		return (u16)(ntohs(protocol) % dev->real_num_tx_queues);
++	}
++
++	if (addr1 > addr2)
++		swap(addr1, addr2);
++
++	switch (ip_proto) {
++	case IPPROTO_TCP:
++	case IPPROTO_UDP:
++	case IPPROTO_DCCP:
++	case IPPROTO_ESP:
++	case IPPROTO_AH:
++	case IPPROTO_SCTP:
++	case IPPROTO_UDPLITE: {
++		if (likely(skb_copy_bits(skb, ihl, &ports.in32, 4) >= 0)) {
++			if (ports.in16[0] > ports.in16[1])
++				swap(ports.in16[0], ports.in16[1]);
++			break;
++		}
++		/* fall-through */
++	}
++	default:
++		ports.in32 = 0;
++		break;
++	}
++
++	if (pull_len != 0) {
++		skb_push(skb, pull_len);
++		skb->network_header -= pull_len;
++	}
++
++	hash = jhash_3words(addr1, addr2, ports.in32, imq_hashrnd ^ ip_proto);
++
++	return (u16)(((u64)hash * dev->real_num_tx_queues) >> 32);
++}
++
++static inline bool sk_tx_queue_recorded(struct sock *sk)
++{
++	return (sk_tx_queue_get(sk) >= 0);
++}
++
++static struct netdev_queue *imq_select_queue(struct net_device *dev,
++						struct sk_buff *skb)
++{
++	u16 queue_index = 0;
++	u32 hash;
++
++	if (likely(dev->real_num_tx_queues == 1))
++		goto out;
++
++	/* IMQ can be receiving ingress or engress packets. */
++
++	/* Check first for if rx_queue is set */
++	if (skb_rx_queue_recorded(skb)) {
++		queue_index = skb_get_rx_queue(skb);
++		goto out;
++	}
++
++	/* Check if socket has tx_queue set */
++	if (sk_tx_queue_recorded(skb->sk)) {
++		queue_index = sk_tx_queue_get(skb->sk);
++		goto out;
++	}
++
++	/* Try use socket hash */
++	if (skb->sk && skb->sk->sk_hash) {
++		hash = skb->sk->sk_hash;
++		queue_index =
++			(u16)(((u64)hash * dev->real_num_tx_queues) >> 32);
++		goto out;
++	}
++
++	/* Generate hash from packet data */
++	queue_index = imq_hash(dev, skb);
++
++out:
++	if (unlikely(queue_index >= dev->real_num_tx_queues))
++		queue_index = (u16)((u32)queue_index % dev->real_num_tx_queues);
++
++	skb_set_queue_mapping(skb, queue_index);
++	return netdev_get_tx_queue(dev, queue_index);
++}
++
++static struct net_device_stats *imq_get_stats(struct net_device *dev)
++{
++	return &dev->stats;
++}
++
++/* called for packets kfree'd in qdiscs at places other than enqueue */
++static void imq_skb_destructor(struct sk_buff *skb)
++{
++	struct nf_queue_entry *entry = skb->nf_queue_entry;
++
++	skb->nf_queue_entry = NULL;
++
++	if (entry) {
++		nf_queue_entry_release_refs(entry);
++		kfree(entry);
++	}
++
++	skb_restore_cb(skb); /* kfree backup */
++}
++
++static void imq_done_check_queue_mapping(struct sk_buff *skb,
++					 struct net_device *dev)
++{
++	unsigned int queue_index;
++
++	/* Don't let queue_mapping be left too large after exiting IMQ */
++	if (likely(skb->dev != dev && skb->dev != NULL)) {
++		queue_index = skb_get_queue_mapping(skb);
++		if (unlikely(queue_index >= skb->dev->real_num_tx_queues)) {
++			queue_index = (u16)((u32)queue_index %
++						skb->dev->real_num_tx_queues);
++			skb_set_queue_mapping(skb, queue_index);
++		}
++	} else {
++		/* skb->dev was IMQ device itself or NULL, be on safe side and
++		 * just clear queue mapping.
++		 */
++		skb_set_queue_mapping(skb, 0);
++	}
++}
++
++static netdev_tx_t imq_dev_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++	struct nf_queue_entry *entry = skb->nf_queue_entry;
++
++	rcu_read_lock();
++
++	skb->nf_queue_entry = NULL;
++	netif_trans_update(dev);
++
++	dev->stats.tx_bytes += skb->len;
++	dev->stats.tx_packets++;
++
++	if (unlikely(entry == NULL)) {
++		/* We don't know what is going on here.. packet is queued for
++		 * imq device, but (probably) not by us.
++		 *
++		 * If this packet was not send here by imq_nf_queue(), then
++		 * skb_save_cb() was not used and skb_free() should not show:
++		 *   WARNING: IMQ: kfree_skb: skb->cb_next:..
++		 * and/or
++		 *   WARNING: IMQ: kfree_skb: skb->nf_queue_entry...
++		 *
++		 * However if this message is shown, then IMQ is somehow broken
++		 * and you should report this to linuximq.net.
++		 */
++
++		/* imq_dev_xmit is black hole that eats all packets, report that
++		 * we eat this packet happily and increase dropped counters.
++		 */
++
++		dev->stats.tx_dropped++;
++		dev_kfree_skb(skb);
++
++		rcu_read_unlock();
++		return NETDEV_TX_OK;
++	}
++
++	skb_restore_cb(skb); /* restore skb->cb */
++
++	skb->imq_flags = 0;
++	skb->destructor = NULL;
++
++	imq_done_check_queue_mapping(skb, dev);
++
++	nf_reinject(entry, NF_ACCEPT);
++
++	rcu_read_unlock();
++	return NETDEV_TX_OK;
++}
++
++static struct net_device *get_imq_device_by_index(int index)
++{
++	struct net_device *dev = NULL;
++	struct net *net;
++	char buf[8];
++
++	/* get device by name and cache result */
++	snprintf(buf, sizeof(buf), "imq%d", index);
++
++	/* Search device from all namespaces. */
++	for_each_net(net) {
++		dev = dev_get_by_name(net, buf);
++		if (dev)
++			break;
++	}
++
++	if (WARN_ON_ONCE(dev == NULL)) {
++		/* IMQ device not found. Exotic config? */
++		return ERR_PTR(-ENODEV);
++	}
++
++	imq_devs_cache[index] = dev;
++	dev_put(dev);
++
++	return dev;
++}
++
++static struct nf_queue_entry *nf_queue_entry_dup(struct nf_queue_entry *e)
++{
++	struct nf_queue_entry *entry = kmemdup(e, e->size, GFP_ATOMIC);
++	if (entry) {
++		nf_queue_entry_get_refs(entry);
++		return entry;
++	}
++	return NULL;
++}
++
++#ifdef CONFIG_BRIDGE_NETFILTER
++/* When called from bridge netfilter, skb->data must point to MAC header
++ * before calling skb_gso_segment(). Else, original MAC header is lost
++ * and segmented skbs will be sent to wrong destination.
++ */
++static void nf_bridge_adjust_skb_data(struct sk_buff *skb)
++{
++	if (skb->nf_bridge)
++		__skb_push(skb, skb->network_header - skb->mac_header);
++}
++
++static void nf_bridge_adjust_segmented_data(struct sk_buff *skb)
++{
++	if (skb->nf_bridge)
++		__skb_pull(skb, skb->network_header - skb->mac_header);
++}
++#else
++#define nf_bridge_adjust_skb_data(s) do {} while (0)
++#define nf_bridge_adjust_segmented_data(s) do {} while (0)
++#endif
++
++static void free_entry(struct nf_queue_entry *entry)
++{
++	nf_queue_entry_release_refs(entry);
++	kfree(entry);
++}
++
++static int __imq_nf_queue(struct nf_queue_entry *entry, struct net_device *dev);
++
++static int __imq_nf_queue_gso(struct nf_queue_entry *entry,
++			      struct net_device *dev, struct sk_buff *skb)
++{
++	int ret = -ENOMEM;
++	struct nf_queue_entry *entry_seg;
++
++	nf_bridge_adjust_segmented_data(skb);
++
++	if (skb->next == NULL) { /* last packet, no need to copy entry */
++		struct sk_buff *gso_skb = entry->skb;
++		entry->skb = skb;
++		ret = __imq_nf_queue(entry, dev);
++		if (ret)
++			entry->skb = gso_skb;
++		return ret;
++	}
++
++	skb->next = NULL;
++
++	entry_seg = nf_queue_entry_dup(entry);
++	if (entry_seg) {
++		entry_seg->skb = skb;
++		ret = __imq_nf_queue(entry_seg, dev);
++		if (ret)
++			free_entry(entry_seg);
++	}
++	return ret;
++}
++
++static int imq_nf_queue(struct nf_queue_entry *entry, unsigned queue_num)
++{
++	struct sk_buff *skb, *segs;
++	struct net_device *dev;
++	unsigned int queued;
++	int index, retval, err;
++
++	index = entry->skb->imq_flags & IMQ_F_IFMASK;
++	if (unlikely(index > numdevs - 1)) {
++		if (net_ratelimit())
++			pr_warn("IMQ: invalid device specified, highest is %u\n",
++				numdevs - 1);
++		retval = -EINVAL;
++		goto out_no_dev;
++	}
++
++	/* check for imq device by index from cache */
++	dev = imq_devs_cache[index];
++	if (unlikely(!dev)) {
++		dev = get_imq_device_by_index(index);
++		if (IS_ERR(dev)) {
++			retval = PTR_ERR(dev);
++			goto out_no_dev;
++		}
++	}
++
++	if (unlikely(!(dev->flags & IFF_UP))) {
++		entry->skb->imq_flags = 0;
++		retval = -ECANCELED;
++		goto out_no_dev;
++	}
++
++	/* Since 3.10.x, GSO handling moved here as result of upstream commit
++	 * a5fedd43d5f6c94c71053a66e4c3d2e35f1731a2 (netfilter: move
++	 * skb_gso_segment into nfnetlink_queue module).
++	 *
++	 * Following code replicates the gso handling from
++	 * 'net/netfilter/nfnetlink_queue_core.c':nfqnl_enqueue_packet().
++	 */
++
++	skb = entry->skb;
++
++	switch (entry->state.pf) {
++	case NFPROTO_IPV4:
++		skb->protocol = htons(ETH_P_IP);
++		break;
++	case NFPROTO_IPV6:
++		skb->protocol = htons(ETH_P_IPV6);
++		break;
++	}
++
++	if (!skb_is_gso(entry->skb))
++		return __imq_nf_queue(entry, dev);
++
++	nf_bridge_adjust_skb_data(skb);
++	segs = skb_gso_segment(skb, 0);
++	/* Does not use PTR_ERR to limit the number of error codes that can be
++	 * returned by nf_queue.  For instance, callers rely on -ECANCELED to
++	 * mean 'ignore this hook'.
++	 */
++	err = -ENOBUFS;
++	if (IS_ERR(segs))
++		goto out_err;
++	queued = 0;
++	err = 0;
++	do {
++		struct sk_buff *nskb = segs->next;
++		if (nskb && nskb->next)
++			nskb->cb_next = NULL;
++		if (err == 0)
++			err = __imq_nf_queue_gso(entry, dev, segs);
++		if (err == 0)
++			queued++;
++		else
++			kfree_skb(segs);
++		segs = nskb;
++	} while (segs);
++
++	if (queued) {
++		if (err) /* some segments are already queued */
++			free_entry(entry);
++		kfree_skb(skb);
++		return 0;
++	}
++
++out_err:
++	nf_bridge_adjust_segmented_data(skb);
++	retval = err;
++out_no_dev:
++	return retval;
++}
++
++static int __imq_nf_queue(struct nf_queue_entry *entry, struct net_device *dev)
++{
++	struct sk_buff *skb_orig, *skb, *skb_shared, *skb_popd;
++	struct Qdisc *q;
++	struct sk_buff *to_free = NULL;
++	struct netdev_queue *txq;
++	spinlock_t *root_lock;
++	int users;
++	int retval = -EINVAL;
++	unsigned int orig_queue_index;
++
++	dev->last_rx = jiffies;
++
++	skb = entry->skb;
++	skb_orig = NULL;
++
++	/* skb has owner? => make clone */
++	if (unlikely(skb->destructor)) {
++		skb_orig = skb;
++		skb = skb_clone(skb, GFP_ATOMIC);
++		if (unlikely(!skb)) {
++			retval = -ENOMEM;
++			goto out;
++		}
++		skb->cb_next = NULL;
++		entry->skb = skb;
++	}
++
++	dev->stats.rx_bytes += skb->len;
++	dev->stats.rx_packets++;
++
++	if (!skb->dev) {
++		/* skb->dev == NULL causes problems, try the find cause. */
++		if (net_ratelimit()) {
++			dev_warn(&dev->dev,
++				 "received packet with skb->dev == NULL\n");
++			dump_stack();
++		}
++
++		skb->dev = dev;
++	}
++
++	/* Disables softirqs for lock below */
++	rcu_read_lock_bh();
++
++	/* Multi-queue selection */
++	orig_queue_index = skb_get_queue_mapping(skb);
++	txq = imq_select_queue(dev, skb);
++
++	q = rcu_dereference(txq->qdisc);
++	if (unlikely(!q->enqueue))
++		goto packet_not_eaten_by_imq_dev;
++
++	skb->nf_queue_entry = entry;
++	root_lock = qdisc_lock(q);
++	spin_lock(root_lock);
++
++	users = refcount_read(&skb->users);
++
++	skb_shared = skb_get(skb); /* increase reference count by one */
++
++	/* backup skb->cb, as qdisc layer will overwrite it */
++	skb_save_cb(skb_shared);
++	qdisc_enqueue_root(skb_shared, q, &to_free); /* might kfree_skb */
++	if (likely(refcount_read(&skb_shared->users) == users + 1)) {
++		bool validate;
++
++		kfree_skb(skb_shared); /* decrease reference count by one */
++
++		skb->destructor = &imq_skb_destructor;
++
++		skb_popd = qdisc_dequeue_skb(q, &validate);
++
++		/* cloned? */
++		if (unlikely(skb_orig))
++			kfree_skb(skb_orig); /* free original */
++
++		spin_unlock(root_lock);
++
++#if 0
++		/* schedule qdisc dequeue */
++		__netif_schedule(q);
++#else
++		if (likely(skb_popd)) {
++			/* Note that we validate skb (GSO, checksum, ...) outside of locks */
++			if (validate)
++        		skb_popd = validate_xmit_skb_list(skb_popd, dev);
++
++			if (skb_popd) {
++				int dummy_ret;
++				int cpu = smp_processor_id(); /* ok because BHs are off */
++
++				txq = skb_get_tx_queue(dev, skb_popd);
++				/*
++				IMQ device will not be frozen or stoped, and it always be successful.
++				So we need not check its status and return value to accelerate.
++				*/
++				if (imq_dev_accurate_stats && txq->xmit_lock_owner != cpu) {
++					HARD_TX_LOCK(dev, txq, cpu);
++					if (!netif_xmit_frozen_or_stopped(txq)) {
++						dev_hard_start_xmit(skb_popd, dev, txq, &dummy_ret);
++					}
++					HARD_TX_UNLOCK(dev, txq);
++				} else {
++					if (!netif_xmit_frozen_or_stopped(txq)) {
++						dev_hard_start_xmit(skb_popd, dev, txq, &dummy_ret);
++					}
++				}
++			}
++		} else {
++			/* No ready skb, then schedule it */
++			__netif_schedule(q);
++		}
++#endif
++		rcu_read_unlock_bh();
++		retval = 0;
++		goto out;
++	} else {
++		skb_restore_cb(skb_shared); /* restore skb->cb */
++		skb->nf_queue_entry = NULL;
++		/*
++		 * qdisc dropped packet and decreased skb reference count of
++		 * skb, so we don't really want to and try refree as that would
++		 * actually destroy the skb.
++		 */
++		spin_unlock(root_lock);
++		goto packet_not_eaten_by_imq_dev;
++	}
++
++packet_not_eaten_by_imq_dev:
++	skb_set_queue_mapping(skb, orig_queue_index);
++	rcu_read_unlock_bh();
++
++	/* cloned? restore original */
++	if (unlikely(skb_orig)) {
++		kfree_skb(skb);
++		entry->skb = skb_orig;
++	}
++	retval = -1;
++out:
++	if (unlikely(to_free)) {
++		kfree_skb_list(to_free);
++	}
++	return retval;
++}
++static unsigned int imq_nf_hook(void *priv,
++				struct sk_buff *skb,
++				const struct nf_hook_state *state)
++{
++	return (skb->imq_flags & IMQ_F_ENQUEUE) ? NF_IMQ_QUEUE : NF_ACCEPT;
++}
++
++static int imq_close(struct net_device *dev)
++{
++	netif_stop_queue(dev);
++	return 0;
++}
++
++static int imq_open(struct net_device *dev)
++{
++	netif_start_queue(dev);
++	return 0;
++}
++
++static struct device_type imq_device_type = {
++	.name = "imq",
++};
++
++static const struct net_device_ops imq_netdev_ops = {
++	.ndo_open		= imq_open,
++	.ndo_stop		= imq_close,
++	.ndo_start_xmit		= imq_dev_xmit,
++	.ndo_get_stats		= imq_get_stats,
++};
++
++static void imq_setup(struct net_device *dev)
++{
++	dev->netdev_ops		= &imq_netdev_ops;
++	dev->type		= ARPHRD_VOID;
++	dev->mtu		= 16000; /* too small? */
++	dev->tx_queue_len	= 11000; /* too big? */
++	dev->flags		= IFF_NOARP;
++	dev->features		= NETIF_F_SG | NETIF_F_FRAGLIST |
++				  NETIF_F_GSO | NETIF_F_HW_CSUM |
++				  NETIF_F_HIGHDMA;
++	dev->priv_flags		&= ~(IFF_XMIT_DST_RELEASE |
++				     IFF_TX_SKB_SHARING);
++}
++
++static int imq_validate(struct nlattr *tb[], struct nlattr *data[],
++			struct netlink_ext_ack *extack)
++{
++	int ret = 0;
++
++	if (tb[IFLA_ADDRESS]) {
++		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) {
++			ret = -EINVAL;
++			goto end;
++		}
++		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) {
++			ret = -EADDRNOTAVAIL;
++			goto end;
++		}
++	}
++	return 0;
++end:
++	pr_warn("IMQ: imq_validate failed (%d)\n", ret);
++	return ret;
++}
++
++static struct rtnl_link_ops imq_link_ops __read_mostly = {
++	.kind		= "imq",
++	.priv_size	= 0,
++	.setup		= imq_setup,
++	.validate	= imq_validate,
++};
++
++static const struct nf_queue_handler imq_nfqh = {
++	.outfn = imq_nf_queue,
++};
++
++static int __net_init imq_nf_register(struct net *net)
++{
++	return nf_register_net_hooks(net, imq_ops,
++				    ARRAY_SIZE(imq_ops));
++};
++
++static void __net_exit imq_nf_unregister(struct net *net)
++{
++	nf_unregister_net_hooks(net, imq_ops,
++			    ARRAY_SIZE(imq_ops));
++};
++
++static struct pernet_operations imq_net_ops = {
++	.init		= imq_nf_register,
++	.exit		= imq_nf_unregister,
++};
++
++static int __net_init imq_init_hooks(void)
++{
++	int ret;
++	nf_register_queue_imq_handler(&imq_nfqh);
++
++	ret = register_pernet_subsys(&imq_net_ops);
++	if (ret < 0)
++		nf_unregister_queue_imq_handler();
++
++	return ret;
++}
++
++#ifdef CONFIG_LOCKDEP
++	static struct lock_class_key imq_netdev_addr_lock_key;
++
++	static void __init imq_dev_set_lockdep_one(struct net_device *dev,
++				    struct netdev_queue *txq, void *arg)
++	{
++	/*
++	 * the IMQ transmit locks can be taken recursively,
++	 * for example with one IMQ rule for input- and one for
++	 * output network devices in iptables!
++	 * until we find a better solution ignore them.
++	 */
++		lockdep_set_novalidate_class(&txq->_xmit_lock);
++	}
++
++	static void imq_dev_set_lockdep_class(struct net_device *dev)
++		{
++			lockdep_set_class_and_name(&dev->addr_list_lock,
++			   			   &imq_netdev_addr_lock_key, "_xmit_addr_IMQ");
++			netdev_for_each_tx_queue(dev, imq_dev_set_lockdep_one, NULL);
++}
++#else
++	static inline void imq_dev_set_lockdep_class(struct net_device *dev)
++		{
++		}
++#endif
++
++static int __init imq_init_one(int index)
++{
++	struct net_device *dev;
++	int ret;
++
++	dev = alloc_netdev_mq(0, "imq%d", NET_NAME_UNKNOWN, imq_setup, numqueues);
++	if (!dev)
++		return -ENOMEM;
++
++	ret = dev_alloc_name(dev, dev->name);
++	if (ret < 0)
++		goto fail;
++
++	dev->rtnl_link_ops = &imq_link_ops;
++	SET_NETDEV_DEVTYPE(dev, &imq_device_type);
++	ret = register_netdevice(dev);
++	if (ret < 0)
++		goto fail;
++
++	imq_dev_set_lockdep_class(dev);
++
++	return 0;
++fail:
++	free_netdev(dev);
++	return ret;
++}
++
++static int __init imq_init_devs(void)
++{
++	int err, i;
++
++	if (numdevs < 1 || numdevs > IMQ_MAX_DEVS) {
++		pr_err("IMQ: numdevs has to be betweed 1 and %u\n",
++		       IMQ_MAX_DEVS);
++		return -EINVAL;
++	}
++
++	if (numqueues < 1 || numqueues > IMQ_MAX_QUEUES) {
++		pr_err("IMQ: numqueues has to be betweed 1 and %u\n",
++		       IMQ_MAX_QUEUES);
++		return -EINVAL;
++	}
++
++	get_random_bytes(&imq_hashrnd, sizeof(imq_hashrnd));
++
++	rtnl_lock();
++	err = __rtnl_link_register(&imq_link_ops);
++
++	for (i = 0; i < numdevs && !err; i++)
++		err = imq_init_one(i);
++
++	if (err) {
++		__rtnl_link_unregister(&imq_link_ops);
++		memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
++	}
++	rtnl_unlock();
++
++	return err;
++}
++
++static int __init imq_init_module(void)
++{
++	int err;
++
++#if defined(CONFIG_IMQ_NUM_DEVS)
++	BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS > 16);
++	BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS < 2);
++	BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS - 1 > IMQ_F_IFMASK);
++#endif
++
++	err = imq_init_devs();
++	if (err) {
++		pr_err("IMQ: Error trying imq_init_devs(net)\n");
++		return err;
++	}
++
++	err = imq_init_hooks();
++	if (err) {
++		pr_err(KERN_ERR "IMQ: Error trying imq_init_hooks()\n");
++		rtnl_link_unregister(&imq_link_ops);
++		memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
++		return err;
++	}
++
++	pr_info("IMQ driver loaded successfully. (numdevs = %d, numqueues = %d, imq_dev_accurate_stats = %d)\n",
++		numdevs, numqueues, imq_dev_accurate_stats);
++
++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
++	pr_info("\tHooking IMQ before NAT on PREROUTING.\n");
++#else
++	pr_info("\tHooking IMQ after NAT on PREROUTING.\n");
++#endif
++#if defined(CONFIG_IMQ_BEHAVIOR_AB) || defined(CONFIG_IMQ_BEHAVIOR_BB)
++	pr_info("\tHooking IMQ before NAT on POSTROUTING.\n");
++#else
++	pr_info("\tHooking IMQ after NAT on POSTROUTING.\n");
++#endif
++
++	return 0;
++}
++
++static void __exit imq_unhook(void)
++{
++	unregister_pernet_subsys(&imq_net_ops);
++	nf_unregister_queue_imq_handler();
++}
++
++static void __exit imq_cleanup_devs(void)
++{
++	rtnl_link_unregister(&imq_link_ops);
++	memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
++}
++
++static void __exit imq_exit_module(void)
++{
++	imq_unhook();
++	imq_cleanup_devs();
++	pr_info("IMQ driver unloaded successfully.\n");
++}
++
++module_init(imq_init_module);
++module_exit(imq_exit_module);
++
++module_param(numdevs, int, 0);
++module_param(numqueues, int, 0);
++module_param(imq_dev_accurate_stats, int, 0);
++MODULE_PARM_DESC(numdevs, "number of IMQ devices (how many imq* devices will be created)");
++MODULE_PARM_DESC(numqueues, "number of queues per IMQ device");
++MODULE_PARM_DESC(imq_dev_accurate_stats, "Notify if need the accurate imq device stats");
++
++MODULE_AUTHOR("https://github.com/imq/linuximq");
++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See https://github.com/imq/linuximq/wiki for more information.");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS_RTNL_LINK("imq");
+diff -Naupr linux-4.14_orig/drivers/net/Kconfig linux-4.14/drivers/net/Kconfig
+--- linux-4.14_orig/drivers/net/Kconfig	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/drivers/net/Kconfig	2017-11-13 11:46:45.844089945 +0700
+@@ -277,6 +277,125 @@ config RIONET_RX_SIZE
+ 	depends on RIONET
+ 	default "128"
+ 
++config IMQ
++	tristate "IMQ (intermediate queueing device) support"
++	depends on NETDEVICES && NETFILTER
++	---help---
++	  The IMQ device(s) is used as placeholder for QoS queueing
++	  disciplines. Every packet entering/leaving the IP stack can be
++	  directed through the IMQ device where it's enqueued/dequeued to the
++	  attached qdisc. This allows you to treat network devices as classes
++	  and distribute bandwidth among them. Iptables is used to specify
++	  through which IMQ device, if any, packets travel.
++
++	  More information at: https://github.com/imq/linuximq
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called imq.  If unsure, say N.
++
++choice
++	prompt "IMQ behavior (PRE/POSTROUTING)"
++	depends on IMQ
++	default IMQ_BEHAVIOR_AB
++	help
++	  This setting defines how IMQ behaves in respect to its
++	  hooking in PREROUTING and POSTROUTING.
++
++	  IMQ can work in any of the following ways:
++
++	      PREROUTING   |      POSTROUTING
++	  -----------------|-------------------
++	  #1  After NAT    |      After NAT
++	  #2  After NAT    |      Before NAT
++	  #3  Before NAT   |      After NAT
++	  #4  Before NAT   |      Before NAT
++
++	  The default behavior is to hook before NAT on PREROUTING
++	  and after NAT on POSTROUTING (#3).
++
++	  This settings are specially usefull when trying to use IMQ
++	  to shape NATed clients.
++
++	  More information can be found at: https://github.com/imq/linuximq
++
++	  If not sure leave the default settings alone.
++
++config IMQ_BEHAVIOR_AA
++	bool "IMQ AA"
++	help
++	  This setting defines how IMQ behaves in respect to its
++	  hooking in PREROUTING and POSTROUTING.
++
++	  Choosing this option will make IMQ hook like this:
++
++	  PREROUTING:   After NAT
++	  POSTROUTING:  After NAT
++
++	  More information can be found at: https://github.com/imq/linuximq
++
++	  If not sure leave the default settings alone.
++
++config IMQ_BEHAVIOR_AB
++	bool "IMQ AB"
++	help
++	  This setting defines how IMQ behaves in respect to its
++	  hooking in PREROUTING and POSTROUTING.
++
++	  Choosing this option will make IMQ hook like this:
++
++	  PREROUTING:   After NAT
++	  POSTROUTING:  Before NAT
++
++	  More information can be found at: https://github.com/imq/linuximq
++
++	  If not sure leave the default settings alone.
++
++config IMQ_BEHAVIOR_BA
++	bool "IMQ BA"
++	help
++	  This setting defines how IMQ behaves in respect to its
++	  hooking in PREROUTING and POSTROUTING.
++
++	  Choosing this option will make IMQ hook like this:
++
++	  PREROUTING:   Before NAT
++	  POSTROUTING:  After NAT
++
++	  More information can be found at: https://github.com/imq/linuximq
++
++	  If not sure leave the default settings alone.
++
++config IMQ_BEHAVIOR_BB
++	bool "IMQ BB"
++	help
++	  This setting defines how IMQ behaves in respect to its
++	  hooking in PREROUTING and POSTROUTING.
++
++	  Choosing this option will make IMQ hook like this:
++
++	  PREROUTING:   Before NAT
++	  POSTROUTING:  Before NAT
++
++	  More information can be found at: https://github.com/imq/linuximq
++
++	  If not sure leave the default settings alone.
++
++endchoice
++
++config IMQ_NUM_DEVS
++	int "Number of IMQ devices"
++	range 2 16
++	depends on IMQ
++	default "16"
++	help
++	  This setting defines how many IMQ devices will be created.
++
++	  The default value is 16.
++
++	  More information can be found at: https://github.com/imq/linuximq
++
++	  If not sure leave the default settings alone.
++
+ config TUN
+ 	tristate "Universal TUN/TAP device driver support"
+ 	depends on INET
+diff -Naupr linux-4.14_orig/drivers/net/Makefile linux-4.14/drivers/net/Makefile
+--- linux-4.14_orig/drivers/net/Makefile	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/drivers/net/Makefile	2017-11-13 11:46:45.844089945 +0700
+@@ -13,6 +13,7 @@ obj-$(CONFIG_DUMMY) += dummy.o
+ obj-$(CONFIG_EQUALIZER) += eql.o
+ obj-$(CONFIG_IFB) += ifb.o
+ obj-$(CONFIG_MACSEC) += macsec.o
++obj-$(CONFIG_IMQ) += imq.o
+ obj-$(CONFIG_MACVLAN) += macvlan.o
+ obj-$(CONFIG_MACVTAP) += macvtap.o
+ obj-$(CONFIG_MII) += mii.o
+diff -Naupr linux-4.14_orig/include/linux/imq.h linux-4.14/include/linux/imq.h
+--- linux-4.14_orig/include/linux/imq.h	1970-01-01 07:00:00.000000000 +0700
++++ linux-4.14/include/linux/imq.h	2017-11-13 11:46:45.844089945 +0700
+@@ -0,0 +1,13 @@
++#ifndef _IMQ_H
++#define _IMQ_H
++
++/* IFMASK (16 device indexes, 0 to 15) and flag(s) fit in 5 bits */
++#define IMQ_F_BITS	5
++
++#define IMQ_F_IFMASK	0x0f
++#define IMQ_F_ENQUEUE	0x10
++
++#define IMQ_MAX_DEVS	(IMQ_F_IFMASK + 1)
++
++#endif /* _IMQ_H */
++
+diff -Naupr linux-4.14_orig/include/linux/netdevice.h linux-4.14/include/linux/netdevice.h
+--- linux-4.14_orig/include/linux/netdevice.h	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/include/linux/netdevice.h	2017-11-13 11:46:45.844089945 +0700
+@@ -1771,6 +1771,11 @@ struct net_device {
+ /*
+  * Cache lines mostly used on receive path (including eth_type_trans())
+  */
++
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++	unsigned long		last_rx;
++#endif
++
+ 	/* Interface address info used in eth_type_trans() */
+ 	unsigned char		*dev_addr;
+ 
+@@ -3631,6 +3636,19 @@ static inline void netif_tx_unlock_bh(st
+ 	}						\
+ }
+ 
++#define HARD_TX_LOCK_BH(dev, txq) {           \
++    if ((dev->features & NETIF_F_LLTX) == 0) {  \
++        __netif_tx_lock_bh(txq);      \
++    }                       \
++}
++
++#define HARD_TX_UNLOCK_BH(dev, txq) {          \
++    if ((dev->features & NETIF_F_LLTX) == 0) {  \
++        __netif_tx_unlock_bh(txq);         \
++    }                       \
++}
++
++
+ static inline void netif_tx_disable(struct net_device *dev)
+ {
+ 	unsigned int i;
+diff -Naupr linux-4.14_orig/include/linux/netfilter/xt_IMQ.h linux-4.14/include/linux/netfilter/xt_IMQ.h
+--- linux-4.14_orig/include/linux/netfilter/xt_IMQ.h	1970-01-01 07:00:00.000000000 +0700
++++ linux-4.14/include/linux/netfilter/xt_IMQ.h	2017-11-13 11:46:45.847423298 +0700
+@@ -0,0 +1,9 @@
++#ifndef _XT_IMQ_H
++#define _XT_IMQ_H
++
++struct xt_imq_info {
++	unsigned int todev;     /* target imq device */
++};
++
++#endif /* _XT_IMQ_H */
++
+diff -Naupr linux-4.14_orig/include/linux/netfilter_ipv4/ipt_IMQ.h linux-4.14/include/linux/netfilter_ipv4/ipt_IMQ.h
+--- linux-4.14_orig/include/linux/netfilter_ipv4/ipt_IMQ.h	1970-01-01 07:00:00.000000000 +0700
++++ linux-4.14/include/linux/netfilter_ipv4/ipt_IMQ.h	2017-11-13 11:46:45.847423298 +0700
+@@ -0,0 +1,10 @@
++#ifndef _IPT_IMQ_H
++#define _IPT_IMQ_H
++
++/* Backwards compatibility for old userspace */
++#include <linux/netfilter/xt_IMQ.h>
++
++#define ipt_imq_info xt_imq_info
++
++#endif /* _IPT_IMQ_H */
++
+diff -Naupr linux-4.14_orig/include/linux/netfilter_ipv6/ip6t_IMQ.h linux-4.14/include/linux/netfilter_ipv6/ip6t_IMQ.h
+--- linux-4.14_orig/include/linux/netfilter_ipv6/ip6t_IMQ.h	1970-01-01 07:00:00.000000000 +0700
++++ linux-4.14/include/linux/netfilter_ipv6/ip6t_IMQ.h	2017-11-13 11:46:45.847423298 +0700
+@@ -0,0 +1,10 @@
++#ifndef _IP6T_IMQ_H
++#define _IP6T_IMQ_H
++
++/* Backwards compatibility for old userspace */
++#include <linux/netfilter/xt_IMQ.h>
++
++#define ip6t_imq_info xt_imq_info
++
++#endif /* _IP6T_IMQ_H */
++
+diff -Naupr linux-4.14_orig/include/linux/skbuff.h linux-4.14/include/linux/skbuff.h
+--- linux-4.14_orig/include/linux/skbuff.h	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/include/linux/skbuff.h	2017-11-13 11:46:45.847423298 +0700
+@@ -41,6 +41,10 @@
+ #include <linux/in6.h>
+ #include <linux/if_packet.h>
+ #include <net/flow.h>
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++#include <linux/imq.h>
++#endif
++
+ 
+ /* The interface for checksum offload between the stack and networking drivers
+  * is as follows...
+@@ -581,7 +585,7 @@ typedef unsigned int sk_buff_data_t;
+ typedef unsigned char *sk_buff_data_t;
+ #endif
+ 
+-/** 
++/**
+  *	struct sk_buff - socket buffer
+  *	@next: Next buffer in list
+  *	@prev: Previous buffer in list
+@@ -684,6 +688,9 @@ struct sk_buff {
+ 	 * first. This is owned by whoever has the skb queued ATM.
+ 	 */
+ 	char			cb[48] __aligned(8);
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++	void			*cb_next;
++#endif
+ 
+ 	unsigned long		_skb_refdst;
+ 	void			(*destructor)(struct sk_buff *skb);
+@@ -693,6 +700,9 @@ struct sk_buff {
+ #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+ 	unsigned long		 _nfct;
+ #endif
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++       struct nf_queue_entry   *nf_queue_entry;
++#endif
+ #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+ 	struct nf_bridge_info	*nf_bridge;
+ #endif
+@@ -772,6 +782,9 @@ struct sk_buff {
+ #ifdef CONFIG_NET_SWITCHDEV
+ 	__u8			offload_fwd_mark:1;
+ #endif
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++	__u8			imq_flags:IMQ_F_BITS;
++#endif
+ #ifdef CONFIG_NET_CLS_ACT
+ 	__u8			tc_skip_classify:1;
+ 	__u8			tc_at_ingress:1;
+@@ -870,7 +883,7 @@ static inline bool skb_pfmemalloc(const
+  */
+ static inline struct dst_entry *skb_dst(const struct sk_buff *skb)
+ {
+-	/* If refdst was not refcounted, check we still are in a 
++	/* If refdst was not refcounted, check we still are in a
+ 	 * rcu_read_lock section
+ 	 */
+ 	WARN_ON((skb->_skb_refdst & SKB_DST_NOREF) &&
+@@ -960,6 +973,12 @@ void skb_tx_error(struct sk_buff *skb);
+ void consume_skb(struct sk_buff *skb);
+ void __consume_stateless_skb(struct sk_buff *skb);
+ void  __kfree_skb(struct sk_buff *skb);
++
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++int skb_save_cb(struct sk_buff *skb);
++int skb_restore_cb(struct sk_buff *skb);
++#endif
++
+ extern struct kmem_cache *skbuff_head_cache;
+ 
+ void kfree_skb_partial(struct sk_buff *skb, bool head_stolen);
+@@ -3785,8 +3804,12 @@ static inline void __nf_copy(struct sk_b
+ 	dst->_nfct = src->_nfct;
+ 	nf_conntrack_get(skb_nfct(src));
+ #endif
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++	dst->imq_flags = src->imq_flags;
++  dst->nf_queue_entry = src->nf_queue_entry;
++#endif
+ #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+-	dst->nf_bridge  = src->nf_bridge;
++	dst->nf_bridge = src->nf_bridge;
+ 	nf_bridge_get(src->nf_bridge);
+ #endif
+ #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || defined(CONFIG_NF_TABLES)
+diff -Naupr linux-4.14_orig/include/net/netfilter/nf_queue.h linux-4.14/include/net/netfilter/nf_queue.h
+--- linux-4.14_orig/include/net/netfilter/nf_queue.h	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/include/net/netfilter/nf_queue.h	2017-11-13 11:46:45.847423298 +0700
+@@ -31,6 +31,12 @@ struct nf_queue_handler {
+ void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh);
+ void nf_unregister_queue_handler(struct net *net);
+ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict);
++void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
++
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++void nf_register_queue_imq_handler(const struct nf_queue_handler *qh);
++void nf_unregister_queue_imq_handler(void);
++#endif
+ 
+ void nf_queue_entry_get_refs(struct nf_queue_entry *entry);
+ void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
+diff -Naupr linux-4.14_orig/include/net/pkt_sched.h linux-4.14/include/net/pkt_sched.h
+--- linux-4.14_orig/include/net/pkt_sched.h	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/include/net/pkt_sched.h	2017-11-13 11:46:45.850756651 +0700
+@@ -109,6 +109,8 @@ int sch_direct_xmit(struct sk_buff *skb,
+ 
+ void __qdisc_run(struct Qdisc *q);
+ 
++struct sk_buff *qdisc_dequeue_skb(struct Qdisc *q, bool *validate);
++
+ static inline void qdisc_run(struct Qdisc *q)
+ {
+ 	if (qdisc_run_begin(q))
+diff -Naupr linux-4.14_orig/include/net/sch_generic.h linux-4.14/include/net/sch_generic.h
+--- linux-4.14_orig/include/net/sch_generic.h	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/include/net/sch_generic.h	2017-11-13 11:46:45.850756651 +0700
+@@ -567,6 +567,13 @@ static inline int qdisc_enqueue(struct s
+ 	return sch->enqueue(skb, sch, to_free);
+ }
+ 
++static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch,
++				      struct sk_buff **to_free)
++{
++    qdisc_skb_cb(skb)->pkt_len = skb->len;
++    return qdisc_enqueue(skb, sch, to_free) & NET_XMIT_MASK;
++}
++
+ static inline bool qdisc_is_percpu_stats(const struct Qdisc *q)
+ {
+ 	return q->flags & TCQ_F_CPUSTATS;
+diff -Naupr linux-4.14_orig/include/uapi/linux/netfilter.h linux-4.14/include/uapi/linux/netfilter.h
+--- linux-4.14_orig/include/uapi/linux/netfilter.h	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/include/uapi/linux/netfilter.h	2017-11-13 11:46:45.850756651 +0700
+@@ -14,7 +14,8 @@
+ #define NF_QUEUE 3
+ #define NF_REPEAT 4
+ #define NF_STOP 5	/* Deprecated, for userspace nf_queue compatibility. */
+-#define NF_MAX_VERDICT NF_STOP
++#define NF_IMQ_QUEUE 6
++#define NF_MAX_VERDICT NF_IMQ_QUEUE
+ 
+ /* we overload the higher bits for encoding auxiliary data such as the queue
+  * number or errno values. Not nice, but better than additional function
+diff -Naupr linux-4.14_orig/net/core/dev.c linux-4.14/net/core/dev.c
+--- linux-4.14_orig/net/core/dev.c	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/net/core/dev.c	2017-11-13 11:46:45.854090004 +0700
+@@ -143,6 +143,9 @@
+ #include <linux/hrtimer.h>
+ #include <linux/netfilter_ingress.h>
+ #include <linux/crash_dump.h>
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++#include <linux/imq.h>
++#endif
+ #include <linux/sctp.h>
+ #include <net/udp_tunnel.h>
+ 
+@@ -2971,7 +2974,12 @@ static int xmit_one(struct sk_buff *skb,
+ 	unsigned int len;
+ 	int rc;
+ 
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++	if ((!list_empty(&ptype_all) || !list_empty(&dev->ptype_all)) &&
++		!(skb->imq_flags & IMQ_F_ENQUEUE))
++#else
+ 	if (!list_empty(&ptype_all) || !list_empty(&dev->ptype_all))
++#endif
+ 		dev_queue_xmit_nit(skb, dev);
+ 
+ 	len = skb->len;
+@@ -3010,6 +3018,8 @@ out:
+ 	return skb;
+ }
+ 
++EXPORT_SYMBOL_GPL(dev_hard_start_xmit);
++
+ static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb,
+ 					  netdev_features_t features)
+ {
+diff -Naupr linux-4.14_orig/net/core/skbuff.c linux-4.14/net/core/skbuff.c
+--- linux-4.14_orig/net/core/skbuff.c	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/net/core/skbuff.c	2017-11-13 11:46:45.854090004 +0700
+@@ -82,6 +82,87 @@ struct kmem_cache *skbuff_head_cache __r
+ static struct kmem_cache *skbuff_fclone_cache __read_mostly;
+ int sysctl_max_skb_frags __read_mostly = MAX_SKB_FRAGS;
+ EXPORT_SYMBOL(sysctl_max_skb_frags);
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++static struct kmem_cache *skbuff_cb_store_cache __read_mostly;
++#endif
++
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++/* Control buffer save/restore for IMQ devices */
++struct skb_cb_table {
++	char			cb[48] __aligned(8);
++	void			*cb_next;
++	atomic_t		refcnt;
++};
++
++static DEFINE_SPINLOCK(skb_cb_store_lock);
++
++int skb_save_cb(struct sk_buff *skb)
++{
++	struct skb_cb_table *next;
++
++	next = kmem_cache_alloc(skbuff_cb_store_cache, GFP_ATOMIC);
++	if (!next)
++		return -ENOMEM;
++
++	BUILD_BUG_ON(sizeof(skb->cb) != sizeof(next->cb));
++
++	memcpy(next->cb, skb->cb, sizeof(skb->cb));
++	next->cb_next = skb->cb_next;
++
++	atomic_set(&next->refcnt, 1);
++
++	skb->cb_next = next;
++	return 0;
++}
++EXPORT_SYMBOL(skb_save_cb);
++
++int skb_restore_cb(struct sk_buff *skb)
++{
++	struct skb_cb_table *next;
++
++	if (!skb->cb_next)
++		return 0;
++
++	next = skb->cb_next;
++
++	BUILD_BUG_ON(sizeof(skb->cb) != sizeof(next->cb));
++
++	memcpy(skb->cb, next->cb, sizeof(skb->cb));
++	skb->cb_next = next->cb_next;
++
++	spin_lock(&skb_cb_store_lock);
++
++	if (atomic_dec_and_test(&next->refcnt))
++		kmem_cache_free(skbuff_cb_store_cache, next);
++
++	spin_unlock(&skb_cb_store_lock);
++
++	return 0;
++}
++EXPORT_SYMBOL(skb_restore_cb);
++
++static void skb_copy_stored_cb(struct sk_buff *   , const struct sk_buff *     ) __attribute__ ((unused));
++static void skb_copy_stored_cb(struct sk_buff *new, const struct sk_buff *__old)
++{
++	struct skb_cb_table *next;
++	struct sk_buff *old;
++
++	if (!__old->cb_next) {
++		new->cb_next = NULL;
++		return;
++	}
++
++	spin_lock(&skb_cb_store_lock);
++
++	old = (struct sk_buff *)__old;
++
++	next = old->cb_next;
++	atomic_inc(&next->refcnt);
++	new->cb_next = next;
++
++	spin_unlock(&skb_cb_store_lock);
++}
++#endif
+ 
+ /**
+  *	skb_panic - private function for out-of-line support
+@@ -615,6 +696,28 @@ void skb_release_head_state(struct sk_bu
+ 		WARN_ON(in_irq());
+ 		skb->destructor(skb);
+ 	}
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++	/*
++	 * This should not happen. When it does, avoid memleak by restoring
++	 * the chain of cb-backups.
++	 */
++	while (skb->cb_next != NULL) {
++		if (net_ratelimit())
++			pr_warn("IMQ: kfree_skb: skb->cb_next: %08x\n",
++				(unsigned int)(uintptr_t)skb->cb_next);
++
++		skb_restore_cb(skb);
++	}
++	/*
++	 * This should not happen either, nf_queue_entry is nullified in
++	 * imq_dev_xmit(). If we have non-NULL nf_queue_entry then we are
++	 * leaking entry pointers, maybe memory. We don't know if this is
++	 * pointer to already freed memory, or should this be freed.
++	 * If this happens we need to add refcounting, etc for nf_queue_entry.
++	 */
++	if (skb->nf_queue_entry && net_ratelimit())
++		pr_warn("%s\n", "IMQ: kfree_skb: skb->nf_queue_entry != NULL");
++#endif
+ #if IS_ENABLED(CONFIG_NF_CONNTRACK)
+ 	nf_conntrack_put(skb_nfct(skb));
+ #endif
+@@ -804,6 +907,10 @@ static void __copy_skb_header(struct sk_
+ 	new->sp			= secpath_get(old->sp);
+ #endif
+ 	__nf_copy(new, old, false);
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++	new->cb_next = NULL;
++	/*skb_copy_stored_cb(new, old);*/
++#endif
+ 
+ 	/* Note : this field could be in headers_start/headers_end section
+ 	 * It is not yet because we do not want to have a 16 bit hole
+@@ -3902,6 +4009,13 @@ void __init skb_init(void)
+ 						0,
+ 						SLAB_HWCACHE_ALIGN|SLAB_PANIC,
+ 						NULL);
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++	skbuff_cb_store_cache = kmem_cache_create("skbuff_cb_store_cache",
++						  sizeof(struct skb_cb_table),
++						  0,
++						  SLAB_HWCACHE_ALIGN|SLAB_PANIC,
++						  NULL);
++#endif
+ }
+ 
+ static int
+diff -Naupr linux-4.14_orig/net/netfilter/core.c linux-4.14/net/netfilter/core.c
+--- linux-4.14_orig/net/netfilter/core.c	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/net/netfilter/core.c	2017-11-13 14:16:05.896850774 +0700
+@@ -474,6 +474,11 @@ int nf_hook_slow(struct sk_buff *skb, st
+ 			if (ret == 0)
+ 				ret = -EPERM;
+ 			return ret;
++		case NF_IMQ_QUEUE:
++			ret = nf_queue(skb, state, e, s, verdict);
++			if (ret == -ECANCELED)
++				continue;
++			return ret;
+ 		case NF_QUEUE:
+ 			ret = nf_queue(skb, state, e, s, verdict);
+ 			if (ret == 1)
+diff -Naupr linux-4.14_orig/net/netfilter/Kconfig linux-4.14/net/netfilter/Kconfig
+--- linux-4.14_orig/net/netfilter/Kconfig	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/net/netfilter/Kconfig	2017-11-13 11:46:45.857423358 +0700
+@@ -867,6 +867,18 @@ config NETFILTER_XT_TARGET_LOG
+ 
+ 	  To compile it as a module, choose M here.  If unsure, say N.
+ 
++config NETFILTER_XT_TARGET_IMQ
++        tristate '"IMQ" target support'
++	depends on NETFILTER_XTABLES
++	depends on IP_NF_MANGLE || IP6_NF_MANGLE
++	select IMQ
++	default m if NETFILTER_ADVANCED=n
++        help
++          This option adds a `IMQ' target which is used to specify if and
++          to which imq device packets should get enqueued/dequeued.
++
++          To compile it as a module, choose M here.  If unsure, say N.
++
+ config NETFILTER_XT_TARGET_MARK
+ 	tristate '"MARK" target support'
+ 	depends on NETFILTER_ADVANCED
+diff -Naupr linux-4.14_orig/net/netfilter/Makefile linux-4.14/net/netfilter/Makefile
+--- linux-4.14_orig/net/netfilter/Makefile	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/net/netfilter/Makefile	2017-11-13 11:46:45.857423358 +0700
+@@ -125,6 +125,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) +=
+ obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
++obj-$(CONFIG_NETFILTER_XT_TARGET_IMQ) += xt_IMQ.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_NETMAP) += xt_NETMAP.o
+diff -Naupr linux-4.14_orig/net/netfilter/nf_queue.c linux-4.14/net/netfilter/nf_queue.c
+--- linux-4.14_orig/net/netfilter/nf_queue.c	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/net/netfilter/nf_queue.c	2017-11-13 14:25:21.436864671 +0700
+@@ -1,4 +1,4 @@
+-/*
++	/*
+  * Rusty Russell (C)2000 -- This code is GPL.
+  * Patrick McHardy (c) 2006-2012
+  */
+@@ -27,6 +27,23 @@
+  * receives, no matter what.
+  */
+ 
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++static const struct nf_queue_handler __rcu *queue_imq_handler __read_mostly;
++
++void nf_register_queue_imq_handler(const struct nf_queue_handler *qh)
++{
++	rcu_assign_pointer(queue_imq_handler, qh);
++}
++EXPORT_SYMBOL_GPL(nf_register_queue_imq_handler);
++
++void nf_unregister_queue_imq_handler(void)
++{
++	RCU_INIT_POINTER(queue_imq_handler, NULL);
++	synchronize_rcu();
++}
++EXPORT_SYMBOL_GPL(nf_unregister_queue_imq_handler);
++#endif
++
+ /* return EBUSY when somebody else is registered, return EEXIST if the
+  * same handler is registered, return 0 in case of success. */
+ void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh)
+@@ -113,16 +130,29 @@ EXPORT_SYMBOL_GPL(nf_queue_nf_hook_drop)
+ 
+ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
+ 		      const struct nf_hook_entries *entries,
+-		      unsigned int index, unsigned int queuenum)
++		      unsigned int index, unsigned int verdict)
+ {
+ 	int status = -ENOENT;
+ 	struct nf_queue_entry *entry = NULL;
+ 	const struct nf_afinfo *afinfo;
+ 	const struct nf_queue_handler *qh;
+ 	struct net *net = state->net;
++	unsigned int queuetype = verdict & NF_VERDICT_MASK;
++	unsigned int queuenum  = verdict >> NF_VERDICT_QBITS;
+ 
+ 	/* QUEUE == DROP if no one is waiting, to be safe. */
+-	qh = rcu_dereference(net->nf.queue_handler);
++
++	if (queuetype == NF_IMQ_QUEUE) {
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++	qh = rcu_dereference(queue_imq_handler);
++#else
++	BUG();
++	goto err_unlock;
++#endif
++	} else {
++		qh = rcu_dereference(net->nf.queue_handler);
++	}
++
+ 	if (!qh) {
+ 		status = -ESRCH;
+ 		goto err;
+@@ -169,8 +199,16 @@ int nf_queue(struct sk_buff *skb, struct
+ {
+ 	int ret;
+ 
+-	ret = __nf_queue(skb, state, entries, index, verdict >> NF_VERDICT_QBITS);
++	ret = __nf_queue(skb, state, entries, index, verdict);
+ 	if (ret < 0) {
++
++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
++	/* IMQ Bypass */
++	if (ret == -ECANCELED && skb->imq_flags == 0) {
++		return 1;
++	}
++#endif
++
+ 		if (ret == -ESRCH &&
+ 		    (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
+ 			return 1;
+@@ -256,6 +294,7 @@ next_hook:
+ 		local_bh_enable();
+ 		break;
+ 	case NF_QUEUE:
++	case NF_IMQ_QUEUE:
+ 		err = nf_queue(skb, &entry->state, hooks, i, verdict);
+ 		if (err == 1)
+ 			goto next_hook;
+diff -Naupr linux-4.14_orig/net/netfilter/xt_IMQ.c linux-4.14/net/netfilter/xt_IMQ.c
+--- linux-4.14_orig/net/netfilter/xt_IMQ.c	1970-01-01 07:00:00.000000000 +0700
++++ linux-4.14/net/netfilter/xt_IMQ.c	2017-11-13 11:46:45.857423358 +0700
+@@ -0,0 +1,72 @@
++/*
++ * This target marks packets to be enqueued to an imq device
++ */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter/x_tables.h>
++#include <linux/netfilter/xt_IMQ.h>
++#include <linux/imq.h>
++
++static unsigned int imq_target(struct sk_buff *pskb,
++				const struct xt_action_param *par)
++{
++	const struct xt_imq_info *mr = par->targinfo;
++
++	pskb->imq_flags = (mr->todev & IMQ_F_IFMASK) | IMQ_F_ENQUEUE;
++
++	return XT_CONTINUE;
++}
++
++static int imq_checkentry(const struct xt_tgchk_param *par)
++{
++	struct xt_imq_info *mr = par->targinfo;
++
++	if (mr->todev > IMQ_MAX_DEVS - 1) {
++		pr_warn("IMQ: invalid device specified, highest is %u\n",
++			IMQ_MAX_DEVS - 1);
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static struct xt_target xt_imq_reg[] __read_mostly = {
++	{
++		.name           = "IMQ",
++		.family		= AF_INET,
++		.checkentry     = imq_checkentry,
++		.target         = imq_target,
++		.targetsize	= sizeof(struct xt_imq_info),
++		.table		= "mangle",
++		.me             = THIS_MODULE
++	},
++	{
++		.name           = "IMQ",
++		.family		= AF_INET6,
++		.checkentry     = imq_checkentry,
++		.target         = imq_target,
++		.targetsize	= sizeof(struct xt_imq_info),
++		.table		= "mangle",
++		.me             = THIS_MODULE
++	},
++};
++
++static int __init imq_init(void)
++{
++	return xt_register_targets(xt_imq_reg, ARRAY_SIZE(xt_imq_reg));
++}
++
++static void __exit imq_fini(void)
++{
++	xt_unregister_targets(xt_imq_reg, ARRAY_SIZE(xt_imq_reg));
++}
++
++module_init(imq_init);
++module_exit(imq_fini);
++
++MODULE_AUTHOR("https://github.com/imq/linuximq");
++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See https://github.com/imq/linuximq/wiki for more information.");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("ipt_IMQ");
++MODULE_ALIAS("ip6t_IMQ");
++
+diff -Naupr linux-4.14_orig/net/sched/sch_generic.c linux-4.14/net/sched/sch_generic.c
+--- linux-4.14_orig/net/sched/sch_generic.c	2017-11-13 01:46:13.000000000 +0700
++++ linux-4.14/net/sched/sch_generic.c	2017-11-13 11:46:45.857423358 +0700
+@@ -158,6 +158,14 @@ trace:
+ 	return skb;
+ }
+ 
++struct sk_buff *qdisc_dequeue_skb(struct Qdisc *q, bool *validate)
++{
++	int packets;
++
++	return dequeue_skb(q, validate, &packets);
++}
++EXPORT_SYMBOL(qdisc_dequeue_skb);
++
+ /*
+  * Transmit possibly several skbs, and handle the return status as
+  * required. Owning running seqcount bit guarantees that
Index: npl/kernel/linux_src/linux-4.4.5-imq.diff
===================================================================
--- npl/kernel/linux_src/linux-4.4.5-imq.diff	(revision f6630dda9bbc206e69f0475bfb8672e20d5beaa1)
+++ 	(revision )
@@ -1,1720 +1,0 @@
-diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
-index f184fb5..0e08522 100644
---- a/drivers/net/Kconfig
-+++ b/drivers/net/Kconfig
-@@ -234,6 +234,125 @@ config RIONET_RX_SIZE
- 	depends on RIONET
- 	default "128"
- 
-+config IMQ
-+	tristate "IMQ (intermediate queueing device) support"
-+	depends on NETDEVICES && NETFILTER
-+	---help---
-+	  The IMQ device(s) is used as placeholder for QoS queueing
-+	  disciplines. Every packet entering/leaving the IP stack can be
-+	  directed through the IMQ device where it's enqueued/dequeued to the
-+	  attached qdisc. This allows you to treat network devices as classes
-+	  and distribute bandwidth among them. Iptables is used to specify
-+	  through which IMQ device, if any, packets travel.
-+
-+	  More information at: https://github.com/imq/linuximq
-+
-+	  To compile this driver as a module, choose M here: the module
-+	  will be called imq.  If unsure, say N.
-+
-+choice
-+	prompt "IMQ behavior (PRE/POSTROUTING)"
-+	depends on IMQ
-+	default IMQ_BEHAVIOR_AB
-+	help
-+	  This setting defines how IMQ behaves in respect to its
-+	  hooking in PREROUTING and POSTROUTING.
-+
-+	  IMQ can work in any of the following ways:
-+
-+	      PREROUTING   |      POSTROUTING
-+	  -----------------|-------------------
-+	  #1  After NAT    |      After NAT
-+	  #2  After NAT    |      Before NAT
-+	  #3  Before NAT   |      After NAT
-+	  #4  Before NAT   |      Before NAT
-+
-+	  The default behavior is to hook before NAT on PREROUTING
-+	  and after NAT on POSTROUTING (#3).
-+
-+	  This settings are specially usefull when trying to use IMQ
-+	  to shape NATed clients.
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
-+config IMQ_BEHAVIOR_AA
-+	bool "IMQ AA"
-+	help
-+	  This setting defines how IMQ behaves in respect to its
-+	  hooking in PREROUTING and POSTROUTING.
-+
-+	  Choosing this option will make IMQ hook like this:
-+
-+	  PREROUTING:   After NAT
-+	  POSTROUTING:  After NAT
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
-+config IMQ_BEHAVIOR_AB
-+	bool "IMQ AB"
-+	help
-+	  This setting defines how IMQ behaves in respect to its
-+	  hooking in PREROUTING and POSTROUTING.
-+
-+	  Choosing this option will make IMQ hook like this:
-+
-+	  PREROUTING:   After NAT
-+	  POSTROUTING:  Before NAT
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
-+config IMQ_BEHAVIOR_BA
-+	bool "IMQ BA"
-+	help
-+	  This setting defines how IMQ behaves in respect to its
-+	  hooking in PREROUTING and POSTROUTING.
-+
-+	  Choosing this option will make IMQ hook like this:
-+
-+	  PREROUTING:   Before NAT
-+	  POSTROUTING:  After NAT
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
-+config IMQ_BEHAVIOR_BB
-+	bool "IMQ BB"
-+	help
-+	  This setting defines how IMQ behaves in respect to its
-+	  hooking in PREROUTING and POSTROUTING.
-+
-+	  Choosing this option will make IMQ hook like this:
-+
-+	  PREROUTING:   Before NAT
-+	  POSTROUTING:  Before NAT
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
-+endchoice
-+
-+config IMQ_NUM_DEVS
-+	int "Number of IMQ devices"
-+	range 2 16
-+	depends on IMQ
-+	default "16"
-+	help
-+	  This setting defines how many IMQ devices will be created.
-+
-+	  The default value is 16.
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
- config TUN
- 	tristate "Universal TUN/TAP device driver support"
- 	depends on INET
-diff --git a/drivers/net/Makefile b/drivers/net/Makefile
-index 900b0c5..e093402 100644
---- a/drivers/net/Makefile
-+++ b/drivers/net/Makefile
-@@ -10,6 +10,7 @@ obj-$(CONFIG_IPVLAN) += ipvlan/
- obj-$(CONFIG_DUMMY) += dummy.o
- obj-$(CONFIG_EQUALIZER) += eql.o
- obj-$(CONFIG_IFB) += ifb.o
-+obj-$(CONFIG_IMQ) += imq.o
- obj-$(CONFIG_MACVLAN) += macvlan.o
- obj-$(CONFIG_MACVTAP) += macvtap.o
- obj-$(CONFIG_MII) += mii.o
-diff --git a/drivers/net/imq.c b/drivers/net/imq.c
-new file mode 100644
-index 0000000..f80258f
---- /dev/null
-+++ b/drivers/net/imq.c
-@@ -0,0 +1,903 @@
-+/*
-+ *             Pseudo-driver for the intermediate queue device.
-+ *
-+ *             This program is free software; you can redistribute it and/or
-+ *             modify it under the terms of the GNU General Public License
-+ *             as published by the Free Software Foundation; either version
-+ *             2 of the License, or (at your option) any later version.
-+ *
-+ * Authors:    Patrick McHardy, <kaber@trash.net>
-+ *
-+ *            The first version was written by Martin Devera, <devik@cdi.cz>
-+ *
-+ *			   See Creditis.txt
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/moduleparam.h>
-+#include <linux/list.h>
-+#include <linux/skbuff.h>
-+#include <linux/netdevice.h>
-+#include <linux/etherdevice.h>
-+#include <linux/rtnetlink.h>
-+#include <linux/if_arp.h>
-+#include <linux/netfilter.h>
-+#include <linux/netfilter_ipv4.h>
-+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-+	#include <linux/netfilter_ipv6.h>
-+#endif
-+#include <linux/imq.h>
-+#include <net/pkt_sched.h>
-+#include <net/netfilter/nf_queue.h>
-+#include <net/sock.h>
-+#include <linux/ip.h>
-+#include <linux/ipv6.h>
-+#include <linux/if_vlan.h>
-+#include <linux/if_pppox.h>
-+#include <net/ip.h>
-+#include <net/ipv6.h>
-+
-+static int imq_nf_queue(struct nf_queue_entry *entry, unsigned queue_num);
-+
-+static nf_hookfn imq_nf_hook;
-+
-+static struct nf_hook_ops imq_ops[] = {
-+	{
-+	/* imq_ingress_ipv4 */
-+		.hook		= imq_nf_hook,
-+		.pf		= PF_INET,
-+		.hooknum	= NF_INET_PRE_ROUTING,
-+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
-+		.priority	= NF_IP_PRI_MANGLE + 1,
-+#else
-+		.priority	= NF_IP_PRI_NAT_DST + 1,
-+#endif
-+	},
-+	{
-+	/* imq_egress_ipv4 */
-+		.hook		= imq_nf_hook,
-+		.pf		= PF_INET,
-+		.hooknum	= NF_INET_POST_ROUTING,
-+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA)
-+		.priority	= NF_IP_PRI_LAST,
-+#else
-+		.priority	= NF_IP_PRI_NAT_SRC - 1,
-+#endif
-+	},
-+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-+	{
-+	/* imq_ingress_ipv6 */
-+		.hook		= imq_nf_hook,
-+		.pf		= PF_INET6,
-+		.hooknum	= NF_INET_PRE_ROUTING,
-+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
-+		.priority	= NF_IP6_PRI_MANGLE + 1,
-+#else
-+		.priority	= NF_IP6_PRI_NAT_DST + 1,
-+#endif
-+	},
-+	{
-+	/* imq_egress_ipv6 */
-+		.hook		= imq_nf_hook,
-+		.pf		= PF_INET6,
-+		.hooknum	= NF_INET_POST_ROUTING,
-+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA)
-+		.priority	= NF_IP6_PRI_LAST,
-+#else
-+		.priority	= NF_IP6_PRI_NAT_SRC - 1,
-+#endif
-+	},
-+#endif
-+};
-+
-+#if defined(CONFIG_IMQ_NUM_DEVS)
-+static int numdevs = CONFIG_IMQ_NUM_DEVS;
-+#else
-+static int numdevs = IMQ_MAX_DEVS;
-+#endif
-+
-+static struct net_device *imq_devs_cache[IMQ_MAX_DEVS];
-+
-+#define IMQ_MAX_QUEUES 32
-+static int numqueues = 1;
-+static u32 imq_hashrnd;
-+static int imq_dev_accurate_stats = 1;
-+
-+static inline __be16 pppoe_proto(const struct sk_buff *skb)
-+{
-+	return *((__be16 *)(skb_mac_header(skb) + ETH_HLEN +
-+			sizeof(struct pppoe_hdr)));
-+}
-+
-+static u16 imq_hash(struct net_device *dev, struct sk_buff *skb)
-+{
-+	unsigned int pull_len;
-+	u16 protocol = skb->protocol;
-+	u32 addr1, addr2;
-+	u32 hash, ihl = 0;
-+	union {
-+		u16 in16[2];
-+		u32 in32;
-+	} ports;
-+	u8 ip_proto;
-+
-+	pull_len = 0;
-+
-+recheck:
-+	switch (protocol) {
-+	case htons(ETH_P_8021Q): {
-+		if (unlikely(skb_pull(skb, VLAN_HLEN) == NULL))
-+			goto other;
-+
-+		pull_len += VLAN_HLEN;
-+		skb->network_header += VLAN_HLEN;
-+
-+		protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
-+		goto recheck;
-+	}
-+
-+	case htons(ETH_P_PPP_SES): {
-+		if (unlikely(skb_pull(skb, PPPOE_SES_HLEN) == NULL))
-+			goto other;
-+
-+		pull_len += PPPOE_SES_HLEN;
-+		skb->network_header += PPPOE_SES_HLEN;
-+
-+		protocol = pppoe_proto(skb);
-+		goto recheck;
-+	}
-+
-+	case htons(ETH_P_IP): {
-+		const struct iphdr *iph = ip_hdr(skb);
-+
-+		if (unlikely(!pskb_may_pull(skb, sizeof(struct iphdr))))
-+			goto other;
-+
-+		addr1 = iph->daddr;
-+		addr2 = iph->saddr;
-+
-+		ip_proto = !(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) ?
-+				 iph->protocol : 0;
-+		ihl = ip_hdrlen(skb);
-+
-+		break;
-+	}
-+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-+	case htons(ETH_P_IPV6): {
-+		const struct ipv6hdr *iph = ipv6_hdr(skb);
-+		__be16 fo = 0;
-+
-+		if (unlikely(!pskb_may_pull(skb, sizeof(struct ipv6hdr))))
-+			goto other;
-+
-+		addr1 = iph->daddr.s6_addr32[3];
-+		addr2 = iph->saddr.s6_addr32[3];
-+		ihl = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &ip_proto,
-+				       &fo);
-+		if (unlikely(ihl < 0))
-+			goto other;
-+
-+		break;
-+	}
-+#endif
-+	default:
-+other:
-+		if (pull_len != 0) {
-+			skb_push(skb, pull_len);
-+			skb->network_header -= pull_len;
-+		}
-+
-+		return (u16)(ntohs(protocol) % dev->real_num_tx_queues);
-+	}
-+
-+	if (addr1 > addr2)
-+		swap(addr1, addr2);
-+
-+	switch (ip_proto) {
-+	case IPPROTO_TCP:
-+	case IPPROTO_UDP:
-+	case IPPROTO_DCCP:
-+	case IPPROTO_ESP:
-+	case IPPROTO_AH:
-+	case IPPROTO_SCTP:
-+	case IPPROTO_UDPLITE: {
-+		if (likely(skb_copy_bits(skb, ihl, &ports.in32, 4) >= 0)) {
-+			if (ports.in16[0] > ports.in16[1])
-+				swap(ports.in16[0], ports.in16[1]);
-+			break;
-+		}
-+		/* fall-through */
-+	}
-+	default:
-+		ports.in32 = 0;
-+		break;
-+	}
-+
-+	if (pull_len != 0) {
-+		skb_push(skb, pull_len);
-+		skb->network_header -= pull_len;
-+	}
-+
-+	hash = jhash_3words(addr1, addr2, ports.in32, imq_hashrnd ^ ip_proto);
-+
-+	return (u16)(((u64)hash * dev->real_num_tx_queues) >> 32);
-+}
-+
-+static inline bool sk_tx_queue_recorded(struct sock *sk)
-+{
-+	return (sk_tx_queue_get(sk) >= 0);
-+}
-+
-+static struct netdev_queue *imq_select_queue(struct net_device *dev,
-+						struct sk_buff *skb)
-+{
-+	u16 queue_index = 0;
-+	u32 hash;
-+
-+	if (likely(dev->real_num_tx_queues == 1))
-+		goto out;
-+
-+	/* IMQ can be receiving ingress or engress packets. */
-+
-+	/* Check first for if rx_queue is set */
-+	if (skb_rx_queue_recorded(skb)) {
-+		queue_index = skb_get_rx_queue(skb);
-+		goto out;
-+	}
-+
-+	/* Check if socket has tx_queue set */
-+	if (sk_tx_queue_recorded(skb->sk)) {
-+		queue_index = sk_tx_queue_get(skb->sk);
-+		goto out;
-+	}
-+
-+	/* Try use socket hash */
-+	if (skb->sk && skb->sk->sk_hash) {
-+		hash = skb->sk->sk_hash;
-+		queue_index =
-+			(u16)(((u64)hash * dev->real_num_tx_queues) >> 32);
-+		goto out;
-+	}
-+
-+	/* Generate hash from packet data */
-+	queue_index = imq_hash(dev, skb);
-+
-+out:
-+	if (unlikely(queue_index >= dev->real_num_tx_queues))
-+		queue_index = (u16)((u32)queue_index % dev->real_num_tx_queues);
-+
-+	skb_set_queue_mapping(skb, queue_index);
-+	return netdev_get_tx_queue(dev, queue_index);
-+}
-+
-+static struct net_device_stats *imq_get_stats(struct net_device *dev)
-+{
-+	return &dev->stats;
-+}
-+
-+/* called for packets kfree'd in qdiscs at places other than enqueue */
-+static void imq_skb_destructor(struct sk_buff *skb)
-+{
-+	struct nf_queue_entry *entry = skb->nf_queue_entry;
-+
-+	skb->nf_queue_entry = NULL;
-+
-+	if (entry) {
-+		nf_queue_entry_release_refs(entry);
-+		kfree(entry);
-+	}
-+
-+	skb_restore_cb(skb); /* kfree backup */
-+}
-+
-+static void imq_done_check_queue_mapping(struct sk_buff *skb,
-+					 struct net_device *dev)
-+{
-+	unsigned int queue_index;
-+
-+	/* Don't let queue_mapping be left too large after exiting IMQ */
-+	if (likely(skb->dev != dev && skb->dev != NULL)) {
-+		queue_index = skb_get_queue_mapping(skb);
-+		if (unlikely(queue_index >= skb->dev->real_num_tx_queues)) {
-+			queue_index = (u16)((u32)queue_index %
-+						skb->dev->real_num_tx_queues);
-+			skb_set_queue_mapping(skb, queue_index);
-+		}
-+	} else {
-+		/* skb->dev was IMQ device itself or NULL, be on safe side and
-+		 * just clear queue mapping.
-+		 */
-+		skb_set_queue_mapping(skb, 0);
-+	}
-+}
-+
-+static netdev_tx_t imq_dev_xmit(struct sk_buff *skb, struct net_device *dev)
-+{
-+	struct nf_queue_entry *entry = skb->nf_queue_entry;
-+
-+	skb->nf_queue_entry = NULL;
-+	dev->trans_start = jiffies;
-+
-+	dev->stats.tx_bytes += skb->len;
-+	dev->stats.tx_packets++;
-+
-+	if (unlikely(entry == NULL)) {
-+		/* We don't know what is going on here.. packet is queued for
-+		 * imq device, but (probably) not by us.
-+		 *
-+		 * If this packet was not send here by imq_nf_queue(), then
-+		 * skb_save_cb() was not used and skb_free() should not show:
-+		 *   WARNING: IMQ: kfree_skb: skb->cb_next:..
-+		 * and/or
-+		 *   WARNING: IMQ: kfree_skb: skb->nf_queue_entry...
-+		 *
-+		 * However if this message is shown, then IMQ is somehow broken
-+		 * and you should report this to linuximq.net.
-+		 */
-+
-+		/* imq_dev_xmit is black hole that eats all packets, report that
-+		 * we eat this packet happily and increase dropped counters.
-+		 */
-+
-+		dev->stats.tx_dropped++;
-+		dev_kfree_skb(skb);
-+
-+		return NETDEV_TX_OK;
-+	}
-+
-+	skb_restore_cb(skb); /* restore skb->cb */
-+
-+	skb->imq_flags = 0;
-+	skb->destructor = NULL;
-+
-+	imq_done_check_queue_mapping(skb, dev);
-+
-+	nf_reinject(entry, NF_ACCEPT);
-+
-+	return NETDEV_TX_OK;
-+}
-+
-+static struct net_device *get_imq_device_by_index(int index)
-+{
-+	struct net_device *dev = NULL;
-+	struct net *net;
-+	char buf[8];
-+
-+	/* get device by name and cache result */
-+	snprintf(buf, sizeof(buf), "imq%d", index);
-+
-+	/* Search device from all namespaces. */
-+	for_each_net(net) {
-+		dev = dev_get_by_name(net, buf);
-+		if (dev)
-+			break;
-+	}
-+
-+	if (WARN_ON_ONCE(dev == NULL)) {
-+		/* IMQ device not found. Exotic config? */
-+		return ERR_PTR(-ENODEV);
-+	}
-+
-+	imq_devs_cache[index] = dev;
-+	dev_put(dev);
-+
-+	return dev;
-+}
-+
-+static struct nf_queue_entry *nf_queue_entry_dup(struct nf_queue_entry *e)
-+{
-+	struct nf_queue_entry *entry = kmemdup(e, e->size, GFP_ATOMIC);
-+	if (entry) {
-+		nf_queue_entry_get_refs(entry);
-+			return entry;
-+	}
-+	return NULL;
-+}
-+
-+#ifdef CONFIG_BRIDGE_NETFILTER
-+/* When called from bridge netfilter, skb->data must point to MAC header
-+ * before calling skb_gso_segment(). Else, original MAC header is lost
-+ * and segmented skbs will be sent to wrong destination.
-+ */
-+static void nf_bridge_adjust_skb_data(struct sk_buff *skb)
-+{
-+	if (skb->nf_bridge)
-+		__skb_push(skb, skb->network_header - skb->mac_header);
-+}
-+
-+static void nf_bridge_adjust_segmented_data(struct sk_buff *skb)
-+{
-+	if (skb->nf_bridge)
-+		__skb_pull(skb, skb->network_header - skb->mac_header);
-+}
-+#else
-+#define nf_bridge_adjust_skb_data(s) do {} while (0)
-+#define nf_bridge_adjust_segmented_data(s) do {} while (0)
-+#endif
-+
-+static void free_entry(struct nf_queue_entry *entry)
-+{
-+	nf_queue_entry_release_refs(entry);
-+	kfree(entry);
-+}
-+
-+static int __imq_nf_queue(struct nf_queue_entry *entry, struct net_device *dev);
-+
-+static int __imq_nf_queue_gso(struct nf_queue_entry *entry,
-+			      struct net_device *dev, struct sk_buff *skb)
-+{
-+	int ret = -ENOMEM;
-+	struct nf_queue_entry *entry_seg;
-+
-+	nf_bridge_adjust_segmented_data(skb);
-+
-+	if (skb->next == NULL) { /* last packet, no need to copy entry */
-+		struct sk_buff *gso_skb = entry->skb;
-+		entry->skb = skb;
-+		ret = __imq_nf_queue(entry, dev);
-+		if (ret)
-+			entry->skb = gso_skb;
-+		return ret;
-+	}
-+
-+	skb->next = NULL;
-+
-+	entry_seg = nf_queue_entry_dup(entry);
-+	if (entry_seg) {
-+		entry_seg->skb = skb;
-+		ret = __imq_nf_queue(entry_seg, dev);
-+		if (ret)
-+			free_entry(entry_seg);
-+	}
-+	return ret;
-+}
-+
-+static int imq_nf_queue(struct nf_queue_entry *entry, unsigned queue_num)
-+{
-+	struct sk_buff *skb, *segs;
-+	struct net_device *dev;
-+	unsigned int queued;
-+	int index, retval, err;
-+
-+	index = entry->skb->imq_flags & IMQ_F_IFMASK;
-+	if (unlikely(index > numdevs - 1)) {
-+		if (net_ratelimit())
-+			pr_warn("IMQ: invalid device specified, highest is %u\n",
-+				numdevs - 1);
-+		retval = -EINVAL;
-+		goto out_no_dev;
-+	}
-+
-+	/* check for imq device by index from cache */
-+	dev = imq_devs_cache[index];
-+	if (unlikely(!dev)) {
-+		dev = get_imq_device_by_index(index);
-+		if (IS_ERR(dev)) {
-+			retval = PTR_ERR(dev);
-+			goto out_no_dev;
-+		}
-+	}
-+
-+	if (unlikely(!(dev->flags & IFF_UP))) {
-+		entry->skb->imq_flags = 0;
-+		retval = -ECANCELED;
-+		goto out_no_dev;
-+	}
-+
-+	/* Since 3.10.x, GSO handling moved here as result of upstream commit
-+	 * a5fedd43d5f6c94c71053a66e4c3d2e35f1731a2 (netfilter: move
-+	 * skb_gso_segment into nfnetlink_queue module).
-+	 *
-+	 * Following code replicates the gso handling from
-+	 * 'net/netfilter/nfnetlink_queue_core.c':nfqnl_enqueue_packet().
-+	 */
-+
-+	skb = entry->skb;
-+
-+	switch (entry->state.pf) {
-+	case NFPROTO_IPV4:
-+		skb->protocol = htons(ETH_P_IP);
-+		break;
-+	case NFPROTO_IPV6:
-+		skb->protocol = htons(ETH_P_IPV6);
-+		break;
-+	}
-+
-+	if (!skb_is_gso(entry->skb))
-+		return __imq_nf_queue(entry, dev);
-+
-+	nf_bridge_adjust_skb_data(skb);
-+	segs = skb_gso_segment(skb, 0);
-+	/* Does not use PTR_ERR to limit the number of error codes that can be
-+	 * returned by nf_queue.  For instance, callers rely on -ECANCELED to
-+	 * mean 'ignore this hook'.
-+	 */
-+	err = -ENOBUFS;
-+	if (IS_ERR(segs))
-+		goto out_err;
-+	queued = 0;
-+	err = 0;
-+	do {
-+		struct sk_buff *nskb = segs->next;
-+		if (nskb && nskb->next)
-+			nskb->cb_next = NULL;
-+		if (err == 0)
-+			err = __imq_nf_queue_gso(entry, dev, segs);
-+		if (err == 0)
-+			queued++;
-+		else
-+			kfree_skb(segs);
-+		segs = nskb;
-+	} while (segs);
-+
-+	if (queued) {
-+		if (err) /* some segments are already queued */
-+			free_entry(entry);
-+		kfree_skb(skb);
-+		return 0;
-+	}
-+
-+out_err:
-+	nf_bridge_adjust_segmented_data(skb);
-+	retval = err;
-+out_no_dev:
-+	return retval;
-+}
-+
-+static int __imq_nf_queue(struct nf_queue_entry *entry, struct net_device *dev)
-+{
-+	struct sk_buff *skb_orig, *skb, *skb_shared, *skb_popd;
-+	struct Qdisc *q;
-+	struct netdev_queue *txq;
-+	spinlock_t *root_lock;
-+	int users;
-+	int retval = -EINVAL;
-+	unsigned int orig_queue_index;
-+
-+	dev->last_rx = jiffies;
-+
-+	skb = entry->skb;
-+	skb_orig = NULL;
-+
-+	/* skb has owner? => make clone */
-+	if (unlikely(skb->destructor)) {
-+		skb_orig = skb;
-+		skb = skb_clone(skb, GFP_ATOMIC);
-+		if (unlikely(!skb)) {
-+			retval = -ENOMEM;
-+			goto out;
-+		}
-+		skb->cb_next = NULL;
-+		entry->skb = skb;
-+	}
-+
-+	dev->stats.rx_bytes += skb->len;
-+	dev->stats.rx_packets++;
-+
-+	if (!skb->dev) {
-+		/* skb->dev == NULL causes problems, try the find cause. */
-+		if (net_ratelimit()) {
-+			dev_warn(&dev->dev,
-+				 "received packet with skb->dev == NULL\n");
-+			dump_stack();
-+		}
-+
-+		skb->dev = dev;
-+	}
-+
-+	/* Disables softirqs for lock below */
-+	rcu_read_lock_bh();
-+
-+	/* Multi-queue selection */
-+	orig_queue_index = skb_get_queue_mapping(skb);
-+	txq = imq_select_queue(dev, skb);
-+
-+	q = rcu_dereference(txq->qdisc);
-+	if (unlikely(!q->enqueue))
-+		goto packet_not_eaten_by_imq_dev;
-+
-+	skb->nf_queue_entry = entry;
-+	root_lock = qdisc_lock(q);
-+	spin_lock(root_lock);
-+
-+	users = atomic_read(&skb->users);
-+
-+	skb_shared = skb_get(skb); /* increase reference count by one */
-+
-+	/* backup skb->cb, as qdisc layer will overwrite it */
-+	skb_save_cb(skb_shared);
-+	qdisc_enqueue_root(skb_shared, q); /* might kfree_skb */
-+	if (likely(atomic_read(&skb_shared->users) == users + 1)) {
-+		bool validate;
-+
-+		kfree_skb(skb_shared); /* decrease reference count by one */
-+
-+		skb->destructor = &imq_skb_destructor;
-+
-+		skb_popd = qdisc_dequeue_skb(q, &validate);
-+
-+		/* cloned? */
-+		if (unlikely(skb_orig))
-+			kfree_skb(skb_orig); /* free original */
-+
-+		spin_unlock(root_lock);
-+
-+#if 0
-+		/* schedule qdisc dequeue */
-+		__netif_schedule(q);
-+#else
-+		if (likely(skb_popd)) {
-+			/* Note that we validate skb (GSO, checksum, ...) outside of locks */
-+			if (validate)
-+        		skb_popd = validate_xmit_skb_list(skb_popd, dev);
-+			
-+			if (skb_popd) {
-+				int dummy_ret;
-+				int cpu = smp_processor_id(); /* ok because BHs are off */
-+
-+				txq = skb_get_tx_queue(dev, skb_popd);
-+				/* 
-+				IMQ device will not be frozen or stoped, and it always be successful.
-+				So we need not check its status and return value to accelerate.
-+				*/
-+				if (imq_dev_accurate_stats && txq->xmit_lock_owner != cpu) {
-+					HARD_TX_LOCK(dev, txq, cpu);
-+					if (!netif_xmit_frozen_or_stopped(txq)) {
-+						dev_hard_start_xmit(skb_popd, dev, txq, &dummy_ret);
-+					}
-+					HARD_TX_UNLOCK(dev, txq);
-+				} else {
-+					if (!netif_xmit_frozen_or_stopped(txq)) {
-+						dev_hard_start_xmit(skb_popd, dev, txq, &dummy_ret);
-+					}
-+				}
-+			}
-+		} else {
-+			/* No ready skb, then schedule it */
-+			__netif_schedule(q);
-+		}
-+#endif
-+		rcu_read_unlock_bh();
-+		retval = 0;
-+		goto out;
-+	} else {
-+		skb_restore_cb(skb_shared); /* restore skb->cb */
-+		skb->nf_queue_entry = NULL;
-+		/*
-+		 * qdisc dropped packet and decreased skb reference count of
-+		 * skb, so we don't really want to and try refree as that would
-+		 * actually destroy the skb.
-+		 */
-+		spin_unlock(root_lock);
-+		goto packet_not_eaten_by_imq_dev;
-+	}
-+
-+packet_not_eaten_by_imq_dev:
-+	skb_set_queue_mapping(skb, orig_queue_index);
-+	rcu_read_unlock_bh();
-+
-+	/* cloned? restore original */
-+	if (unlikely(skb_orig)) {
-+		kfree_skb(skb);
-+		entry->skb = skb_orig;
-+	}
-+	retval = -1;
-+out:
-+	return retval;
-+}
-+static unsigned int imq_nf_hook(void *priv,
-+				struct sk_buff *skb,
-+				const struct nf_hook_state *state)
-+{
-+	return (skb->imq_flags & IMQ_F_ENQUEUE) ? NF_IMQ_QUEUE : NF_ACCEPT;
-+}
-+
-+static int imq_close(struct net_device *dev)
-+{
-+	netif_stop_queue(dev);
-+	return 0;
-+}
-+
-+static int imq_open(struct net_device *dev)
-+{
-+	netif_start_queue(dev);
-+	return 0;
-+}
-+
-+static const struct net_device_ops imq_netdev_ops = {
-+	.ndo_open		= imq_open,
-+	.ndo_stop		= imq_close,
-+	.ndo_start_xmit		= imq_dev_xmit,
-+	.ndo_get_stats		= imq_get_stats,
-+};
-+
-+static void imq_setup(struct net_device *dev)
-+{
-+	dev->netdev_ops		= &imq_netdev_ops;
-+	dev->type		= ARPHRD_VOID;
-+	dev->mtu		= 16000; /* too small? */
-+	dev->tx_queue_len	= 11000; /* too big? */
-+	dev->flags		= IFF_NOARP;
-+	dev->features		= NETIF_F_SG | NETIF_F_FRAGLIST |
-+				  NETIF_F_GSO | NETIF_F_HW_CSUM |
-+				  NETIF_F_HIGHDMA;
-+	dev->priv_flags		&= ~(IFF_XMIT_DST_RELEASE |
-+				     IFF_TX_SKB_SHARING);
-+}
-+
-+static int imq_validate(struct nlattr *tb[], struct nlattr *data[])
-+{
-+	int ret = 0;
-+
-+	if (tb[IFLA_ADDRESS]) {
-+		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) {
-+			ret = -EINVAL;
-+			goto end;
-+		}
-+		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) {
-+			ret = -EADDRNOTAVAIL;
-+			goto end;
-+		}
-+	}
-+	return 0;
-+end:
-+	pr_warn("IMQ: imq_validate failed (%d)\n", ret);
-+	return ret;
-+}
-+
-+static struct rtnl_link_ops imq_link_ops __read_mostly = {
-+	.kind		= "imq",
-+	.priv_size	= 0,
-+	.setup		= imq_setup,
-+	.validate	= imq_validate,
-+};
-+
-+static const struct nf_queue_handler imq_nfqh = {
-+	.outfn = imq_nf_queue,
-+};
-+
-+static int __init imq_init_hooks(void)
-+{
-+	int ret;
-+
-+	nf_register_queue_imq_handler(&imq_nfqh);
-+
-+	ret = nf_register_hooks(imq_ops, ARRAY_SIZE(imq_ops));
-+	if (ret < 0)
-+		nf_unregister_queue_imq_handler();
-+
-+	return ret;
-+}
-+
-+static int __init imq_init_one(int index)
-+{
-+	struct net_device *dev;
-+	int ret;
-+
-+	dev = alloc_netdev_mq(0, "imq%d", NET_NAME_UNKNOWN, imq_setup, numqueues);
-+	if (!dev)
-+		return -ENOMEM;
-+
-+	ret = dev_alloc_name(dev, dev->name);
-+	if (ret < 0)
-+		goto fail;
-+
-+	dev->rtnl_link_ops = &imq_link_ops;
-+	ret = register_netdevice(dev);
-+	if (ret < 0)
-+		goto fail;
-+
-+	return 0;
-+fail:
-+	free_netdev(dev);
-+	return ret;
-+}
-+
-+static int __init imq_init_devs(void)
-+{
-+	int err, i;
-+
-+	if (numdevs < 1 || numdevs > IMQ_MAX_DEVS) {
-+		pr_err("IMQ: numdevs has to be betweed 1 and %u\n",
-+		       IMQ_MAX_DEVS);
-+		return -EINVAL;
-+	}
-+
-+	if (numqueues < 1 || numqueues > IMQ_MAX_QUEUES) {
-+		pr_err("IMQ: numqueues has to be betweed 1 and %u\n",
-+		       IMQ_MAX_QUEUES);
-+		return -EINVAL;
-+	}
-+
-+	get_random_bytes(&imq_hashrnd, sizeof(imq_hashrnd));
-+
-+	rtnl_lock();
-+	err = __rtnl_link_register(&imq_link_ops);
-+
-+	for (i = 0; i < numdevs && !err; i++)
-+		err = imq_init_one(i);
-+
-+	if (err) {
-+		__rtnl_link_unregister(&imq_link_ops);
-+		memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
-+	}
-+	rtnl_unlock();
-+
-+	return err;
-+}
-+
-+static int __init imq_init_module(void)
-+{
-+	int err;
-+
-+#if defined(CONFIG_IMQ_NUM_DEVS)
-+	BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS > 16);
-+	BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS < 2);
-+	BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS - 1 > IMQ_F_IFMASK);
-+#endif
-+
-+	err = imq_init_devs();
-+	if (err) {
-+		pr_err("IMQ: Error trying imq_init_devs(net)\n");
-+		return err;
-+	}
-+
-+	err = imq_init_hooks();
-+	if (err) {
-+		pr_err(KERN_ERR "IMQ: Error trying imq_init_hooks()\n");
-+		rtnl_link_unregister(&imq_link_ops);
-+		memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
-+		return err;
-+	}
-+
-+	pr_info("IMQ driver loaded successfully. (numdevs = %d, numqueues = %d, imq_dev_accurate_stats = %d)\n",
-+		numdevs, numqueues, imq_dev_accurate_stats);
-+
-+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
-+	pr_info("\tHooking IMQ before NAT on PREROUTING.\n");
-+#else
-+	pr_info("\tHooking IMQ after NAT on PREROUTING.\n");
-+#endif
-+#if defined(CONFIG_IMQ_BEHAVIOR_AB) || defined(CONFIG_IMQ_BEHAVIOR_BB)
-+	pr_info("\tHooking IMQ before NAT on POSTROUTING.\n");
-+#else
-+	pr_info("\tHooking IMQ after NAT on POSTROUTING.\n");
-+#endif
-+
-+	return 0;
-+}
-+
-+static void __exit imq_unhook(void)
-+{
-+	nf_unregister_hooks(imq_ops, ARRAY_SIZE(imq_ops));
-+	nf_unregister_queue_imq_handler();
-+}
-+
-+static void __exit imq_cleanup_devs(void)
-+{
-+	rtnl_link_unregister(&imq_link_ops);
-+	memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
-+}
-+
-+static void __exit imq_exit_module(void)
-+{
-+	imq_unhook();
-+	imq_cleanup_devs();
-+	pr_info("IMQ driver unloaded successfully.\n");
-+}
-+
-+module_init(imq_init_module);
-+module_exit(imq_exit_module);
-+
-+module_param(numdevs, int, 0);
-+module_param(numqueues, int, 0);
-+module_param(imq_dev_accurate_stats, int, 0);
-+MODULE_PARM_DESC(numdevs, "number of IMQ devices (how many imq* devices will be created)");
-+MODULE_PARM_DESC(numqueues, "number of queues per IMQ device");
-+MODULE_PARM_DESC(imq_dev_accurate_stats, "Notify if need the accurate imq device stats");
-+
-+MODULE_AUTHOR("http://https://github.com/imq/linuximq");
-+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See https://github.com/imq/linuximq/wiki for more information.");
-+MODULE_LICENSE("GPL");
-+MODULE_ALIAS_RTNL_LINK("imq");
-diff --git a/include/linux/imq.h b/include/linux/imq.h
-new file mode 100644
-index 0000000..1babb09
---- /dev/null
-+++ b/include/linux/imq.h
-@@ -0,0 +1,13 @@
-+#ifndef _IMQ_H
-+#define _IMQ_H
-+
-+/* IFMASK (16 device indexes, 0 to 15) and flag(s) fit in 5 bits */
-+#define IMQ_F_BITS	5
-+
-+#define IMQ_F_IFMASK	0x0f
-+#define IMQ_F_ENQUEUE	0x10
-+
-+#define IMQ_MAX_DEVS	(IMQ_F_IFMASK + 1)
-+
-+#endif /* _IMQ_H */
-+
-diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
-index 3143c84..e213b31 100644
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -3341,6 +3341,19 @@ static inline void netif_tx_unlock_bh(struct net_device *dev)
- 	}						\
- }
- 
-+#define HARD_TX_LOCK_BH(dev, txq) {           \
-+    if ((dev->features & NETIF_F_LLTX) == 0) {  \
-+        __netif_tx_lock_bh(txq);      \
-+    }                       \
-+}
-+
-+#define HARD_TX_UNLOCK_BH(dev, txq) {          \
-+    if ((dev->features & NETIF_F_LLTX) == 0) {  \
-+        __netif_tx_unlock_bh(txq);         \
-+    }                       \
-+}
-+
-+
- static inline void netif_tx_disable(struct net_device *dev)
- {
- 	unsigned int i;
-diff --git a/include/linux/netfilter/xt_IMQ.h b/include/linux/netfilter/xt_IMQ.h
-new file mode 100644
-index 0000000..9b07230
---- /dev/null
-+++ b/include/linux/netfilter/xt_IMQ.h
-@@ -0,0 +1,9 @@
-+#ifndef _XT_IMQ_H
-+#define _XT_IMQ_H
-+
-+struct xt_imq_info {
-+	unsigned int todev;     /* target imq device */
-+};
-+
-+#endif /* _XT_IMQ_H */
-+
-diff --git a/include/linux/netfilter_ipv4/ipt_IMQ.h b/include/linux/netfilter_ipv4/ipt_IMQ.h
-new file mode 100644
-index 0000000..7af320f
---- /dev/null
-+++ b/include/linux/netfilter_ipv4/ipt_IMQ.h
-@@ -0,0 +1,10 @@
-+#ifndef _IPT_IMQ_H
-+#define _IPT_IMQ_H
-+
-+/* Backwards compatibility for old userspace */
-+#include <linux/netfilter/xt_IMQ.h>
-+
-+#define ipt_imq_info xt_imq_info
-+
-+#endif /* _IPT_IMQ_H */
-+
-diff --git a/include/linux/netfilter_ipv6/ip6t_IMQ.h b/include/linux/netfilter_ipv6/ip6t_IMQ.h
-new file mode 100644
-index 0000000..198ac01
---- /dev/null
-+++ b/include/linux/netfilter_ipv6/ip6t_IMQ.h
-@@ -0,0 +1,10 @@
-+#ifndef _IP6T_IMQ_H
-+#define _IP6T_IMQ_H
-+
-+/* Backwards compatibility for old userspace */
-+#include <linux/netfilter/xt_IMQ.h>
-+
-+#define ip6t_imq_info xt_imq_info
-+
-+#endif /* _IP6T_IMQ_H */
-+
-diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
-index 4355129..47914c0 100644
---- a/include/linux/skbuff.h
-+++ b/include/linux/skbuff.h
-@@ -38,6 +38,10 @@
- #include <linux/splice.h>
- #include <linux/in6.h>
- #include <net/flow.h>
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+#include <linux/imq.h>
-+#endif
-+
- 
- /* A. Checksumming of received packets by device.
-  *
-@@ -566,6 +570,9 @@ struct sk_buff {
- 	 * first. This is owned by whoever has the skb queued ATM.
- 	 */
- 	char			cb[48] __aligned(8);
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	void			*cb_next;
-+#endif
- 
- 	unsigned long		_skb_refdst;
- 	void			(*destructor)(struct sk_buff *skb);
-@@ -575,6 +582,9 @@ struct sk_buff {
- #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
- 	struct nf_conntrack	*nfct;
- #endif
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+       struct nf_queue_entry   *nf_queue_entry;
-+#endif
- #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
- 	struct nf_bridge_info	*nf_bridge;
- #endif
-@@ -642,6 +652,9 @@ struct sk_buff {
- 	__u8			inner_protocol_type:1;
- 	__u8			remcsum_offload:1;
- 	/* 3 or 5 bit hole */
-+	#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	__u8                    imq_flags:IMQ_F_BITS;
-+	#endif
- 
- #ifdef CONFIG_NET_SCHED
- 	__u16			tc_index;	/* traffic control index */
-@@ -798,6 +811,12 @@ void kfree_skb_list(struct sk_buff *segs);
- void skb_tx_error(struct sk_buff *skb);
- void consume_skb(struct sk_buff *skb);
- void  __kfree_skb(struct sk_buff *skb);
-+
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+int skb_save_cb(struct sk_buff *skb);
-+int skb_restore_cb(struct sk_buff *skb);
-+#endif
-+
- extern struct kmem_cache *skbuff_head_cache;
- 
- void kfree_skb_partial(struct sk_buff *skb, bool head_stolen);
-@@ -3344,6 +3363,10 @@ static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src,
- 	if (copy)
- 		dst->nfctinfo = src->nfctinfo;
- #endif
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+       dst->imq_flags = src->imq_flags;
-+       dst->nf_queue_entry = src->nf_queue_entry;
-+#endif
- #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
- 	dst->nf_bridge  = src->nf_bridge;
- 	nf_bridge_get(src->nf_bridge);
-diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h
-index 9c5638a..b173aa7 100644
---- a/include/net/netfilter/nf_queue.h
-+++ b/include/net/netfilter/nf_queue.h
-@@ -31,6 +31,12 @@ struct nf_queue_handler {
- void nf_register_queue_handler(const struct nf_queue_handler *qh);
- void nf_unregister_queue_handler(void);
- void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict);
-+void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
-+
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+void nf_register_queue_imq_handler(const struct nf_queue_handler *qh);
-+void nf_unregister_queue_imq_handler(void);
-+#endif
- 
- void nf_queue_entry_get_refs(struct nf_queue_entry *entry);
- void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
-diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
-index 401038d..4668849 100644
---- a/include/net/pkt_sched.h
-+++ b/include/net/pkt_sched.h
-@@ -104,6 +104,8 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
- 
- void __qdisc_run(struct Qdisc *q);
- 
-+struct sk_buff *qdisc_dequeue_skb(struct Qdisc *q, bool *validate);
-+
- static inline void qdisc_run(struct Qdisc *q)
- {
- 	if (qdisc_run_begin(q))
-diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
-index b2a8e63..d9feaa3 100644
---- a/include/net/sch_generic.h
-+++ b/include/net/sch_generic.h
-@@ -506,6 +506,12 @@ static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
- 	return sch->enqueue(skb, sch);
- }
- 
-+static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch)
-+{
-+    qdisc_skb_cb(skb)->pkt_len = skb->len;
-+    return qdisc_enqueue(skb, sch) & NET_XMIT_MASK;
-+}
-+
- static inline bool qdisc_is_percpu_stats(const struct Qdisc *q)
- {
- 	return q->flags & TCQ_F_CPUSTATS;
-diff --git a/include/uapi/linux/netfilter.h b/include/uapi/linux/netfilter.h
-index d93f949..23fb6d1 100644
---- a/include/uapi/linux/netfilter.h
-+++ b/include/uapi/linux/netfilter.h
-@@ -14,7 +14,8 @@
- #define NF_QUEUE 3
- #define NF_REPEAT 4
- #define NF_STOP 5
--#define NF_MAX_VERDICT NF_STOP
-+#define NF_IMQ_QUEUE 6
-+#define NF_MAX_VERDICT NF_IMQ_QUEUE
- 
- /* we overload the higher bits for encoding auxiliary data such as the queue
-  * number or errno values. Not nice, but better than additional function
-diff --git a/net/core/dev.c b/net/core/dev.c
-index ae00b89..1cdcd02 100644
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -137,6 +137,9 @@
- #include <linux/errqueue.h>
- #include <linux/hrtimer.h>
- #include <linux/netfilter_ingress.h>
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+#include <linux/imq.h>
-+#endif
- 
- #include "net-sysfs.h"
- 
-@@ -2705,7 +2708,12 @@ static int xmit_one(struct sk_buff *skb, struct net_device *dev,
- 	unsigned int len;
- 	int rc;
- 
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	if ((!list_empty(&ptype_all) || !list_empty(&dev->ptype_all)) &&
-+		!(skb->imq_flags & IMQ_F_ENQUEUE))
-+#else
- 	if (!list_empty(&ptype_all) || !list_empty(&dev->ptype_all))
-+#endif
- 		dev_queue_xmit_nit(skb, dev);
- 
- 	len = skb->len;
-@@ -2743,6 +2751,7 @@ out:
- 	*ret = rc;
- 	return skb;
- }
-+EXPORT_SYMBOL(dev_hard_start_xmit);
- 
- static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb,
- 					  netdev_features_t features)
-@@ -2831,6 +2840,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
- 	}
- 	return head;
- }
-+//EXPORT_SYMBOL(validate_xmit_skb_list);
- 
- static void qdisc_pkt_len_init(struct sk_buff *skb)
- {
-diff --git a/net/core/skbuff.c b/net/core/skbuff.c
-index b2df375..bc3c51e 100644
---- a/net/core/skbuff.c
-+++ b/net/core/skbuff.c
-@@ -79,6 +79,87 @@
- static struct kmem_cache *skbuff_fclone_cache __read_mostly;
- int sysctl_max_skb_frags __read_mostly = MAX_SKB_FRAGS;
- EXPORT_SYMBOL(sysctl_max_skb_frags);
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+static struct kmem_cache *skbuff_cb_store_cache __read_mostly;
-+#endif
-+
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+/* Control buffer save/restore for IMQ devices */
-+struct skb_cb_table {
-+	char			cb[48] __aligned(8);
-+	void			*cb_next;
-+	atomic_t		refcnt;
-+};
-+
-+static DEFINE_SPINLOCK(skb_cb_store_lock);
-+
-+int skb_save_cb(struct sk_buff *skb)
-+{
-+	struct skb_cb_table *next;
-+
-+	next = kmem_cache_alloc(skbuff_cb_store_cache, GFP_ATOMIC);
-+	if (!next)
-+		return -ENOMEM;
-+
-+	BUILD_BUG_ON(sizeof(skb->cb) != sizeof(next->cb));
-+
-+	memcpy(next->cb, skb->cb, sizeof(skb->cb));
-+	next->cb_next = skb->cb_next;
-+
-+	atomic_set(&next->refcnt, 1);
-+
-+	skb->cb_next = next;
-+	return 0;
-+}
-+EXPORT_SYMBOL(skb_save_cb);
-+
-+int skb_restore_cb(struct sk_buff *skb)
-+{
-+	struct skb_cb_table *next;
-+
-+	if (!skb->cb_next)
-+		return 0;
-+
-+	next = skb->cb_next;
-+
-+	BUILD_BUG_ON(sizeof(skb->cb) != sizeof(next->cb));
-+
-+	memcpy(skb->cb, next->cb, sizeof(skb->cb));
-+	skb->cb_next = next->cb_next;
-+
-+	spin_lock(&skb_cb_store_lock);
-+
-+	if (atomic_dec_and_test(&next->refcnt))
-+		kmem_cache_free(skbuff_cb_store_cache, next);
-+
-+	spin_unlock(&skb_cb_store_lock);
-+
-+	return 0;
-+}
-+EXPORT_SYMBOL(skb_restore_cb);
-+
-+static void skb_copy_stored_cb(struct sk_buff *   , const struct sk_buff *     ) __attribute__ ((unused));
-+static void skb_copy_stored_cb(struct sk_buff *new, const struct sk_buff *__old)
-+{
-+	struct skb_cb_table *next;
-+	struct sk_buff *old;
-+
-+	if (!__old->cb_next) {
-+		new->cb_next = NULL;
-+		return;
-+	}
-+
-+	spin_lock(&skb_cb_store_lock);
-+
-+	old = (struct sk_buff *)__old;
-+
-+	next = old->cb_next;
-+	atomic_inc(&next->refcnt);
-+	new->cb_next = next;
-+
-+	spin_unlock(&skb_cb_store_lock);
-+}
-+#endif
- 
- /**
-  *	skb_panic - private function for out-of-line support
-@@ -643,6 +724,28 @@ static void skb_release_head_state(struct sk_buff *skb)
- 		WARN_ON(in_irq());
- 		skb->destructor(skb);
- 	}
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	/*
-+	 * This should not happen. When it does, avoid memleak by restoring
-+	 * the chain of cb-backups.
-+	 */
-+	while (skb->cb_next != NULL) {
-+		if (net_ratelimit())
-+			pr_warn("IMQ: kfree_skb: skb->cb_next: %08x\n",
-+				(unsigned int)(uintptr_t)skb->cb_next);
-+
-+		skb_restore_cb(skb);
-+	}
-+	/*
-+	 * This should not happen either, nf_queue_entry is nullified in
-+	 * imq_dev_xmit(). If we have non-NULL nf_queue_entry then we are
-+	 * leaking entry pointers, maybe memory. We don't know if this is
-+	 * pointer to already freed memory, or should this be freed.
-+	 * If this happens we need to add refcounting, etc for nf_queue_entry.
-+	 */
-+	if (skb->nf_queue_entry && net_ratelimit())
-+		pr_warn("%s\n", "IMQ: kfree_skb: skb->nf_queue_entry != NULL");
-+#endif
- #if IS_ENABLED(CONFIG_NF_CONNTRACK)
- 	nf_conntrack_put(skb->nfct);
- #endif
-@@ -765,6 +868,10 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
- 	new->sp			= secpath_get(old->sp);
- #endif
- 	__nf_copy(new, old, false);
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	new->cb_next = NULL;
-+	/*skb_copy_stored_cb(new, old);*/
-+#endif
- 
- 	/* Note : this field could be in headers_start/headers_end section
- 	 * It is not yet because we do not want to have a 16 bit hole
-@@ -3325,6 +3432,13 @@ void __init skb_init(void)
- 						0,
- 						SLAB_HWCACHE_ALIGN|SLAB_PANIC,
- 						NULL);
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	skbuff_cb_store_cache = kmem_cache_create("skbuff_cb_store_cache",
-+						  sizeof(struct skb_cb_table),
-+						  0,
-+						  SLAB_HWCACHE_ALIGN|SLAB_PANIC,
-+						  NULL);
-+#endif
- }
- 
- /**
-diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
-index e6a7bd15..c81496e 100644
---- a/net/ipv6/ip6_output.c
-+++ b/net/ipv6/ip6_output.c
-@@ -65,9 +65,6 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
- 	struct in6_addr *nexthop;
- 	int ret;
- 
--	skb->protocol = htons(ETH_P_IPV6);
--	skb->dev = dev;
--
- 	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
- 		struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
- 
-@@ -142,6 +139,13 @@ int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
- 		return 0;
- 	}
- 
-+	/*
-+	* IMQ-patch: moved setting skb->dev and skb->protocol from
-+	* ip6_finish_output2 to fix crashing at netif_skb_features().
-+	*/
-+	skb->protocol = htons(ETH_P_IPV6);
-+	skb->dev = dev;
-+
- 	return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
- 			    net, sk, skb, NULL, dev,
- 			    ip6_finish_output,
-diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
-index 4692782..dce47db 100644
---- a/net/netfilter/Kconfig
-+++ b/net/netfilter/Kconfig
-@@ -785,6 +785,18 @@ config NETFILTER_XT_TARGET_LOG
- 
- 	  To compile it as a module, choose M here.  If unsure, say N.
- 
-+config NETFILTER_XT_TARGET_IMQ
-+        tristate '"IMQ" target support'
-+	depends on NETFILTER_XTABLES
-+	depends on IP_NF_MANGLE || IP6_NF_MANGLE
-+	select IMQ
-+	default m if NETFILTER_ADVANCED=n
-+        help
-+          This option adds a `IMQ' target which is used to specify if and
-+          to which imq device packets should get enqueued/dequeued.
-+
-+          To compile it as a module, choose M here.  If unsure, say N.
-+
- config NETFILTER_XT_TARGET_MARK
- 	tristate '"MARK" target support'
- 	depends on NETFILTER_ADVANCED
-diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
-index 7638c36..614ad8a 100644
---- a/net/netfilter/Makefile
-+++ b/net/netfilter/Makefile
-@@ -108,6 +108,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
-+obj-$(CONFIG_NETFILTER_XT_TARGET_IMQ) += xt_IMQ.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_NETMAP) += xt_NETMAP.o
-diff --git a/net/netfilter/core.c b/net/netfilter/core.c
-index f39276d..9877a27 100644
---- a/net/netfilter/core.c
-+++ b/net/netfilter/core.c
-@@ -311,9 +311,11 @@ next_hook:
- 		ret = NF_DROP_GETERR(verdict);
- 		if (ret == 0)
- 			ret = -EPERM;
--	} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
-+	} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE ||
-+		(verdict & NF_VERDICT_MASK) == NF_IMQ_QUEUE) {
- 		int err = nf_queue(skb, elem, state,
--				   verdict >> NF_VERDICT_QBITS);
-+				   verdict >> NF_VERDICT_QBITS,
-+				  verdict & NF_VERDICT_MASK);
- 		if (err < 0) {
- 			if (err == -ESRCH &&
- 			   (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
-diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h
-index 0655225..25d4141 100644
---- a/net/netfilter/nf_internals.h
-+++ b/net/netfilter/nf_internals.h
-@@ -18,7 +18,7 @@ unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb,
- 
- /* nf_queue.c */
- int nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem,
--	     struct nf_hook_state *state, unsigned int queuenum);
-+	     struct nf_hook_state *state, unsigned int queuenum, unsigned int queuetype);
- void nf_queue_nf_hook_drop(struct net *net, struct nf_hook_ops *ops);
- int __init netfilter_queue_init(void);
- 
-diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
-index 5baa8e2..9740e8c 100644
---- a/net/netfilter/nf_queue.c
-+++ b/net/netfilter/nf_queue.c
-@@ -28,6 +28,23 @@
-  */
- static const struct nf_queue_handler __rcu *queue_handler __read_mostly;
- 
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+static const struct nf_queue_handler __rcu *queue_imq_handler __read_mostly;
-+
-+void nf_register_queue_imq_handler(const struct nf_queue_handler *qh)
-+{
-+	rcu_assign_pointer(queue_imq_handler, qh);
-+}
-+EXPORT_SYMBOL_GPL(nf_register_queue_imq_handler);
-+
-+void nf_unregister_queue_imq_handler(void)
-+{
-+	RCU_INIT_POINTER(queue_imq_handler, NULL);
-+	synchronize_rcu();
-+}
-+EXPORT_SYMBOL_GPL(nf_unregister_queue_imq_handler);
-+#endif
-+
- /* return EBUSY when somebody else is registered, return EEXIST if the
-  * same handler is registered, return 0 in case of success. */
- void nf_register_queue_handler(const struct nf_queue_handler *qh)
-@@ -116,7 +133,8 @@ void nf_queue_nf_hook_drop(struct net *net, struct nf_hook_ops *ops)
- int nf_queue(struct sk_buff *skb,
- 	     struct nf_hook_ops *elem,
- 	     struct nf_hook_state *state,
--	     unsigned int queuenum)
-+	     unsigned int queuenum,
-+		 unsigned int queuetype)
- {
- 	int status = -ENOENT;
- 	struct nf_queue_entry *entry = NULL;
-@@ -124,7 +142,17 @@ int nf_queue(struct sk_buff *skb,
- 	const struct nf_queue_handler *qh;
- 
- 	/* QUEUE == DROP if no one is waiting, to be safe. */
--	qh = rcu_dereference(queue_handler);
-+	if (queuetype == NF_IMQ_QUEUE) {
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+		qh = rcu_dereference(queue_imq_handler);
-+#else
-+		BUG();
-+		goto err_unlock;
-+#endif
-+	} else {
-+		qh = rcu_dereference(queue_handler);
-+	}
-+
- 	if (!qh) {
- 		status = -ESRCH;
- 		goto err;
-@@ -199,8 +227,10 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
- 		local_bh_enable();
- 		break;
- 	case NF_QUEUE:
-+	case NF_IMQ_QUEUE:
- 		err = nf_queue(skb, elem, &entry->state,
--			       verdict >> NF_VERDICT_QBITS);
-+			       verdict >> NF_VERDICT_QBITS,
-+				   verdict & NF_VERDICT_MASK);
- 		if (err < 0) {
- 			if (err == -ESRCH &&
- 			   (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
-diff --git a/net/netfilter/xt_IMQ.c b/net/netfilter/xt_IMQ.c
-new file mode 100644
-index 0000000..86d7b84
---- /dev/null
-+++ b/net/netfilter/xt_IMQ.c
-@@ -0,0 +1,72 @@
-+/*
-+ * This target marks packets to be enqueued to an imq device
-+ */
-+#include <linux/module.h>
-+#include <linux/skbuff.h>
-+#include <linux/netfilter/x_tables.h>
-+#include <linux/netfilter/xt_IMQ.h>
-+#include <linux/imq.h>
-+
-+static unsigned int imq_target(struct sk_buff *pskb,
-+				const struct xt_action_param *par)
-+{
-+	const struct xt_imq_info *mr = par->targinfo;
-+
-+	pskb->imq_flags = (mr->todev & IMQ_F_IFMASK) | IMQ_F_ENQUEUE;
-+
-+	return XT_CONTINUE;
-+}
-+
-+static int imq_checkentry(const struct xt_tgchk_param *par)
-+{
-+	struct xt_imq_info *mr = par->targinfo;
-+
-+	if (mr->todev > IMQ_MAX_DEVS - 1) {
-+		pr_warn("IMQ: invalid device specified, highest is %u\n",
-+			IMQ_MAX_DEVS - 1);
-+		return -EINVAL;
-+	}
-+
-+	return 0;
-+}
-+
-+static struct xt_target xt_imq_reg[] __read_mostly = {
-+	{
-+		.name           = "IMQ",
-+		.family		= AF_INET,
-+		.checkentry     = imq_checkentry,
-+		.target         = imq_target,
-+		.targetsize	= sizeof(struct xt_imq_info),
-+		.table		= "mangle",
-+		.me             = THIS_MODULE
-+	},
-+	{
-+		.name           = "IMQ",
-+		.family		= AF_INET6,
-+		.checkentry     = imq_checkentry,
-+		.target         = imq_target,
-+		.targetsize	= sizeof(struct xt_imq_info),
-+		.table		= "mangle",
-+		.me             = THIS_MODULE
-+	},
-+};
-+
-+static int __init imq_init(void)
-+{
-+	return xt_register_targets(xt_imq_reg, ARRAY_SIZE(xt_imq_reg));
-+}
-+
-+static void __exit imq_fini(void)
-+{
-+	xt_unregister_targets(xt_imq_reg, ARRAY_SIZE(xt_imq_reg));
-+}
-+
-+module_init(imq_init);
-+module_exit(imq_fini);
-+
-+MODULE_AUTHOR("http://https://github.com/imq/linuximq");
-+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See https://github.com/imq/linuximq/wiki for more information.");
-+MODULE_LICENSE("GPL");
-+MODULE_ALIAS("ipt_IMQ");
-+MODULE_ALIAS("ip6t_IMQ");
-+
-diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
-index e82a1ad..b55331d 100644
---- a/net/sched/sch_generic.c
-+++ b/net/sched/sch_generic.c
-@@ -108,6 +108,14 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate,
- 	return skb;
- }
- 
-+struct sk_buff *qdisc_dequeue_skb(struct Qdisc *q, bool *validate)
-+{
-+	int packets;
-+
-+	return dequeue_skb(q, validate, &packets);
-+}
-+EXPORT_SYMBOL(qdisc_dequeue_skb);
-+
- static inline int handle_dev_cpu_collision(struct sk_buff *skb,
- 					   struct netdev_queue *dev_queue,
- 					   struct Qdisc *q)
Index: npl/kernel/linux_src/linux-4.9-imq.diff
===================================================================
--- npl/kernel/linux_src/linux-4.9-imq.diff	(revision f6630dda9bbc206e69f0475bfb8672e20d5beaa1)
+++ 	(revision )
@@ -1,1718 +1,0 @@
-diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
-index 95c32f2..93fada5 100644
---- a/drivers/net/Kconfig
-+++ b/drivers/net/Kconfig
-@@ -260,6 +260,125 @@ config RIONET_RX_SIZE
- 	depends on RIONET
- 	default "128"
- 
-+config IMQ
-+	tristate "IMQ (intermediate queueing device) support"
-+	depends on NETDEVICES && NETFILTER
-+	---help---
-+	  The IMQ device(s) is used as placeholder for QoS queueing
-+	  disciplines. Every packet entering/leaving the IP stack can be
-+	  directed through the IMQ device where it's enqueued/dequeued to the
-+	  attached qdisc. This allows you to treat network devices as classes
-+	  and distribute bandwidth among them. Iptables is used to specify
-+	  through which IMQ device, if any, packets travel.
-+
-+	  More information at: https://github.com/imq/linuximq
-+
-+	  To compile this driver as a module, choose M here: the module
-+	  will be called imq.  If unsure, say N.
-+
-+choice
-+	prompt "IMQ behavior (PRE/POSTROUTING)"
-+	depends on IMQ
-+	default IMQ_BEHAVIOR_AB
-+	help
-+	  This setting defines how IMQ behaves in respect to its
-+	  hooking in PREROUTING and POSTROUTING.
-+
-+	  IMQ can work in any of the following ways:
-+
-+	      PREROUTING   |      POSTROUTING
-+	  -----------------|-------------------
-+	  #1  After NAT    |      After NAT
-+	  #2  After NAT    |      Before NAT
-+	  #3  Before NAT   |      After NAT
-+	  #4  Before NAT   |      Before NAT
-+
-+	  The default behavior is to hook before NAT on PREROUTING
-+	  and after NAT on POSTROUTING (#3).
-+
-+	  This settings are specially usefull when trying to use IMQ
-+	  to shape NATed clients.
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
-+config IMQ_BEHAVIOR_AA
-+	bool "IMQ AA"
-+	help
-+	  This setting defines how IMQ behaves in respect to its
-+	  hooking in PREROUTING and POSTROUTING.
-+
-+	  Choosing this option will make IMQ hook like this:
-+
-+	  PREROUTING:   After NAT
-+	  POSTROUTING:  After NAT
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
-+config IMQ_BEHAVIOR_AB
-+	bool "IMQ AB"
-+	help
-+	  This setting defines how IMQ behaves in respect to its
-+	  hooking in PREROUTING and POSTROUTING.
-+
-+	  Choosing this option will make IMQ hook like this:
-+
-+	  PREROUTING:   After NAT
-+	  POSTROUTING:  Before NAT
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
-+config IMQ_BEHAVIOR_BA
-+	bool "IMQ BA"
-+	help
-+	  This setting defines how IMQ behaves in respect to its
-+	  hooking in PREROUTING and POSTROUTING.
-+
-+	  Choosing this option will make IMQ hook like this:
-+
-+	  PREROUTING:   Before NAT
-+	  POSTROUTING:  After NAT
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
-+config IMQ_BEHAVIOR_BB
-+	bool "IMQ BB"
-+	help
-+	  This setting defines how IMQ behaves in respect to its
-+	  hooking in PREROUTING and POSTROUTING.
-+
-+	  Choosing this option will make IMQ hook like this:
-+
-+	  PREROUTING:   Before NAT
-+	  POSTROUTING:  Before NAT
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
-+endchoice
-+
-+config IMQ_NUM_DEVS
-+	int "Number of IMQ devices"
-+	range 2 16
-+	depends on IMQ
-+	default "16"
-+	help
-+	  This setting defines how many IMQ devices will be created.
-+
-+	  The default value is 16.
-+
-+	  More information can be found at: https://github.com/imq/linuximq
-+
-+	  If not sure leave the default settings alone.
-+
- config TUN
- 	tristate "Universal TUN/TAP device driver support"
- 	depends on INET
-diff --git a/drivers/net/Makefile b/drivers/net/Makefile
-index 7336cbd..d6d7ad4 100644
---- a/drivers/net/Makefile
-+++ b/drivers/net/Makefile
-@@ -11,6 +11,7 @@ obj-$(CONFIG_DUMMY) += dummy.o
- obj-$(CONFIG_EQUALIZER) += eql.o
- obj-$(CONFIG_IFB) += ifb.o
- obj-$(CONFIG_MACSEC) += macsec.o
-+obj-$(CONFIG_IMQ) += imq.o
- obj-$(CONFIG_MACVLAN) += macvlan.o
- obj-$(CONFIG_MACVTAP) += macvtap.o
- obj-$(CONFIG_MII) += mii.o
-diff --git a/drivers/net/imq.c b/drivers/net/imq.c
-new file mode 100644
-index 0000000..bc3b997
---- /dev/null
-+++ b/drivers/net/imq.c
-@@ -0,0 +1,907 @@
-+/*
-+ *             Pseudo-driver for the intermediate queue device.
-+ *
-+ *             This program is free software; you can redistribute it and/or
-+ *             modify it under the terms of the GNU General Public License
-+ *             as published by the Free Software Foundation; either version
-+ *             2 of the License, or (at your option) any later version.
-+ *
-+ * Authors:    Patrick McHardy, <kaber@trash.net>
-+ *
-+ *            The first version was written by Martin Devera, <devik@cdi.cz>
-+ *
-+ *			   See Creditis.txt
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/moduleparam.h>
-+#include <linux/list.h>
-+#include <linux/skbuff.h>
-+#include <linux/netdevice.h>
-+#include <linux/etherdevice.h>
-+#include <linux/rtnetlink.h>
-+#include <linux/if_arp.h>
-+#include <linux/netfilter.h>
-+#include <linux/netfilter_ipv4.h>
-+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-+	#include <linux/netfilter_ipv6.h>
-+#endif
-+#include <linux/imq.h>
-+#include <net/pkt_sched.h>
-+#include <net/netfilter/nf_queue.h>
-+#include <net/sock.h>
-+#include <linux/ip.h>
-+#include <linux/ipv6.h>
-+#include <linux/if_vlan.h>
-+#include <linux/if_pppox.h>
-+#include <net/ip.h>
-+#include <net/ipv6.h>
-+
-+static int imq_nf_queue(struct nf_queue_entry *entry, unsigned queue_num);
-+
-+static nf_hookfn imq_nf_hook;
-+
-+static struct nf_hook_ops imq_ops[] = {
-+	{
-+	/* imq_ingress_ipv4 */
-+		.hook		= imq_nf_hook,
-+		.pf		= PF_INET,
-+		.hooknum	= NF_INET_PRE_ROUTING,
-+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
-+		.priority	= NF_IP_PRI_MANGLE + 1,
-+#else
-+		.priority	= NF_IP_PRI_NAT_DST + 1,
-+#endif
-+	},
-+	{
-+	/* imq_egress_ipv4 */
-+		.hook		= imq_nf_hook,
-+		.pf		= PF_INET,
-+		.hooknum	= NF_INET_POST_ROUTING,
-+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA)
-+		.priority	= NF_IP_PRI_LAST,
-+#else
-+		.priority	= NF_IP_PRI_NAT_SRC - 1,
-+#endif
-+	},
-+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-+	{
-+	/* imq_ingress_ipv6 */
-+		.hook		= imq_nf_hook,
-+		.pf		= PF_INET6,
-+		.hooknum	= NF_INET_PRE_ROUTING,
-+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
-+		.priority	= NF_IP6_PRI_MANGLE + 1,
-+#else
-+		.priority	= NF_IP6_PRI_NAT_DST + 1,
-+#endif
-+	},
-+	{
-+	/* imq_egress_ipv6 */
-+		.hook		= imq_nf_hook,
-+		.pf		= PF_INET6,
-+		.hooknum	= NF_INET_POST_ROUTING,
-+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA)
-+		.priority	= NF_IP6_PRI_LAST,
-+#else
-+		.priority	= NF_IP6_PRI_NAT_SRC - 1,
-+#endif
-+	},
-+#endif
-+};
-+
-+#if defined(CONFIG_IMQ_NUM_DEVS)
-+static int numdevs = CONFIG_IMQ_NUM_DEVS;
-+#else
-+static int numdevs = IMQ_MAX_DEVS;
-+#endif
-+
-+static struct net_device *imq_devs_cache[IMQ_MAX_DEVS];
-+
-+#define IMQ_MAX_QUEUES 32
-+static int numqueues = 1;
-+static u32 imq_hashrnd;
-+static int imq_dev_accurate_stats = 1;
-+
-+static inline __be16 pppoe_proto(const struct sk_buff *skb)
-+{
-+	return *((__be16 *)(skb_mac_header(skb) + ETH_HLEN +
-+			sizeof(struct pppoe_hdr)));
-+}
-+
-+static u16 imq_hash(struct net_device *dev, struct sk_buff *skb)
-+{
-+	unsigned int pull_len;
-+	u16 protocol = skb->protocol;
-+	u32 addr1, addr2;
-+	u32 hash, ihl = 0;
-+	union {
-+		u16 in16[2];
-+		u32 in32;
-+	} ports;
-+	u8 ip_proto;
-+
-+	pull_len = 0;
-+
-+recheck:
-+	switch (protocol) {
-+	case htons(ETH_P_8021Q): {
-+		if (unlikely(skb_pull(skb, VLAN_HLEN) == NULL))
-+			goto other;
-+
-+		pull_len += VLAN_HLEN;
-+		skb->network_header += VLAN_HLEN;
-+
-+		protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
-+		goto recheck;
-+	}
-+
-+	case htons(ETH_P_PPP_SES): {
-+		if (unlikely(skb_pull(skb, PPPOE_SES_HLEN) == NULL))
-+			goto other;
-+
-+		pull_len += PPPOE_SES_HLEN;
-+		skb->network_header += PPPOE_SES_HLEN;
-+
-+		protocol = pppoe_proto(skb);
-+		goto recheck;
-+	}
-+
-+	case htons(ETH_P_IP): {
-+		const struct iphdr *iph = ip_hdr(skb);
-+
-+		if (unlikely(!pskb_may_pull(skb, sizeof(struct iphdr))))
-+			goto other;
-+
-+		addr1 = iph->daddr;
-+		addr2 = iph->saddr;
-+
-+		ip_proto = !(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) ?
-+				 iph->protocol : 0;
-+		ihl = ip_hdrlen(skb);
-+
-+		break;
-+	}
-+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-+	case htons(ETH_P_IPV6): {
-+		const struct ipv6hdr *iph = ipv6_hdr(skb);
-+		__be16 fo = 0;
-+
-+		if (unlikely(!pskb_may_pull(skb, sizeof(struct ipv6hdr))))
-+			goto other;
-+
-+		addr1 = iph->daddr.s6_addr32[3];
-+		addr2 = iph->saddr.s6_addr32[3];
-+		ihl = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &ip_proto,
-+				       &fo);
-+		if (unlikely(ihl < 0))
-+			goto other;
-+
-+		break;
-+	}
-+#endif
-+	default:
-+other:
-+		if (pull_len != 0) {
-+			skb_push(skb, pull_len);
-+			skb->network_header -= pull_len;
-+		}
-+
-+		return (u16)(ntohs(protocol) % dev->real_num_tx_queues);
-+	}
-+
-+	if (addr1 > addr2)
-+		swap(addr1, addr2);
-+
-+	switch (ip_proto) {
-+	case IPPROTO_TCP:
-+	case IPPROTO_UDP:
-+	case IPPROTO_DCCP:
-+	case IPPROTO_ESP:
-+	case IPPROTO_AH:
-+	case IPPROTO_SCTP:
-+	case IPPROTO_UDPLITE: {
-+		if (likely(skb_copy_bits(skb, ihl, &ports.in32, 4) >= 0)) {
-+			if (ports.in16[0] > ports.in16[1])
-+				swap(ports.in16[0], ports.in16[1]);
-+			break;
-+		}
-+		/* fall-through */
-+	}
-+	default:
-+		ports.in32 = 0;
-+		break;
-+	}
-+
-+	if (pull_len != 0) {
-+		skb_push(skb, pull_len);
-+		skb->network_header -= pull_len;
-+	}
-+
-+	hash = jhash_3words(addr1, addr2, ports.in32, imq_hashrnd ^ ip_proto);
-+
-+	return (u16)(((u64)hash * dev->real_num_tx_queues) >> 32);
-+}
-+
-+static inline bool sk_tx_queue_recorded(struct sock *sk)
-+{
-+	return (sk_tx_queue_get(sk) >= 0);
-+}
-+
-+static struct netdev_queue *imq_select_queue(struct net_device *dev,
-+						struct sk_buff *skb)
-+{
-+	u16 queue_index = 0;
-+	u32 hash;
-+
-+	if (likely(dev->real_num_tx_queues == 1))
-+		goto out;
-+
-+	/* IMQ can be receiving ingress or engress packets. */
-+
-+	/* Check first for if rx_queue is set */
-+	if (skb_rx_queue_recorded(skb)) {
-+		queue_index = skb_get_rx_queue(skb);
-+		goto out;
-+	}
-+
-+	/* Check if socket has tx_queue set */
-+	if (sk_tx_queue_recorded(skb->sk)) {
-+		queue_index = sk_tx_queue_get(skb->sk);
-+		goto out;
-+	}
-+
-+	/* Try use socket hash */
-+	if (skb->sk && skb->sk->sk_hash) {
-+		hash = skb->sk->sk_hash;
-+		queue_index =
-+			(u16)(((u64)hash * dev->real_num_tx_queues) >> 32);
-+		goto out;
-+	}
-+
-+	/* Generate hash from packet data */
-+	queue_index = imq_hash(dev, skb);
-+
-+out:
-+	if (unlikely(queue_index >= dev->real_num_tx_queues))
-+		queue_index = (u16)((u32)queue_index % dev->real_num_tx_queues);
-+
-+	skb_set_queue_mapping(skb, queue_index);
-+	return netdev_get_tx_queue(dev, queue_index);
-+}
-+
-+static struct net_device_stats *imq_get_stats(struct net_device *dev)
-+{
-+	return &dev->stats;
-+}
-+
-+/* called for packets kfree'd in qdiscs at places other than enqueue */
-+static void imq_skb_destructor(struct sk_buff *skb)
-+{
-+	struct nf_queue_entry *entry = skb->nf_queue_entry;
-+
-+	skb->nf_queue_entry = NULL;
-+
-+	if (entry) {
-+		nf_queue_entry_release_refs(entry);
-+		kfree(entry);
-+	}
-+
-+	skb_restore_cb(skb); /* kfree backup */
-+}
-+
-+static void imq_done_check_queue_mapping(struct sk_buff *skb,
-+					 struct net_device *dev)
-+{
-+	unsigned int queue_index;
-+
-+	/* Don't let queue_mapping be left too large after exiting IMQ */
-+	if (likely(skb->dev != dev && skb->dev != NULL)) {
-+		queue_index = skb_get_queue_mapping(skb);
-+		if (unlikely(queue_index >= skb->dev->real_num_tx_queues)) {
-+			queue_index = (u16)((u32)queue_index %
-+						skb->dev->real_num_tx_queues);
-+			skb_set_queue_mapping(skb, queue_index);
-+		}
-+	} else {
-+		/* skb->dev was IMQ device itself or NULL, be on safe side and
-+		 * just clear queue mapping.
-+		 */
-+		skb_set_queue_mapping(skb, 0);
-+	}
-+}
-+
-+static netdev_tx_t imq_dev_xmit(struct sk_buff *skb, struct net_device *dev)
-+{
-+	struct nf_queue_entry *entry = skb->nf_queue_entry;
-+
-+	skb->nf_queue_entry = NULL;
-+	netif_trans_update(dev);
-+
-+	dev->stats.tx_bytes += skb->len;
-+	dev->stats.tx_packets++;
-+
-+	if (unlikely(entry == NULL)) {
-+		/* We don't know what is going on here.. packet is queued for
-+		 * imq device, but (probably) not by us.
-+		 *
-+		 * If this packet was not send here by imq_nf_queue(), then
-+		 * skb_save_cb() was not used and skb_free() should not show:
-+		 *   WARNING: IMQ: kfree_skb: skb->cb_next:..
-+		 * and/or
-+		 *   WARNING: IMQ: kfree_skb: skb->nf_queue_entry...
-+		 *
-+		 * However if this message is shown, then IMQ is somehow broken
-+		 * and you should report this to linuximq.net.
-+		 */
-+
-+		/* imq_dev_xmit is black hole that eats all packets, report that
-+		 * we eat this packet happily and increase dropped counters.
-+		 */
-+
-+		dev->stats.tx_dropped++;
-+		dev_kfree_skb(skb);
-+
-+		return NETDEV_TX_OK;
-+	}
-+
-+	skb_restore_cb(skb); /* restore skb->cb */
-+
-+	skb->imq_flags = 0;
-+	skb->destructor = NULL;
-+
-+	imq_done_check_queue_mapping(skb, dev);
-+
-+	nf_reinject(entry, NF_ACCEPT);
-+
-+	return NETDEV_TX_OK;
-+}
-+
-+static struct net_device *get_imq_device_by_index(int index)
-+{
-+	struct net_device *dev = NULL;
-+	struct net *net;
-+	char buf[8];
-+
-+	/* get device by name and cache result */
-+	snprintf(buf, sizeof(buf), "imq%d", index);
-+
-+	/* Search device from all namespaces. */
-+	for_each_net(net) {
-+		dev = dev_get_by_name(net, buf);
-+		if (dev)
-+			break;
-+	}
-+
-+	if (WARN_ON_ONCE(dev == NULL)) {
-+		/* IMQ device not found. Exotic config? */
-+		return ERR_PTR(-ENODEV);
-+	}
-+
-+	imq_devs_cache[index] = dev;
-+	dev_put(dev);
-+
-+	return dev;
-+}
-+
-+static struct nf_queue_entry *nf_queue_entry_dup(struct nf_queue_entry *e)
-+{
-+	struct nf_queue_entry *entry = kmemdup(e, e->size, GFP_ATOMIC);
-+	if (entry) {
-+		nf_queue_entry_get_refs(entry);
-+			return entry;
-+	}
-+	return NULL;
-+}
-+
-+#ifdef CONFIG_BRIDGE_NETFILTER
-+/* When called from bridge netfilter, skb->data must point to MAC header
-+ * before calling skb_gso_segment(). Else, original MAC header is lost
-+ * and segmented skbs will be sent to wrong destination.
-+ */
-+static void nf_bridge_adjust_skb_data(struct sk_buff *skb)
-+{
-+	if (skb->nf_bridge)
-+		__skb_push(skb, skb->network_header - skb->mac_header);
-+}
-+
-+static void nf_bridge_adjust_segmented_data(struct sk_buff *skb)
-+{
-+	if (skb->nf_bridge)
-+		__skb_pull(skb, skb->network_header - skb->mac_header);
-+}
-+#else
-+#define nf_bridge_adjust_skb_data(s) do {} while (0)
-+#define nf_bridge_adjust_segmented_data(s) do {} while (0)
-+#endif
-+
-+static void free_entry(struct nf_queue_entry *entry)
-+{
-+	nf_queue_entry_release_refs(entry);
-+	kfree(entry);
-+}
-+
-+static int __imq_nf_queue(struct nf_queue_entry *entry, struct net_device *dev);
-+
-+static int __imq_nf_queue_gso(struct nf_queue_entry *entry,
-+			      struct net_device *dev, struct sk_buff *skb)
-+{
-+	int ret = -ENOMEM;
-+	struct nf_queue_entry *entry_seg;
-+
-+	nf_bridge_adjust_segmented_data(skb);
-+
-+	if (skb->next == NULL) { /* last packet, no need to copy entry */
-+		struct sk_buff *gso_skb = entry->skb;
-+		entry->skb = skb;
-+		ret = __imq_nf_queue(entry, dev);
-+		if (ret)
-+			entry->skb = gso_skb;
-+		return ret;
-+	}
-+
-+	skb->next = NULL;
-+
-+	entry_seg = nf_queue_entry_dup(entry);
-+	if (entry_seg) {
-+		entry_seg->skb = skb;
-+		ret = __imq_nf_queue(entry_seg, dev);
-+		if (ret)
-+			free_entry(entry_seg);
-+	}
-+	return ret;
-+}
-+
-+static int imq_nf_queue(struct nf_queue_entry *entry, unsigned queue_num)
-+{
-+	struct sk_buff *skb, *segs;
-+	struct net_device *dev;
-+	unsigned int queued;
-+	int index, retval, err;
-+
-+	index = entry->skb->imq_flags & IMQ_F_IFMASK;
-+	if (unlikely(index > numdevs - 1)) {
-+		if (net_ratelimit())
-+			pr_warn("IMQ: invalid device specified, highest is %u\n",
-+				numdevs - 1);
-+		retval = -EINVAL;
-+		goto out_no_dev;
-+	}
-+
-+	/* check for imq device by index from cache */
-+	dev = imq_devs_cache[index];
-+	if (unlikely(!dev)) {
-+		dev = get_imq_device_by_index(index);
-+		if (IS_ERR(dev)) {
-+			retval = PTR_ERR(dev);
-+			goto out_no_dev;
-+		}
-+	}
-+
-+	if (unlikely(!(dev->flags & IFF_UP))) {
-+		entry->skb->imq_flags = 0;
-+		retval = -ECANCELED;
-+		goto out_no_dev;
-+	}
-+
-+	/* Since 3.10.x, GSO handling moved here as result of upstream commit
-+	 * a5fedd43d5f6c94c71053a66e4c3d2e35f1731a2 (netfilter: move
-+	 * skb_gso_segment into nfnetlink_queue module).
-+	 *
-+	 * Following code replicates the gso handling from
-+	 * 'net/netfilter/nfnetlink_queue_core.c':nfqnl_enqueue_packet().
-+	 */
-+
-+	skb = entry->skb;
-+
-+	switch (entry->state.pf) {
-+	case NFPROTO_IPV4:
-+		skb->protocol = htons(ETH_P_IP);
-+		break;
-+	case NFPROTO_IPV6:
-+		skb->protocol = htons(ETH_P_IPV6);
-+		break;
-+	}
-+
-+	if (!skb_is_gso(entry->skb))
-+		return __imq_nf_queue(entry, dev);
-+
-+	nf_bridge_adjust_skb_data(skb);
-+	segs = skb_gso_segment(skb, 0);
-+	/* Does not use PTR_ERR to limit the number of error codes that can be
-+	 * returned by nf_queue.  For instance, callers rely on -ECANCELED to
-+	 * mean 'ignore this hook'.
-+	 */
-+	err = -ENOBUFS;
-+	if (IS_ERR(segs))
-+		goto out_err;
-+	queued = 0;
-+	err = 0;
-+	do {
-+		struct sk_buff *nskb = segs->next;
-+		if (nskb && nskb->next)
-+			nskb->cb_next = NULL;
-+		if (err == 0)
-+			err = __imq_nf_queue_gso(entry, dev, segs);
-+		if (err == 0)
-+			queued++;
-+		else
-+			kfree_skb(segs);
-+		segs = nskb;
-+	} while (segs);
-+
-+	if (queued) {
-+		if (err) /* some segments are already queued */
-+			free_entry(entry);
-+		kfree_skb(skb);
-+		return 0;
-+	}
-+
-+out_err:
-+	nf_bridge_adjust_segmented_data(skb);
-+	retval = err;
-+out_no_dev:
-+	return retval;
-+}
-+
-+static int __imq_nf_queue(struct nf_queue_entry *entry, struct net_device *dev)
-+{
-+	struct sk_buff *skb_orig, *skb, *skb_shared, *skb_popd;
-+	struct Qdisc *q;
-+	struct sk_buff *to_free = NULL;
-+	struct netdev_queue *txq;
-+	spinlock_t *root_lock;
-+	int users;
-+	int retval = -EINVAL;
-+	unsigned int orig_queue_index;
-+
-+	dev->last_rx = jiffies;
-+
-+	skb = entry->skb;
-+	skb_orig = NULL;
-+
-+	/* skb has owner? => make clone */
-+	if (unlikely(skb->destructor)) {
-+		skb_orig = skb;
-+		skb = skb_clone(skb, GFP_ATOMIC);
-+		if (unlikely(!skb)) {
-+			retval = -ENOMEM;
-+			goto out;
-+		}
-+		skb->cb_next = NULL;
-+		entry->skb = skb;
-+	}
-+
-+	dev->stats.rx_bytes += skb->len;
-+	dev->stats.rx_packets++;
-+
-+	if (!skb->dev) {
-+		/* skb->dev == NULL causes problems, try the find cause. */
-+		if (net_ratelimit()) {
-+			dev_warn(&dev->dev,
-+				 "received packet with skb->dev == NULL\n");
-+			dump_stack();
-+		}
-+
-+		skb->dev = dev;
-+	}
-+
-+	/* Disables softirqs for lock below */
-+	rcu_read_lock_bh();
-+
-+	/* Multi-queue selection */
-+	orig_queue_index = skb_get_queue_mapping(skb);
-+	txq = imq_select_queue(dev, skb);
-+
-+	q = rcu_dereference(txq->qdisc);
-+	if (unlikely(!q->enqueue))
-+		goto packet_not_eaten_by_imq_dev;
-+
-+	skb->nf_queue_entry = entry;
-+	root_lock = qdisc_lock(q);
-+	spin_lock(root_lock);
-+
-+	users = atomic_read(&skb->users);
-+
-+	skb_shared = skb_get(skb); /* increase reference count by one */
-+
-+	/* backup skb->cb, as qdisc layer will overwrite it */
-+	skb_save_cb(skb_shared);
-+	qdisc_enqueue_root(skb_shared, q, &to_free); /* might kfree_skb */
-+	if (likely(atomic_read(&skb_shared->users) == users + 1)) {
-+		bool validate;
-+
-+		kfree_skb(skb_shared); /* decrease reference count by one */
-+
-+		skb->destructor = &imq_skb_destructor;
-+
-+		skb_popd = qdisc_dequeue_skb(q, &validate);
-+
-+		/* cloned? */
-+		if (unlikely(skb_orig))
-+			kfree_skb(skb_orig); /* free original */
-+
-+		spin_unlock(root_lock);
-+
-+#if 0
-+		/* schedule qdisc dequeue */
-+		__netif_schedule(q);
-+#else
-+		if (likely(skb_popd)) {
-+			/* Note that we validate skb (GSO, checksum, ...) outside of locks */
-+			if (validate)
-+        		skb_popd = validate_xmit_skb_list(skb_popd, dev);
-+
-+			if (skb_popd) {
-+				int dummy_ret;
-+				int cpu = smp_processor_id(); /* ok because BHs are off */
-+
-+				txq = skb_get_tx_queue(dev, skb_popd);
-+				/*
-+				IMQ device will not be frozen or stoped, and it always be successful.
-+				So we need not check its status and return value to accelerate.
-+				*/
-+				if (imq_dev_accurate_stats && txq->xmit_lock_owner != cpu) {
-+					HARD_TX_LOCK(dev, txq, cpu);
-+					if (!netif_xmit_frozen_or_stopped(txq)) {
-+						dev_hard_start_xmit(skb_popd, dev, txq, &dummy_ret);
-+					}
-+					HARD_TX_UNLOCK(dev, txq);
-+				} else {
-+					if (!netif_xmit_frozen_or_stopped(txq)) {
-+						dev_hard_start_xmit(skb_popd, dev, txq, &dummy_ret);
-+					}
-+				}
-+			}
-+		} else {
-+			/* No ready skb, then schedule it */
-+			__netif_schedule(q);
-+		}
-+#endif
-+		rcu_read_unlock_bh();
-+		retval = 0;
-+		goto out;
-+	} else {
-+		skb_restore_cb(skb_shared); /* restore skb->cb */
-+		skb->nf_queue_entry = NULL;
-+		/*
-+		 * qdisc dropped packet and decreased skb reference count of
-+		 * skb, so we don't really want to and try refree as that would
-+		 * actually destroy the skb.
-+		 */
-+		spin_unlock(root_lock);
-+		goto packet_not_eaten_by_imq_dev;
-+	}
-+
-+packet_not_eaten_by_imq_dev:
-+	skb_set_queue_mapping(skb, orig_queue_index);
-+	rcu_read_unlock_bh();
-+
-+	/* cloned? restore original */
-+	if (unlikely(skb_orig)) {
-+		kfree_skb(skb);
-+		entry->skb = skb_orig;
-+	}
-+	retval = -1;
-+out:
-+	if (unlikely(to_free)) {
-+		kfree_skb_list(to_free);
-+	}
-+	return retval;
-+}
-+static unsigned int imq_nf_hook(void *priv,
-+				struct sk_buff *skb,
-+				const struct nf_hook_state *state)
-+{
-+	return (skb->imq_flags & IMQ_F_ENQUEUE) ? NF_IMQ_QUEUE : NF_ACCEPT;
-+}
-+
-+static int imq_close(struct net_device *dev)
-+{
-+	netif_stop_queue(dev);
-+	return 0;
-+}
-+
-+static int imq_open(struct net_device *dev)
-+{
-+	netif_start_queue(dev);
-+	return 0;
-+}
-+
-+static const struct net_device_ops imq_netdev_ops = {
-+	.ndo_open		= imq_open,
-+	.ndo_stop		= imq_close,
-+	.ndo_start_xmit		= imq_dev_xmit,
-+	.ndo_get_stats		= imq_get_stats,
-+};
-+
-+static void imq_setup(struct net_device *dev)
-+{
-+	dev->netdev_ops		= &imq_netdev_ops;
-+	dev->type		= ARPHRD_VOID;
-+	dev->mtu		= 16000; /* too small? */
-+	dev->tx_queue_len	= 11000; /* too big? */
-+	dev->flags		= IFF_NOARP;
-+	dev->features		= NETIF_F_SG | NETIF_F_FRAGLIST |
-+				  NETIF_F_GSO | NETIF_F_HW_CSUM |
-+				  NETIF_F_HIGHDMA;
-+	dev->priv_flags		&= ~(IFF_XMIT_DST_RELEASE |
-+				     IFF_TX_SKB_SHARING);
-+}
-+
-+static int imq_validate(struct nlattr *tb[], struct nlattr *data[])
-+{
-+	int ret = 0;
-+
-+	if (tb[IFLA_ADDRESS]) {
-+		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) {
-+			ret = -EINVAL;
-+			goto end;
-+		}
-+		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) {
-+			ret = -EADDRNOTAVAIL;
-+			goto end;
-+		}
-+	}
-+	return 0;
-+end:
-+	pr_warn("IMQ: imq_validate failed (%d)\n", ret);
-+	return ret;
-+}
-+
-+static struct rtnl_link_ops imq_link_ops __read_mostly = {
-+	.kind		= "imq",
-+	.priv_size	= 0,
-+	.setup		= imq_setup,
-+	.validate	= imq_validate,
-+};
-+
-+static const struct nf_queue_handler imq_nfqh = {
-+	.outfn = imq_nf_queue,
-+};
-+
-+static int __init imq_init_hooks(void)
-+{
-+	int ret;
-+
-+	nf_register_queue_imq_handler(&imq_nfqh);
-+
-+	ret = nf_register_hooks(imq_ops, ARRAY_SIZE(imq_ops));
-+	if (ret < 0)
-+		nf_unregister_queue_imq_handler();
-+
-+	return ret;
-+}
-+
-+static int __init imq_init_one(int index)
-+{
-+	struct net_device *dev;
-+	int ret;
-+
-+	dev = alloc_netdev_mq(0, "imq%d", NET_NAME_UNKNOWN, imq_setup, numqueues);
-+	if (!dev)
-+		return -ENOMEM;
-+
-+	ret = dev_alloc_name(dev, dev->name);
-+	if (ret < 0)
-+		goto fail;
-+
-+	dev->rtnl_link_ops = &imq_link_ops;
-+	ret = register_netdevice(dev);
-+	if (ret < 0)
-+		goto fail;
-+
-+	return 0;
-+fail:
-+	free_netdev(dev);
-+	return ret;
-+}
-+
-+static int __init imq_init_devs(void)
-+{
-+	int err, i;
-+
-+	if (numdevs < 1 || numdevs > IMQ_MAX_DEVS) {
-+		pr_err("IMQ: numdevs has to be betweed 1 and %u\n",
-+		       IMQ_MAX_DEVS);
-+		return -EINVAL;
-+	}
-+
-+	if (numqueues < 1 || numqueues > IMQ_MAX_QUEUES) {
-+		pr_err("IMQ: numqueues has to be betweed 1 and %u\n",
-+		       IMQ_MAX_QUEUES);
-+		return -EINVAL;
-+	}
-+
-+	get_random_bytes(&imq_hashrnd, sizeof(imq_hashrnd));
-+
-+	rtnl_lock();
-+	err = __rtnl_link_register(&imq_link_ops);
-+
-+	for (i = 0; i < numdevs && !err; i++)
-+		err = imq_init_one(i);
-+
-+	if (err) {
-+		__rtnl_link_unregister(&imq_link_ops);
-+		memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
-+	}
-+	rtnl_unlock();
-+
-+	return err;
-+}
-+
-+static int __init imq_init_module(void)
-+{
-+	int err;
-+
-+#if defined(CONFIG_IMQ_NUM_DEVS)
-+	BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS > 16);
-+	BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS < 2);
-+	BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS - 1 > IMQ_F_IFMASK);
-+#endif
-+
-+	err = imq_init_devs();
-+	if (err) {
-+		pr_err("IMQ: Error trying imq_init_devs(net)\n");
-+		return err;
-+	}
-+
-+	err = imq_init_hooks();
-+	if (err) {
-+		pr_err(KERN_ERR "IMQ: Error trying imq_init_hooks()\n");
-+		rtnl_link_unregister(&imq_link_ops);
-+		memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
-+		return err;
-+	}
-+
-+	pr_info("IMQ driver loaded successfully. (numdevs = %d, numqueues = %d, imq_dev_accurate_stats = %d)\n",
-+		numdevs, numqueues, imq_dev_accurate_stats);
-+
-+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
-+	pr_info("\tHooking IMQ before NAT on PREROUTING.\n");
-+#else
-+	pr_info("\tHooking IMQ after NAT on PREROUTING.\n");
-+#endif
-+#if defined(CONFIG_IMQ_BEHAVIOR_AB) || defined(CONFIG_IMQ_BEHAVIOR_BB)
-+	pr_info("\tHooking IMQ before NAT on POSTROUTING.\n");
-+#else
-+	pr_info("\tHooking IMQ after NAT on POSTROUTING.\n");
-+#endif
-+
-+	return 0;
-+}
-+
-+static void __exit imq_unhook(void)
-+{
-+	nf_unregister_hooks(imq_ops, ARRAY_SIZE(imq_ops));
-+	nf_unregister_queue_imq_handler();
-+}
-+
-+static void __exit imq_cleanup_devs(void)
-+{
-+	rtnl_link_unregister(&imq_link_ops);
-+	memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
-+}
-+
-+static void __exit imq_exit_module(void)
-+{
-+	imq_unhook();
-+	imq_cleanup_devs();
-+	pr_info("IMQ driver unloaded successfully.\n");
-+}
-+
-+module_init(imq_init_module);
-+module_exit(imq_exit_module);
-+
-+module_param(numdevs, int, 0);
-+module_param(numqueues, int, 0);
-+module_param(imq_dev_accurate_stats, int, 0);
-+MODULE_PARM_DESC(numdevs, "number of IMQ devices (how many imq* devices will be created)");
-+MODULE_PARM_DESC(numqueues, "number of queues per IMQ device");
-+MODULE_PARM_DESC(imq_dev_accurate_stats, "Notify if need the accurate imq device stats");
-+
-+MODULE_AUTHOR("https://github.com/imq/linuximq");
-+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See https://github.com/imq/linuximq/wiki for more information.");
-+MODULE_LICENSE("GPL");
-+MODULE_ALIAS_RTNL_LINK("imq");
-diff --git a/include/linux/imq.h b/include/linux/imq.h
-new file mode 100644
-index 0000000..1babb09
---- /dev/null
-+++ b/include/linux/imq.h
-@@ -0,0 +1,13 @@
-+#ifndef _IMQ_H
-+#define _IMQ_H
-+
-+/* IFMASK (16 device indexes, 0 to 15) and flag(s) fit in 5 bits */
-+#define IMQ_F_BITS	5
-+
-+#define IMQ_F_IFMASK	0x0f
-+#define IMQ_F_ENQUEUE	0x10
-+
-+#define IMQ_MAX_DEVS	(IMQ_F_IFMASK + 1)
-+
-+#endif /* _IMQ_H */
-+
-diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
-index e16a2a9..4a1090a 100644
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -3669,6 +3669,19 @@ static inline void netif_tx_unlock_bh(struct net_device *dev)
- 	}						\
- }
- 
-+#define HARD_TX_LOCK_BH(dev, txq) {           \
-+    if ((dev->features & NETIF_F_LLTX) == 0) {  \
-+        __netif_tx_lock_bh(txq);      \
-+    }                       \
-+}
-+
-+#define HARD_TX_UNLOCK_BH(dev, txq) {          \
-+    if ((dev->features & NETIF_F_LLTX) == 0) {  \
-+        __netif_tx_unlock_bh(txq);         \
-+    }                       \
-+}
-+
-+
- static inline void netif_tx_disable(struct net_device *dev)
- {
- 	unsigned int i;
-diff --git a/include/linux/netfilter/xt_IMQ.h b/include/linux/netfilter/xt_IMQ.h
-new file mode 100644
-index 0000000..9b07230
---- /dev/null
-+++ b/include/linux/netfilter/xt_IMQ.h
-@@ -0,0 +1,9 @@
-+#ifndef _XT_IMQ_H
-+#define _XT_IMQ_H
-+
-+struct xt_imq_info {
-+	unsigned int todev;     /* target imq device */
-+};
-+
-+#endif /* _XT_IMQ_H */
-+
-diff --git a/include/linux/netfilter_ipv4/ipt_IMQ.h b/include/linux/netfilter_ipv4/ipt_IMQ.h
-new file mode 100644
-index 0000000..7af320f
---- /dev/null
-+++ b/include/linux/netfilter_ipv4/ipt_IMQ.h
-@@ -0,0 +1,10 @@
-+#ifndef _IPT_IMQ_H
-+#define _IPT_IMQ_H
-+
-+/* Backwards compatibility for old userspace */
-+#include <linux/netfilter/xt_IMQ.h>
-+
-+#define ipt_imq_info xt_imq_info
-+
-+#endif /* _IPT_IMQ_H */
-+
-diff --git a/include/linux/netfilter_ipv6/ip6t_IMQ.h b/include/linux/netfilter_ipv6/ip6t_IMQ.h
-new file mode 100644
-index 0000000..198ac01
---- /dev/null
-+++ b/include/linux/netfilter_ipv6/ip6t_IMQ.h
-@@ -0,0 +1,10 @@
-+#ifndef _IP6T_IMQ_H
-+#define _IP6T_IMQ_H
-+
-+/* Backwards compatibility for old userspace */
-+#include <linux/netfilter/xt_IMQ.h>
-+
-+#define ip6t_imq_info xt_imq_info
-+
-+#endif /* _IP6T_IMQ_H */
-+
-diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
-index c8f9fa6..6c425c2 100644
---- a/include/linux/skbuff.h
-+++ b/include/linux/skbuff.h
-@@ -39,6 +39,10 @@
- #include <linux/in6.h>
- #include <linux/if_packet.h>
- #include <net/flow.h>
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+#include <linux/imq.h>
-+#endif
-+
- 
- /* The interface for checksum offload between the stack and networking drivers
-  * is as follows...
-@@ -654,6 +658,9 @@ struct sk_buff {
- 	 * first. This is owned by whoever has the skb queued ATM.
- 	 */
- 	char			cb[48] __aligned(8);
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	void			*cb_next;
-+#endif
- 
- 	unsigned long		_skb_refdst;
- 	void			(*destructor)(struct sk_buff *skb);
-@@ -663,6 +670,9 @@ struct sk_buff {
- #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
- 	struct nf_conntrack	*nfct;
- #endif
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+       struct nf_queue_entry   *nf_queue_entry;
-+#endif
- #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
- 	struct nf_bridge_info	*nf_bridge;
- #endif
-@@ -743,6 +753,9 @@ struct sk_buff {
- 	__u8			offload_fwd_mark:1;
- #endif
- 	/* 2, 4 or 5 bit hole */
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	__u8			imq_flags:IMQ_F_BITS;
-+#endif
- 
- #ifdef CONFIG_NET_SCHED
- 	__u16			tc_index;	/* traffic control index */
-@@ -903,6 +916,12 @@ void kfree_skb_list(struct sk_buff *segs);
- void skb_tx_error(struct sk_buff *skb);
- void consume_skb(struct sk_buff *skb);
- void  __kfree_skb(struct sk_buff *skb);
-+
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+int skb_save_cb(struct sk_buff *skb);
-+int skb_restore_cb(struct sk_buff *skb);
-+#endif
-+
- extern struct kmem_cache *skbuff_head_cache;
- 
- void kfree_skb_partial(struct sk_buff *skb, bool head_stolen);
-@@ -3594,6 +3613,10 @@ static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src,
- 	if (copy)
- 		dst->nfctinfo = src->nfctinfo;
- #endif
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+       dst->imq_flags = src->imq_flags;
-+       dst->nf_queue_entry = src->nf_queue_entry;
-+#endif
- #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
- 	dst->nf_bridge  = src->nf_bridge;
- 	nf_bridge_get(src->nf_bridge);
-diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h
-index 2280cfe..ec8fa51 100644
---- a/include/net/netfilter/nf_queue.h
-+++ b/include/net/netfilter/nf_queue.h
-@@ -30,6 +30,12 @@ struct nf_queue_handler {
- void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh);
- void nf_unregister_queue_handler(struct net *net);
- void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict);
-+void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
-+
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+void nf_register_queue_imq_handler(const struct nf_queue_handler *qh);
-+void nf_unregister_queue_imq_handler(void);
-+#endif
- 
- void nf_queue_entry_get_refs(struct nf_queue_entry *entry);
- void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
-diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
-index cd334c9..6757228 100644
---- a/include/net/pkt_sched.h
-+++ b/include/net/pkt_sched.h
-@@ -105,6 +105,8 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
- 
- void __qdisc_run(struct Qdisc *q);
- 
-+struct sk_buff *qdisc_dequeue_skb(struct Qdisc *q, bool *validate);
-+
- static inline void qdisc_run(struct Qdisc *q)
- {
- 	if (qdisc_run_begin(q))
-diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
-index e6aa0a2..08b37dc 100644
---- a/include/net/sch_generic.h
-+++ b/include/net/sch_generic.h
-@@ -518,6 +518,13 @@ static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
- 	return sch->enqueue(skb, sch, to_free);
- }
- 
-+static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch,
-+				      struct sk_buff **to_free)
-+{
-+    qdisc_skb_cb(skb)->pkt_len = skb->len;
-+    return qdisc_enqueue(skb, sch, to_free) & NET_XMIT_MASK;
-+}
-+
- static inline bool qdisc_is_percpu_stats(const struct Qdisc *q)
- {
- 	return q->flags & TCQ_F_CPUSTATS;
-diff --git a/include/uapi/linux/netfilter.h b/include/uapi/linux/netfilter.h
-index d93f949..23fb6d1 100644
---- a/include/uapi/linux/netfilter.h
-+++ b/include/uapi/linux/netfilter.h
-@@ -14,7 +14,8 @@
- #define NF_QUEUE 3
- #define NF_REPEAT 4
- #define NF_STOP 5
--#define NF_MAX_VERDICT NF_STOP
-+#define NF_IMQ_QUEUE 6
-+#define NF_MAX_VERDICT NF_IMQ_QUEUE
- 
- /* we overload the higher bits for encoding auxiliary data such as the queue
-  * number or errno values. Not nice, but better than additional function
-diff --git a/net/core/dev.c b/net/core/dev.c
-index 6666b28..3e12add 100644
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -141,6 +141,9 @@
- #include <linux/netfilter_ingress.h>
- #include <linux/sctp.h>
- #include <linux/crash_dump.h>
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+#include <linux/imq.h>
-+#endif
- 
- #include "net-sysfs.h"
- 
-@@ -2906,7 +2909,12 @@ static int xmit_one(struct sk_buff *skb, struct net_device *dev,
- 	unsigned int len;
- 	int rc;
- 
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	if ((!list_empty(&ptype_all) || !list_empty(&dev->ptype_all)) &&
-+		!(skb->imq_flags & IMQ_F_ENQUEUE))
-+#else
- 	if (!list_empty(&ptype_all) || !list_empty(&dev->ptype_all))
-+#endif
- 		dev_queue_xmit_nit(skb, dev);
- 
- 	len = skb->len;
-@@ -2945,6 +2953,8 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *de
- 	return skb;
- }
- 
-+EXPORT_SYMBOL_GPL(dev_hard_start_xmit);
-+
- static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb,
- 					  netdev_features_t features)
- {
-diff --git a/net/core/skbuff.c b/net/core/skbuff.c
-index 9bf1289..e3fcf17 100644
---- a/net/core/skbuff.c
-+++ b/net/core/skbuff.c
-@@ -82,6 +82,87 @@ struct kmem_cache *skbuff_head_cache __read_mostly;
- static struct kmem_cache *skbuff_fclone_cache __read_mostly;
- int sysctl_max_skb_frags __read_mostly = MAX_SKB_FRAGS;
- EXPORT_SYMBOL(sysctl_max_skb_frags);
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+static struct kmem_cache *skbuff_cb_store_cache __read_mostly;
-+#endif
-+
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+/* Control buffer save/restore for IMQ devices */
-+struct skb_cb_table {
-+	char			cb[48] __aligned(8);
-+	void			*cb_next;
-+	atomic_t		refcnt;
-+};
-+
-+static DEFINE_SPINLOCK(skb_cb_store_lock);
-+
-+int skb_save_cb(struct sk_buff *skb)
-+{
-+	struct skb_cb_table *next;
-+
-+	next = kmem_cache_alloc(skbuff_cb_store_cache, GFP_ATOMIC);
-+	if (!next)
-+		return -ENOMEM;
-+
-+	BUILD_BUG_ON(sizeof(skb->cb) != sizeof(next->cb));
-+
-+	memcpy(next->cb, skb->cb, sizeof(skb->cb));
-+	next->cb_next = skb->cb_next;
-+
-+	atomic_set(&next->refcnt, 1);
-+
-+	skb->cb_next = next;
-+	return 0;
-+}
-+EXPORT_SYMBOL(skb_save_cb);
-+
-+int skb_restore_cb(struct sk_buff *skb)
-+{
-+	struct skb_cb_table *next;
-+
-+	if (!skb->cb_next)
-+		return 0;
-+
-+	next = skb->cb_next;
-+
-+	BUILD_BUG_ON(sizeof(skb->cb) != sizeof(next->cb));
-+
-+	memcpy(skb->cb, next->cb, sizeof(skb->cb));
-+	skb->cb_next = next->cb_next;
-+
-+	spin_lock(&skb_cb_store_lock);
-+
-+	if (atomic_dec_and_test(&next->refcnt))
-+		kmem_cache_free(skbuff_cb_store_cache, next);
-+
-+	spin_unlock(&skb_cb_store_lock);
-+
-+	return 0;
-+}
-+EXPORT_SYMBOL(skb_restore_cb);
-+
-+static void skb_copy_stored_cb(struct sk_buff *   , const struct sk_buff *     ) __attribute__ ((unused));
-+static void skb_copy_stored_cb(struct sk_buff *new, const struct sk_buff *__old)
-+{
-+	struct skb_cb_table *next;
-+	struct sk_buff *old;
-+
-+	if (!__old->cb_next) {
-+		new->cb_next = NULL;
-+		return;
-+	}
-+
-+	spin_lock(&skb_cb_store_lock);
-+
-+	old = (struct sk_buff *)__old;
-+
-+	next = old->cb_next;
-+	atomic_inc(&next->refcnt);
-+	new->cb_next = next;
-+
-+	spin_unlock(&skb_cb_store_lock);
-+}
-+#endif
- 
- /**
-  *	skb_panic - private function for out-of-line support
-@@ -654,6 +735,28 @@ static void skb_release_head_state(struct sk_buff *skb)
- 		WARN_ON(in_irq());
- 		skb->destructor(skb);
- 	}
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	/*
-+	 * This should not happen. When it does, avoid memleak by restoring
-+	 * the chain of cb-backups.
-+	 */
-+	while (skb->cb_next != NULL) {
-+		if (net_ratelimit())
-+			pr_warn("IMQ: kfree_skb: skb->cb_next: %08x\n",
-+				(unsigned int)(uintptr_t)skb->cb_next);
-+
-+		skb_restore_cb(skb);
-+	}
-+	/*
-+	 * This should not happen either, nf_queue_entry is nullified in
-+	 * imq_dev_xmit(). If we have non-NULL nf_queue_entry then we are
-+	 * leaking entry pointers, maybe memory. We don't know if this is
-+	 * pointer to already freed memory, or should this be freed.
-+	 * If this happens we need to add refcounting, etc for nf_queue_entry.
-+	 */
-+	if (skb->nf_queue_entry && net_ratelimit())
-+		pr_warn("%s\n", "IMQ: kfree_skb: skb->nf_queue_entry != NULL");
-+#endif
- #if IS_ENABLED(CONFIG_NF_CONNTRACK)
- 	nf_conntrack_put(skb->nfct);
- #endif
-@@ -843,6 +946,10 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
- 	new->sp			= secpath_get(old->sp);
- #endif
- 	__nf_copy(new, old, false);
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	new->cb_next = NULL;
-+	/*skb_copy_stored_cb(new, old);*/
-+#endif
- 
- 	/* Note : this field could be in headers_start/headers_end section
- 	 * It is not yet because we do not want to have a 16 bit hole
-@@ -3464,6 +3571,13 @@ void __init skb_init(void)
- 						0,
- 						SLAB_HWCACHE_ALIGN|SLAB_PANIC,
- 						NULL);
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+	skbuff_cb_store_cache = kmem_cache_create("skbuff_cb_store_cache",
-+						  sizeof(struct skb_cb_table),
-+						  0,
-+						  SLAB_HWCACHE_ALIGN|SLAB_PANIC,
-+						  NULL);
-+#endif
- }
- 
- /**
-diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
-index 59eb4ed..8020b07 100644
---- a/net/ipv6/ip6_output.c
-+++ b/net/ipv6/ip6_output.c
-@@ -66,9 +66,6 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
- 	struct in6_addr *nexthop;
- 	int ret;
- 
--	skb->protocol = htons(ETH_P_IPV6);
--	skb->dev = dev;
--
- 	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
- 		struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
- 
-@@ -150,6 +147,13 @@ int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
- 		return 0;
- 	}
- 
-+	/*
-+	* IMQ-patch: moved setting skb->dev and skb->protocol from
-+	* ip6_finish_output2 to fix crashing at netif_skb_features().
-+	*/
-+	skb->protocol = htons(ETH_P_IPV6);
-+	skb->dev = dev;
-+
- 	return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
- 			    net, sk, skb, NULL, dev,
- 			    ip6_finish_output,
-diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
-index 4a2e5a3..fc5cc9a 100644
---- a/net/netfilter/Kconfig
-+++ b/net/netfilter/Kconfig
-@@ -833,6 +833,18 @@ config NETFILTER_XT_TARGET_LOG
- 
- 	  To compile it as a module, choose M here.  If unsure, say N.
- 
-+config NETFILTER_XT_TARGET_IMQ
-+        tristate '"IMQ" target support'
-+	depends on NETFILTER_XTABLES
-+	depends on IP_NF_MANGLE || IP6_NF_MANGLE
-+	select IMQ
-+	default m if NETFILTER_ADVANCED=n
-+        help
-+          This option adds a `IMQ' target which is used to specify if and
-+          to which imq device packets should get enqueued/dequeued.
-+
-+          To compile it as a module, choose M here.  If unsure, say N.
-+
- config NETFILTER_XT_TARGET_MARK
- 	tristate '"MARK" target support'
- 	depends on NETFILTER_ADVANCED
-diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
-index e5c5e1e..3128bc5 100644
---- a/net/netfilter/Makefile
-+++ b/net/netfilter/Makefile
-@@ -119,6 +119,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
-+obj-$(CONFIG_NETFILTER_XT_TARGET_IMQ) += xt_IMQ.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_NETMAP) += xt_NETMAP.o
-diff --git a/net/netfilter/core.c b/net/netfilter/core.c
-index 004af03..768a08b 100644
---- a/net/netfilter/core.c
-+++ b/net/netfilter/core.c
-@@ -360,8 +360,11 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state)
- 		ret = NF_DROP_GETERR(verdict);
- 		if (ret == 0)
- 			ret = -EPERM;
--	} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
-+	} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE ||
-+		   (verdict & NF_VERDICT_MASK) == NF_IMQ_QUEUE) {
- 		ret = nf_queue(skb, state, &entry, verdict);
-+		if (ret == -ECANCELED)
-+			goto next_hook;
- 		if (ret == 1 && entry)
- 			goto next_hook;
- 	}
-diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
-index 8f08d75..c12c9eb 100644
---- a/net/netfilter/nf_queue.c
-+++ b/net/netfilter/nf_queue.c
-@@ -27,6 +27,23 @@
-  * receives, no matter what.
-  */
- 
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+static const struct nf_queue_handler __rcu *queue_imq_handler __read_mostly;
-+
-+void nf_register_queue_imq_handler(const struct nf_queue_handler *qh)
-+{
-+	rcu_assign_pointer(queue_imq_handler, qh);
-+}
-+EXPORT_SYMBOL_GPL(nf_register_queue_imq_handler);
-+
-+void nf_unregister_queue_imq_handler(void)
-+{
-+	RCU_INIT_POINTER(queue_imq_handler, NULL);
-+	synchronize_rcu();
-+}
-+EXPORT_SYMBOL_GPL(nf_unregister_queue_imq_handler);
-+#endif
-+
- /* return EBUSY when somebody else is registered, return EEXIST if the
-  * same handler is registered, return 0 in case of success. */
- void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh)
-@@ -108,16 +125,28 @@ void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_entry *entry)
- }
- 
- static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
--		      unsigned int queuenum)
-+		      unsigned int verdict)
- {
- 	int status = -ENOENT;
- 	struct nf_queue_entry *entry = NULL;
- 	const struct nf_afinfo *afinfo;
- 	const struct nf_queue_handler *qh;
- 	struct net *net = state->net;
-+	unsigned int queuetype = verdict & NF_VERDICT_MASK;
-+	unsigned int queuenum  = verdict >> NF_VERDICT_QBITS;
- 
- 	/* QUEUE == DROP if no one is waiting, to be safe. */
--	qh = rcu_dereference(net->nf.queue_handler);
-+	if (queuetype == NF_IMQ_QUEUE) {
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+		qh = rcu_dereference(queue_imq_handler);
-+#else
-+		BUG();
-+		goto err_unlock;
-+#endif
-+	} else {
-+		qh = rcu_dereference(net->nf.queue_handler);
-+	}
-+
- 	if (!qh) {
- 		status = -ESRCH;
- 		goto err;
-@@ -164,8 +193,14 @@ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
- 	int ret;
- 
- 	RCU_INIT_POINTER(state->hook_entries, entry);
--	ret = __nf_queue(skb, state, verdict >> NF_VERDICT_QBITS);
-+	ret = __nf_queue(skb, state, verdict);
- 	if (ret < 0) {
-+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
-+		if (ret == -ECANCELED && skb->imq_flags == 0) { // down interface
-+			*entryp = rcu_dereference(entry->next);
-+			return 1;
-+		}
-+#endif
- 		if (ret == -ESRCH &&
- 		    (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) {
- 			*entryp = rcu_dereference(entry->next);
-@@ -218,6 +253,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
- 		local_bh_enable();
- 		break;
- 	case NF_QUEUE:
-+	case NF_IMQ_QUEUE:
- 		err = nf_queue(skb, &entry->state, &hook_entry, verdict);
- 		if (err == 1) {
- 			if (hook_entry)
-diff --git a/net/netfilter/xt_IMQ.c b/net/netfilter/xt_IMQ.c
-new file mode 100644
-index 0000000..f9c5817
---- /dev/null
-+++ b/net/netfilter/xt_IMQ.c
-@@ -0,0 +1,72 @@
-+/*
-+ * This target marks packets to be enqueued to an imq device
-+ */
-+#include <linux/module.h>
-+#include <linux/skbuff.h>
-+#include <linux/netfilter/x_tables.h>
-+#include <linux/netfilter/xt_IMQ.h>
-+#include <linux/imq.h>
-+
-+static unsigned int imq_target(struct sk_buff *pskb,
-+				const struct xt_action_param *par)
-+{
-+	const struct xt_imq_info *mr = par->targinfo;
-+
-+	pskb->imq_flags = (mr->todev & IMQ_F_IFMASK) | IMQ_F_ENQUEUE;
-+
-+	return XT_CONTINUE;
-+}
-+
-+static int imq_checkentry(const struct xt_tgchk_param *par)
-+{
-+	struct xt_imq_info *mr = par->targinfo;
-+
-+	if (mr->todev > IMQ_MAX_DEVS - 1) {
-+		pr_warn("IMQ: invalid device specified, highest is %u\n",
-+			IMQ_MAX_DEVS - 1);
-+		return -EINVAL;
-+	}
-+
-+	return 0;
-+}
-+
-+static struct xt_target xt_imq_reg[] __read_mostly = {
-+	{
-+		.name           = "IMQ",
-+		.family		= AF_INET,
-+		.checkentry     = imq_checkentry,
-+		.target         = imq_target,
-+		.targetsize	= sizeof(struct xt_imq_info),
-+		.table		= "mangle",
-+		.me             = THIS_MODULE
-+	},
-+	{
-+		.name           = "IMQ",
-+		.family		= AF_INET6,
-+		.checkentry     = imq_checkentry,
-+		.target         = imq_target,
-+		.targetsize	= sizeof(struct xt_imq_info),
-+		.table		= "mangle",
-+		.me             = THIS_MODULE
-+	},
-+};
-+
-+static int __init imq_init(void)
-+{
-+	return xt_register_targets(xt_imq_reg, ARRAY_SIZE(xt_imq_reg));
-+}
-+
-+static void __exit imq_fini(void)
-+{
-+	xt_unregister_targets(xt_imq_reg, ARRAY_SIZE(xt_imq_reg));
-+}
-+
-+module_init(imq_init);
-+module_exit(imq_fini);
-+
-+MODULE_AUTHOR("https://github.com/imq/linuximq");
-+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See https://github.com/imq/linuximq/wiki for more information.");
-+MODULE_LICENSE("GPL");
-+MODULE_ALIAS("ipt_IMQ");
-+MODULE_ALIAS("ip6t_IMQ");
-+
-diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
-index 6cfb6e9..4c675e9 100644
---- a/net/sched/sch_generic.c
-+++ b/net/sched/sch_generic.c
-@@ -154,6 +154,14 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate,
- 	return skb;
- }
- 
-+struct sk_buff *qdisc_dequeue_skb(struct Qdisc *q, bool *validate)
-+{
-+	int packets;
-+
-+	return dequeue_skb(q, validate, &packets);
-+}
-+EXPORT_SYMBOL(qdisc_dequeue_skb);
-+
- /*
-  * Transmit possibly several skbs, and handle the return status as
-  * required. Owning running seqcount bit guarantees that
Index: npl/kernel/linux_src/linux_src.SlackBuild
===================================================================
--- npl/kernel/linux_src/linux_src.SlackBuild	(revision f6630dda9bbc206e69f0475bfb8672e20d5beaa1)
+++ npl/kernel/linux_src/linux_src.SlackBuild	(revision e8565120de16c582dc1bd83033f7d7370bacd011)
@@ -14,10 +14,10 @@
 
 #unpack basic kernel and apply all incremental patches
-tar -xf $CWD/linux-4.9.tar.xz || exit 1
+tar -xf $CWD/linux-4.14.tar.xz || exit 1
 cd linux-* || exit 1
 #if you want to create a newer kernel: just download newest patches, and increase patchlevel
 #also increase .major file to trigger rebuilds!
-PATCHLEVEL=215
-VER=4.9.$PATCHLEVEL
+PATCHLEVEL=172
+VER=4.14.$PATCHLEVEL
 #bunzip2 $CWD/patch-$VER.bz2 || exit 1
 #gunzip $CWD/patch-$VER.gz || exit 1
@@ -46,4 +46,5 @@
 
 #from https://dev.gentoo.org/~mpagano/genpatches/patches-4.9-143.html
+#https://dev.gentoo.org/~mpagano/genpatches/patches-4.14-182.html
 patch  -p1 < $CWD/4200_fbcondecor.patch || exit 1
 
@@ -55,5 +56,7 @@
 #patch -p1 -F3 < $CWD/linux-3.13.10_hardened_gentoo.diff || exit 1
 #patch -p1  < $CWD/linux-4.4.5-imq.diff || exit 1
-patch -p1  < $CWD/linux-4.9-imq.diff || exit 1
+#patch -p1  < $CWD/linux-4.9-imq.diff || exit 1
+#from https://raw.githubusercontent.com/imq/linuximq/master/kernel/v4.x/linux-4.14-imq.diff
+patch -p1  < $CWD/linux-4.14-imq.diff || exit 1
 
 
Index: npl/kernel/linux_src/linux_src.build
===================================================================
--- npl/kernel/linux_src/linux_src.build	(revision f6630dda9bbc206e69f0475bfb8672e20d5beaa1)
+++ npl/kernel/linux_src/linux_src.build	(revision e8565120de16c582dc1bd83033f7d7370bacd011)
@@ -1,1 +1,1 @@
-6565
+6567
Index: npl/kernel/linux_src/linux_src.major
===================================================================
--- npl/kernel/linux_src/linux_src.major	(revision f6630dda9bbc206e69f0475bfb8672e20d5beaa1)
+++ npl/kernel/linux_src/linux_src.major	(revision e8565120de16c582dc1bd83033f7d7370bacd011)
@@ -1,2 +1,2 @@
-409215006
+414172001
 
Index: npl/kernel/linux_src/linux_src.md5
===================================================================
--- npl/kernel/linux_src/linux_src.md5	(revision f6630dda9bbc206e69f0475bfb8672e20d5beaa1)
+++ npl/kernel/linux_src/linux_src.md5	(revision e8565120de16c582dc1bd83033f7d7370bacd011)
@@ -1,11 +1,14 @@
-1c0d964fcb8ab76dd04f19f83bdabbc2  ./4200_fbcondecor.patch
-81aefdabd0946c19aa091ecc8d81ef4a  ./config
+8e12dfcbf36f937747e1fb36b115c14b  ./4200_fbcondecor.patch
+c020bdeb4d74c11d59a479ccb2a848b4  ./config
 8b7ca23aa660578023a0a244ae235888  ./fbcondecor-3.19.patch
+914a5254c91f2564a7654c2180f4e911  ./linux-4.14-imq.diff
+bacdb9ffdcd922aa069a5e1520160e24  ./linux-4.14.tar.xz
 467d0a59ae2bfe466a370d65768b8ace  ./linux-4.4.5-imq.diff
 6b10d4581a7f626ea0634ce0e2ad0250  ./linux-4.9-imq.diff
 0a68ef3615c64bd5ee54a3320e46667d  ./linux-4.9.tar.xz
-6a08b0b3f2d90271418ea2fdca56a8fa  ./linux_src.pkg
-46c1586c3534ec384459d20ce91286a5  ./linux_src.SlackBuild
-da6c644cd6f7e701a8ddb8c02c5b3143  ./linux_src.SlackBuild.log.gz
+0c49be6828fc56d490cf0e33c045edae  ./linux_src.pkg
+783aa6b6e7734ef62d98f19d350448e7  ./linux_src.SlackBuild
+94822d59f44255707e84dedde44dbc25  ./linux_src.SlackBuild.log.gz
+4ae0221e47d6329233c38875ec0b090a  ./patch-4.14.172.xz
 a64148a9d7dc6ba241790f3cb320a9d7  ./patch-4.9.184.xz
 1cf03959fb7ab6bf20bbc87993631bce  ./patch-4.9.215.xz
Index: npl/kernel/linux_src/linux_src.version
===================================================================
--- npl/kernel/linux_src/linux_src.version	(revision f6630dda9bbc206e69f0475bfb8672e20d5beaa1)
+++ npl/kernel/linux_src/linux_src.version	(revision e8565120de16c582dc1bd83033f7d7370bacd011)
@@ -1,1 +1,1 @@
-4.9.215_bigmem_syn3
+4.14.172_bigmem_syn3
