Ubuntu16.04 安装VM12:解决网卡驱动vmnet安装失败的问题

本文针对 Ubuntu 16.04 下 VMware 12 安装后虚拟网卡驱动编译失败的问题提供了解决方案。通过对日志文件的分析,发现该问题是由于 Linux 内核升级导致。通过修改 vmnet-only 文件夹下的 bridge.c 文件,并进行相应的编译步骤,可以成功解决驱动编译失败的问题。

本人Ubuntu是16.04,在尝试安装VM12的时候,发现安装后,首次启动会弹出一个安装驱动的对话框,但安装过程中,第二步就失败了。提示如下:

可见,此错误在于虚拟网卡驱动编译不通过。这个问题在VM 14中并不会出现。

博客主页:https://blog.youkuaiyun.com/xs1102

经分析日志和查阅资料,发现此问题,是由于linux内核升级导致的。

1、根据日志文件,定位到目录 /usr/lib/vmware/modules/source/ 下的vmnet.tar。解压vmnet.tar到自己的任意目录下,得到vmnet-only文件夹。

2、修改vmnet-only文件夹下的bridge.c文件的第639行,由

atomic_inc(&clone->users);

改为

atomic_inc((atomic_t*)&(clone->users.refs));

强制类型转换 (atomic_t*) 不写也没有问题。若由于版本不同,代码位置有差异的请自行搜索。修改完后,不放心的话,可以使用make命令编译一下,不报错即可。

3、将vmnet-only文件夹打包为vmnet.tar,放回原位置替换掉,替换前最好备份,以防万一。

4、重新运行VMware即可,驱动编译通过。

至此,上述问题完美解决。该问题博主自己遇到了两次,第一次费劲解决了,第二次安装时又有此问题,所以才记录下来,方便下次查阅,也给大家提供些帮助。OK,非技术人员可以离开了,下边开始偏底层的问题代码分析。





出现上述问题的原因在于:linux内核在新版本,修改了一些底层函数,原子操作函数 atomic_inc的形参类型为atomic_t* ,而sk_buff结构体中users的类型为refcount_t,在早期的linux内核中,users的类型为atomic_t,refcount_t是把atomic_t又包了一层。所以这就导致了指针类型不同,而编译失败。

所以在编译时,就出现了以下错误信息:

vmnet-only/bridge.c: In function ‘VNetBridgeReceiveFromVNet’:

vmnet-only/bridge.c:639:14: error: passing argument 1 of ‘atomic_inc’ from incompatible pointer type [-Werror=incompatible-pointer-types]
   atomic_inc(&(clone->users));
              ^
In file included from ./include/linux/atomic.h:4:0,
                 from ./include/linux/rcupdate.h:38,
                 from ./include/linux/rculist.h:10,
                 from ./include/linux/pid.h:4,
                 from ./include/linux/sched.h:13,
                 from vmnet-only/bridge.c:25:
./arch/x86/include/asm/atomic.h:89:29: note: expected ‘atomic_t * {aka struct <anonymous> *}’ but argument is of type ‘refcount_t * {aka struct refcount_struct *}’
static __always_inline void atomic_inc(atomic_t *v)
                            ^

在linux-4.10-rc2中11个模块,引用计数从atomic_t转为refcount_t:

  kernel, mm: convert from atomic_t to refcount_t
  net: convert from atomic_t to refcount_t
  fs: convert from atomic_t to refcount_t
  security: convert from atomic_t to refcount_t
  sound: convert from atomic_t to refcount_t
  ipc: covert from atomic_t to refcount_t
  tools: convert from atomic_t to refcount_t
  block: convert from atomic_t to refcount_t
  drivers: net convert from atomic_t to refcount_t
  drivers: misc drivers convert from atomic_t to refcount_t
  drivers: infiniband convert from atomic_t to refcount_t

atomic_t结构体的定义如下:

typedef struct {  
    volatile int counter;  
} atomic_t; 

refcount_t结构体定义如下:

typedef struct refcount_struct {
	atomic_t refs;
} refcount_t;

新版本的linux内核中sk_buff结构体的定义为:(/usr/src/linux-headers-4.13.0-39/include/linux/skbuff.h)

struct sk_buff {
	union {
		struct {
			/* These two members must be first. */
			struct sk_buff		*next;
			struct sk_buff		*prev;

			union {
				ktime_t		tstamp;
				u64		skb_mstamp;
			};
		};
		struct rb_node	rbnode; /* used in netem & tcp stack */
	};
	struct sock		*sk;

	union {
		struct net_device	*dev;
		/* Some protocols might use this space to store information,
		 * while device pointer would be NULL.
		 * UDP receive path is one user.
		 */
		unsigned long		dev_scratch;
	};
	/*
	 * This is the control buffer. It is free to use for every
	 * layer. Please put your private variables there. If you
	 * want to keep them across layers you have to do a skb_clone()
	 * first. This is owned by whoever has the skb queued ATM.
	 */
	char			cb[48] __aligned(8);

	unsigned long		_skb_refdst;
	void			(*destructor)(struct sk_buff *skb);
#ifdef CONFIG_XFRM
	struct	sec_path	*sp;
#endif
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
	unsigned long		 _nfct;
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
	struct nf_bridge_info	*nf_bridge;
#endif
	unsigned int		len,
				data_len;
	__u16			mac_len,
				hdr_len;

	/* Following fields are _not_ copied in __copy_skb_header()
	 * Note that queue_mapping is here mostly to fill a hole.
	 */
	kmemcheck_bitfield_begin(flags1);
	__u16			queue_mapping;

/* if you move cloned around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define CLONED_MASK	(1 << 7)
#else
#define CLONED_MASK	1
#endif
#define CLONED_OFFSET()		offsetof(struct sk_buff, __cloned_offset)

	__u8			__cloned_offset[0];
	__u8			cloned:1,
				nohdr:1,
				fclone:2,
				peeked:1,
				head_frag:1,
				xmit_more:1,
				__unused:1; /* one bit hole */
	kmemcheck_bitfield_end(flags1);

	/* fields enclosed in headers_start/headers_end are copied
	 * using a single memcpy() in __copy_skb_header()
	 */
	/* private: */
	__u32			headers_start[0];
	/* public: */

/* if you move pkt_type around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define PKT_TYPE_MAX	(7 << 5)
#else
#define PKT_TYPE_MAX	7
#endif
#define PKT_TYPE_OFFSET()	offsetof(struct sk_buff, __pkt_type_offset)

	__u8			__pkt_type_offset[0];
	__u8			pkt_type:3;
	__u8			pfmemalloc:1;
	__u8			ignore_df:1;

	__u8			nf_trace:1;
	__u8			ip_summed:2;
	__u8			ooo_okay:1;
	__u8			l4_hash:1;
	__u8			sw_hash:1;
	__u8			wifi_acked_valid:1;
	__u8			wifi_acked:1;

	__u8			no_fcs:1;
	/* Indicates the inner headers are valid in the skbuff. */
	__u8			encapsulation:1;
	__u8			encap_hdr_csum:1;
	__u8			csum_valid:1;
	__u8			csum_complete_sw:1;
	__u8			csum_level:2;
	__u8			csum_not_inet:1;

	__u8			dst_pending_confirm:1;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
	__u8			ndisc_nodetype:2;
#endif
	__u8			ipvs_property:1;
	__u8			inner_protocol_type:1;
	__u8			remcsum_offload:1;
#ifdef CONFIG_NET_SWITCHDEV
	__u8			offload_fwd_mark:1;
#endif
#ifdef CONFIG_NET_CLS_ACT
	__u8			tc_skip_classify:1;
	__u8			tc_at_ingress:1;
	__u8			tc_redirected:1;
	__u8			tc_from_ingress:1;
#endif

#ifdef CONFIG_NET_SCHED
	__u16			tc_index;	/* traffic control index */
#endif

	union {
		__wsum		csum;
		struct {
			__u16	csum_start;
			__u16	csum_offset;
		};
	};
	__u32			priority;
	int			skb_iif;
	__u32			hash;
	__be16			vlan_proto;
	__u16			vlan_tci;
#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)
	union {
		unsigned int	napi_id;
		unsigned int	sender_cpu;
	};
#endif
#ifdef CONFIG_NETWORK_SECMARK
	__u32		secmark;
#endif

	union {
		__u32		mark;
		__u32		reserved_tailroom;
	};

	union {
		__be16		inner_protocol;
		__u8		inner_ipproto;
	};

	__u16			inner_transport_header;
	__u16			inner_network_header;
	__u16			inner_mac_header;

	__be16			protocol;
	__u16			transport_header;
	__u16			network_header;
	__u16			mac_header;

	/* private: */
	__u32			headers_end[0];
	/* public: */

	/* These elements must be at the end, see alloc_skb() for details.  */
	sk_buff_data_t		tail;
	sk_buff_data_t		end;
	unsigned char		*head,
				*data;
	unsigned int		truesize;
	refcount_t		users;
};
旧版本的linux内核sk_buff结构体定义:
struct sk_buff {  
         /* These two members must be first. */  
         struct sk_buff          *next;  
         struct sk_buff          *prev;  
   
         struct sock             *sk;  
         struct skb_timeval      tstamp;  
         struct net_device       *dev;  
         struct net_device       *input_dev;  
   
         union {  
                 struct tcphdr   *th;  
                 struct udphdr   *uh;  
                 struct icmphdr  *icmph;  
                 struct igmphdr  *igmph;  
                 struct iphdr    *ipiph;  
                 struct ipv6hdr  *ipv6h;  
                 unsigned char   *raw;  
         } h;  
   
         union {  
                 struct iphdr    *iph;  
                 struct ipv6hdr  *ipv6h;  
                 struct arphdr   *arph;  
                 unsigned char   *raw;  
         } nh;  
   
         union {  
                 unsigned char   *raw;  
         } mac;  
   
         struct  dst_entry       *dst;  
         struct  sec_path        *sp;  
   
         /* 
          * This is the control buffer. It is free to use for every 
          * layer. Please put your private variables there. If you 
          * want to keep them across layers you have to do a skb_clone() 
          * first. This is owned by whoever has the skb queued ATM. 
          */  
         char                    cb[48];  
   
         unsigned int            len,  
                                 data_len,  
                                 mac_len,  
                                 csum;  
         __u32                   priority;  
         __u8                    local_df:1,  
                                 cloned:1,  
                                 ip_summed:2,  
                                 nohdr:1,  
                                 nfctinfo:3;  
         __u8                    pkt_type:3,  
                                 fclone:2,  
                                 ipvs_property:1;  
         __be16                  protocol;  
   
         void                    (*destructor)(struct sk_buff *skb);  
#ifdef CONFIG_NETFILTER  
         __u32                   nfmark;  
         struct nf_conntrack     *nfct;  
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)  
         struct sk_buff          *nfct_reasm;  
#endif  
#ifdef CONFIG_BRIDGE_NETFILTER  
         struct nf_bridge_info   *nf_bridge;  
#endif  
#endif /* CONFIG_NETFILTER */  
#ifdef CONFIG_NET_SCHED  
         __u16                   tc_index;       /* traffic control index */  
#ifdef CONFIG_NET_CLS_ACT  
         __u16                   tc_verd;        /* traffic control verdict */  
#endif  
#endif  
   
   
         /* These elements must be at the end, see alloc_skb() for details.  */  
         unsigned int            truesize;  
         atomic_t                users;  
         unsigned char           *head,  
                                 *data,  
                                 *tail,  
                                 *end;  
}; 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值