diff options
Diffstat (limited to 'net/can/af_can.c')
| -rw-r--r-- | net/can/af_can.c | 67 | 
1 files changed, 42 insertions, 25 deletions
diff --git a/net/can/af_can.c b/net/can/af_can.c index 733d66f1b05..094fc5332d4 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -84,8 +84,8 @@ static DEFINE_SPINLOCK(can_rcvlists_lock);  static struct kmem_cache *rcv_cache __read_mostly;  /* table of registered CAN protocols */ -static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly; -static DEFINE_SPINLOCK(proto_tab_lock); +static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly; +static DEFINE_MUTEX(proto_tab_lock);  struct timer_list can_stattimer;   /* timer for statistics update */  struct s_stats    can_stats;       /* packet statistics */ @@ -115,11 +115,29 @@ static void can_sock_destruct(struct sock *sk)  	skb_queue_purge(&sk->sk_receive_queue);  } +static const struct can_proto *can_get_proto(int protocol) +{ +	const struct can_proto *cp; + +	rcu_read_lock(); +	cp = rcu_dereference(proto_tab[protocol]); +	if (cp && !try_module_get(cp->prot->owner)) +		cp = NULL; +	rcu_read_unlock(); + +	return cp; +} + +static inline void can_put_proto(const struct can_proto *cp) +{ +	module_put(cp->prot->owner); +} +  static int can_create(struct net *net, struct socket *sock, int protocol,  		      int kern)  {  	struct sock *sk; -	struct can_proto *cp; +	const struct can_proto *cp;  	int err = 0;  	sock->state = SS_UNCONNECTED; @@ -130,9 +148,12 @@ static int can_create(struct net *net, struct socket *sock, int protocol,  	if (!net_eq(net, &init_net))  		return -EAFNOSUPPORT; +	cp = can_get_proto(protocol); +  #ifdef CONFIG_MODULES -	/* try to load protocol module kernel is modular */ -	if (!proto_tab[protocol]) { +	if (!cp) { +		/* try to load protocol module if kernel is modular */ +  		err = request_module("can-proto-%d", protocol);  		/* @@ -143,22 +164,18 @@ static int can_create(struct net *net, struct socket *sock, int protocol,  		if (err && printk_ratelimit())  			printk(KERN_ERR "can: request_module "  			       "(can-proto-%d) failed.\n", protocol); + +		cp = can_get_proto(protocol);  	}  #endif -	spin_lock(&proto_tab_lock); -	cp = proto_tab[protocol]; -	if (cp && !try_module_get(cp->prot->owner)) -		cp = NULL; -	spin_unlock(&proto_tab_lock); -  	/* check for available protocol and correct usage */  	if (!cp)  		return -EPROTONOSUPPORT;  	if (cp->type != sock->type) { -		err = -EPROTONOSUPPORT; +		err = -EPROTOTYPE;  		goto errout;  	} @@ -183,7 +200,7 @@ static int can_create(struct net *net, struct socket *sock, int protocol,  	}   errout: -	module_put(cp->prot->owner); +	can_put_proto(cp);  	return err;  } @@ -679,7 +696,7 @@ drop:   *  -EBUSY  protocol already in use   *  -ENOBUF if proto_register() fails   */ -int can_proto_register(struct can_proto *cp) +int can_proto_register(const struct can_proto *cp)  {  	int proto = cp->protocol;  	int err = 0; @@ -694,15 +711,16 @@ int can_proto_register(struct can_proto *cp)  	if (err < 0)  		return err; -	spin_lock(&proto_tab_lock); +	mutex_lock(&proto_tab_lock); +  	if (proto_tab[proto]) {  		printk(KERN_ERR "can: protocol %d already registered\n",  		       proto);  		err = -EBUSY;  	} else -		proto_tab[proto] = cp; +		rcu_assign_pointer(proto_tab[proto], cp); -	spin_unlock(&proto_tab_lock); +	mutex_unlock(&proto_tab_lock);  	if (err < 0)  		proto_unregister(cp->prot); @@ -715,17 +733,16 @@ EXPORT_SYMBOL(can_proto_register);   * can_proto_unregister - unregister CAN transport protocol   * @cp: pointer to CAN protocol structure   */ -void can_proto_unregister(struct can_proto *cp) +void can_proto_unregister(const struct can_proto *cp)  {  	int proto = cp->protocol; -	spin_lock(&proto_tab_lock); -	if (!proto_tab[proto]) { -		printk(KERN_ERR "BUG: can: protocol %d is not registered\n", -		       proto); -	} -	proto_tab[proto] = NULL; -	spin_unlock(&proto_tab_lock); +	mutex_lock(&proto_tab_lock); +	BUG_ON(proto_tab[proto] != cp); +	rcu_assign_pointer(proto_tab[proto], NULL); +	mutex_unlock(&proto_tab_lock); + +	synchronize_rcu();  	proto_unregister(cp->prot);  }  |