本人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;
};